diff --git a/drivers/net/wireless/rockchip_wlan/Kconfig b/drivers/net/wireless/rockchip_wlan/Kconfig index c5bcbf5ebb6f..316555a1ee4f 100644 --- a/drivers/net/wireless/rockchip_wlan/Kconfig +++ b/drivers/net/wireless/rockchip_wlan/Kconfig @@ -37,5 +37,6 @@ source "drivers/net/wireless/rockchip_wlan/rtl8723bu/Kconfig" source "drivers/net/wireless/rockchip_wlan/rtl8723cs/Kconfig" source "drivers/net/wireless/rockchip_wlan/rtl8723ds/Kconfig" source "drivers/net/wireless/rockchip_wlan/rtl8822be/Kconfig" +source "drivers/net/wireless/rockchip_wlan/mvl88w8977/Kconfig" endif # WL_ROCKCHIP diff --git a/drivers/net/wireless/rockchip_wlan/Makefile b/drivers/net/wireless/rockchip_wlan/Makefile index a941feedba49..d1aebf7d6bf3 100644 --- a/drivers/net/wireless/rockchip_wlan/Makefile +++ b/drivers/net/wireless/rockchip_wlan/Makefile @@ -9,5 +9,6 @@ obj-$(CONFIG_RTL8723BU) += rtl8723bu/ obj-$(CONFIG_RTL8723CS) += rtl8723cs/ obj-$(CONFIG_RTL8723DS) += rtl8723ds/ obj-$(CONFIG_RTL8822BE) += rtl8822be/ +obj-$(CONFIG_MVL88W8977) += mvl88w8977/ obj-$(CONFIG_WL_ROCKCHIP) += wifi_sys/rkwifi_sys_iface.o obj-$(CONFIG_WL_ROCKCHIP) += rkwifi/rk_wifi_config.o diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/Kconfig b/drivers/net/wireless/rockchip_wlan/mvl88w8977/Kconfig new file mode 100644 index 000000000000..4d323212ef2a --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/Kconfig @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0 +config MVL88W8977 + tristate "Marvell 88W8977 SDIO WiFi" + ---help--- + Help message of MVL88W8977 diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/Makefile b/drivers/net/wireless/rockchip_wlan/mvl88w8977/Makefile new file mode 100644 index 000000000000..9aabb31026c3 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/Makefile @@ -0,0 +1,567 @@ +# File: Makefile +# +# Copyright (C) 2008-2017, Marvell International Ltd. +# +# This software file (the "File") is distributed by Marvell International +# Ltd. under the terms of the GNU General Public License Version 2, June 1991 +# (the "License"). You may use, redistribute and/or modify this File in +# accordance with the terms and conditions of the License, a copy of which +# is available by writing to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the +# worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. +# +# A copy of the GPL is available in file gpl-2.0.txt accompanying in this +# deliverables. +# +# THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE +# IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE +# ARE EXPRESSLY DISCLAIMED. The License provides additional details about +# this warranty disclaimer. + +COMPATDIR=/lib/modules/$(KERNELVERSION_X86)/build/compat-wireless-3.2-rc1-1/include +CC= $(CROSS_COMPILE)gcc -I$(COMPATDIR) +LD= $(CROSS_COMPILE)ld +BACKUP= /root/backup +YMD= `date +%Y%m%d%H%M` + +ifneq ($(COMPAT_VERSION_CODE),) +DRV_DIR ?= $(shell pwd) +export DRV_DIR +COMPAT_VERSION=$(shell echo $(COMPAT_VERSION_CODE) | awk -F '.' '{print $$1}') +COMPAT_PATCHLEVEL=$(shell echo $(COMPAT_VERSION_CODE) | awk -F '.' '{print $$2}') +COMPAT_SUBLEVEL=$(shell echo $(COMPAT_VERSION_CODE) | awk -F '.' '{print $$3}') +DECL_HEADER_FILE=$(DRV_DIR)/mlinux/moal_main.h +$(shell sed -i 's/COMPAT_VERSION_CODE KERNEL_VERSION.*/COMPAT_VERSION_CODE KERNEL_VERSION(\ + $(COMPAT_VERSION), $(COMPAT_PATCHLEVEL), $(COMPAT_SUBLEVEL))/g' $(DECL_HEADER_FILE)) +endif + +############################################################################# +# Configuration Options +############################################################################# + +# Debug Option +# DEBUG LEVEL n/1/2: +# n: NO DEBUG +# 1: Only PRINTM(MMSG,...), PRINTM(MFATAL,...), ... +# 2: All PRINTM() +CONFIG_DEBUG=1 + +# Proc debug file +CONFIG_PROC_DEBUG=y + +# Enable STA mode support +CONFIG_STA_SUPPORT=y + +# Enable uAP mode support +CONFIG_UAP_SUPPORT=y + +# Enable WIFIDIRECT support +CONFIG_WIFI_DIRECT_SUPPORT=y + +# Enable WIFIDISPLAY support +CONFIG_WIFI_DISPLAY_SUPPORT=y + +# Re-association in driver +CONFIG_REASSOCIATION=y + + +# Manufacturing firmware support +CONFIG_MFG_CMD_SUPPORT=y + +# OpenWrt support +CONFIG_OPENWRT_SUPPORT=n + +# Big-endian platform +CONFIG_BIG_ENDIAN=n + + +# Enable driver based authenticator +CONFIG_DRV_EMBEDDED_AUTHENTICATOR=y + +# Enable driver based supplicant +CONFIG_DRV_EMBEDDED_SUPPLICANT=y + +ifeq ($(CONFIG_DRV_EMBEDDED_SUPPLICANT), y) +CONFIG_EMBEDDED_SUPP_AUTH=y +else +ifeq ($(CONFIG_DRV_EMBEDDED_AUTHENTICATOR), y) +CONFIG_EMBEDDED_SUPP_AUTH=y +endif +endif + +# Enable SDIO multi-port Tx aggregation +CONFIG_SDIO_MULTI_PORT_TX_AGGR=y + +# Enable SDIO multi-port Rx aggregation +CONFIG_SDIO_MULTI_PORT_RX_AGGR=y + +# SDIO suspend/resume +CONFIG_SDIO_SUSPEND_RESUME=y + +# DFS testing support +CONFIG_DFS_TESTING_SUPPORT=y + +# Multi-channel support +CONFIG_MULTI_CHAN_SUPPORT=y + + +CONFIG_ANDROID_KERNEL=y + + +#32bit app over 64bit kernel support +CONFIG_USERSPACE_32BIT_OVER_KERNEL_64BIT=n + +############################################################################# +# Select Platform Tools +############################################################################# +KERNELDIR := ./../../../../../ +CURRENT_DIR := $(shell pwd) +MODEXT = ko + +ccflags-y += -I$(M)/mlan +ccflags-y += -DLINUX + +ccflags-y += -Idrivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/ +ccflags-y += -Idrivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux +ccflags-y += -Idrivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa +ccflags-y += -Idrivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common + +ifeq ($(CONFIG_EMBEDDED_SUPP_AUTH), y) +ccflags-y += -I$(M)/mlan/esa +ccflags-y += -I$(M)/mlan/esa/common +endif + +LD += -S + +BINDIR = ../bin_sd8977 + +############################################################################# +# Compiler Flags +############################################################################# + + ccflags-y += -I$(KERNELDIR)/include + ccflags-y += -I$(PWD)/mlan/esa + ccflags-y += -I$(PWD)/mlan/esa/common + + ccflags-y += -DFPNUM='"68"' + +ifeq ($(CONFIG_DEBUG),1) + ccflags-y += -DDEBUG_LEVEL1 +endif + +ifeq ($(CONFIG_DEBUG),2) + ccflags-y += -DDEBUG_LEVEL1 + ccflags-y += -DDEBUG_LEVEL2 + DBG= -dbg +endif + +ifeq ($(CONFIG_PROC_DEBUG),y) + ccflags-y += -DPROC_DEBUG + export CONFIG_PROC_DEBUG +endif + +ifeq ($(CONFIG_64BIT), y) + ccflags-y += -DMLAN_64BIT +endif + +ifeq ($(CONFIG_STA_SUPPORT),y) + ccflags-y += -DSTA_SUPPORT +ifeq ($(CONFIG_REASSOCIATION),y) + ccflags-y += -DREASSOCIATION +endif +else +CONFIG_WIFI_DIRECT_SUPPORT=n +CONFIG_WIFI_DISPLAY_SUPPORT=n +CONFIG_STA_WEXT=n +CONFIG_STA_CFG80211=n +endif + +ifeq ($(CONFIG_UAP_SUPPORT),y) + ccflags-y += -DUAP_SUPPORT +else +CONFIG_WIFI_DIRECT_SUPPORT=n +CONFIG_WIFI_DISPLAY_SUPPORT=n +CONFIG_UAP_WEXT=n +CONFIG_UAP_CFG80211=n +endif + +ifeq ($(CONFIG_WIFI_DIRECT_SUPPORT),y) + ccflags-y += -DWIFI_DIRECT_SUPPORT +endif +ifeq ($(CONFIG_WIFI_DISPLAY_SUPPORT),y) + ccflags-y += -DWIFI_DISPLAY_SUPPORT +endif + +ifeq ($(CONFIG_MFG_CMD_SUPPORT),y) + ccflags-y += -DMFG_CMD_SUPPORT +endif + +ifeq ($(CONFIG_BIG_ENDIAN),y) + ccflags-y += -DBIG_ENDIAN_SUPPORT +endif + +ifeq ($(CONFIG_USERSPACE_32BIT_OVER_KERNEL_64BIT),y) + ccflags-y += -DUSERSPACE_32BIT_OVER_KERNEL_64BIT +endif + +ifeq ($(CONFIG_SDIO_MULTI_PORT_TX_AGGR),y) + ccflags-y += -DSDIO_MULTI_PORT_TX_AGGR +endif + +ifeq ($(CONFIG_SDIO_MULTI_PORT_RX_AGGR),y) + ccflags-y += -DSDIO_MULTI_PORT_RX_AGGR +endif + +ifeq ($(CONFIG_SDIO_SUSPEND_RESUME),y) + ccflags-y += -DSDIO_SUSPEND_RESUME +endif + +ifeq ($(CONFIG_MULTI_CHAN_SUPPORT),y) + ccflags-y += -DMULTI_CHAN_SUPPORT +endif + +ifeq ($(CONFIG_DFS_TESTING_SUPPORT),y) + ccflags-y += -DDFS_TESTING_SUPPORT +endif + + +ifeq ($(CONFIG_ANDROID_KERNEL), y) + ccflags-y += -DANDROID_KERNEL +endif + +ifeq ($(CONFIG_OPENWRT_SUPPORT), y) + ccflags-y += -DOPENWRT +endif + + +ifeq ($(CONFIG_T50), y) + ccflags-y += -DT50 + ccflags-y += -DT40 + ccflags-y += -DT3T +endif + +# add -Wno-packed-bitfield-compat when GCC version greater than 4.4 +GCC_VERSION := $(shell echo `gcc -dumpversion | cut -f1-2 -d.` \>= 4.4 | sed -e 's/\./*100+/g' | bc ) +ifeq ($(GCC_VERSION),1) + ccflags-y += -Wno-packed-bitfield-compat +endif + +############################################################################# +# Make Targets +############################################################################# + +ifneq ($(KERNELRELEASE),) + +ifeq ($(CONFIG_WIRELESS_EXT),y) +ifeq ($(CONFIG_WEXT_PRIV),y) + # Enable WEXT for STA + CONFIG_STA_WEXT=y + # Enable WEXT for uAP + CONFIG_UAP_WEXT=y +else +# Disable WEXT for STA + CONFIG_STA_WEXT=n +# Disable WEXT for uAP + CONFIG_UAP_WEXT=n +endif +endif + +# Enable CFG80211 for STA +ifeq ($(CONFIG_CFG80211),y) + CONFIG_STA_CFG80211=y +else +ifeq ($(CONFIG_CFG80211),m) + CONFIG_STA_CFG80211=y +else + CONFIG_STA_CFG80211=n +endif +endif + +# OpenWrt +ifeq ($(CONFIG_OPENWRT_SUPPORT), y) +ifeq ($(CPTCFG_CFG80211),y) + CONFIG_STA_CFG80211=y +else +ifeq ($(CPTCFG_CFG80211),m) + CONFIG_STA_CFG80211=y +else + CONFIG_STA_CFG80211=n +endif +endif +endif + +# Enable CFG80211 for uAP +ifeq ($(CONFIG_CFG80211),y) + CONFIG_UAP_CFG80211=y +else +ifeq ($(CONFIG_CFG80211),m) + CONFIG_UAP_CFG80211=y +else + CONFIG_UAP_CFG80211=n +endif +endif + +# OpenWrt +ifeq ($(CONFIG_OPENWRT_SUPPORT), y) +ifeq ($(CPTCFG_CFG80211),y) + CONFIG_UAP_CFG80211=y +else +ifeq ($(CPTCFG_CFG80211),m) + CONFIG_UAP_CFG80211=y +else + CONFIG_UAP_CFG80211=n +endif +endif +endif + +ifneq ($(CONFIG_STA_SUPPORT),y) + CONFIG_WIFI_DIRECT_SUPPORT=n + CONFIG_WIFI_DISPLAY_SUPPORT=n + CONFIG_STA_WEXT=n + CONFIG_STA_CFG80211=n +endif + +ifneq ($(CONFIG_UAP_SUPPORT),y) + CONFIG_WIFI_DIRECT_SUPPORT=n + CONFIG_WIFI_DISPLAY_SUPPORT=n + CONFIG_UAP_WEXT=n + CONFIG_UAP_CFG80211=n +endif + +ifeq ($(CONFIG_STA_SUPPORT),y) +ifeq ($(CONFIG_STA_WEXT),y) + ccflags-y += -DSTA_WEXT +endif +ifeq ($(CONFIG_STA_CFG80211),y) + ccflags-y += -DSTA_CFG80211 +endif +endif +ifeq ($(CONFIG_UAP_SUPPORT),y) +ifeq ($(CONFIG_UAP_WEXT),y) + ccflags-y += -DUAP_WEXT +endif +ifeq ($(CONFIG_UAP_CFG80211),y) + ccflags-y += -DUAP_CFG80211 +endif +endif + +print: +ifeq ($(CONFIG_STA_SUPPORT),y) +ifeq ($(CONFIG_STA_WEXT),n) +ifeq ($(CONFIG_STA_CFG80211),n) + @echo "Can not build STA without WEXT or CFG80211" + exit 2 +endif +endif +endif +ifeq ($(CONFIG_UAP_SUPPORT),y) +ifeq ($(CONFIG_UAP_WEXT),n) +ifeq ($(CONFIG_UAP_CFG80211),n) + @echo "Can not build UAP without WEXT or CFG80211" + exit 2 +endif +endif +endif + + +ifeq ($(CONFIG_DRV_EMBEDDED_AUTHENTICATOR), y) + ccflags-y += -DDRV_EMBEDDED_AUTHENTICATOR +endif + +ifeq ($(CONFIG_DRV_EMBEDDED_SUPPLICANT), y) + ccflags-y += -DDRV_EMBEDDED_SUPPLICANT +endif + + +MOALOBJS = mlinux/moal_main.o \ + mlinux/moal_ioctl.o \ + mlinux/moal_shim.o \ + mlinux/moal_eth_ioctl.o + +MLANOBJS = mlan/mlan_shim.o mlan/mlan_init.o \ + mlan/mlan_txrx.o \ + mlan/mlan_cmdevt.o mlan/mlan_misc.o \ + mlan/mlan_cfp.o \ + mlan/mlan_module.o + +MLANOBJS += mlan/mlan_wmm.o +MLANOBJS += mlan/mlan_sdio.o +MLANOBJS += mlan/mlan_11n_aggr.o +MLANOBJS += mlan/mlan_11n_rxreorder.o +MLANOBJS += mlan/mlan_11n.o +MLANOBJS += mlan/mlan_11d.o +MLANOBJS += mlan/mlan_11h.o +ifeq ($(CONFIG_STA_SUPPORT),y) +MLANOBJS += mlan/mlan_meas.o +MLANOBJS += mlan/mlan_scan.o \ + mlan/mlan_sta_ioctl.o \ + mlan/mlan_sta_rx.o \ + mlan/mlan_sta_tx.o \ + mlan/mlan_sta_event.o \ + mlan/mlan_sta_cmd.o \ + mlan/mlan_sta_cmdresp.o \ + mlan/mlan_join.o +ifeq ($(CONFIG_STA_WEXT),y) +MOALOBJS += mlinux/moal_priv.o \ + mlinux/moal_wext.o +endif +endif +ifeq ($(CONFIG_UAP_SUPPORT),y) +MLANOBJS += mlan/mlan_uap_ioctl.o +MLANOBJS += mlan/mlan_uap_cmdevent.o +MLANOBJS += mlan/mlan_uap_txrx.o +MOALOBJS += mlinux/moal_uap.o +ifeq ($(CONFIG_UAP_WEXT),y) +MOALOBJS += mlinux/moal_uap_priv.o +MOALOBJS += mlinux/moal_uap_wext.o +endif +endif +ifeq ($(CONFIG_STA_CFG80211),y) +MOALOBJS += mlinux/moal_cfg80211.o +MOALOBJS += mlinux/moal_cfgvendor.o +MOALOBJS += mlinux/moal_sta_cfg80211.o +endif +ifeq ($(CONFIG_UAP_CFG80211),y) +MOALOBJS += mlinux/moal_cfg80211.o +MOALOBJS += mlinux/moal_cfgvendor.o +MOALOBJS += mlinux/moal_uap_cfg80211.o +endif + +ifdef CONFIG_PROC_FS +MOALOBJS += mlinux/moal_proc.o +ifeq ($(CONFIG_PROC_DEBUG),y) +MOALOBJS += mlinux/moal_debug.o +endif +endif + + + + +ifeq ($(CONFIG_EMBEDDED_SUPP_AUTH), y) +MLANOBJS += mlan/esa/common/rc4.o \ + mlan/esa/common/aes_cmac_rom.o \ + mlan/esa/common/hmac_sha1.o \ + mlan/esa/common/md5.o \ + mlan/esa/common/mrvl_sha256_crypto.o \ + mlan/esa/common/hmac_md5.o \ + mlan/esa/common/crypt_new_rom.o \ + mlan/esa/common/rijndael.o \ + mlan/esa/common/sha1.o \ + mlan/esa/common/sha256.o \ + mlan/esa/common/pass_phrase.o \ + mlan/esa/common/pmkCache.o \ + mlan/esa/common/pmkCache_rom.o \ + mlan/esa/common/parser.o \ + mlan/esa/common/parser_rom.o \ + mlan/esa/keyMgmtApStaCommon.o \ + mlan/esa/hostsa_init.o \ + mlan/esa/authenticator_api.o +endif + + +ifeq ($(CONFIG_DRV_EMBEDDED_SUPPLICANT),y) +MLANOBJS += mlan/esa/keyMgmtSta.o \ + mlan/esa/keyMgmtSta_rom.o \ + mlan/esa/supplicant.o +endif + +ifeq ($(CONFIG_DRV_EMBEDDED_AUTHENTICATOR),y) +MLANOBJS += mlan/esa/AssocAp_srv_rom.o \ + mlan/esa/keyMgmtAp_rom.o \ + mlan/esa/keyMgmtAp.o +endif + +ifeq ($(CONFIG_MULTI_INTERFACE),y) +obj-m := mlan_sdio.o +mlan_sdio-objs := $(MLANOBJS) +else +obj-m := mlan.o +mlan-objs := $(MLANOBJS) +endif +MOALOBJS += mlinux/moal_sdio_mmc.o +obj-m += sd8xxx.o +sd8xxx-objs := $(MOALOBJS) + +# Otherwise we were called directly from the command line; invoke the kernel build system. +else + +default: + $(MAKE) -C $(KERNELDIR) M=$(PWD) modules + +endif + +############################################################### + +export CC LD ccflags-y KERNELDIR + +echo: + +build: echo default + + @if [ ! -d $(BINDIR) ]; then \ + mkdir $(BINDIR); \ + fi + +ifeq ($(CONFIG_MULTI_INTERFACE),y) + cp -f mlan_sdio.$(MODEXT) $(BINDIR)/mlan_sdio$(DBG).$(MODEXT) +else + cp -f mlan.$(MODEXT) $(BINDIR)/mlan$(DBG).$(MODEXT) +endif + cp -f sd8xxx.$(MODEXT) $(BINDIR)/sd8977$(DBG).$(MODEXT) + cp -rpf script/sdio_mmc/* $(BINDIR)/ + +ifeq ($(CONFIG_STA_SUPPORT),y) + cp -f README $(BINDIR) + cp -f README_MLAN $(BINDIR) +ifeq ($(CONFIG_OPENWRT_SUPPORT),y) + cp -f README_OPENWRT $(BINDIR) +endif +endif +ifeq ($(CONFIG_UAP_SUPPORT),y) + cp -f README_UAP $(BINDIR) +endif +ifeq ($(CONFIG_WIFI_DIRECT_SUPPORT),y) + cp -f README_WIFIDIRECT $(BINDIR) + cp -rpf script/wifidirect $(BINDIR) +ifeq ($(CONFIG_WIFI_DISPLAY_SUPPORT),y) + cp -rpf script/wifidisplay $(BINDIR) +endif +endif + +clean: + -find . -name "*.o" -exec rm {} \; + -find . -name "*.ko" -exec rm {} \; + -find . -name ".*.cmd" -exec rm {} \; + -find . -name "*.mod.c" -exec rm {} \; + -find . -name "Module.symvers" -exec rm {} \; + -find . -name "Module.markers" -exec rm {} \; + -find . -name "modules.order" -exec rm {} \; + -find . -name ".*.dwo" -exec rm {} \; + -find . -name "*dwo" -exec rm {} \; + -rm -rf .tmp_versions + +install: default + + cp -f mlan.$(MODEXT) $(INSTALLDIR)/mlan$(DBG).$(MODEXT) + cp -f ../io/sdio/$(PLATFORM)/sdio.$(MODEXT) $(INSTALLDIR) + cp -f sd8xxx.$(MODEXT) $(INSTALLDIR)/sd8977$(DBG).$(MODEXT) + echo "sd8977 Driver Installed" + +distclean: + -find . -name "*.o" -exec rm {} \; + -find . -name "*.orig" -exec rm {} \; + -find . -name "*.swp" -exec rm {} \; + -find . -name "*.*~" -exec rm {} \; + -find . -name "*~" -exec rm {} \; + -find . -name "*.d" -exec rm {} \; + -find . -name "*.a" -exec rm {} \; + -find . -name "tags" -exec rm {} \; + -find . -name ".*" -exec rm -rf 2> /dev/null \; + -find . -name "*.ko" -exec rm {} \; + -find . -name ".*.cmd" -exec rm {} \; + -find . -name "*.mod.c" -exec rm {} \; + -find . -name ".*.dwo" -exec rm {} \; + -find . -name "*dwo" -exec rm {} \; + -rm -rf .tmp_versions + +# End of file diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/AssocAp_srv_rom.c b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/AssocAp_srv_rom.c new file mode 100644 index 000000000000..d2c1268c245d --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/AssocAp_srv_rom.c @@ -0,0 +1,356 @@ +/** @file AssocAp_src_rom.c + * + * @brief This file defines the function for checking security type and ie + * + * Copyright (C) 2014-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 03/07/2014: Initial version +******************************************************/ +#include "wltypes.h" +#include "hostsa_ext_def.h" +#include "IEEE_types.h" + +#include "authenticator.h" +#include "AssocAp_srv_rom.h" +#include "parser.h" +#include "keyMgmtAp.h" + +BOOLEAN +AssocSrvAp_checkCipherSupport(Cipher_t cipher, Cipher_t allowedCiphers) +{ + BOOLEAN match = FALSE; + + if (cipher.ccmp && (allowedCiphers.ccmp)) { + match = TRUE; + } else if (cipher.tkip && (allowedCiphers.tkip)) { + match = TRUE; + } else if (cipher.wep40 && (allowedCiphers.wep40)) { + match = TRUE; + } else if (cipher.wep104 && (allowedCiphers.wep104)) { + match = TRUE; + } + + return match; +} + +UINT16 +AssocSrvAp_checkAkm(phostsa_private priv, AkmSuite_t *pAkm, UINT16 allowedAkms) +{ + hostsa_util_fns *util_fns = &priv->util_fns; + UINT16 matchedAkms; + + matchedAkms = 0; + + if ((memcmp(util_fns, pAkm->akmOui, wpa_oui, sizeof(wpa_oui)) != 0) && + ((memcmp(util_fns, pAkm->akmOui, kde_oui, sizeof(kde_oui)) != 0))) { + return matchedAkms; + } + + switch (pAkm->akmType) { + case AKM_1X: + matchedAkms = (allowedAkms & UAP_HOSTCMD_KEYMGMT_EAP); + break; + + case AKM_PSK: + matchedAkms = (allowedAkms & UAP_HOSTCMD_KEYMGMT_PSK); + break; + case AKM_SHA256_PSK: + matchedAkms = (allowedAkms & UAP_HOSTCMD_KEYMGMT_PSK_SHA256); + break; + default: + break; + } + + return matchedAkms; +} + +WL_STATUS +assocSrvAp_validate4WayHandshakeIe(phostsa_private priv, + SecurityMode_t secType, + Cipher_t pwCipher, + Cipher_t grpCipher, + apKeyMgmtInfoStaRom_t *pKeyMgmtInfo, + UINT8 akmType, + UINT16 rsnCap, Cipher_t config_mcstCipher) +{ + hostsa_util_fns *util_fns = &priv->util_fns; + + if (memcmp + (util_fns, (void *)&secType, (void *)&pKeyMgmtInfo->staSecType, + sizeof(secType)) != 0) { + return FAIL; + } + + if (memcmp(util_fns, (void *)&grpCipher, + (void *)&config_mcstCipher, sizeof(grpCipher)) != 0) { + return FAIL; + } + + if (memcmp + (util_fns, (void *)&pwCipher, (void *)&pKeyMgmtInfo->staUcstCipher, + sizeof(pwCipher)) != 0) { + return FAIL; + } + + if (akmType != pKeyMgmtInfo->staAkmType) { + + return FAIL; + } + + return SUCCESS; +} + +void +AssocSrvAp_InitKeyMgmtInfo(phostsa_private priv, + apKeyMgmtInfoStaRom_t *pKeyMgmtInfo, + SecurityMode_t *secType, Cipher_t *pwCipher, + UINT16 staRsnCap, UINT8 akmType) +{ + hostsa_util_fns *util_fns = &priv->util_fns; + pKeyMgmtInfo->keyMgmtState = HSK_NOT_STARTED; + memcpy(util_fns, (void *)&pKeyMgmtInfo->staSecType, (void *)secType, + sizeof(SecurityMode_t)); + memcpy(util_fns, (void *)&pKeyMgmtInfo->staUcstCipher, (void *)pwCipher, + sizeof(Cipher_t)); + pKeyMgmtInfo->staAkmType = akmType; + if (secType->wpa2) { + pKeyMgmtInfo->staRsnCap = staRsnCap; + } +} + +void +AssocSrvAp_InitStaKeyInfo(cm_Connection *connPtr, + SecurityMode_t *secType, + Cipher_t *pwCipher, UINT16 staRsnCap, UINT8 akmType) +{ + apKeyMgmtInfoSta_t *pKeyMgmtInfo; + + phostsa_private priv = (phostsa_private)connPtr->priv; + hostsa_util_fns *util_fns = &priv->util_fns; + + KeyMgmtStopHskTimer(connPtr); + + pKeyMgmtInfo = &connPtr->staData.keyMgmtInfo; + memset(util_fns, (void *)pKeyMgmtInfo, 0x00, + sizeof(apKeyMgmtInfoSta_t)); + AssocSrvAp_InitKeyMgmtInfo(priv, &pKeyMgmtInfo->rom, secType, pwCipher, + staRsnCap, akmType); + pKeyMgmtInfo->EAPOLProtoVersion = EAPOL_PROTOCOL_V1; +} + +WL_STATUS +assocSrvAp_checkRsnWpa(cm_Connection *connPtr, + apKeyMgmtInfoStaRom_t *pKeyMgmtInfo, + Cipher_t apWpaCipher, + Cipher_t apWpa2Cipher, + Cipher_t apMcstCipher, + UINT16 apAuthKey, + SecurityMode_t *pSecType, + IEEEtypes_RSNElement_t *pRsn, + IEEEtypes_WPAElement_t *pWpa, + BOOLEAN validate4WayHandshakeIE) +{ + phostsa_private priv = (phostsa_private)connPtr->priv; + hostsa_util_fns *util_fns = &priv->util_fns; + WL_STATUS result = SUCCESS; + Cipher_t apCipher; + Cipher_t pwCipher; + Cipher_t grpCipher; + SecurityMode_t wpaType; + AkmSuite_t akm[AKM_SUITE_MAX]; + UINT8 minimumRsnLen; + + /* staRsnCap field is only used to compare RsnCap received in AssocRequest + with rsnCap received in 4 way handshake PWKMsg2. + + we use 0xFFFF signature. If rsnCap is not present in pRsn, + signature 0xFFFF would be saved in pKeyMgmtInfo->staRsnCap. + */ + + union { + UINT16 shortInt; + IEEEtypes_RSNCapability_t cfg; + } staRsnCap; + + memset(util_fns, &wpaType, 0x00, sizeof(wpaType)); + memset(util_fns, &apCipher, 0x00, sizeof(apCipher)); + memset(util_fns, &pwCipher, 0x00, sizeof(pwCipher)); + memset(util_fns, &grpCipher, 0x00, sizeof(grpCipher)); + staRsnCap.shortInt = 0xFFFF; + + if (pRsn && (pSecType->wpa2 == 1)) { + + /* + In pRsn , All elements after Ver field are optional per the spec. + + we reject Assoc Request, if GrpKeyCipher, pwsKey and AuthKey + is not present. + + we can rely on minimum length check as we are rejecting Assoc Request + having pwsKeyCnt > 1 and AuthKeyCnt > 1 + + */ + + minimumRsnLen = (unsigned long)&pRsn->RsnCap - + (unsigned long)&pRsn->Ver; + + if (pRsn->Len < minimumRsnLen) { + PRINTM(MERROR, "pRsn->Len %x < minimumRsnLen %x\n", + pRsn->Len, minimumRsnLen); + return FAIL; + } + + if (pRsn->PwsKeyCnt == 1 && pRsn->AuthKeyCnt == 1) { + apCipher = apWpa2Cipher; + + supplicantParseRsnIe(priv, pRsn, + &wpaType, + &grpCipher, + &pwCipher, + akm, + NELEMENTS(akm), + &staRsnCap.cfg, NULL); + } else { + PRINTM(MERROR, + "pRsn->PwsKeyCnt %x pRsn->AuthKeyCnt %x\n", + pRsn->PwsKeyCnt, pRsn->AuthKeyCnt); + result = FAIL; + } + } else if (pWpa && (pSecType->wpa == 1)) { + if (pWpa->PwsKeyCnt == 1 && pWpa->AuthKeyCnt == 1) { + apCipher = apWpaCipher; + supplicantParseWpaIe(priv, pWpa, + &wpaType, + &grpCipher, + &pwCipher, akm, NELEMENTS(akm)); + } else { + PRINTM(MERROR, + "pWpa->PwsKeyCnt %x pWpa->AuthKeyCnt %x\n", + pWpa->PwsKeyCnt, pWpa->AuthKeyCnt); + result = FAIL; + } + } else { + PRINTM(MERROR, "No wpa or rsn\n"); + result = FAIL; + } + + if ((pwCipher.ccmp == 0) && (pwCipher.tkip == 0)) { + PRINTM(MERROR, + "(pwCipher.ccmp(%x) == 0) && (pwCipher.tkip(%x) == 0)\n", + pwCipher.ccmp, pwCipher.tkip); + result = FAIL; + } + + if ((grpCipher.ccmp == 0) && (grpCipher.tkip == 0)) { + PRINTM(MERROR, + "((grpCipher.ccmp(%x) == 0) && (grpCipher.tkip(%x) == 0))\n", + grpCipher.ccmp, grpCipher.tkip); + result = FAIL; + } + DBG_HEXDUMP(MCMD_D, " akm", (t_u8 *)&akm[0], sizeof(AkmSuite_t)); + if (SUCCESS == result) { +#ifdef DOT11W + if (staRsnCap.shortInt != 0xFFFF) { + /* Save the peer STA PMF capability, which will later used to enable PMF */ + connPtr->staData.peerPMFCapable = staRsnCap.cfg.MFPC; + } +#endif + if (validate4WayHandshakeIE == MFALSE) { + if ((AssocSrvAp_checkCipherSupport(pwCipher, apCipher) + == TRUE) && + (AssocSrvAp_checkCipherSupport + (grpCipher, apMcstCipher) + == TRUE) && + (AssocSrvAp_checkAkm(priv, akm, apAuthKey) != 0)) { + AssocSrvAp_InitStaKeyInfo(connPtr, &wpaType, + &pwCipher, + staRsnCap.shortInt, + akm[0].akmType); + } else { + result = FAIL; + } + } else { + result = assocSrvAp_validate4WayHandshakeIe(priv, + wpaType, + pwCipher, + grpCipher, + pKeyMgmtInfo, + akm[0]. + akmType, + staRsnCap. + shortInt, + apMcstCipher); + } + } + return result; +} + +SINT32 +assocSrvAp_CheckSecurity(cm_Connection *connPtr, + IEEEtypes_WPSElement_t *pWps, + IEEEtypes_RSNElement_t *pRsn, + IEEEtypes_WPAElement_t *pWpa, + IEEEtypes_WAPIElement_t *pWapi, + IEEEtypes_StatusCode_t *pResult) +{ + phostsa_private priv = (phostsa_private)connPtr->priv; + apInfo_t *pApInfo = &priv->apinfo; + BssConfig_t *pBssConfig = NULL; + SINT32 retval = MLME_FAILURE; + + *pResult = IEEEtypes_STATUS_INVALID_RSN_CAPABILITIES; + + pBssConfig = &pApInfo->bssConfig; + + PRINTM(MMSG, "assocSrvAp_CheckSecurity Sectyep wpa %x wpa2 %x\n", + pBssConfig->SecType.wpa, pBssConfig->SecType.wpa2); + if ((pBssConfig->SecType.wpa == 1) || (pBssConfig->SecType.wpa2 == 1)) { + apKeyMgmtInfoSta_t *pKeyMgmtInfo = + &connPtr->staData.keyMgmtInfo; + Cipher_t wpaUcastCipher = pBssConfig->RsnConfig.wpaUcstCipher; + Cipher_t wpa2UcastCipher = pBssConfig->RsnConfig.wpa2UcstCipher; + DBG_HEXDUMP(MCMD_D, " wpa2UcstCipher", + (t_u8 *)&wpa2UcastCipher, sizeof(Cipher_t)); + DBG_HEXDUMP(MCMD_D, " wpaUcastCipher", + (t_u8 *)&wpaUcastCipher, sizeof(Cipher_t)); + connPtr->staData.RSNEnabled = 0; + if (assocSrvAp_checkRsnWpa(connPtr, &pKeyMgmtInfo->rom, + wpaUcastCipher, + wpa2UcastCipher, + pBssConfig->RsnConfig.mcstCipher, + pBssConfig->RsnConfig.AuthKey, + &pBssConfig->SecType, pRsn, pWpa, + MFALSE) == SUCCESS) { + retval = MLME_SUCCESS; + connPtr->staData.RSNEnabled = 1; + } + } else if (pBssConfig->SecType.wepStatic == 1) { + if (!pRsn || !pWpa) { + retval = MLME_SUCCESS; + } + } else if (pBssConfig->SecType.wapi) { + /* if (wapi_ie_check(pBssConfig, pWapi, pResult)) + { + *pResult = 0; + retval = MLME_SUCCESS; + } */ + } + + return retval; +} diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/AssocAp_srv_rom.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/AssocAp_srv_rom.h new file mode 100644 index 000000000000..b8dd85d466c1 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/AssocAp_srv_rom.h @@ -0,0 +1,51 @@ +/** @file AssocAp_src_rom.h + * + * @brief This file contains define check rsn/wpa ie + * + * Copyright (C) 2014-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 03/07/2014: Initial version +******************************************************/ +#ifndef ASSOCAP_SRV_ROM_H_ +#define ASSOCAP_SRV_ROM_H_ + +#include "wltypes.h" +#include "IEEE_types.h" +#include "keyMgmtStaTypes.h" +#include "keyMgmtApTypes_rom.h" +#include "authenticator.h" + +WL_STATUS assocSrvAp_checkRsnWpa(cm_Connection *connPtr, + apKeyMgmtInfoStaRom_t *pKeyMgmtInfo, + Cipher_t apWpaCipher, + Cipher_t apWpa2Cipher, + Cipher_t apMcstCipher, + UINT16 apAuthKey, + SecurityMode_t *pSecType, + IEEEtypes_RSNElement_t *pRsn, + IEEEtypes_WPAElement_t *pWpa, + BOOLEAN validate4WayHandshakeIE); + +SINT32 assocSrvAp_CheckSecurity(cm_Connection *connPtr, + IEEEtypes_WPSElement_t *pWps, + IEEEtypes_RSNElement_t *pRsn, + IEEEtypes_WPAElement_t *pWpa, + IEEEtypes_WAPIElement_t *pWapi, + IEEEtypes_StatusCode_t *pResult); +#endif diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/KeyApiStaDefs.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/KeyApiStaDefs.h new file mode 100644 index 000000000000..10cb561db33d --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/KeyApiStaDefs.h @@ -0,0 +1,126 @@ +/** @file KeyApiStaDefs.h + * + * @brief This file Contains data structures and defines related to key material. + * + * Copyright (C) 2014-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 03/07/2014: Initial version +******************************************************/ +#ifndef _KEY_API_STA_DEFS_H +#define _KEY_API_STA_DEFS_H +/******************************************************************/ +/*! + * \ingroup wpa_supplicant + * \file keyApiStaDefs.h + * \brief Contains data structures and defines related to key material + * + *******************************************************************/ + +/* Key Material for TKIP type key */ +#define CRYPTO_TKIP_KEY_LEN_MAX 16 +#define CRYPTO_KEY_LEN_MAX 16 +#define MIC_KEY_LEN_MAX 8 + +/* Key Material for AES type key */ +#define CRYPTO_AES_KEY_LEN_MAX 16 + +#define WAPI_KEY_LEN 16 +#define WAPI_MIC_LEN 16 +#define WAPI_PN_LEN 16 + +#define CRYPTO_AES_CMAC_KEY_LEN 16 +#define CRYPTO_AES_CMAC_IPN_LEN 6 +#define CRYPTO_WEP_KEY_LEN_MAX 13 + +/* + WIN7 softAP (uAP) : + BIT14 and BIT15 of keyInfo is used to pass keyID for broadcast frame +*/ + +#define KEYDATA_KEY_ID_MASK (BIT14 | BIT15) +#define KEYDATA_KEY_ID_OFFSET 14 + +#define KEY_INFO_MULTICAST 0x1 +#define KEY_INFO_UNICAST 0x2 +#define KEY_INFO_ENABLED 0x4 +#define KEY_INFO_DEFAULT 0x8 +#define KEY_INFO_TX 0x10 +#define KEY_INFO_RX 0x20 +#define KEY_INFO_MULTICAST_IGTK 0x400 + +typedef MLAN_PACK_START struct { + UINT8 key[CRYPTO_TKIP_KEY_LEN_MAX]; + UINT8 txMicKey[MIC_KEY_LEN_MAX]; + UINT8 rxMicKey[MIC_KEY_LEN_MAX]; +} MLAN_PACK_END key_Type_TKIP_t; + +/* This struct is used in ROM and existing fields should not be changed. + * New fields can be added at the end. + */ +typedef MLAN_PACK_START struct { + UINT8 keyIndex; + UINT8 isDefaultTx; + UINT8 key[CRYPTO_WEP_KEY_LEN_MAX]; +} MLAN_PACK_END key_Type_WEP_t; + +typedef MLAN_PACK_START struct { + UINT8 key[CRYPTO_AES_KEY_LEN_MAX]; +} MLAN_PACK_END key_Type_AES_t; + +typedef MLAN_PACK_START struct { + UINT8 keyIndex; + UINT8 isDefKey; + UINT8 key[WAPI_KEY_LEN]; + UINT8 mickey[WAPI_MIC_LEN]; + UINT8 rxPN[WAPI_PN_LEN]; +} MLAN_PACK_END key_Type_WAPI_t; + +typedef MLAN_PACK_START struct { + UINT8 ipn[CRYPTO_AES_CMAC_IPN_LEN]; + UINT8 reserved[2]; + UINT8 key[CRYPTO_AES_CMAC_KEY_LEN]; +} MLAN_PACK_END key_Type_AES_CMAC_t; + +typedef MLAN_PACK_START struct { + UINT16 keyType; + UINT16 keyInfo; + UINT16 keyLen; + MLAN_PACK_START union { + key_Type_TKIP_t TKIP; + key_Type_AES_t AES; + key_Type_WEP_t WEP; + +//#if defined(WAPI_HW_SUPPORT) || defined(WAPI_FW_SUPPORT) +// key_Type_WAPI_t WAPI; +//#endif + key_Type_AES_CMAC_t iGTK; + + } MLAN_PACK_END keyEncypt; +} MLAN_PACK_END key_MgtMaterial_t; + +/* deprecated but still around for backwards compatibility */ +#define KEY_INFO_TKIP_MULTICAST 0x1 +#define KEY_INFO_TKIP_UNICAST 0x2 +#define KEY_INFO_TKIP_ENABLED 0x4 + +#define KEY_INFO_AES_MULTICAST 0x1 +#define KEY_INFO_AES_UNICAST 0x2 +#define KEY_INFO_AES_ENABLED 0x4 + +#endif diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/authenticator.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/authenticator.h new file mode 100644 index 000000000000..6026d557ebf1 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/authenticator.h @@ -0,0 +1,367 @@ +/** @file authenticator.h + * + * @brief This file contains the data structure for authenticator and supplicant. + * + * Copyright (C) 2014-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 03/07/2014: Initial version +******************************************************/ +#ifndef _AUTHENTICATOR_H +#define _AUTHENTICATOR_H + +#include "wltypes.h" +#include "IEEE_types.h" +#include "wl_mib_rom.h" +#include "KeyApiStaDefs.h" +#include "keyApiStaTypes.h" +#include "keyCommonDef.h" +#include "keyMgmtApTypes.h" +#include "pmkCache_rom.h" + +#include "hostsa_def.h" + +extern const uint8 wpa_oui02[4]; /* WPA TKIP */ +extern const uint8 wpa_oui04[4]; /* WPA AES */ +extern const uint8 wpa_oui01[4]; /* WPA WEP-40 */ +extern const uint8 wpa_oui05[4]; /* WPA WEP-104 */ +extern const uint8 wpa_oui_none[4]; /* WPA NONE */ + +extern const uint8 wpa2_oui02[4]; /* WPA2 TKIP */ +extern const uint8 wpa2_oui04[4]; /* WPA2 AES */ +extern const uint8 wpa2_oui01[4]; /* WPA2 WEP-40 */ +extern const uint8 wpa2_oui05[4]; /* WPA2 WEP-104 */ + +extern const uint8 wpa_oui[3]; +extern const uint8 kde_oui[3]; + +typedef enum { + NO_MIC_FAILURE, + FIRST_MIC_FAIL_IN_60_SEC, + SECOND_MIC_FAIL_IN_60_SEC +} MIC_Fail_State_e; + +typedef struct { + MIC_Fail_State_e status; + BOOLEAN MICCounterMeasureEnabled; + UINT32 disableStaAsso; +} MIC_Error_t; + +typedef struct { + UINT8 TKIPICVErrors; + UINT8 TKIPLocalMICFailures; + UINT8 TKIPCounterMeasuresInvoked; + +} customMIB_RSNStats_t; + +typedef struct { + UINT8 kck[16]; /* PTK_KCK = L(PTK, 0, 128); */ + UINT8 kek[16]; /* PTK_KEK = L(PTK, 128, 128); */ + UINT8 tk[16]; /* PTK_TK = L(PTK, 256, 128); */ + +} CcmPtk_t; + +typedef struct { + UINT8 kck[16]; /* PTK_KCK = L(PTK, 0, 128); */ + UINT8 kek[16]; /* PTK_KEK = L(PTK, 128, 128); */ + UINT8 tk[16]; /* PTK_TK = L(PTK, 256, 128); */ + UINT8 rxMicKey[8]; + UINT8 txMicKey[8]; + +} TkipPtk_t; + +typedef struct { + MIC_Error_t apMicError; + t_void *apMicTimer; + + UINT32 ageOutCnt; + UINT32 stateInfo; + //key mgmt data + apKeyMgmtInfoSta_t keyMgmtInfo; + + t_u8 RSNEnabled; + UINT16 deauthReason; + + UINT8 txPauseState; + //RateChangeInfo[] is used by MAC HW to decide the start TX rate. + //It should be placed in SQ. If staData_t is placed in ITCM/DTCM then put + //staRateTable in SQ and use a pointer here + //staRateTable RateChangeInfo; + UINT16 stickyTimCount; + BOOLEAN stickyTimEnabled; + +#ifdef DOT11W + /* Peer STA PMF capability */ + BOOLEAN peerPMFCapable; +#endif + +} staData_t; +/**connectioninfo*/ +typedef struct _cm_Connection { + /**Hand shake timer*/ + t_void *HskTimer; + /** Timer set flag */ + t_u8 timer_is_set; + /** authenticator Private pointer */ + t_void *priv; + t_u8 mac_addr[MLAN_MAC_ADDR_LENGTH]; + /**sta data for authenticator*/ + staData_t staData; + /**handshake data*/ + eapolHskData_t hskData; +} cm_Connection; + +typedef struct { + IEEEtypes_CapInfo_t CapInfo; + UINT32 AssocStationsCnt; + + BOOLEAN updatePassPhrase; + + KeyData_t grpKeyData; + UINT8 GNonce[32]; + + /* Following two variables contain that multiple of BI which is just + ** greater than user configured ageout time in normal and PS mode. These + ** variables get updated at bss_start, and then are used whenever FW + ** resets STA age. + */ + UINT32 staAgeOutBcnCnt; + UINT32 psStaAgeOutBcnCnt; + + // Store group rekey time as a multiple of beacon interval. + UINT32 grpRekeyCntConfigured; + UINT32 grpRekeyCntRemaining; + +} BssData_t; + +typedef struct { + UINT16 keyExchange:1; + UINT16 authenticate:1; + UINT16 reserved:14; +} Operation_t; + +typedef struct { + Cipher_t mcstCipher; + UINT8 mcstCipherCount; + + Cipher_t wpaUcstCipher; + UINT8 wpaUcstCipherCount; + + Cipher_t wpa2UcstCipher; + UINT8 wpa2UcstCipherCount; + + UINT16 AuthKey; + UINT16 AuthKeyCount; + Operation_t Akmp; + UINT32 GrpReKeyTime; + UINT8 PSKPassPhrase[PSK_PASS_PHRASE_LEN_MAX]; + UINT8 PSKPassPhraseLen; + UINT8 PSKValue[PMK_LEN_MAX]; + UINT8 MaxPwsHskRetries; + UINT8 MaxGrpHskRetries; + UINT32 PwsHskTimeOut; + UINT32 GrpHskTimeOut; + UINT8 RSNReplayProtEn; /* RSN Replay Attack Protection flag */ +} apRsnConfig_t; + +typedef struct { + UINT8 ieSet; + UINT8 version; +/* UINT8 akmCnt ; */ + UINT8 akmTypes; +/* UINT8 uCastCnt ; */ + UINT8 uCastTypes; + UINT8 mCastTypes; + UINT8 capInfo; +} wapi_ie_cfg_t; + +typedef struct { + /* The This section only used for initialization of the connPtr */ + IEEEtypes_SsId_t SsId; + IEEEtypes_Len_t SsIdLen; + // odd-sized ele clubbed together to keep even alignment + IEEEtypes_DtimPeriod_t DtimPeriod; + IEEEtypes_BcnInterval_t BcnPeriod; + + IEEEtypes_MacAddr_t BssId; + UINT16 RtsThresh; + UINT16 FragThresh; + UINT8 ShortRetryLim; + UINT8 LongRetryLim; + + // Used in MBSS mode for software beacon suppression + UINT8 MbssBcnIntFac; + UINT8 MbssCurBcnIntCnt; + UINT16 Reserved; +} CommonMlmeData_t; + +typedef struct { + IEEEtypes_SsId_t SsId; + IEEEtypes_Len_t SsIdLen; + + UINT8 wpa_ie[MAX_IE_SIZE]; + UINT16 wpa_ielen; + UINT8 rsn_ie[MAX_IE_SIZE]; + UINT16 rsn_ielen; + UINT32 StaAgeOutTime; + UINT32 PsStaAgeOutTime; + + /* If the BssAddr field is not aligned on word boundary the hal + functions which update mac registers are unsafe for non-word + aligned pointers. Avoid direct use of the pointer to BssId + field in the hal functions */ + /* this field is no longer used and we use mibOpdata_p->StaMacAddr + in its place now */ + IEEEtypes_MacAddr_t EepromMacAddr_defunct; + IEEEtypes_DataRate_t OpRateSet[IEEEtypes_MAX_DATA_RATES_G]; + + // odd-sized ele clubbed together to keep even alignment + UINT8 AuthType; + UINT8 TxPowerLevel; + IEEEtypes_DataRate_t TxDataRate; + IEEEtypes_DataRate_t TxMCBCDataRate; + UINT8 MaxStaSupported; + + SecurityMode_t SecType; + UINT8 Padding1[1]; //****** Use this for adding new members ******* + BOOLEAN apWmmEn; + IEEEtypes_WMM_ParamElement_t apWmmParamSet; + + BOOLEAN ap11nEn; + + cipher_key_buf_t *pWepKeyBuf; + cipher_key_buf_t *pGtkKeyBuf; + UINT8 ScanChanCount; + UINT8 AclStaCnt; + + UINT8 Padding3[1]; //****** Use this for adding new members ******* + apRsnConfig_t RsnConfig; + BOOLEAN apWmmPsEn; + channelInfo_t ScanChanList[IEEEtypes_MAX_CHANNELS]; /* Channels to scan */ + CommonMlmeData_t comData; + IEEEtypes_OBSS_ScanParam_t ObssScanParam; + + cipher_key_buf_t *piGtkKeyBuf; + UINT32 mgmtFrameSubtypeFwdEn; + UINT8 Ht2040CoexEn; // Enable/Disable 2040 Coex feature in uAP + + UINT8 Padding4[1]; //****** Use this for adding new members ******* + + wapi_ie_cfg_t wapiCfg; + IEEEtypes_ExtCapability_t ExtCap; + UINT8 Padding6[1]; //****** Use this for adding new members ******* +} BssConfig_t; + +typedef struct { + BssConfig_t bssConfig; + BssData_t bssData; +} apInfo_t; +#ifdef DRV_EMBEDDED_SUPPLICANT +typedef struct { + /* This structure is ROM'd */ + + UINT8 RSNEnabled:1; /* WPA, WPA2 */ + UINT8 pmkidValid:1; /* PMKID valid */ + UINT8 rsnCapValid:1; + UINT8 grpMgmtCipherValid:1; + UINT8 rsvd:4; /* rsvd */ + + SecurityMode_t wpaType; + Cipher_t mcstCipher; + Cipher_t ucstCipher; + AkmSuite_t AKM; + UINT8 PMKID[16]; + + IEEEtypes_RSNCapability_t rsnCap; + + Cipher_t grpMgmtCipher; + +} RSNConfig_t; + +typedef struct { + UINT8 ANonce[NONCE_SIZE]; + UINT8 SNonce[NONCE_SIZE]; + UINT8 EAPOL_MIC_Key[EAPOL_MIC_KEY_SIZE]; + UINT8 EAPOL_Encr_Key[EAPOL_ENCR_KEY_SIZE]; + UINT32 apCounterLo; /* last valid replay counter from authenticator */ + UINT32 apCounterHi; + UINT32 apCounterZeroDone; /* have we processed replay == 0? */ + UINT32 staCounterLo; /* counter used in request EAPOL frames */ + UINT32 staCounterHi; + + BOOLEAN RSNDataTrafficEnabled; /* Enabled after 4way handshake */ + BOOLEAN RSNSecured; /* Enabled after group key is established */ + BOOLEAN pwkHandshakeComplete; + cipher_key_t *pRxDecryptKey; + + KeyData_t PWKey; + KeyData_t GRKey; + + KeyData_t newPWKey; + + MIC_Error_t sta_MIC_Error; + t_void *rsnTimer; + t_void *micTimer; + t_void *deauthDelayTimer; /* hacked in to delay the deauth */ + + //phostsa_private psapriv; + + KeyData_t IGtk; + +} keyMgmtInfoSta_t; + +typedef struct supplicantData { + BOOLEAN inUse; + BOOLEAN suppInitialized; + IEEEtypes_SsIdElement_t hashSsId; + IEEEtypes_MacAddr_t localBssid; + IEEEtypes_MacAddr_t localStaAddr; + customMIB_RSNStats_t customMIB_RSNStats; + RSNConfig_t customMIB_RSNConfig; + keyMgmtInfoSta_t keyMgmtInfoSta; + SecurityParams_t currParams; + UINT8 wpa_rsn_ie[MAX_IE_SIZE]; +} supplicantData_t; +#endif + +/** supplicant/authenticator private structure */ +typedef struct _hostsa_private { + /** pmlan_private */ + t_void *pmlan_private; + /** pmlan_adapter */ + t_void *pmlan_adapter; + /** Utility functions table */ + hostsa_util_fns util_fns; + /** MLAN APIs table */ + hostsa_mlan_fns mlan_fns; + /**apinf_t*/ + apInfo_t apinfo; + /**group rekey timer*/ + t_void *GrpRekeytimer; + /**Group rekey timer set flag*/ + t_u8 GrpRekeyTimerIsSet; + /**local mac address*/ + t_u8 curr_addr[MLAN_MAC_ADDR_LENGTH]; +#ifdef DRV_EMBEDDED_SUPPLICANT + /**supplicant data*/ + supplicantData_t *suppData; +#endif + /* GTK installed status */ + t_u8 gtk_installed; +} hostsa_private, *phostsa_private; +#endif diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/authenticator_api.c b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/authenticator_api.c new file mode 100644 index 000000000000..46a961c9f9a9 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/authenticator_api.c @@ -0,0 +1,896 @@ +/** @file Authenticator_api.c + * + * @brief This file defines the main APIs for authenticator. + * + * Copyright (C) 2014-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 03/07/2014: Initial version +******************************************************/ +//Authenticator related function definitions +#include "wltypes.h" +#include "IEEE_types.h" + +#include "hostsa_ext_def.h" +#include "authenticator.h" + +#include "wl_macros.h" +#include "wlpd.h" +#include "pass_phrase.h" +#include "sha1.h" +#include "crypt_new.h" +#include "parser.h" +#include "keyCommonDef.h" +#include "keyMgmtStaTypes.h" +#include "AssocAp_srv_rom.h" +#include "pmkCache.h" +#include "keyMgmtApTypes.h" +#include "keyMgmtAp.h" +#include "rc4.h" +#include "keyMgmtAp.h" + +/********************* + Local Variables + *********************/ + +/********************* + Global Variables + *********************/ + +/********************* + Local Functions + *********************/ + +/********************* + Global Functions + *********************/ +#ifdef DRV_EMBEDDED_SUPPLICANT +extern void allocSupplicantData(void *phostsa_priv); +extern void freeSupplicantData(void *phostsa_priv); +extern mlan_status initSupplicantTimer(void *priv); +extern void keyMgmtSta_RomInit(void); +extern void freeSupplicantTimer(void *priv); +#endif + +/********************* + Utility Handler + *********************/ +#ifdef DRV_EMBEDDED_AUTHENTICATOR +static UINT32 +util_CountBits(UINT32 val) +{ + UINT32 count = 0; + + for (count = 0; val; count++) { + val &= (val - 1); + } + return count; +} + +static void +initMicErrorParams(UINT32 wpa, MIC_Error_t *pApMicError) +{ + if (wpa) { + pApMicError->MICCounterMeasureEnabled = 1; + pApMicError->disableStaAsso = 0; + pApMicError->status = NO_MIC_FAILURE; + } +} + +/** + * @brief whether authenticator is enabled or not + * + * @param priv A void pointer to phostsa private struct + * + * @return + */ +t_u8 +IsAuthenticatorEnabled(void *priv) +{ + phostsa_private psapriv = (phostsa_private)priv; + apInfo_t *pApInfo = NULL; + apRsnConfig_t *pRsnConfig = NULL; + t_u8 ret = 0; + + ENTER(); + + if (!psapriv) { + LEAVE(); + return ret; + } + + pApInfo = &psapriv->apinfo; + if (pApInfo == NULL) { + LEAVE(); + return ret; + } + + pRsnConfig = &pApInfo->bssConfig.RsnConfig; + + // If Passphrase lengh is nonozero then + // authenticator in the driver is to be used, + // otherwise authenticator in the host is not to be used. + if (pRsnConfig->PSKPassPhraseLen != 0) { + ret = 1; + } + + LEAVE(); + return ret; +} + +/** + * @brief BSS configure initialized + * + * @param priv A void pointer to phostsa private struct + * + * @return + */ +void +AuthenitcatorInitBssConfig(void *priv) +{ + phostsa_private psapriv = (phostsa_private)priv; + hostsa_util_fns *util_fns = MNULL; + apInfo_t *pApInfo = MNULL; + BssConfig_t *pBssConfig = MNULL; + apRsnConfig_t *pRsnConfig = MNULL; + + ENTER(); + + if (!psapriv) { + LEAVE(); + return; + } + util_fns = &psapriv->util_fns; + pApInfo = &psapriv->apinfo; + pBssConfig = &pApInfo->bssConfig; + pRsnConfig = &pBssConfig->RsnConfig; + + memset(util_fns, pBssConfig, 0x00, sizeof(BssConfig_t)); + /**default ap ssid*/ + pBssConfig->SsIdLen = wlan_strlen(AP_DEFAULT_SSID); + memset(util_fns, pBssConfig->SsId, 0x00, IEEEtypes_SSID_SIZE); + memcpy(util_fns, pBssConfig->SsId, AP_DEFAULT_SSID, + IEEEtypes_SSID_SIZE); + /**default retry times ans timeout*/ + pRsnConfig->PwsHskTimeOut = PWS_HSK_TIMEOUT; + pRsnConfig->MaxPwsHskRetries = PWS_HSK_RETRIES; + pRsnConfig->GrpHskTimeOut = GRP_HSK_TIMEOUT; + pRsnConfig->MaxGrpHskRetries = GRP_HSK_RETRIES; + /**Group key rekey time*/ + pRsnConfig->GrpReKeyTime = GRP_REKEY_TIME; + + LEAVE(); +} + +/** + * @brief get bss config + * + * @param priv A void pointer to phostsa private struct + * @param pbss_config a pointer to mlan_uap_bss_param + * @param SetConfigToMlan 1 set releated config to mlan , 0 get related config from mlan + * + * @return + */ +void +AuthenticatorGetBssConfig(hostsa_private *psapriv, t_u8 *pbss_config, + t_u8 SetConfigToMlan) +{ + hostsa_util_fns *util_fns = &psapriv->util_fns; + apInfo_t *pApInfo = &psapriv->apinfo; + mlan_uap_bss_param *bss_config = (mlan_uap_bss_param *)pbss_config; + BssConfig_t *pBssConfig = MNULL; + apRsnConfig_t *pRsnConfig = MNULL; + t_u8 zero_mac[] = { 0, 0, 0, 0, 0, 0 }; + + ENTER(); + + pBssConfig = &pApInfo->bssConfig; + pRsnConfig = &pBssConfig->RsnConfig; + + /**set bss config to mlan*/ + if (SetConfigToMlan) { + if ((pRsnConfig->PwsHskTimeOut != 0) && + (pRsnConfig->PwsHskTimeOut < (MAX_VALID_DWORD))) { + bss_config->pairwise_update_timeout = + pRsnConfig->PwsHskTimeOut; + } + + if ((pRsnConfig->MaxPwsHskRetries != 0) && + (pRsnConfig->MaxPwsHskRetries < (MAX_VALID_DWORD))) + bss_config->pwk_retries = pRsnConfig->MaxPwsHskRetries; + + if ((pRsnConfig->GrpHskTimeOut != 0) && + (pRsnConfig->GrpHskTimeOut < (MAX_VALID_DWORD))) + bss_config->groupwise_update_timeout = + pRsnConfig->GrpHskTimeOut; + + if ((pRsnConfig->MaxGrpHskRetries != 0) && + (pRsnConfig->MaxGrpHskRetries < (MAX_VALID_DWORD))) + bss_config->gwk_retries = pRsnConfig->MaxGrpHskRetries; + + if (pRsnConfig->PSKPassPhraseLen != 0) { + bss_config->wpa_cfg.length = + pRsnConfig->PSKPassPhraseLen; + memcpy(util_fns, bss_config->wpa_cfg.passphrase, + pRsnConfig->PSKPassPhrase, + pRsnConfig->PSKPassPhraseLen); + } + + if ((pRsnConfig->GrpReKeyTime != 0) && + (pRsnConfig->GrpReKeyTime < MAX_GRP_TIMER)) + bss_config->wpa_cfg.gk_rekey_time = + pRsnConfig->GrpReKeyTime; + + LEAVE(); + return; + } + + if (((pBssConfig->SsIdLen != bss_config->ssid.ssid_len) || + (memcmp(util_fns, pBssConfig->SsId, + bss_config->ssid.ssid, bss_config->ssid.ssid_len) != 0)) && + bss_config->ssid.ssid_len) { + pApInfo->bssData.updatePassPhrase = 1; + memcpy(util_fns, pBssConfig->SsId, bss_config->ssid.ssid, + bss_config->ssid.ssid_len); + pBssConfig->SsIdLen = bss_config->ssid.ssid_len; + } + + if (memcmp + (util_fns, zero_mac, &bss_config->mac_addr, MLAN_MAC_ADDR_LENGTH)) { + memset(util_fns, psapriv->curr_addr, 0x00, + MLAN_MAC_ADDR_LENGTH); + memcpy(util_fns, psapriv->curr_addr, &bss_config->mac_addr, + MLAN_MAC_ADDR_LENGTH); + } + + if ((bss_config->max_sta_count != 0) && + (bss_config->max_sta_count <= MAX_STA_COUNT)) + pBssConfig->MaxStaSupported = bss_config->max_sta_count; + + if ((bss_config->pairwise_update_timeout != 0) && + (bss_config->pairwise_update_timeout < (MAX_VALID_DWORD))) { + pRsnConfig->PwsHskTimeOut = bss_config->pairwise_update_timeout; + } + + if ((bss_config->pwk_retries != 0) && + (bss_config->pwk_retries < (MAX_VALID_DWORD))) + pRsnConfig->MaxPwsHskRetries = bss_config->pwk_retries; + + if ((bss_config->groupwise_update_timeout != 0) && + (bss_config->groupwise_update_timeout < (MAX_VALID_DWORD))) + pRsnConfig->GrpHskTimeOut = + bss_config->groupwise_update_timeout; + + if ((bss_config->gwk_retries != 0) && + (bss_config->gwk_retries < (MAX_VALID_DWORD))) + pRsnConfig->MaxGrpHskRetries = bss_config->gwk_retries; + + if ((bss_config->auth_mode <= MLAN_AUTH_MODE_SHARED) || + (bss_config->auth_mode == MLAN_AUTH_MODE_AUTO)) + pBssConfig->AuthType = bss_config->auth_mode; + + memcpy(util_fns, &pBssConfig->SecType, &bss_config->protocol, + sizeof(pBssConfig->SecType)); + + if ((bss_config->protocol & PROTOCOL_WPA) || + (bss_config->protocol & PROTOCOL_WPA2) || + (bss_config->protocol & PROTOCOL_EAP)) { + pRsnConfig->AuthKey = bss_config->key_mgmt; + pRsnConfig->AuthKeyCount = util_CountBits(pRsnConfig->AuthKey); + memcpy(util_fns, (char *)&pRsnConfig->Akmp, + (char *)&bss_config->key_mgmt_operation, + sizeof(bss_config->key_mgmt_operation)); + + if ((bss_config->wpa_cfg. + pairwise_cipher_wpa & VALID_CIPHER_BITMAP) && + (bss_config->wpa_cfg.pairwise_cipher_wpa != 0xff)) { + memset(util_fns, (t_u8 *)&pRsnConfig->wpaUcstCipher, + 0x00, sizeof(Cipher_t)); + memcpy(util_fns, (t_u8 *)&pRsnConfig->wpaUcstCipher, + (t_u8 *)&bss_config->wpa_cfg.pairwise_cipher_wpa, + sizeof(Cipher_t)); + pRsnConfig->wpaUcstCipherCount = + util_CountBits(bss_config->wpa_cfg. + pairwise_cipher_wpa); + } + + if ((bss_config->wpa_cfg. + pairwise_cipher_wpa2 & VALID_CIPHER_BITMAP) && + (bss_config->wpa_cfg.pairwise_cipher_wpa2 != 0xff)) { + memset(util_fns, (t_u8 *)&pRsnConfig->wpa2UcstCipher, + 0x00, sizeof(Cipher_t)); + memcpy(util_fns, (t_u8 *)&pRsnConfig->wpa2UcstCipher, + (t_u8 *)&bss_config->wpa_cfg. + pairwise_cipher_wpa2, sizeof(Cipher_t)); + pRsnConfig->wpa2UcstCipherCount = + util_CountBits(bss_config->wpa_cfg. + pairwise_cipher_wpa2); + } + DBG_HEXDUMP(MCMD_D, " wpa2UcstCipher", + (t_u8 *)&pRsnConfig->wpa2UcstCipher, + sizeof(Cipher_t)); + DBG_HEXDUMP(MCMD_D, " wpaUcastCipher", + (t_u8 *)&pRsnConfig->wpaUcstCipher, + sizeof(Cipher_t)); + + if (bss_config->wpa_cfg.group_cipher & VALID_CIPHER_BITMAP) { + memset(util_fns, (t_u8 *)&pRsnConfig->mcstCipher, 0x00, + sizeof(Cipher_t)); + memcpy(util_fns, (t_u8 *)&pRsnConfig->mcstCipher, + (t_u8 *)&bss_config->wpa_cfg.group_cipher, + sizeof(Cipher_t)); + pRsnConfig->mcstCipherCount = + util_CountBits(bss_config->wpa_cfg. + group_cipher); + } + + if (bss_config->wpa_cfg.rsn_protection <= MTRUE) + pRsnConfig->RSNReplayProtEn = + bss_config->wpa_cfg. + rsn_protection ? MTRUE : MFALSE; + + } + + if (((pRsnConfig->PSKPassPhraseLen != bss_config->wpa_cfg.length) || + (memcmp(util_fns, pRsnConfig->PSKPassPhrase, + bss_config->wpa_cfg.passphrase, + bss_config->wpa_cfg.length) != 0)) && + bss_config->wpa_cfg.length) { + pApInfo->bssData.updatePassPhrase = 1; + pRsnConfig->PSKPassPhraseLen = bss_config->wpa_cfg.length; + memset(util_fns, pRsnConfig->PSKPassPhrase, + 0x00, PSK_PASS_PHRASE_LEN_MAX); + memcpy(util_fns, pRsnConfig->PSKPassPhrase, + bss_config->wpa_cfg.passphrase, + bss_config->wpa_cfg.length); + } + + if ((bss_config->wpa_cfg.gk_rekey_time != 0) && + (bss_config->wpa_cfg.gk_rekey_time < MAX_GRP_TIMER)) + pRsnConfig->GrpReKeyTime = bss_config->wpa_cfg.gk_rekey_time; + + LEAVE(); +} + +/** + * @brief get bss config for authenticator and append wpa/rsn ie to FW + * + * @param priv A void pointer to phostsa private struct + * @param pbss_config a pointer to mlan_uap_bss_param + * @param appendIE 1 append rsn/wpa ie to fw, 0 not append + * @param clearIE 1 clear rsn/wpa ie to fw, 0 not clear + * @param SetConfigToMlan 1 set releated config to mlan , 0 get related config from mlan +* + * + * @return + */ +t_u8 +AuthenticatorBssConfig(void *priv, t_u8 *pbss_config, t_u8 appendIE, + t_u8 clearIE, t_u8 SetConfigToMlan) +{ + phostsa_private psapriv = (phostsa_private)priv; + hostsa_util_fns *util_fns = MNULL; + hostsa_mlan_fns *pm_fns = MNULL; + apInfo_t *pApInfo = MNULL; + BssConfig_t *pBssConfig = MNULL; + apRsnConfig_t *pRsnConfig = MNULL; + t_u16 ielen = 0; + t_u8 ret = 0; + + ENTER(); + + if (!psapriv) { + LEAVE(); + return ret; + } + util_fns = &psapriv->util_fns; + pm_fns = &psapriv->mlan_fns; + pApInfo = &psapriv->apinfo; + + pBssConfig = &pApInfo->bssConfig; + pRsnConfig = &pBssConfig->RsnConfig; + + if (pbss_config) + AuthenticatorGetBssConfig(psapriv, pbss_config, + SetConfigToMlan); + + if (appendIE && pRsnConfig->PSKPassPhraseLen && + (pBssConfig->SecType.wpa || pBssConfig->SecType.wpa2 || + pBssConfig->SecType.wpaNone)) { + PRINTM(MMSG, "authenticator set mgmt IE\n"); + if (pBssConfig->wpa_ielen) { + pm_fns->Hostsa_set_mgmt_ie(psapriv->pmlan_private, + pBssConfig->wpa_ie, + pBssConfig->wpa_ielen, 1); + pBssConfig->wpa_ielen = 0; + memset(util_fns, pBssConfig->wpa_ie, 0x00, MAX_IE_SIZE); + } + /**construct wpa or RSN ie*/ + if (pBssConfig->SecType.wpa) { + memset(util_fns, pBssConfig->wpa_ie, 0x00, MAX_IE_SIZE); + ielen = keyMgmtAp_FormatWPARSN_IE(psapriv, + (IEEEtypes_InfoElementHdr_t + *)pBssConfig->wpa_ie, + 0, + &pBssConfig-> + RsnConfig. + wpaUcstCipher, + pBssConfig->RsnConfig. + wpaUcstCipherCount, + &pBssConfig-> + RsnConfig.mcstCipher, + pBssConfig->RsnConfig. + AuthKey, + pBssConfig->RsnConfig. + AuthKeyCount); + pBssConfig->wpa_ielen = ielen; + pm_fns->Hostsa_set_mgmt_ie(psapriv->pmlan_private, + pBssConfig->wpa_ie, ielen, + 0); + } + + if (pBssConfig->rsn_ielen) { + pm_fns->Hostsa_set_mgmt_ie(psapriv->pmlan_private, + pBssConfig->rsn_ie, + pBssConfig->rsn_ielen, 1); + pBssConfig->rsn_ielen = 0; + memset(util_fns, pBssConfig->rsn_ie, 0x00, MAX_IE_SIZE); + } + + if (pBssConfig->SecType.wpa2) { + memset(util_fns, pBssConfig->rsn_ie, 0x00, MAX_IE_SIZE); + ielen = keyMgmtAp_FormatWPARSN_IE(psapriv, + (IEEEtypes_InfoElementHdr_t + *)pBssConfig->rsn_ie, + 1, + &pBssConfig-> + RsnConfig. + wpa2UcstCipher, + pBssConfig->RsnConfig. + wpa2UcstCipherCount, + &pBssConfig-> + RsnConfig.mcstCipher, + pBssConfig->RsnConfig. + AuthKey, + pBssConfig->RsnConfig. + AuthKeyCount); + pBssConfig->rsn_ielen = ielen; + pm_fns->Hostsa_set_mgmt_ie(psapriv->pmlan_private, + pBssConfig->rsn_ie, ielen, + 0); + } + } + + if (clearIE) { + PRINTM(MMSG, "authenticator clear mgmt IE\n"); + if (pBssConfig->wpa_ielen) { + pm_fns->Hostsa_set_mgmt_ie(psapriv->pmlan_private, + pBssConfig->wpa_ie, + pBssConfig->wpa_ielen, 1); + pBssConfig->wpa_ielen = 0; + memset(util_fns, pBssConfig->wpa_ie, 0x00, MAX_IE_SIZE); + } + if (pBssConfig->rsn_ielen) { + pm_fns->Hostsa_set_mgmt_ie(psapriv->pmlan_private, + pBssConfig->rsn_ie, + pBssConfig->rsn_ielen, 1); + pBssConfig->rsn_ielen = 0; + memset(util_fns, pBssConfig->rsn_ie, 0x00, MAX_IE_SIZE); + } + } + LEAVE(); + return ret; +} + +/** + * @brief initialize key + * + * @param priv A void pointer to phostsa private struct + * + * @return + */ +void +AuthenticatorKeyMgmtInit(void *priv, t_u8 *addr) +{ + phostsa_private psapriv = (phostsa_private)priv; + hostsa_util_fns *util_fns = &psapriv->util_fns; + apInfo_t *pApInfo = &psapriv->apinfo; + BssConfig_t *pBssConfig = MNULL; + + ENTER(); + + pBssConfig = &pApInfo->bssConfig; + /**mac address */ + memcpy(util_fns, psapriv->curr_addr, addr, MLAN_MAC_ADDR_LENGTH); + /**Reset Group Key*/ + memset(util_fns, &pBssConfig->pGtkKeyBuf, 0x00, + sizeof(pBssConfig->pGtkKeyBuf)); + /**key init */ + KeyMgmtInit(priv); + /**Group key update time,*/ + if (pBssConfig->RsnConfig.GrpReKeyTime) { + pApInfo->bssData.grpRekeyCntConfigured = pApInfo->bssConfig.RsnConfig.GrpReKeyTime / 60; + /**every 60s grpRekeyCntRemaining -- */ + + pApInfo->bssData.grpRekeyCntRemaining + = pApInfo->bssData.grpRekeyCntConfigured; + } + + keyApi_ApUpdateKeyMaterial(priv, MNULL, MTRUE); + + /**start Group rekey timer*/ + if (pBssConfig->RsnConfig.GrpReKeyTime && !psapriv->GrpRekeyTimerIsSet) { + util_fns->moal_start_timer(util_fns->pmoal_handle, + psapriv->GrpRekeytimer, + MFALSE, MRVDRV_TIMER_60S); + psapriv->GrpRekeyTimerIsSet = MTRUE; + } + + LEAVE(); +} + +/** + * @brief clear key + * + * @param priv A void pointer to phostsa private struct + * + * @return + */ +void +AuthenticatorkeyClear(void *priv) +{ + phostsa_private psapriv = (phostsa_private)priv; + hostsa_util_fns *putil_fns = &psapriv->util_fns; + apInfo_t *pApInfo = &psapriv->apinfo; + BssConfig_t *pBssConfig = &pApInfo->bssConfig; + + if (psapriv->GrpRekeyTimerIsSet) { + psapriv->GrpRekeyTimerIsSet = MFALSE; + putil_fns->moal_stop_timer(putil_fns->pmoal_handle, + psapriv->GrpRekeytimer); + /**Reset Group Key*/ + memset(putil_fns, &pBssConfig->pGtkKeyBuf, 0x00, + sizeof(pBssConfig->pGtkKeyBuf)); + } +} + +/** + * @brief process received eapol packet + * + * @param psapriv A void pointer to phostsa private struct + * @param pbuf a pointer to packet buf + * @param len buffer len + * @ + * @return + */ +t_u8 +AuthenticatorProcessEapolPacket(t_void *psapriv, t_u8 *pbuf, t_u32 len) +{ + phostsa_private priv = (phostsa_private)psapriv; + hostsa_mlan_fns *pm_fns = &priv->mlan_fns; + EAPOL_KeyMsg_t *rx_eapol_ptr; + Status_e status = FAIL; + cm_Connection *connPtr = MNULL; + cm_Connection *pconnPtr = MNULL; + t_u8 *sta_addr = pbuf + MLAN_MAC_ADDR_LENGTH; + t_u8 ret = 0; + t_u32 eapol_pkt_len, least_len; + + ENTER(); + + rx_eapol_ptr = (EAPOL_KeyMsg_t *)(pbuf + ETHII_HEADER_LEN); + + if (rx_eapol_ptr->hdr_8021x.pckt_type != + IEEE_8021X_PACKET_TYPE_EAPOL_KEY) { + ret = 1; + return ret; + + } + + eapol_pkt_len = len - ETHII_HEADER_LEN; + least_len = sizeof(EAPOL_KeyMsg_t) - sizeof(rx_eapol_ptr->key_data); + if (eapol_pkt_len < least_len) { + PRINTM(MERROR, + "Invalid EAPOL Key Packet, received len: %u, least len: %u\n", + eapol_pkt_len, least_len); + ret = 1; + return ret; + } + + pm_fns->Hostsa_get_station_entry(priv->pmlan_private, sta_addr, + (t_void *)&pconnPtr); + connPtr = (cm_Connection *)pconnPtr; + if (connPtr) { + // rx_eapol_ptr = (EAPOL_KeyMsg_t *)(pbuf + ETHII_HEADER_LEN); + + if (rx_eapol_ptr->key_info.Error) { + ApMicCounterMeasureInvoke((t_void *)connPtr); + return ret; + } + + if (!isValidReplayCount(priv, &connPtr->staData.keyMgmtInfo, + (UINT8 *)&rx_eapol_ptr->replay_cnt[0])) + { + return ret; + } + + if (connPtr->staData.keyMgmtInfo.rom.keyMgmtState == + WAITING_4_MSG2) { + status = ProcessPWKMsg2(priv, connPtr, pbuf, len); + } else if (connPtr->staData.keyMgmtInfo.rom.keyMgmtState == + WAITING_4_MSG4) { + status = ProcessPWKMsg4(priv, connPtr, pbuf, len); + } else if ((connPtr->staData.keyMgmtInfo.rom.keyMgmtState + == WAITING_4_GRPMSG2) + || (connPtr->staData.keyMgmtInfo.rom.keyMgmtState + == WAITING_4_GRP_REKEY_MSG2)) { + status = ProcessGrpMsg2(priv, connPtr, pbuf, len); + } + } + LEAVE(); + return ret; +} + +/** + * @brief send eapol packet + * + * @param psapriv A void pointer to phostsa private struct + * @param pconnPtr a pointer to connection + * @ + * @return + */ +t_void +AuthenticatorSendEapolPacket(t_void *priv, t_void *pconnPtr) +{ + phostsa_private psapriv = (phostsa_private)priv; + cm_Connection *connPtr = (cm_Connection *)pconnPtr; + apInfo_t *pApInfo = &psapriv->apinfo; + + ENTER(); + /**check whether wpa/rsn is used*/ + if (!connPtr->staData.RSNEnabled) + return; + + /**init Mic error parameters*/ + initMicErrorParams(pApInfo->bssConfig.SecType.wpa, + &connPtr->staData.apMicError); + connPtr->staData.keyMgmtInfo.rom.keyMgmtState = MSG1_PENDING; + //If it is in **_pending state + if (((connPtr->staData.keyMgmtInfo.rom.keyMgmtState) & 0x1) != 0) { + GenerateApEapolMsg(psapriv, connPtr, + connPtr->staData.keyMgmtInfo.rom. + keyMgmtState); + + } + + LEAVE(); +} + +/** + * @brief get station security infor from (re)assocaite request + * + * @param priv A void pointer to phostsa private struct + * @param pconnPtr a pointer to connection + * @param pIe a pointer to associate request + * @param ieLen len of ie + * + * @return + */ +void +authenticator_get_sta_security_info(void *priv, + t_void *pconnPtr, t_u8 *pIe, t_u8 ieLen) +{ + phostsa_private psapriv = (phostsa_private)priv; + hostsa_util_fns *util_fns = &psapriv->util_fns; + cm_Connection *connPtr = (cm_Connection *)pconnPtr; + IEPointers_t iePointers; + IEEEtypes_StatusCode_t retcode; + + ENTER(); + + ieLen = GetIEPointers((t_void *)priv, pIe, ieLen, &iePointers); + + assocSrvAp_CheckSecurity(connPtr, iePointers.pWps, + iePointers.pRsn, iePointers.pWpa, + iePointers.pWapi, &retcode); + /* clean key */ + memset(util_fns, (t_u8 *)&connPtr->hskData, 0x00, + sizeof(eapolHskData_t)); + + LEAVE(); +} + +/** + * @brief initialize client + * + * @param priv A void pointer to phostsa private struct + * @param pconnPtr a pointer to pointer to connection + * @param mac a pointer to mac address + * + * @return + */ +void +authenticator_init_client(void *priv, void **ppconnPtr, t_u8 *mac) +{ + phostsa_private psapriv = (hostsa_private *)priv; + hostsa_util_fns *util_fns = &psapriv->util_fns; + cm_Connection *connPtr = MNULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + ret = malloc(util_fns, sizeof(cm_Connection), (t_u8 **)&connPtr); + if ((ret != MLAN_STATUS_SUCCESS) || (!connPtr)) { + PRINTM(MERROR, "%s: could not allocate hostsa_private.\n", + __FUNCTION__); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + memset(util_fns, connPtr, 0x00, sizeof(cm_Connection)); + connPtr->priv = priv; + memcpy(util_fns, connPtr->mac_addr, mac, MLAN_MAC_ADDR_LENGTH); + + util_fns->moal_init_timer(util_fns->pmoal_handle, &connPtr->HskTimer, + KeyMgmtHskTimeout, connPtr); + util_fns->moal_init_timer(util_fns->pmoal_handle, + &connPtr->staData.apMicTimer, + ApMicErrTimerExpCb, connPtr); + +done: + if (ret == MLAN_STATUS_SUCCESS) + *ppconnPtr = (void *)connPtr; + else + *ppconnPtr = MNULL; + LEAVE(); + +} + +/** + * @brief free client + * + * @param priv A void pointer to phostsa private struct + * @param pconnPtr a pointer to connection + * + * @return + */ +void +authenticator_free_client(void *priv, void *ppconnPtr) +{ + phostsa_private psapriv = (phostsa_private)priv; + hostsa_util_fns *util_fns = &psapriv->util_fns; + cm_Connection *connPtr = (cm_Connection *)ppconnPtr; + + ENTER(); + + if (connPtr) { + util_fns->moal_stop_timer(util_fns->pmoal_handle, + connPtr->HskTimer); + util_fns->moal_stop_timer(util_fns->pmoal_handle, + connPtr->staData.apMicTimer); + + util_fns->moal_free_timer(util_fns->pmoal_handle, + connPtr->HskTimer); + util_fns->moal_free_timer(util_fns->pmoal_handle, + connPtr->staData.apMicTimer); + free(util_fns, (t_u8 *)connPtr); + } + LEAVE(); +} +#endif + +/** + * @brief Init hostsa data + * + * @param pphostsa_priv A pointer to pointer to a hostsa private data structure + * @param psa_util_fns A pointer to hostsa utility functions table + * @param psa_mlan_fns A pointer to MLAN APIs table + * @param addr a pointer to address + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +supplicant_authenticator_init(t_void **pphostsa_priv, t_void *psa_util_fns, + t_void *psa_mlan_fns, t_u8 *addr) +{ + hostsa_util_fns *putil_fns = (hostsa_util_fns *)psa_util_fns; + hostsa_mlan_fns *pmlan_fns = (hostsa_mlan_fns *)psa_mlan_fns; + mlan_status ret; + hostsa_private *priv = MNULL; + + ENTER(); + + ret = malloc(putil_fns, sizeof(hostsa_private), (t_u8 **)&priv); + if ((ret != MLAN_STATUS_SUCCESS) || (!priv)) { + PRINTM(MERROR, "%s: could not allocate hostsa_private.\n", + __FUNCTION__); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + memset(putil_fns, priv, 0x00, sizeof(hostsa_private)); + memset(putil_fns, &priv->apinfo, 0x00, sizeof(apInfo_t)); + + memcpy(putil_fns, &priv->util_fns, putil_fns, sizeof(hostsa_util_fns)); + memcpy(putil_fns, &priv->mlan_fns, pmlan_fns, sizeof(hostsa_mlan_fns)); + + priv->pmlan_adapter = pmlan_fns->pmlan_adapter; + priv->pmlan_private = pmlan_fns->pmlan_private; + + memcpy(putil_fns, priv->curr_addr, addr, MLAN_MAC_ADDR_LENGTH); +#ifdef DRV_EMBEDDED_AUTHENTICATOR + putil_fns->moal_init_timer(putil_fns->pmoal_handle, + &priv->GrpRekeytimer, + KeyMgmtGrpRekeyCountUpdate, priv); + /**Bss configure initialize*/ + AuthenitcatorInitBssConfig(priv); +#endif +#ifdef DRV_EMBEDDED_SUPPLICANT + priv->suppData = MNULL; + allocSupplicantData(priv); + ret = initSupplicantTimer(priv); + if (ret != MLAN_STATUS_SUCCESS) { + goto done; + } + pmkCacheInit(priv); + pmkCacheRomInit(); + keyMgmtSta_RomInit(); +#endif + +done: + if (ret == MLAN_STATUS_SUCCESS) + *pphostsa_priv = (t_void *)priv; + else + *pphostsa_priv = MNULL; + LEAVE(); + return ret; +} + +/** + * @brief Cleanup hostsa data + * + * @param phostsa_priv A pointer to a hostsa private data structure + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +supplicant_authenticator_free(t_void *phostsa_priv) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + phostsa_private priv = (hostsa_private *)phostsa_priv; + hostsa_util_fns *putil_fns = &priv->util_fns; + + ENTER(); + + if (!phostsa_priv) + goto done; +#ifdef DRV_EMBEDDED_SUPPLICANT + freeSupplicantData(priv); + freeSupplicantTimer(priv); +#endif +#ifdef DRV_EMBEDDED_AUTHENTICATOR + putil_fns->moal_stop_timer(putil_fns->pmoal_handle, + priv->GrpRekeytimer); + putil_fns->moal_free_timer(putil_fns->pmoal_handle, + priv->GrpRekeytimer); +#endif + free(putil_fns, (t_u8 *)priv); +done: + LEAVE(); + return ret; +} diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/authenticator_api.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/authenticator_api.h new file mode 100644 index 000000000000..816630c525fc --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/authenticator_api.h @@ -0,0 +1,70 @@ +/** @file Authenticator_api.h + * + * @brief This file delare the main APIs for authenticator. + * + * Copyright (C) 2014-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 03/07/2014: Initial version +******************************************************/ +#ifndef _AUTHENTICATORAPI_H +#define _AUTHENTICATORAPI_H + +/****************************************************** +Change log: + 03/01/2014: Initial version +******************************************************/ +#ifdef DRV_EMBEDDED_AUTHENTICATOR +extern t_u8 IsAuthenticatorEnabled(void *priv); +extern void AuthenitcatorInitBssConfig(void *priv); +extern void AuthenticatorKeyMgmtInit(void *priv, t_u8 *addr); +extern void AuthenticatorkeyClear(void *priv); +extern void authenticator_init_client(void *priv, void **ppconnPtr, t_u8 *mac); +extern void authenticator_free_client(void *priv, void *ppconnPtr); +extern void authenticator_get_sta_security_info(void *priv, + t_void *pconnPtr, t_u8 *pIe, + t_u8 ieLen); +extern t_void AuthenticatorSendEapolPacket(t_void *priv, t_void *pconnPtr); +extern t_u8 AuthenticatorProcessEapolPacket(void *priv, t_u8 *pbuf, t_u32 len); +extern t_u8 AuthenticatorBssConfig(void *priv, t_u8 *pbss_config, t_u8 appendIE, + t_u8 clearIE, t_u8 SetConfigToMlan); +#endif +mlan_status supplicant_authenticator_init(t_void **pphostsa_priv, + t_void *psa_util_fns, + t_void *psa_mlan_fns, t_u8 *addr); +mlan_status supplicant_authenticator_free(t_void *phostsa_priv); +#ifdef DRV_EMBEDDED_SUPPLICANT +extern void supplicantClrEncryptKey(void *priv); +extern void *processRsnWpaInfo(void *priv, void *prsnwpa_ie); +extern void pmkCacheDeletePMK(void *priv, t_u8 *pBssid); +extern void supplicantInitSession(void *priv, + t_u8 *pSsid, + t_u16 len, t_u8 *pBssid, t_u8 *pStaAddr); +extern void supplicantStopSessionTimer(void *priv); +extern t_u8 supplicantIsEnabled(void *priv); +extern void supplicantQueryPassphraseAndEnable(void *priv, t_u8 *pbuf); +extern void supplicantDisable(void *priv); +extern t_u8 ProcessEAPoLPkt(void *priv, mlan_buffer *pmbuf); +extern t_u8 EAPoLKeyPkt_Validation(mlan_buffer *pmbuf); +extern void SupplicantClearPMK(void *priv, void *pPassphrase); +extern t_u16 SupplicantSetPassphrase(void *priv, void *pPassphraseBuf); +extern void SupplicantQueryPassphrase(void *priv, void *pPassphraseBuf); +extern t_u8 supplicantFormatRsnWpaTlv(void *priv, void *rsn_wpa_ie, + void *rsn_ie_tlv); +#endif +#endif /**_AUTHENTICATORAPI_H*/ diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/IEEE_types.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/IEEE_types.h new file mode 100644 index 000000000000..959b3bf1400c --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/IEEE_types.h @@ -0,0 +1,3194 @@ +/** @file IEEE_types.h + * + * @brief This file contains definitions relating to messages specified in the + * IEEE 802.11 spec. + * + * Copyright (C) 2014-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 03/07/2014: Initial version +******************************************************/ +#ifndef _IEEE_TYPES_H_ +#define _IEEE_TYPES_H_ + +/*==========================================================================*/ +/* INCLUDE FILES */ +/*==========================================================================*/ +#include "wltypes.h" + +/*==========================================================================*/ +/* PUBLIC DEFINITIONS */ +/*==========================================================================*/ + +#define IS_BROADCAST(macaddr) ((*(UINT16 *)macaddr == 0xffff) && \ + (*(UINT16 *)((UINT8 *)macaddr+2) == 0xffff) && \ + (*(UINT16 *)((UINT8 *)macaddr+4) == 0xffff)) + +#define IS_MULTICAST(macaddr) ((*(UINT8*)macaddr & 0x01) == 0x01) + +#define IS_GROUP(macaddr) ((*(UINT8*)macaddr & 0x01) == 0x01) + +#define ADDR_NOT_EQUAL(a, b) (((a)[0] != (b)[0]) || ((a)[1] != (b)[1]) || \ + ((a)[2] != (b)[2]) || ((a)[3] != (b)[3]) || \ + ((a)[4] != (b)[4]) || ((a)[5] != (b)[5])) + +#define LLC_SNAP_SIZE 6 +#define ETHERTYPE_LEN 2 + +#define IEEE_MSG_TYPE(Hdr_p) ((Hdr_p)->FrmCtl.Type) +#define IEEE_MSG_SUBTYPE(Hdr_p) ((Hdr_p)->FrmCtl.Subtype) + +/*--------------------------------------------------------------*/ +/* Reason Codes - these codes are used in management message */ +/* frame bodies to indicate why an action is taking place (such */ +/* as a disassociation or deauthentication). */ +/*--------------------------------------------------------------*/ +#define IEEEtypes_REASON_RSVD 0 +#define IEEEtypes_REASON_UNSPEC 1 +#define IEEEtypes_REASON_PRIOR_AUTH_INVALID 2 +#define IEEEtypes_REASON_DEAUTH_LEAVING 3 +#define IEEEtypes_REASON_DISASSOC_INACTIVE 4 +#define IEEEtypes_REASON_DISASSOC_AP_BUSY 5 +#define IEEEtypes_REASON_CLASS2_NONAUTH 6 +#define IEEEtypes_REASON_CLASS3_NONASSOC 7 +#define IEEEtypes_REASON_DISASSOC_STA_HASLEFT 8 +#define IEEEtypes_REASON_CANT_ASSOC_NONAUTH 9 +/***************802.11h Reasons***************/ +#define IEEEtypes_REASON_DISASSOC_BAD_POWERCAP 10 +#define IEEEtypes_REASON_DISASSOC_BAD_SUPPCHAN 11 +/***************802.11v Reasons***************/ +#define IEEEtypes_REASON_BSS_TRANSITION_MGMT 12 +/***************WPA Reasons*******************/ +#define IEEEtypes_REASON_INVALID_IE 13 +#define IEEEtypes_REASON_MIC_FAILURE 14 +#define IEEEtypes_REASON_4WAY_HANDSHK_TIMEOUT 15 +#define IEEEtypes_REASON_GRP_KEY_UPD_TIMEOUT 16 +#define IEEEtypes_REASON_IE_4WAY_DIFF 17 +#define IEEEtypes_REASON_INVALID_MCAST_CIPHER 18 +#define IEEEtypes_REASON_INVALID_UNICAST_CIPHER 19 +#define IEEEtypes_REASON_INVALID_AKMP 20 +#define IEEEtypes_REASON_UNSUPT_RSN_VER 21 +#define IEEEtypes_REASON_INVALID_RSN_CAP 22 +#define IEEEtypes_REASON_8021X_AUTH_FAIL 23 +#define IEEEtypes_REASON_CIPHER_POLICY_REJECT 24 +/*************** 802.11z(TDLS) Reasons*************/ +#define IEEEtypes_REASON_TDLS_TEARDOWN_TDLSPEER_UNREACHABLE 25 +#define IEEEtypes_REASON_TDLS_TEARDOWN_UNSPEC 26 +/***************802.11e Reasons***************/ +#define IEEEtypes_REASON_DISASSOC_UNSPEC_QOS 32 +#define IEEEtypes_REASON_DISASSOC_QAP_NO_BNDWDTH 33 +#define IEEEtypes_REASON_DISASSOC_FRM_LOSS_BAD_CH 34 +#define IEEEtypes_REASON_DISASSOC_QSTA_VIOL_TXOP 35 +#define IEEEtypes_REASON_REQ_PEER_LEAVE_QBSS 36 +#define IEEEtypes_REASON_REQ_PEER_NO_THANKS 37 +#define IEEEtypes_REASON_REQ_PEER_ACM_MISMATCH 38 +#define IEEEtypes_REASON_REQ_PEER_TIMEOUT 39 +#define IEEEtypes_REASON_PEER_QSTA_NO_SUPP_CIPHER 45 +/*********************************************/ + +/*------------------------------------------------------------*/ +/* Status Codes - these codes are used in management message */ +/* frame bodies to indicate the results of an operation (such */ +/* as association, reassociation, and authentication). */ +/*------------------------------------------------------------*/ +#define IEEEtypes_STATUS_SUCCESS 0 +#define IEEEtypes_STATUS_UNSPEC_FAILURE 1 + +/****************BEGIN: 802.11z(TDLS) status codes********/ +#define IEEEtypes_STATUS_TDLS_WAKEUPSCHEDULE_REJECTED_BUT_ALT_PROVIDED 2 +#define IEEEtypes_STATUS_TDLS_WAKEUPSCHEDULE_REJECTED 3 +#define IEEEtypes_STATUS_SECURITY_DISABLED 5 +#define IEEEtypes_STATUS_UNACCEPTABLE_LIFETIME 6 +#define IEEEtypes_STATUS_NOT_IN_SAME_BSS 7 +/****************END: 802.11z(TDLS) status codes********/ +#define IEEEtypes_STATUS_CAPS_UNSUPPORTED 10 +#define IEEEtypes_STATUS_REASSOC_NO_ASSOC 11 +#define IEEEtypes_STATUS_ASSOC_DENIED_UNSPEC 12 +#define IEEEtypes_STATUS_UNSUPPORTED_AUTHALG 13 +#define IEEEtypes_STATUS_RX_AUTH_NOSEQ 14 +#define IEEEtypes_STATUS_CHALLENGE_FAIL 15 +#define IEEEtypes_STATUS_AUTH_TIMEOUT 16 +#define IEEEtypes_STATUS_ASSOC_DENIED_BUSY 17 +#define IEEEtypes_STATUS_ASSOC_DENIED_RATES 18 +#define IEEEtypes_STATUS_ASSOC_DENIED_NOSHORT 19 +#define IEEEtypes_STATUS_ASSOC_DENIED_NOPBCC 20 +#define IEEEtypes_STATUS_ASSOC_DENIED_NOAGILITY 21 +#define IEEEtypes_STATUS_ASSOC_DENIED_SPECMGMT_REQD 22 +#define IEEEtypes_STATUS_ASSOC_DENIED_BAD_POWERCAP 23 +#define IEEEtypes_STATUS_ASSOC_DENIED_BAD_SUPPCHAN 24 +#define IEEEtypes_STATUS_ASSOC_DENIED_NOSHORTSLOTTIME 25 +#define IEEEtypes_STATUS_ASSOC_DENIED_NODSSSOFDM 26 + +#define IEEEtypes_STATUS_R0KH_UNAVAILABLE 28 + +#define IEEEtypes_STATUS_TEMP_REJECTION 30 +#define IEEEtypes_STATUS_ROBUST_MGMT_VIOLAION 31 +#define IEEEtypes_STATUS_UNSPEC_QOS_FAILURE 32 +#define IEEEtypes_STATUS_ASSOC_DENIED_QAP_INSUFF_BNDWDTH 33 +#define IEEEtypes_STATUS_ASSOC_DENIED_EXC_FRM_LOSS_BAD_CH 34 +#define IEEEtypes_STATUS_ASSOC_DENIED_STA_NO_QOS_SUPP 35 + +#define IEEEtypes_STATUS_REQ_DECLINED 37 +#define IEEEtypes_STATUS_REQ_FAIL_INVALID_PARAMS 38 +#define IEEEtypes_STATUS_FAIL_TS_AP_THINKS_ITS_SMART_THO 39 +#define IEEEtypes_STATUS_INVALID_IE 40 +#define IEEEtypes_STATUS_INVALID_GROUP_CIPHER 41 +#define IEEEtypes_STATUS_INVALID_PAIRWISE_CIPHER 42 +#define IEEEtypes_STATUS_INVALID_AKMP 43 +#define IEEEtypes_STATUS_UNSUPPORTED_RSN_VER 44 +#define IEEEtypes_STATUS_INVALID_RSN_CAPABILITIES 45 +#define IEEEtypes_STATUS_CIPHER_POLICY_REJECT 46 +#define IEEEtypes_STATUS_FAIL_TS_TRY_LATER_AFTER_TS_DELAY 47 +#define IEEEtypes_STATUS_DIRECT_LINK_NOT_ALLOWED 48 +#define IEEEtypes_STATUS_DEST_STA_NOT_IN_QBSS 49 +#define IEEEtypes_STATUS_DEST_STA_NOT_A_QSTA 50 +#define IEEEtypes_STATUS_LISTEN_INTERVAL_TOO_LARGE 51 +#define IEEEtypes_STATUS_INVALID_FT_ACT_FRAME_COUNT 52 +#define IEEEtypes_STATUS_INVALID_PMKID 53 +#define IEEEtypes_STATUS_INVALID_MDIE 54 +#define IEEEtypes_STATUS_INVALID_FTIE 55 +#define IEEEtypes_STATUS_REQ_TCLAS_NOT_SUPPORTED 56 +#define IEEEtypes_STATUS_INSF_TCLAS_RSOURCES 57 +#define IEEEtypes_STATUS_TS_FAIL_TRANS_SUGGESTED 58 +#define IEEEtypes_STATUS_UAPSD_COEX_NOT_SUPPORTED 59 +#define IEEEtypes_STATUS_REQ_UAPSD_COEX_MODE_NOT_SUP 60 +#define IEEEtypes_STATUS_REQ_INVL_WITH_UAPSD_COEX_NOT_SUP 61 +#define IEEEtypes_STATUS_INVALID_CONTENTS_OF_RSNIE 72 + +/*--------------------------------------------*/ +/* Various sizes used in IEEE 802.11 messages */ +/*--------------------------------------------*/ +#define IEEEtypes_ADDRESS_SIZE 6 +#define IEEEtypes_BITMAP_SIZE 251 +#define IEEEtypes_CHALLENGE_TEXT_SIZE 128 +#define IEEEtypes_CHALLENGE_TEXT_LEN 128 +#define IEEEtypes_MAX_DATA_RATES 8 +#define IEEEtypes_MAX_DATA_BODY_LEN 2312 +#define IEEEtypes_MAX_MGMT_BODY_LEN 2312 +#define IEEEtypes_SSID_SIZE 32 +#define IEEEtypes_TIME_STAMP_SIZE 8 +#define IEEEtypes_MAX_CHANNELS 14 +#define IEEEtypes_MAX_BSS_DESCRIPTS 16 +#define IEEEtypes_MAX_DATA_RATES_G 14 +#define IEEEtypes_COUNTRY_CODE_SIZE 3 +#define IEEEtypes_COUNTRY_MAX_TRIPLETS 83 + +/*---------------------------------------------------------------------*/ +/* Define masks used to extract fields from the capability information */ +/* structure in a beacon message. */ +/*---------------------------------------------------------------------*/ +#define IEEEtypes_CAP_INFO_ESS 1 +#define IEEEtypes_CAP_INFO_IBSS 2 +#define IEEEtypes_CAP_INFO_CF_POLLABLE 4 +#define IEEEtypes_CAP_INFO_CF_POLL_RQST 8 +#define IEEEtypes_CAP_INFO_PRIVACY 16 +#define IEEEtypes_CAP_INFO_SHORT_PREAMB 32 +#define IEEEtypes_CAP_INFO_PBCC 64 +#define IEEEtypes_CAP_INFO_CHANGE_AGILITY 128 +#define IEEEtypes_CAP_INFO_SHORT_SLOT_TIME 0x0400 +#define IEEEtypes_CAP_INFO_DSSS_OFDM 0x2000 + +/*---------------------------*/ +/* Miscellaneous definitions */ +/*---------------------------*/ +#define IEEEtypes_PROTOCOL_VERSION 0 + +#define IEEEtypes_BASIC_RATE_FLAG 0x80 +/* */ +/* Used to determine which rates in a list are designated as basic rates */ +/* */ + +#define IEEEtypes_SUPP_RATE_MASK 0x7F + +#define IEEE_DATA_RATE_1Mbps 2 +#define IEEE_DATA_RATE_2Mbps 4 +#define IEEE_DATA_RATE_5_5Mbps 11 +#define IEEE_DATA_RATE_11Mbps 22 + +#define IEEE_DATA_RATE_6Mbps 12 +#define IEEE_DATA_RATE_9Mbps 18 +#define IEEE_DATA_RATE_12Mbps 24 +#define IEEE_DATA_RATE_18Mbps 36 +#define IEEE_DATA_RATE_24Mbps 48 +#define IEEE_DATA_RATE_36Mbps 72 +#define IEEE_DATA_RATE_48Mbps 96 +#define IEEE_DATA_RATE_54Mbps 108 + +/* */ +/* Used to mask off the basic rate flag, if one exists, for given */ +/* data rates */ +/* */ + +#define IEEEtypes_RATE_MIN 2 +/* */ +/* The minimum allowable data rate in units of kb/s */ +/* */ + +#define IEEEtypes_RATE_MAX 127 +/* */ +/* The maximum allowable data rate in units of kb/s */ +/* */ + +#define IEEEtypes_TIME_UNIT 1024 +/* */ +/* The number of microseconds in 1 time unit, as specified in the */ +/* 802.11 spec */ +/* */ +#define CONVERT_TU_TO_MILLISECOND(x) (x * 1024 / 1000) +#define CONVERT_TU_TO_MICROSECOND(x) (x * 1024) + +/** + * 802.11 frame classes + */ +#define IEEEtypes_CLASS1_FRAME 1 +#define IEEEtypes_CLASS2_FRAME 2 +#define IEEEtypes_CLASS3_FRAME 3 + +/* + * Structure of an internet header, naked of options. + * + * ip_len and ip_off are sint16, rather than UINT16 + * pragmatically since otherwise unsigned comparisons can result + * against negative integers quite easily, and fail in subtle ways. + */ +typedef MLAN_PACK_START struct { +#if 1 //BYTE_ORDER == LITTLE_ENDIAN + UINT8 ip_hl:4; /* header length */ + UINT8 ip_v:4; /* version */ +#endif + UINT8 ip_tos; /* type of service */ + sint16 ip_len; /* total length */ + UINT16 ip_id; /* identification */ + sint16 ip_off; /* fragment offset field */ + UINT8 ip_ttl; /* time to live */ + UINT8 ip_p; /* protocol */ + UINT16 ip_sum; /* checksum */ + UINT32 ip_src; // source ip addr + UINT32 ip_dst; // dest ip address +} MLAN_PACK_END ip_hdr_t; +#define IP_DF 0x4000 /* dont fragment flag */ +#define IP_MF 0x2000 /* more fragments flag */ +#define IP_OFFMASK 0x1fff /* mask for fragmenting bits */ + +#define IP_V4 4 +#define IP_V6 6 +#define IP_PROT_TCP 0x06 +#define IP_PROT_UDP 0x11 + +typedef MLAN_PACK_START struct { + UINT8 priority:4; + UINT8 ip_v:4; + UINT8 flow_lbl[3]; + UINT16 payload_len; + UINT8 next_hdr; + UINT8 hop_limit; + UINT8 ip_src[16]; /* source ip addr */ + UINT8 ip_dst[16]; /* dest ip address */ +} MLAN_PACK_END ipv6_hdr_t; + +#define ETHTYPE_ARP 0x0806 +#define ETHTYPE_IP 0x0800 +#define ARP_OP_REQ 1 +#define ARP_OP_RESP 2 +#define ETHTYPE_IPV6 0x86DD +#define ICMPV6_TYPE 0x3a + +/* See RFC 826 for ARP protocol description. */ +typedef MLAN_PACK_START struct { + UINT16 ar_hrd; // hardware address space + UINT16 ar_pro; // prototcol address space + UINT8 ar_hln; // byte length of hardware addr + UINT8 ar_pln; // byte length of protocol addr + UINT16 ar_op; // ARP opcode +} MLAN_PACK_END arp_hdr_t; + +typedef MLAN_PACK_START struct { + arp_hdr_t hdr; // arp header + UINT8 ar_sha[IEEEtypes_ADDRESS_SIZE]; // sender hardware addr + UINT8 ar_spa[4]; // sender protocol addr + UINT8 ar_tha[IEEEtypes_ADDRESS_SIZE]; // target hardware addr + UINT8 ar_tpa[4]; // target protocol addr +} MLAN_PACK_END arp_t; + +/* icmp header struct of echo request and echo reply */ +typedef MLAN_PACK_START struct { + UINT8 type; + UINT8 code; + UINT16 sum; + UINT16 id; + UINT16 seq; +} MLAN_PACK_END icmp_hdr_t; +#define ICMP_ECHO_REQ 8 +#define ICMP_ECHO_REPLY 0 + +typedef MLAN_PACK_START struct { + UINT8 type; + UINT8 icode; + UINT16 csum; + UINT32 reserved; + UINT8 target_addr[16]; +} MLAN_PACK_END icmpv6_nsol_t; +#define ICMPV6_TYPE_NSOL 0x87 +#define ICMPV6_TYPE_NADV 0x88 + +#define ICMPV6_NADV_FLAG_RTR (1<<31) +#define ICMPV6_NADV_FLAG_SOL (1<<30) +#define ICMPV6_NADV_FLAG_OVR (1<<29) + +#define ICMPV6_OPT_TYPE_TLA (0x2) +#define ICMPV6_OPT_TYPE_TLA_LEN (0x1) + +typedef MLAN_PACK_START struct { + UINT8 type; + UINT8 icode; + UINT16 csum; + UINT32 reserved; + UINT8 target_addr[16]; + UINT8 icmp_option_type; + UINT8 icmp_option_length; + UINT8 macAddr[6]; +} MLAN_PACK_END icmpv6_nadv_t; + +/* +***************************************************************************** +** +** +** 802.1x Types +** +** +***************************************************************************** +*/ +typedef MLAN_PACK_START enum { + IEEE_8021X_PACKET_TYPE_EAP_PACKET = 0, + IEEE_8021X_PACKET_TYPE_EAPOL_START = 1, + IEEE_8021X_PACKET_TYPE_EAPOL_LOGOFF = 2, + IEEE_8021X_PACKET_TYPE_EAPOL_KEY = 3, + IEEE_8021X_PACKET_TYPE_ASF_ALERT = 4, + +} MLAN_PACK_END IEEEtypes_8021x_PacketType_e; + +typedef MLAN_PACK_START enum { + IEEE_8021X_CODE_TYPE_REQUEST = 1, + IEEE_8021X_CODE_TYPE_RESPONSE = 2, + IEEE_8021X_CODE_TYPE_SUCCESS = 3, + IEEE_8021X_CODE_TYPE_FAILURE = 4, + +} MLAN_PACK_END IEEEtypes_8021x_CodeType_e; + +/* +***************************************************************************** +** +** +** 802.11 PHY Types +** +** +***************************************************************************** +*/ +typedef MLAN_PACK_START enum { + IEEE_PHY_TYPE_UNKNOWN = 0, + + IEEE_PHY_TYPE_FHSS_2_4_GHz = 1, + IEEE_PHY_TYPE_DSSS_2_4_GHz = 2, + IEEE_PHY_TYPE_IR_BASEBAND = 3, + IEEE_PHY_TYPE_OFDM_5GHz = 4, + IEEE_PHY_TYPE_HRDSSS = 5, + IEEE_PHY_TYPE_ERP = 6, + +} MLAN_PACK_END IEEEtypes_PhyType_e; + +/* +***************************************************************************** +** +** +** 802.11 Message Types +** +** +***************************************************************************** +*/ +typedef enum { + IEEE_TYPE_MANAGEMENT = 0, + IEEE_TYPE_CONTROL, + IEEE_TYPE_DATA +} IEEEtypes_MsgType_e; + +/* +***************************************************************************** +** +** +** 802.11 Mangagement SubTypes +** +** +***************************************************************************** +*/ +typedef enum { + IEEE_MSG_ASSOCIATE_RQST = 0, + IEEE_MSG_ASSOCIATE_RSP, + IEEE_MSG_REASSOCIATE_RQST, + IEEE_MSG_REASSOCIATE_RSP, + IEEE_MSG_PROBE_RQST, + IEEE_MSG_PROBE_RSP, + IEEE_MSG_BEACON = 8, + IEEE_MSG_ATIM, + IEEE_MSG_DISASSOCIATE, + IEEE_MSG_AUTHENTICATE, + IEEE_MSG_DEAUTHENTICATE, + IEEE_MSG_ACTION +} IEEEtypes_MgmtSubType_e; + +/* +***************************************************************************** +** +** +** 802.11 Control Frame SubTypes +** +** +***************************************************************************** +*/ +typedef enum { + BF_RPRT_POLL = 4, + NDPA = 5, + BAR = 8, + BA = 9, + PS_POLL = 10, + RTS = 11, + CTS = 12, + ACK = 13, + CF_END = 14, + CF_END_CF_ACK = 15, +} IEEEtypes_CtlSubType_e; + +/* +***************************************************************************** +** +** +** 802.11 Data Frame SubTypes +** +** +***************************************************************************** +*/ +typedef enum { + DATA = 0, + DATA_CF_ACK = 1, + DATA_CF_POLL = 2, + DATA_CF_ACK_CF_POLL = 3, + NULL_DATA = 4, + CF_ACK = 5, + CF_POLL = 6, + CF_ACK_CF_POLL = 7, + QOS_DATA = 8, + QOS_DATA_CF_ACK = 9, + QOS_DATA_CF_POLL = 10, + QOS_DATA_CF_ACK_CF_POLL = 11, + QOS_NULL = 12, + RESERVED_13 = 13, + QOS_CF_POLL_NO_DATA = 14, + QOS_CF_ACK_CF_POLL_NO_DATA = 15 +} IEEEtypes_DataSubType_e; + +/* +***************************************************************************** +** +** +** 802.11 Action Frame Categories +** +** +***************************************************************************** +*/ +typedef MLAN_PACK_START enum { + IEEE_MGMT_ACTION_CATEGORY_SPECTRUM_MGMT = 0, + IEEE_MGMT_ACTION_CATEGORY_QOS = 1, + IEEE_MGMT_ACTION_CATEGORY_DLS = 2, + IEEE_MGMT_ACTION_CATEGORY_BLOCK_ACK = 3, + IEEE_MGMT_ACTION_CATEGORY_UNPROTECT_PUBLIC = 4, + IEEE_MGMT_ACTION_CATEGORY_RADIO_RSRC = 5, + IEEE_MGMT_ACTION_CATEGORY_FAST_BSS_TRANS = 6, + IEEE_MGMT_ACTION_CATEGORY_HT = 7, + IEEE_MGMT_ACTION_CATEGORY_SA_QUERY = 8, + IEEE_MGMT_ACTION_CATEGORY_PROTECT_PUBLIC = 9, + IEEE_MGMT_ACTION_CATEGORY_PROTECT_WNM = 10, + IEEE_MGMT_ACTION_CATEGORY_UNPROTECT_WNM = 11, + IEEE_MGMT_ACTION_CATEGORY_TDLS = 12, + + IEEE_MGMT_ACTION_CATEGORY_WMM_TSPEC = 17, + IEEE_MGMT_ACTION_CATEGORY_VHT = 21, + IEEE_MGMT_ACTION_CATEGORY_PROTECT_VENDOR_SPECIFIC = 126, + IEEE_MGMT_ACTION_CATEGORY_VENDOR_SPECIFIC = 127 +} MLAN_PACK_END IEEEtypes_ActionCategory_e; + +/* +** The possible types of commands sent from the SME +*/ +typedef enum { + SME_CMD_NONE, + + SME_CMD_AUTHENTICATE, + SME_CMD_ASSOCIATE, + SME_CMD_REASSOCIATE, + + SME_CMD_DEAUTHENTICATE, + SME_CMD_DISASSOCIATE, + + SME_CMD_START, + SME_CMD_JOIN, + + SME_CMD_RESET, + SME_CMD_SCAN, + +} IEEEtypes_SmeCmd_e; + +/* +** The possible types of Basic Service Sets +*/ +typedef enum { + BSS_INFRASTRUCTURE = 1, + BSS_INDEPENDENT, + BSS_ANY, + BSS_TDLS, + // Firmware internal BSS types only + BSS_BT_AMP = 0xF0, + BSS_LAST = 0xFF +} IEEEtypes_Bss_e; + +/* +** 802.11 Element and Subelement IDs +*/ +typedef MLAN_PACK_START enum { + ELEM_ID_SSID = 0, + ELEM_ID_SUPPORTED_RATES = 1, + ELEM_ID_FH_PARAM_SET = 2, + ELEM_ID_DS_PARAM_SET = 3, + ELEM_ID_CF_PARAM_SET = 4, + ELEM_ID_TIM = 5, + ELEM_ID_IBSS_PARAM_SET = 6, + ELEM_ID_COUNTRY = 7, + ELEM_ID_HOP_PARAM = 8, + ELEM_ID_HOP_TABLE = 9, + ELEM_ID_REQUEST = 10, + ELEM_ID_BSS_LOAD = 11, + ELEM_ID_EDCA_PARAM_SET = 12, + ELEM_ID_TSPEC = 13, + ELEM_ID_TCLAS = 14, + ELEM_ID_SCHEDULE = 15, + ELEM_ID_CHALLENGE_TEXT = 16, + + ELEM_ID_POWER_CONSTRAINT = 32, + ELEM_ID_POWER_CAPABILITY = 33, + ELEM_ID_TPC_REQUEST = 34, + ELEM_ID_TPC_REPORT = 35, + ELEM_ID_SUPPORTED_CHANNELS = 36, + ELEM_ID_CHANNEL_SWITCH_ANN = 37, + ELEM_ID_MEASUREMENT_REQ = 38, + ELEM_ID_MEASUREMENT_RPT = 39, + ELEM_ID_QUIET = 40, + ELEM_ID_IBSS_DFS = 41, + ELEM_ID_ERP_INFO = 42, + ELEM_ID_TS_DELAY = 43, + ELEM_ID_TCLAS_PROCESS = 44, + ELEM_ID_HT_CAPABILITY = 45, + ELEM_ID_QOS_CAPABILITY = 46, + + ELEM_ID_RSN = 48, + + ELEM_ID_EXT_SUPPORTED_RATES = 50, + ELEM_ID_AP_CHANNEL_REPORT = 51, + ELEM_ID_NEIGHBOR_REPORT = 52, + ELEM_ID_RCPI = 53, + ELEM_ID_MOBILITY_DOMAIN = 54, + ELEM_ID_FAST_BSS_TRANS = 55, + ELEM_ID_TIMEOUT_INTERVAL = 56, + ELEM_ID_RIC_DATA = 57, + ELEM_ID_DSE_REGISTERED_LOC = 58, + ELEM_ID_SUPPORTED_REGCLASS = 59, + ELEM_ID_EXT_CHAN_SWITCH_ANN = 60, + ELEM_ID_HT_INFORMATION = 61, + ELEM_ID_SECONDARY_CHAN_OFFSET = 62, + ELEM_ID_BSS_ACCESS_DELAY = 63, + ELEM_ID_ANTENNA_INFO = 64, + ELEM_ID_RSNI = 65, + ELEM_ID_MEAS_PILOT_TX_INFO = 66, + ELEM_ID_BSS_AVAIL_ADM_CAP = 67, + ELEM_ID_BSS_AC_ACCESS_DELAY = 68, + + ELEM_ID_RRM_ENABLED_CAP = 70, + ELEM_ID_MULTI_BSSID = 71, + ELEM_ID_2040_BSS_COEXISTENCE = 72, + ELEM_ID_2040_BSS_INTOL_CHRPT = 73, + ELEM_ID_OBSS_SCAN_PARAM = 74, + ELEM_ID_RIC_DESCRIPTOR = 75, + ELEM_ID_MANAGEMENT_MIC = 76, + + ELEM_ID_EVENT_REQUEST = 78, + ELEM_ID_EVENT_REPORT = 79, + ELEM_ID_DIAG_REQUEST = 80, + ELEM_ID_DIAG_REPORT = 81, + ELEM_ID_LOCATION_PARAM = 82, + ELEM_ID_NONTRANS_BSSID_CAP = 83, + ELEM_ID_SSID_LIST = 84, + ELEM_ID_MBSSID_INDEX = 85, + ELEM_ID_FMS_DESCRIPTOR = 86, + ELEM_ID_FMS_REQUEST = 87, + ELEM_ID_FMS_RESPONSE = 88, + ELEM_ID_QOS_TRAFFIC_CAP = 89, + ELEM_ID_BSS_MAX_IDLE_PERIOD = 90, + ELEM_ID_TFS_REQUEST = 91, + ELEM_ID_TFS_RESPONSE = 92, + ELEM_ID_WNM_SLEEP_MODE = 93, + ELEM_ID_TIM_BCAST_REQUEST = 94, + ELEM_ID_TIM_BCAST_RESPONSE = 95, + ELEM_ID_COLLOC_INTF_REPORT = 96, + ELEM_ID_CHANNEL_USAGE = 97, + ELEM_ID_TIME_ZONE = 98, + ELEM_ID_DMS_REQUEST = 99, + ELEM_ID_DMS_RESPONSE = 100, + ELEM_ID_LINK_ID = 101, + ELEM_ID_WAKEUP_SCHEDULE = 102, + ELEM_ID_TDLS_CS_TIMING = 104, + ELEM_ID_PTI_CONTROL = 105, + ELEM_ID_PU_BUFFER_STATUS = 106, + + ELEM_ID_EXT_CAPABILITIES = 127, + ELEM_ID_VHT_CAPABILITIES = 191, + ELEM_ID_VHT_OPERATION = 192, + ELEM_ID_WIDE_BAND_CHAN_SW = 193, + ELEM_ID_AID = 197, + ELEM_ID_VHT_OP_MODE_NOTIFICATION = 199, + + ELEM_ID_VENDOR_SPECIFIC = 221, + + /* Subelement IDs */ + SUBELEM_ID_REPORTED_FRAME_BODY = 1, + SUBELEM_ID_REPORTING_DETAIL = 2, + + SUBELEM_ID_PMK_R1_KEY_HOLDER_ID = 1, + SUBELEM_ID_GTK = 2, + SUBELEM_ID_PMK_R0_KEY_HOLDER_ID = 3, + SUBELEM_ID_IGTK = 4, + + /* Non-IEEE IDs */ + ELEM_ID_WAPI = 68, + +} MLAN_PACK_END IEEEtypes_ElementId_e; + +/* The KDE data types */ +typedef enum { + KDE_DATA_TYPE_RESERVED, + KDE_DATA_TYPE_GTK = 1, + KDE_DATA_TYPE_RESERVED2, + KDE_DATA_TYPE_MACADDR = 3, + KDE_DATA_TYPE_PMKID = 4, + KDE_DATA_TYPE_SMK = 5, + KDE_DATA_TYPE_NONCE = 6, + KDE_DATA_TYPE_LIFETIME = 7, + KDE_DATA_TYPE_ERROR = 8, + KDE_DATA_TYPE_IGTK = 9 +} IEEEtypes_KDEDataType_e; + +/* The possible power management modes */ +typedef enum { + PWR_MODE_ACTIVE, + PWR_MODE_PWR_SAVE +} IEEEtypes_PwrMgmtMode_e; + +/* The possible types of authentication */ +typedef enum { + AUTH_OPEN_SYSTEM = 0, + AUTH_SHARED_KEY = 1, + AUTH_FAST_BSS_TRANSITION = 2, + + AUTH_NETWORK_EAP = 0x80, + + AUTH_NOT_SUPPORTED, + + AUTH_AUTO_OPEN_OR_SHARED = 0xFF +} IEEEtypes_AuthType_e; + +/* The possible responses to a request to scan */ +typedef enum { + SCAN_RESULT_SUCCESS, + SCAN_RESULT_INVALID_PARAMETERS, + SCAN_RESULT_INTERNAL_ERROR, + SCAN_RESULT_PARTIAL_RESULTS, + +} IEEEtypes_ScanResult_e; + +/* The possible responses to a request to join a BSS */ +typedef enum { + JOIN_RESULT_SUCCESS, + JOIN_RESULT_INTERNAL_ERROR, + JOIN_RESULT_TIMEOUT +} IEEEtypes_JoinResult_e; + +/* The possible results to a request to authenticate */ +typedef enum { + AUTH_RESULT_SUCCESS, + AUTH_RESULT_INTERNAL_ERROR, + AUTH_RESULT_TIMEOUT, + AUTH_RESULT_UNUSED, /* Do not reuse, maps to refused assoc result */ + AUTH_RESULT_UNHANDLED_MSG, + AUTH_RESULT_REFUSED, + AUTH_RESULT_INVALID_PARAMETER +} IEEEtypes_AuthResult_e; + +/* The possible results to a request to deauthenticate */ +typedef enum { + DEAUTH_RESULT_SUCCESS, + DEAUTH_RESULT_INVALID_PARAMETERS, + DEAUTH_RESULT_TOO_MANY_SIMULTANEOUS_RQSTS, + DEAUTH_RESULT_TIMEOUT +} IEEEtypes_DeauthResult_e; + +/* The possible results to a request to associate */ +typedef enum { + ASSOC_RESULT_SUCCESS, + ASSOC_RESULT_INTERNAL_ERROR, + ASSOC_RESULT_TIMEOUT, + ASSOC_RESULT_REFUSED, + ASSOC_RESULT_AUTH_UNHANDLED_MSG, + ASSOC_RESULT_AUTH_REFUSED, + ASSOC_RESULT_INVALID_PARAMETER, + +} IEEEtypes_AssocResult_e; + +typedef enum { + /* Failure enumerations must be non-zero since they map to the + ** IEEE status field in the assoc response. The SUCCESS code is left + ** in here as a place holder but is never used. The remaining enums + ** cannot be assigned a value of 0. + */ + ASSOC_CMD_SUCCESS = 0, + + ASSOC_CMD_FAILURE_ASSOC, + ASSOC_CMD_FAILURE_AUTH, + ASSOC_CMD_FAILURE_JOIN +} IEEEtypes_AssocCmdFailurePoint_e; + +typedef enum { + DISASSOC_RESULT_SUCCESS, + DISASSOC_RESULT_INVALID_PARAMETERS, + DISASSOC_RESULT_TIMEOUT, + DISASSOC_RESULT_REFUSED +} IEEEtypes_DisassocResult_e; +/* */ +/* The possible results to a request to disassociate */ +/* */ + +typedef enum { + PWR_MGMT_RESULT_SUCCESS, + PWR_MGMT_RESULT_INVALID_PARAMETERS, + PWR_MGMT_RESULT_NOT_SUPPORTED +} IEEEtypes_PwrMgmtResult_e; +/* */ +/* The possible results to a request to change the power management mode */ +/* */ + +typedef enum { + RESET_RESULT_SUCCESS +} IEEEtypes_ResetResult_e; +/* */ +/* The possible results to a request to reset */ +/* */ + +typedef enum { + START_RESULT_SUCCESS, + START_RESULT_INVALID_PARAMETERS, + START_RESULT_BSS_ALREADY_STARTED_OR_JOINED, + START_RESULT_RESET_REQUIRED_BEFORE_START, + START_RESULT_NOT_SUPPORTED, + START_RESULT_ACS_ENABLED +} IEEEtypes_StartResult_e; +/* */ +/* The possible results to a request to start */ +/* */ + +typedef enum { + TPCADAPT_RESULT_SUCCESS, + TPCADAPT_RESULT_INVALID_PARAMETERS, + TPCADAPT_RESULT_UNSPECIFIED_FAILURE +} IEEEtypes_TPCAdaptResult_e; + +typedef enum { + STATE_IDLE, + STATE_SCANNING, + STATE_JOINING, + + STATE_ASSOCIATING, + STATE_ASSOCIATED, + STATE_ROAMING, + + STATE_IBSS_ACTIVE, + STATE_BSS_ACTIVE, + STATE_TDLS_SETUP_REQ_RCVD, + STATE_TDLS_SETUP_REQ_SENT, + STATE_TDLS_SETUP_RSP_SENT, + STATE_TDLS_ACTIVE, +} IEEEtypes_MacMgmtStates_e; + +/* */ +/* The possible states the MAC Management Service Task can be in */ +/* */ + +/*---------------------------------------------------------------------------*/ +/* Types Used In IEEE 802.11 MAC Message Data Structures */ +/*---------------------------------------------------------------------------*/ +typedef UINT8 IEEEtypes_Len_t; +/* */ +/* Length type */ +/* */ + +typedef UINT8 IEEEtypes_Addr_t; +/* */ +/* Address type */ +/* */ + +typedef IEEEtypes_Addr_t IEEEtypes_MacAddr_t[IEEEtypes_ADDRESS_SIZE]; +/* */ +/* MAC address type */ +/* */ + +typedef UINT8 IEEEtypes_DataRate_t; +/* */ +/* Type used to specify the supported data rates */ +/* */ + +typedef UINT8 IEEEtypes_SsId_t[IEEEtypes_SSID_SIZE]; +/* */ +/* SS ID type */ +/* */ + +/*---------------------------------------------------------------------------*/ +/* IEEE 802.11 MAC Message Data Structures */ +/* */ +/* Each IEEE 802.11 MAC message includes a MAC header, a frame body (which */ +/* can be empty), and a frame check sequence field. This section gives the */ +/* structures that used for the MAC message headers and frame bodies that */ +/* can exist in the three types of MAC messages - 1) Control messages, */ +/* 2) Data messages, and 3) Management messages. */ +/*---------------------------------------------------------------------------*/ +typedef MLAN_PACK_START struct { + UINT16 ProtocolVersion:2; + UINT16 Type:2; + UINT16 Subtype:4; + UINT16 ToDs:1; + UINT16 FromDs:1; + UINT16 MoreFrag:1; + UINT16 Retry:1; + UINT16 PwrMgmt:1; + UINT16 MoreData:1; + UINT16 Protected:1; + UINT16 Order:1; + +} MLAN_PACK_END IEEEtypes_FrameCtl_t; + +typedef MLAN_PACK_START struct { + UINT16 FragNum:4; + UINT16 SeqNum:12; + +} MLAN_PACK_END IEEEtypes_SeqCtl_t; + +typedef struct { + UINT16 FrmBodyLen; + IEEEtypes_FrameCtl_t FrmCtl; + UINT16 DurationId; + IEEEtypes_MacAddr_t Addr1; + IEEEtypes_MacAddr_t Addr2; + IEEEtypes_MacAddr_t Addr3; + IEEEtypes_SeqCtl_t SeqCtl; + IEEEtypes_MacAddr_t Addr4; + +} IEEEtypes_GenHdr_t; + +typedef MLAN_PACK_START struct { + UINT16 FrmBodyLen; + IEEEtypes_FrameCtl_t FrmCtl; + UINT16 Duration; + IEEEtypes_MacAddr_t DestAddr; + IEEEtypes_MacAddr_t SrcAddr; + IEEEtypes_MacAddr_t BssId; + IEEEtypes_SeqCtl_t SeqCtl; + IEEEtypes_MacAddr_t Rsrvd; + +} MLAN_PACK_END IEEEtypes_MgmtHdr_t; + +typedef struct { + IEEEtypes_GenHdr_t Hdr; + UINT8 FrmBody[IEEEtypes_MAX_DATA_BODY_LEN]; + UINT32 FCS; + +} IEEEtypes_DataFrame_t; + +typedef struct { + UINT8 UserPriority:4; + UINT8 Management:1; + UINT8 Reserved:3; + +} IEEEtypes_NonceFlags_t; + +typedef MLAN_PACK_START struct { + UINT8 PN0; + UINT8 PN1; + UINT8 Reserved1; + UINT8 Reserved2:5; + UINT8 ExtIV:1; + UINT8 KeyId:2; + UINT8 PN2; + UINT8 PN3; + UINT8 PN4; + UINT8 PN5; + +} MLAN_PACK_END IEEEtypes_CcmpHeader_t; + +typedef MLAN_PACK_START struct { + UINT8 TSC1; + UINT8 WepSeed; + UINT8 TSC0; + UINT8 Reserved:5; + UINT8 ExtIV:1; + UINT8 KeyId:2; + UINT8 TSC2; + UINT8 TSC3; + UINT8 TSC4; + UINT8 TSC5; + +} MLAN_PACK_END IEEEtypes_TkipHeader_t; + +typedef MLAN_PACK_START struct { + IEEEtypes_FrameCtl_t frameCtl; + IEEEtypes_MacAddr_t addr1; + IEEEtypes_MacAddr_t addr2; + IEEEtypes_MacAddr_t addr3; + +} MLAN_PACK_END IEEEtypes_BIP_AAD_t; + +/************************************************************************/ +/* Control Frame Types */ +/************************************************************************/ +typedef MLAN_PACK_START struct { + UINT16 FrmBodyLen; + IEEEtypes_FrameCtl_t FrmCtl; + UINT16 DurationId; + IEEEtypes_MacAddr_t DestAddr; + IEEEtypes_MacAddr_t SrcAddr; + UINT8 Reserved[14]; /* Header MAC HW is 32 bytes */ + +} MLAN_PACK_END IEEEtypes_CtlHdr_t; + +typedef MLAN_PACK_START struct { + IEEEtypes_CtlHdr_t Hdr; + UINT32 FCS; + +} MLAN_PACK_END IEEEtypes_PsPoll_t; + +typedef MLAN_PACK_START struct { + UINT16 BARAckPolicy:1; + UINT16 Multi_TID:1; + UINT16 CompressedBitmap:1; + UINT16 Reserved:9; + UINT16 TID:4; + +} MLAN_PACK_END IEEEtypes_BARCtl_t; + +typedef MLAN_PACK_START struct { + IEEEtypes_CtlHdr_t Hdr; + + IEEEtypes_BARCtl_t BARCtl; + IEEEtypes_SeqCtl_t SeqCtl; + UINT32 FCS; + +} MLAN_PACK_END IEEEtypes_BlockAckReq_t; + +//NDPA frame components +typedef MLAN_PACK_START struct { + UINT16 AID:12; + UINT16 FbType:1; + UINT16 NcIndex:3; + +} MLAN_PACK_END IEEEtypes_StaInfo_t; + +typedef MLAN_PACK_START struct { + UINT8 Rsvd:2; //[0:1] Reserved + UINT8 SoundingSeq:6; //[2:7]Seq no. + IEEEtypes_StaInfo_t StaInfo; //Currently only 1 sta_info support + +} MLAN_PACK_END IEEEtypes_NDPAFrameBody_t; + +typedef MLAN_PACK_START struct { + IEEEtypes_CtlHdr_t Hdr; + IEEEtypes_NDPAFrameBody_t FrameBody; + UINT32 FCS; + +} MLAN_PACK_END IEEEtypes_NDPAFrame_t; +/*-------------------------------------------------*/ +/* Management Frame Body Components - Fixed Fields */ +/*-------------------------------------------------*/ +typedef UINT16 IEEEtypes_AId_t; +/* */ +/* Association ID assigned by an AP during the association process */ +/* */ + +typedef UINT16 IEEEtypes_AuthAlg_t; +/* */ +/* Number indicating the authentication algorithm used (it can take */ +/* on the values given by IEEEtypes_AuthType_e): */ +/* 0 = Open system */ +/* 1 = Shared key */ +/* All other values reserved */ +/* */ + +typedef UINT16 IEEEtypes_AuthTransSeq_t; +/* */ +/* Authentication transaction sequence number that indicates the current */ +/* state of progress through a multistep transaction */ +/* */ + +typedef UINT16 IEEEtypes_BcnInterval_t; +/* */ +/* Beacon interval that represents the number of time units between */ +/* target beacon transmission times */ +/* */ + +typedef UINT8 IEEEtypes_DtimPeriod_t; +/* + * Interval that represents the number of time units between DTIMs. + */ + +typedef MLAN_PACK_START struct { + UINT16 Ess:1; + UINT16 Ibss:1; + UINT16 CfPollable:1; + UINT16 CfPollRqst:1; + UINT16 Privacy:1; + UINT16 ShortPreamble:1; + UINT16 Pbcc:1; + UINT16 ChanAgility:1; + UINT16 SpectrumMgmt:1; + UINT16 Qos:1; + UINT16 ShortSlotTime:1; + UINT16 APSD:1; + UINT16 RadioMeasurement:1; + UINT16 DsssOfdm:1; + UINT16 DelayedBlockAck:1; + UINT16 ImmediateBlockAck:1; +} MLAN_PACK_END IEEEtypes_CapInfo_t; + +typedef UINT16 IEEEtypes_ListenInterval_t; +/* */ +/* Listen interval to indicate to an AP how often a STA wakes to listen */ +/* to beacon management frames */ +/* */ + +typedef UINT16 IEEEtypes_ReasonCode_t; +/* */ +/* Reason code to indicate the reason that an unsolicited notification */ +/* management frame of type Disassociation or Deauthentication was */ +/* generated */ +/* */ + +typedef UINT16 IEEEtypes_StatusCode_t; +/* */ +/* Status code used in a response management frame to indicate the */ +/* success or failure of a requested operation */ +/* */ + +typedef UINT8 IEEEtypes_TimeStamp_t[IEEEtypes_TIME_STAMP_SIZE]; + +/*-------------------------------------------------------*/ +/* Management Frame Body Components - Information Fields */ +/*-------------------------------------------------------*/ + +typedef MLAN_PACK_START struct { + IEEEtypes_ElementId_e ElementId; + IEEEtypes_Len_t Len; +} MLAN_PACK_END IEEEtypes_InfoElementHdr_t; + +typedef MLAN_PACK_START struct { + IEEEtypes_ElementId_e ElementId; + IEEEtypes_Len_t Len; + + UINT8 Data[255]; +} MLAN_PACK_END IEEEtypes_IE_Param_t; + +/* +** SSID element that idicates the identity of an ESS or IBSS +*/ +typedef MLAN_PACK_START struct { + IEEEtypes_ElementId_e ElementId; + IEEEtypes_Len_t Len; + IEEEtypes_SsId_t SsId; +} MLAN_PACK_END IEEEtypes_SsIdElement_t; + +/* +** Supported rates element that specifies the rates in the operational +** rate set in the MLME join request and the MLME start request +*/ +typedef MLAN_PACK_START struct { + IEEEtypes_ElementId_e ElementId; + IEEEtypes_Len_t Len; + IEEEtypes_DataRate_t Rates[IEEEtypes_MAX_DATA_RATES]; +} MLAN_PACK_END IEEEtypes_SuppRatesElement_t; + +/* +** FH parameter set that conatins the set of parameters necessary to +** allow sychronization for stations using a frequency hopping PHY +*/ +typedef MLAN_PACK_START struct { + IEEEtypes_ElementId_e ElementId; + IEEEtypes_Len_t Len; + UINT16 DwellTime; + UINT8 HopSet; + UINT8 HopPattern; + UINT8 HopIndex; +} MLAN_PACK_END IEEEtypes_FhParamElement_t; + +/* +** DS parameter set that contains information to allow channel number +** identification for stations using a direct sequence spread spectrum PHY +*/ +typedef MLAN_PACK_START struct { + IEEEtypes_ElementId_e ElementId; + IEEEtypes_Len_t Len; + UINT8 CurrentChan; +} MLAN_PACK_END IEEEtypes_DsParamElement_t; + +/* +** CF parameter set that contains the set of parameters necessary to +** support the PCF +*/ +typedef MLAN_PACK_START struct { + IEEEtypes_ElementId_e ElementId; + IEEEtypes_Len_t Len; + UINT8 CfpCnt; + UINT8 CfpPeriod; + UINT16 CfpMaxDuration; + UINT16 CfpDurationRemaining; +} MLAN_PACK_END IEEEtypes_CfParamElement_t; + +/* Since uAP is the only one that holds the TIM statically, we'll +** define a max size for the PVB that accomodates a max AID of 32 +** used by the uAP. Size is 5 to account for broadcast. +*/ +#define MAX_TIM_ELEMENT_PVB_LENGTH 5 +/* +** TIM, which contains: +** 1) DTIM count - how many beacons (including the current beacon +** frame) appear before the next DTIM; a count of 0 indicates the +** current TIM is the DTIM +** +** 2) DTIM period - indicates the number of beacon intervals between +** successive DTIMs +** +** 3) Bitmap control - contains the traffic indicator bit associated +** with association ID 0 - this is set to 1 for TIM elements with a +** a value of 0 in the DTIM count field when one or more broadcast +** or multicast frames are buffered at the AP. The remaining bits +** of the field form the bitmap offset +** +** 4) Partial virtual bitmap - indicates which stations have messages +** buffered at the AP, for which the AP is prepared to deliver at +* the time the beacon frame is transmitted +*/ +typedef MLAN_PACK_START struct { + IEEEtypes_ElementId_e ElementId; + IEEEtypes_Len_t Len; + UINT8 DtimCnt; + UINT8 DtimPeriod; + UINT8 BitmapCtl; + UINT8 PartialVirtualBitmap[MAX_TIM_ELEMENT_PVB_LENGTH]; +} MLAN_PACK_END IEEEtypes_TimElement_t; + +/* +** IBSS parameters necessary to support an IBSS +*/ +typedef MLAN_PACK_START struct { + IEEEtypes_ElementId_e ElementId; + IEEEtypes_Len_t Len; + UINT16 AtimWindow; +} MLAN_PACK_END IEEEtypes_IbssParamElement_t; + +/* +** The challenge text used in authentication exchanges +*/ +typedef MLAN_PACK_START struct { + IEEEtypes_ElementId_e ElementId; + IEEEtypes_Len_t Len; + UINT8 Text[IEEEtypes_CHALLENGE_TEXT_SIZE]; +} MLAN_PACK_END IEEEtypes_ChallengeText_t; + +typedef MLAN_PACK_START struct { + UINT8 ExtCapability; + UINT32 Capability; + + UINT8 SsIdLength; + IEEEtypes_SsId_t SsId; + +} MLAN_PACK_END IEEEtypes_SsIdL_HidSsId_t; + +typedef MLAN_PACK_START struct { + IEEEtypes_ElementId_e ElementId; + IEEEtypes_Len_t Len; + + UINT8 OuiType[4]; /* 00:50:f2:05 */ + + UINT8 PrimarySsIdExtCapability; + + /* Start optional fields */ + + UINT8 SsIdCount; + + /* SsIdCount # of hidden SSIDs, not a fixed size substructure */ + IEEEtypes_SsIdL_HidSsId_t hidSsid[1]; + +} MLAN_PACK_END IEEEtypes_SsIdLElement_t; + +typedef MLAN_PACK_START struct { + IEEEtypes_ElementId_e ElementId; + IEEEtypes_Len_t Len; + UINT8 OuiType[4]; + UINT8 Data[1]; + +} MLAN_PACK_END IEEEtypes_WPS_DataElement_t; + +/* This structure is a member of BssConfig_t which is referenced by ROMed code. +** Any increase in the size of this structure will cause the subsequent elements +** of BssConfig_t to move forward, in turn affecting the ROM code. Therefore, +** any changes to this structure should be done taking into account its effect +** on BssConfig_t and ROM code. +*/ +typedef MLAN_PACK_START struct { + IEEEtypes_ElementId_e ElementId; + IEEEtypes_Len_t Len; + + UINT8 BSS_CoexistSupport:1; /* bit 0 */ + UINT8 Reserved1:1; /* bit 1 */ + UINT8 ExtChanSwitching:1; /* bit 2 */ + UINT8 RejectUnadmFrame:1; /* bit 3 */ + UINT8 PSMP_Capable:1; /* bit 4 */ + UINT8 Reserved5:1; /* bit 5 */ + UINT8 SPSMP_Support:1; /* bit 6 */ + UINT8 Event:1; /* bit 7 */ + UINT8 Diagnostics:1; /* bit 8 */ + UINT8 MulticastDiagnostics:1; /* bit 9 */ + UINT8 LocationTracking:1; /* bit 10 */ + UINT8 FMS:1; /* bit 11 */ + UINT8 ProxyARPService:1; /* bit 12 */ + UINT8 CollocatedIntf:1; /* bit 13 */ + UINT8 CivicLocation:1; /* bit 14 */ + UINT8 GeospatialLocation:1; /* bit 15 */ + UINT8 TFS:1; /* bit 16 */ + UINT8 WNM_Sleep:1; /* bit 17 */ + UINT8 TIM_Broadcast:1; /* bit 18 */ + UINT8 BSS_Transition:1; /* bit 19 */ + UINT8 QoSTrafficCap:1; /* bit 20 */ + UINT8 AC_StationCount:1; /* bit 21 */ + UINT8 MultipleBSSID:1; /* bit 22 */ + UINT8 TimingMeasurement:1; /* bit 23 */ + UINT8 ChannelUsage:1; /* bit 24 */ + UINT8 SSID_List:1; /* bit 25 */ + UINT8 DMS:1; /* bit 26 */ + UINT8 UTC:1; /* bit 27 */ + UINT8 TDLSPeerUAPSDSupport:1; /* bit 28 */ + UINT8 TDLSPeerPSMSupport:1; /* bit 29 */ + UINT8 TDLSChannelSwitching:1; /* bit 30 */ + UINT8 Reserved31:1; + UINT8 Reserved32_36:5; + UINT8 TDLSSupport:1; /* bit 37 */ + UINT8 TDLSProhibited:1; /* bit 38 */ + UINT8 TDLSChlSwitchProhib:1; /* bit 39 */ + UINT8 Reserved40_47:8; + UINT8 Reserved48_55:8; + UINT8 Reserved56_60:5; + UINT8 TDLSWiderBandSupport:1; + UINT8 OpModeNotification:1; /* bit 62 */ + UINT8 Reserved63:1; /* bit 63 */ +/* This structure is a member of BssConfig_t which is referenced by ROMed code. +** Any increase in the size of this structure will cause the subsequent elements +** of BssConfig_t to move forward, in turn affecting the ROM code. Therefore, +** any changes to this structure should be done taking into account its effect +** on BssConfig_t and ROM code. +*/ +} MLAN_PACK_END IEEEtypes_ExtCapability_t; + +/* +** The HT Capability Element +*/ + +typedef enum { + STATIC_SM_PS, + DYNAMIC_SM_PS, + RESERVED_SM_PS, + DISABLE_SM_PS +} IEEEtypes_HtCap_SMPS_e; + +typedef MLAN_PACK_START struct { + UINT16 LdpcCoding:1; + UINT16 SuppChanWidth:1; + UINT16 MIMOPowerSave:2; + UINT16 GFPreamble:1; + UINT16 ShortGI20MHZ:1; + UINT16 ShortGI40MHZ:1; + UINT16 TxSTBC:1; + UINT16 RxSTBC:2; + UINT16 DelayedBA:1; + UINT16 MaximalAMSDUSize:1; + UINT16 DsssCck40MHzMode:1; + UINT16 Psmp:1; + UINT16 FortyMHzIntolerant:1; + UINT16 L_SIG_TXOP_Protection:1; +} MLAN_PACK_END IEEEtypes_HT_Cap_Info_t; + +typedef MLAN_PACK_START struct { + UINT8 MaxRxAMpduFactor:2; + UINT8 MpduDensity:3; + UINT8 Reserved_5_7:3; +} MLAN_PACK_END IEEEtypes_HT_Mac_Params_t; + +typedef MLAN_PACK_START struct { + UINT16 Pco:1; + UINT16 TransitionTime:2; + UINT16 Reserved:5; + + UINT16 McsFeedback:2; + UINT16 HtcSupport:1; + UINT16 RdResponder:1; + UINT16 Reserved2:4; +} MLAN_PACK_END IEEEtypes_HT_Ext_Cap_t; + +typedef MLAN_PACK_START struct { + UINT32 TxBFCapable:1; /* B0 */ + UINT32 RxStaggeredSounding:1; + UINT32 TxStaggeredSounding:1; + UINT32 RxNDPCap:1; + UINT32 TxNDPCap:1; + UINT32 ImplicitTxBF:1; + UINT32 Calibration:2; + UINT32 ExplCSITxBF:1; /* B8 */ + + UINT32 ExplUcompSteerMatrix:1; + UINT32 ExplCompSteerMatrix:1; + UINT32 ExplBFCSIFeedback:2; + UINT32 ExplUcompSteerFeedback:2; + UINT32 ExplCompSteerMatrixFeedback:2; + UINT32 MinimalGrouping:2; + UINT32 CSINumMFAntennae:2; /* B20 */ + + UINT32 UcompSteerMatrixBFAntennae:2; + UINT32 CompSteerMatrixBFAntennae:2; + UINT32 CSIMaxMaxBeamSupported:2; + UINT32 ChanEstCapability:2; + UINT32 Reserved:3; /* B31 */ + +} MLAN_PACK_END IEEEtypes_HT_TXBF_Cap_t; + +typedef MLAN_PACK_START struct { + UINT8 ASCapable:1; + UINT8 ExplCSITxASCapable:1; + UINT8 IndicesFeedbackTxAS:1; + UINT8 ExplicitCSIFeedback:1; + UINT8 AntIndicesFeedback:1; + UINT8 RxASCapable:1; + UINT8 TxSoundingPPDUs:1; + UINT8 Reserved:1; + +} MLAN_PACK_END IEEEtypes_HT_AS_Cap_t; + +typedef enum { + HTC_NO_FEEDBACK = 0, + HTC_CSI, + HTC_UCOMP_BF, + HTC_COMP_BF +} IEEEtypes_HTC_CSI_Types_e; + +typedef MLAN_PACK_START struct { + UINT16 Nc:2; + UINT16 Nr:2; + UINT16 ChanW:1; + UINT16 Ng:2; + UINT16 Coeff_size:2; + UINT16 Codebook_info:2; + UINT16 Re_segments:2; + UINT16 Reserved:2; + + UINT32 SndTimestamp; + +} MLAN_PACK_END IEEEtypes_MIMOCtrl_t; + +typedef MLAN_PACK_START struct { + UINT16 Reserved:1; + UINT16 TRQ:1; + UINT16 MAI:4; + UINT16 MFSI:3; + UINT16 MFB_ASELC:7; + +} MLAN_PACK_END IEEEtypes_LinkAdaptCtrl_t; + +typedef MLAN_PACK_START struct { + IEEEtypes_LinkAdaptCtrl_t LA_Ctrl; + UINT16 CalibPos:2; + UINT16 CalibSeq:2; + UINT16 Reserved:2; + UINT16 CSI:2; + UINT16 NDPAnnounce:1; + UINT16 Reserved2:5; + UINT16 ACConst:1; + UINT16 RDG_MorePPDU:1; + +} MLAN_PACK_END IEEEtypes_HTCtrl_t; + +#define HTC_NDP_ANNOUNCE BIT24 + +#define IEEEtypes_MCS_BITMAP_SIZE 16 +typedef UINT8 IEEEtypes_Supported_MCS_Bitmap_t[IEEEtypes_MCS_BITMAP_SIZE]; + +typedef MLAN_PACK_START struct { + IEEEtypes_ElementId_e ElementId; + IEEEtypes_Len_t Len; + + IEEEtypes_HT_Cap_Info_t HtCapInfo; + IEEEtypes_HT_Mac_Params_t HtMacParams; + IEEEtypes_Supported_MCS_Bitmap_t SupportedMcsBitmap; + IEEEtypes_HT_Ext_Cap_t ExtHTCaps; + IEEEtypes_HT_TXBF_Cap_t TxBFCaps; + IEEEtypes_HT_AS_Cap_t ASCaps; + +} MLAN_PACK_END IEEEtypes_HT_Capability_t; + +/* +** The HT Information Element +*/ +typedef MLAN_PACK_START struct { + IEEEtypes_ElementId_e ElementId; + IEEEtypes_Len_t Len; + + UINT8 PrimaryChan; + /*-----------------------------------------------*/ + UINT8 SecChanOffset:2; + UINT8 ChanWidth:1; + UINT8 RifsMode:1; + UINT8 CtrlLedAccessOnly:1; + UINT8 SrvIntrvlGran:3; + /*-----------------------------------------------*/ + UINT16 HtProtection:2; + UINT16 NonGfHtStaPresent:1; + UINT16 Reserved1:1; + UINT16 ObssNonHtStaPresent:1; + UINT16 Reserved2:11; + /*-----------------------------------------------*/ + UINT16 Reserved3:6; + UINT16 DualBeacon:1; + UINT16 DualCtsProtection:1; + UINT16 StbcBeacon:1; + UINT16 L_SIGTxopProtectionAllowed:1; + UINT16 PcoActive:1; + UINT16 PcoPhase:1; + UINT16 Reserved4:4; + /*-----------------------------------------------*/ + IEEEtypes_Supported_MCS_Bitmap_t BasicMcsSetBitmap; + +} MLAN_PACK_END IEEEtypes_HT_Information_t; + +/* secondary channel offset */ +/* 20/40 BSS Coexistence */ +typedef MLAN_PACK_START struct { + IEEEtypes_ElementId_e ElementId; + IEEEtypes_Len_t Len; + + UINT8 InfoResuest:1; + UINT8 FortyMHzIntolerant:1; + UINT8 Width20MHzRequest:1; + UINT8 Reserved:5; + +} MLAN_PACK_END IEEEtypes_20N40_BSS_Coexist_t; + +/* overlapping BSS Scan Parameters */ +typedef MLAN_PACK_START struct { + IEEEtypes_ElementId_e ElementId; + IEEEtypes_Len_t Len; + + UINT16 OBSSScanPassiveDwell; + UINT16 OBSSScanActiveDwel; + UINT16 BSSChanWidthTriggerScanInt; + UINT16 OBSSScanPassiveTotal; + UINT16 OBSSScanActiveTotal; + UINT16 BSSWidthChanTransDelay; + UINT16 OBSSScanActiveThreshold; + +} MLAN_PACK_END IEEEtypes_OBSS_ScanParam_t; + +typedef MLAN_PACK_START struct { + UINT8 htSupported; + IEEEtypes_HT_Capability_t htCap; + IEEEtypes_HT_Information_t htInfo; + UINT8 secChanOffset; + IEEEtypes_20N40_BSS_Coexist_t coexist; + IEEEtypes_OBSS_ScanParam_t scanParam; + +} MLAN_PACK_END HtEntry_t; + +#define DOT11AC_VHTCAP_MAX_MPDU_LEN_0 3895 +#define DOT11AC_VHTCAP_MAX_MPDU_LEN_1 7991 +#define DOT11AC_VHTCAP_MAX_MPDU_LEN_2 11454 +#define DOT11AC_VHTCAP_MAX_MPDU_LEN_3 3895 //reserved value + + /* IE definitions based on draft 1.4. + */ +typedef MLAN_PACK_START struct { + UINT32 MaxMpduLen:2; + UINT32 SuppChanWidthSet:2; + UINT32 RxLDPC:1; + UINT32 ShortGI_80:1; + UINT32 ShortGI_160:1; + UINT32 TxSTBC:1; + /*------------------------*/ + UINT32 RxSTBC:3; + UINT32 SUBFerCap:1; + UINT32 SUBFeeCap:1; + UINT32 NumBFerAnt:3; + /*------------------------*/ + UINT32 NumSoundingDim:3; + UINT32 MUBFerCap:1; + UINT32 MUBFeeCap:1; + UINT32 VHTTxOPPs:1; + UINT32 HTC_VHT_Cap:1; + UINT32 MaxAMPDULenExp:3; + UINT32 VHTLinkAdaptCap:2; + UINT32 Reserved:4; + +} MLAN_PACK_END IEEEtypes_VHT_Cap_Info_t; + +typedef MLAN_PACK_START struct { + UINT16 RxMCSMap; + UINT16 RxHighestDataRate:13; + UINT16 Reserved1:3; + + UINT16 TxMCSMap; + UINT16 TxHighestDataRate:13; + UINT16 Reserved2:3; + +} MLAN_PACK_END IEEEtypes_VHT_Supp_MCS_Set_t; + +typedef MLAN_PACK_START struct { + IEEEtypes_ElementId_e ElementId; + IEEEtypes_Len_t Len; + + IEEEtypes_VHT_Cap_Info_t VhtCap; + IEEEtypes_VHT_Supp_MCS_Set_t VhtSuppMcsSet; + +} MLAN_PACK_END IEEEtypes_VHT_Capability_t; + +typedef MLAN_PACK_START struct { + UINT8 ChanWidth; + UINT8 ChanCenterFreq1; + UINT8 ChanCenterFreq2; + +} MLAN_PACK_END IEEEtypes_VHT_Op_Info_t; + +/* +** The VHT Operation Element +*/ +typedef MLAN_PACK_START struct { + IEEEtypes_ElementId_e ElementId; + IEEEtypes_Len_t Len; + + IEEEtypes_VHT_Op_Info_t VhtOpInfo; + UINT16 VhtBasicMcsSet; + +} MLAN_PACK_END IEEEtypes_VHT_Operation_t; + +typedef MLAN_PACK_START struct { + UINT8 chanWidth:2; + UINT8 reserved:2; + UINT8 rxNss:3; + UINT8 rxNssType:1; + +} MLAN_PACK_END IEEEtypes_VHT_OpMode_t; + +typedef MLAN_PACK_START struct { + IEEEtypes_ElementId_e ElementId; + IEEEtypes_Len_t Len; + + IEEEtypes_VHT_OpMode_t VhtOpMode; +} MLAN_PACK_END IEEEtypes_VHT_OpModeNotification_t; + +typedef MLAN_PACK_START struct { + IEEEtypes_ElementId_e ElementId; + IEEEtypes_Len_t Len; + /** WideBW has same structure as VHT_OP_INFO*/ + IEEEtypes_VHT_Op_Info_t wideBwCs; +} MLAN_PACK_END IEEEtypes_WIde_BW_CS_t; + +typedef MLAN_PACK_START struct { + IEEEtypes_ElementId_e ElementId; + IEEEtypes_Len_t Len; + IEEEtypes_AId_t Aid; +} MLAN_PACK_END IEEEtypes_AIDElement_t; + +/* +***************************************************************************** +** +** +** 802.11k RRM definitions +** +** +***************************************************************************** +*/ +typedef MLAN_PACK_START struct { + IEEEtypes_ElementId_e ElementId; + IEEEtypes_Len_t Len; + + /* First byte */ + UINT8 LinkMeas:1; + UINT8 NborRpt:1; + UINT8 ParallelMeas:1; + UINT8 RepeatMeas:1; + UINT8 BcnPassiveMeas:1; + UINT8 BcnActiveMeas:1; + UINT8 BcnTableMeas:1; + UINT8 BcnMeasRptCond:1; + + /* Second byte */ + UINT8 FrameMeas:1; + UINT8 ChanLoadMeas:1; + UINT8 NoiseHistMeas:1; + UINT8 StatsMeas:1; + UINT8 LciMeas:1; + UINT8 LciAzimuth:1; + UINT8 TxStreamMeas:1; + UINT8 TrigTxStreamMeas:1; + + /* Third byte */ + UINT8 ApChanRpt:1; + UINT8 RrmMib:1; + UINT8 OpChanMaxMeas:3; + UINT8 NonOpChanMaxMeas:3; + + /* Fourth byte */ + UINT8 MeasPilot:3; + UINT8 MeasPilotTxInfo:1; + UINT8 NborRptTsfOffset:1; + UINT8 RcpiMeas:1; + UINT8 RsniMeas:1; + UINT8 BssAvgAccessDelay:1; + + /* Fifth byte */ + UINT8 BssAvailAdmCap:1; + UINT8 AntennaInfo:1; + UINT8 Reserved:6; + +} MLAN_PACK_END IEEEtypes_RrmEnabledCapabilities_t; + +typedef MLAN_PACK_START struct { + IEEEtypes_ElementId_e ElementId; + IEEEtypes_Len_t Len; + + UINT8 RegulatoryClass; + + UINT8 ChannelList[1]; + +} MLAN_PACK_END IEEEtypes_ApChanRptElement_t; + +typedef enum { + RPT_DTL_NO_FIX_OR_IE = 0, + RPT_DTL_ALL_FIX_AND_REQ_IES = 1, + RPT_DTL_ALL_FIX_AND_ALL_IES = 2, + +} IEEEtypes_ReportDetailLevel_e; + +typedef MLAN_PACK_START struct { + IEEEtypes_ElementId_e ElementId; + IEEEtypes_Len_t Len; + + MLAN_PACK_START IEEEtypes_ReportDetailLevel_e MLAN_PACK_END + RptDetailLevel; + +} MLAN_PACK_END IEEEtypes_ReportingDetailElement_t; + +typedef MLAN_PACK_START struct { + IEEEtypes_ElementId_e ElementId; + IEEEtypes_Len_t Len; + + IEEEtypes_ElementId_e IeList[1]; + +} MLAN_PACK_END IEEEtypes_RequestElement_t; + +/* +***************************************************************************** +** +** +** 802.11d and 802.11j Country IE definitions +** +** +***************************************************************************** +*/ + +/** Regulatory Triplet component in the country IE */ +typedef MLAN_PACK_START struct { + UINT8 RegulatoryExtensionId; + UINT8 RegulatoryClass; + UINT8 CoverageClass; + +} MLAN_PACK_END IEEEtypes_RegulatoryTriplet_t; + +/** Subband Triplet component in the country IE */ +typedef MLAN_PACK_START struct { + UINT8 FirstChan; + UINT8 NumChans; + UINT8 MaxTxPower; + +} MLAN_PACK_END IEEEtypes_SubbandTriplet_t; + +/* Country Info Triplet union comprised of subband and potentially regulatory + * triplets + */ +typedef MLAN_PACK_START union { + UINT8 TripletIdentifier; + IEEEtypes_SubbandTriplet_t Subband; + IEEEtypes_RegulatoryTriplet_t Regulatory; + +} MLAN_PACK_END IEEEtypes_CountryInfoTriplet_t; + +typedef MLAN_PACK_START struct { + IEEEtypes_ElementId_e ElementId; + IEEEtypes_Len_t Len; + UINT8 CountryCode[IEEEtypes_COUNTRY_CODE_SIZE]; + IEEEtypes_CountryInfoTriplet_t Triplets[IEEEtypes_COUNTRY_MAX_TRIPLETS]; + +} MLAN_PACK_END IEEEtypes_CountryInfoElement_t; + +/* +***************************************************************************** +** +** +** 802.11h TPC definitions +** +** +***************************************************************************** +*/ + +/* +** Power Constraint IE - for 802.11h TPC. Specifies a Local +** Power Constraint in a channel. +*/ +typedef MLAN_PACK_START struct { + IEEEtypes_ElementId_e ElementId; + IEEEtypes_Len_t Len; + UINT8 localConstraint; +} MLAN_PACK_END IEEEtypes_PowerConstraintElement_t; + +/* +** Power Capability IE - for 802.11h TPC. Specifies the +** min and max power the station is capable of transmitting with. +*/ +typedef MLAN_PACK_START struct { + IEEEtypes_ElementId_e ElementId; + IEEEtypes_Len_t Len; + UINT8 minTxPwr; + UINT8 maxTxPwr; +} MLAN_PACK_END IEEEtypes_PowerCapabilityElement_t; + +/* +** 802.11h TPC Request IE - used for requesting a peer station +** to send Tx power and Link Margin. +*/ +typedef MLAN_PACK_START struct { + IEEEtypes_ElementId_e ElementId; + IEEEtypes_Len_t Len; +} MLAN_PACK_END IEEEtypes_TPCRequestElement_t; + +typedef MLAN_PACK_START struct { + IEEEtypes_ElementId_e ElementId; + IEEEtypes_Len_t Len; + UINT8 TxPwr; + UINT8 LinkMargin; +} MLAN_PACK_END IEEEtypes_TPCReportElement_t; + +#define WIFI_TPCRPT_OUI_SUBTYPE 0x08 + +typedef MLAN_PACK_START struct { + IEEEtypes_ElementId_e ElementId; + IEEEtypes_Len_t Len; + + UINT8 OuiType[4]; /* 00:50:f2:08 */ + UINT8 OuiSubType; + + UINT8 TxPwr; + UINT8 LinkMargin; + +} MLAN_PACK_END IEEEtypes_WiFi_TPCReportElement_t; + +/* +***************************************************************************** +** +** +** 802.11h DFS definitions +** +** +***************************************************************************** +*/ +typedef MLAN_PACK_START struct { + IEEEtypes_ElementId_e ElementId; + IEEEtypes_Len_t Len; + + UINT8 ChannelSwitchMode; + UINT8 ChannelNumber; + UINT8 ChannelSwitchCount; +} MLAN_PACK_END IEEEtypes_ChannelSwitchElement_t; + +typedef MLAN_PACK_START struct { + UINT8 BSS:1; + UINT8 OFDM_Preamble:1; + UINT8 Unidentified:1; + UINT8 Radar:1; + UINT8 Unmeasured:1; + UINT8 Reserved:3; +} MLAN_PACK_END IEEEtypes_DFS_Map_t; + +typedef MLAN_PACK_START struct { + UINT8 ChannelNumber; + IEEEtypes_DFS_Map_t DFS_Map; +} MLAN_PACK_END IEEEtypes_ChannelMap_t; + +#define MAX_NUMBER_OF_DFS_CHANNELS 25 +typedef MLAN_PACK_START struct { + IEEEtypes_ElementId_e ElementId; + IEEEtypes_Len_t Len; + + IEEEtypes_MacAddr_t DFS_Owner; + UINT8 DFS_Recovery_Interval; + + /* For the channels in .11a */ + IEEEtypes_ChannelMap_t Channel_Map[MAX_NUMBER_OF_DFS_CHANNELS]; +} MLAN_PACK_END IEEEtypes_IbssDfsElement_t; + +typedef MLAN_PACK_START struct { + IEEEtypes_ElementId_e ElementId; + IEEEtypes_Len_t Len; + + UINT8 QuietCount; + UINT8 QuietPeriod; + UINT16 QuietDuration; + UINT16 QuietOffset; +} MLAN_PACK_END IEEEtypes_QuietElement_t; + +typedef MLAN_PACK_START struct { + UINT8 FirstChannelNumber; + UINT8 NumberOfChannels; +} MLAN_PACK_END ChannelsTuple_t; + +#define MAX_CHANNEL_TUPLES 20 +typedef MLAN_PACK_START struct { + IEEEtypes_ElementId_e ElementId; + IEEEtypes_Len_t Len; + + ChannelsTuple_t ChannelTuple[MAX_CHANNEL_TUPLES]; +} MLAN_PACK_END IEEEtypes_SupportedChannelsElement_t; + +typedef MLAN_PACK_START struct { + IEEEtypes_ElementId_e ElementId; + IEEEtypes_Len_t Len; + + UINT8 currRegClass; + UINT8 suppRegClass; +} MLAN_PACK_END IEEEtypes_SupportedRegClasses_t; + +/* +***************************************************************************** +** +** +** 802.11y definitions +** +** +***************************************************************************** +*/ +typedef MLAN_PACK_START struct { + IEEEtypes_ElementId_e ElementId; + IEEEtypes_Len_t Len; + + UINT8 ChannelSwitchMode; + UINT8 RegClass; + UINT8 ChannelNumber; + UINT8 ChannelSwitchCount; +} MLAN_PACK_END IEEEtypes_ExtChannelSwitchElement_t; + +/* +***************************************************************************** +** +** +** 802.11e definitions +** +** +***************************************************************************** +*/ +typedef MLAN_PACK_START struct { + IEEEtypes_ElementId_e ElementId; + IEEEtypes_Len_t Len; + + UINT16 StaCount; + UINT8 ChnlUtil; + UINT16 AdmCap; +} MLAN_PACK_END IEEEtypes_BSSLoadElement_t; + +/* +***************************************************************************** +** +** +** 802.11i and WPA definitions +** +** +***************************************************************************** +*/ +typedef enum { + IEEEtypes_RSN_AUTH_KEY_SUITE_RSVD = 0, + IEEEtypes_RSN_AUTH_KEY_SUITE_8021X = 1, + IEEEtypes_RSN_AUTH_KEY_SUITE_PSK = 2, + IEEEtypes_AKM_SUITE_FT_1X = 3, + IEEEtypes_AKM_SUITE_FT_PSK = 4, + IEEEtypes_AKM_SUITE_1X_SHA256 = 5, + IEEEtypes_AKM_SUITE_PSK_SHA256 = 6 +} IEEEtypes_RSN_Auth_Key_Suite; + +/* Cipher Suite Selector */ +typedef enum { + IEEEtypes_RSN_CIPHER_SUITE_NONE = 0, + IEEEtypes_RSN_CIPHER_SUITE_WEP40, + IEEEtypes_RSN_CIPHER_SUITE_TKIP, + IEEEtypes_RSN_CIPHER_SUITE_WRAP, + IEEEtypes_RSN_CIPHER_SUITE_CCMP, + IEEEtypes_RSN_CIPHER_SUITE_WEP104 +} IEEEtypes_RSN_Cipher_Suite; + +typedef MLAN_PACK_START struct { + IEEEtypes_ElementId_e ElementId; + IEEEtypes_Len_t Len; + + UINT8 OuiType[4]; /* 00:50:f2:01 */ + UINT16 Ver; + UINT8 GrpKeyCipher[4]; + UINT16 PwsKeyCnt; + UINT8 PwsKeyCipherList[4]; + UINT16 AuthKeyCnt; + UINT8 AuthKeyList[4]; +} MLAN_PACK_END IEEEtypes_WPAElement_t; + +typedef MLAN_PACK_START struct { + UINT8 PreAuth:1; /* B0 */ + UINT8 NoPairwise:1; + UINT8 PtksaReplayCtr:2; + UINT8 GtksaReplayCtr:2; + UINT8 MFPR:1; + UINT8 MFPC:1; + + UINT8 Reserved_8:1; /* B8 */ + UINT8 PeerkeyEnabled:1; + UINT8 SppAmsduCap:1; + UINT8 SppAmsduReq:1; + UINT8 PBAC:1; + UINT8 Reserved_13_15:3; +} MLAN_PACK_END IEEEtypes_RSNCapability_t; + +typedef MLAN_PACK_START struct { + IEEEtypes_ElementId_e ElementId; + IEEEtypes_Len_t Len; + + /* All elements after Ver field are optional per the spec. + ** + ** - AuthKeyList and PwsKeyCipherList can have multiple elements so + ** static parsing of this structure is not possible. + ** + ** - RsnCap, PMKIDCnt/List, and GrpMgmtCipher are often not included + ** + ** - If any optional element is included, all preceding elements must + ** also be included. Once an optional element is not inserted, the + ** IE construction ends. (i.e. RsnCap must be included if PMKIDCnt/List + ** is needed). + */ + + UINT16 Ver; + UINT8 GrpKeyCipher[4]; + UINT16 PwsKeyCnt; + UINT8 PwsKeyCipherList[4]; + UINT16 AuthKeyCnt; + UINT8 AuthKeyList[4]; + + IEEEtypes_RSNCapability_t RsnCap; + + UINT16 PMKIDCnt; + UINT8 PMKIDList[16]; + + UINT8 GrpMgmtCipher[4]; +} MLAN_PACK_END IEEEtypes_RSNElement_t; + +/* +***************************************************************************** +** +** +** 802.11r definitions +** +** +***************************************************************************** +*/ + +typedef MLAN_PACK_START struct { + UINT8 FtOverBss:1; + UINT8 FtOverAir:1; + + UINT8 Reserved:6; + +} MLAN_PACK_END IEEEtypes_FT_CapabilityPolicy_t; + +typedef MLAN_PACK_START struct { + IEEEtypes_ElementId_e ElementId; + IEEEtypes_Len_t Len; + + UINT16 MobilityDomainId; + + IEEEtypes_FT_CapabilityPolicy_t FtCapPol; + +} MLAN_PACK_END IEEEtypes_MobilityDomainElement_t; + +typedef MLAN_PACK_START struct { + UINT8 KeyId:2; + + UINT8 Reserved1:6; + UINT8 Reserved2:8; + +} MLAN_PACK_END IEEEtypes_GtkKeyInfo_t; + +typedef MLAN_PACK_START struct { + IEEEtypes_ElementId_e ElementId; + IEEEtypes_Len_t Len; + + IEEEtypes_GtkKeyInfo_t KeyInfo; + UINT8 KeyLen; + UINT8 RSC[8]; + + UINT8 Key[32]; /* Variable length from 5 to 32 */ + +} IEEEtypes_GtkElement_t; + +typedef MLAN_PACK_START struct { + UINT8 Reserved; + UINT8 InfoElementCount; + +} MLAN_PACK_END IEEEtypes_FT_MICControl_t; + +#define FTIE_MIC_LEN 16 +typedef MLAN_PACK_START struct { + IEEEtypes_ElementId_e ElementId; + IEEEtypes_Len_t Len; + + IEEEtypes_FT_MICControl_t MicControl; + + UINT8 MIC[FTIE_MIC_LEN]; + UINT8 ANonce[32]; + UINT8 SNonce[32]; + + UINT8 SubElem[1]; + +} MLAN_PACK_END IEEEtypes_FastBssTransElement_t; + +typedef MLAN_PACK_START enum { + TI_TYPE_RESERVED = 0, + TI_TYPE_REASSOC_DEADLINE_TUS = 1, + TI_TYPE_KEY_LIFETIME_SECS = 2, + TI_TYPE_ASSOCIATION_COMEBACK_TIME = 3, + +} IEEEtypes_TimeoutInterval_e; + +typedef MLAN_PACK_START struct { + IEEEtypes_ElementId_e ElementId; + IEEEtypes_Len_t Len; + + IEEEtypes_TimeoutInterval_e TimeoutIntervalType; + UINT32 TimeoutInterval; + +} MLAN_PACK_END IEEEtypes_TimeoutIntervalElement_t; + +typedef MLAN_PACK_START struct { + IEEEtypes_ElementId_e ElementId; + IEEEtypes_Len_t Len; + + UINT8 Identifier; + UINT8 DescCount; + + IEEEtypes_StatusCode_t StatusCode; + +} MLAN_PACK_END IEEEtypes_RICDataElement_t; + +typedef MLAN_PACK_START enum { + RIC_RESOURCE_BLOCK_ACK = 1, + +} MLAN_PACK_END IEEEtypes_RICResource_e; + +typedef MLAN_PACK_START struct { + IEEEtypes_ElementId_e ElementId; + IEEEtypes_Len_t Len; + + IEEEtypes_RICResource_e ResourceType; + + UINT8 Data[1]; + +} MLAN_PACK_END IEEEtypes_RICDescElement_t; + +/* +***************************************************************************** +** +** +** 802.11w definitions +** +** +***************************************************************************** +*/ +#define MMIE_MIC_LEN 8 +typedef MLAN_PACK_START struct { + IEEEtypes_ElementId_e ElementId; + IEEEtypes_Len_t Len; + + UINT8 KeyId[2]; + UINT8 IPN[6]; + + UINT8 MIC[MMIE_MIC_LEN]; + +} MLAN_PACK_END IEEEtypes_ManagementMICElement_t; + +/* +***************************************************************************** +** +** +** Management Frame Bodies +** +** +***************************************************************************** +*/ + +/* +** The parameter set relevant to the PHY +*/ +typedef MLAN_PACK_START union { + IEEEtypes_FhParamElement_t FhParamSet; + IEEEtypes_DsParamElement_t DsParamSet; + +} MLAN_PACK_END IEEEtypes_PhyParamElement_t; + +/* +** Service set parameters - for a BSS supporting, PCF, the +** CF parameter set is used; for an independent BSS, the IBSS +** parameter set is used. +*/ +typedef MLAN_PACK_START union { + IEEEtypes_CfParamElement_t CfParamSet; + IEEEtypes_IbssParamElement_t IbssParamSet; +} MLAN_PACK_END IEEEtypes_SsParamElement_t; + +typedef MLAN_PACK_START struct { + IEEEtypes_ElementId_e ElementId; + IEEEtypes_Len_t Len; + IEEEtypes_DataRate_t Rates[IEEEtypes_MAX_DATA_RATES]; +} MLAN_PACK_END IEEEtypes_ExtSuppRatesElement_t; + +typedef MLAN_PACK_START struct { + IEEEtypes_ElementId_e ElementId; + IEEEtypes_Len_t Len; + + MLAN_PACK_START union { + MLAN_PACK_START struct { + UINT8 NonERPPresent:1; + UINT8 UseProtection:1; + UINT8 BarkerPreamble:1; + UINT8 Reserved:5; + } bf; + UINT8 erpInfo; + } u; +} MLAN_PACK_END IEEEtypes_ERPInfoElement_t; + +#define IEEEtypes_BCN_FIXED_FIELD_SZ (sizeof(IEEEtypes_TimeStamp_t) \ + + sizeof(IEEEtypes_BcnInterval_t) \ + + sizeof(IEEEtypes_CapInfo_t)) + +typedef MLAN_PACK_START struct { + IEEEtypes_TimeStamp_t TimeStamp; + IEEEtypes_BcnInterval_t BcnInterval; + IEEEtypes_CapInfo_t CapInfo; + IEEEtypes_SsIdElement_t SsId; + IEEEtypes_SuppRatesElement_t SuppRates; + IEEEtypes_PhyParamElement_t PhyParamSet; + IEEEtypes_SsParamElement_t SsParamSet; + IEEEtypes_TimElement_t Tim; + IEEEtypes_ERPInfoElement_t ERPInfo; + IEEEtypes_ExtSuppRatesElement_t ExtSuppRates; +} MLAN_PACK_END IEEEtypes_Bcn_t; + +typedef MLAN_PACK_START struct { + IEEEtypes_ReasonCode_t ReasonCode; +} MLAN_PACK_END IEEEtypes_DisAssoc_t; + +typedef MLAN_PACK_START struct { + IEEEtypes_CapInfo_t CapInfo; + IEEEtypes_ListenInterval_t ListenInterval; + UINT8 IEBuffer[1]; +} MLAN_PACK_END IEEEtypes_AssocRqst_t; + +typedef MLAN_PACK_START struct { + IEEEtypes_CapInfo_t CapInfo; + IEEEtypes_StatusCode_t StatusCode; + IEEEtypes_AId_t AId; +} MLAN_PACK_END IEEEtypes_AssocRsp_t; + +typedef MLAN_PACK_START struct { + IEEEtypes_CapInfo_t CapInfo; + IEEEtypes_ListenInterval_t ListenInterval; + IEEEtypes_MacAddr_t CurrentApAddr; + UINT8 IEBuffer[1]; +} MLAN_PACK_END IEEEtypes_ReAssocRqst_t; + +typedef MLAN_PACK_START struct { + IEEEtypes_CapInfo_t CapInfo; + IEEEtypes_StatusCode_t StatusCode; + IEEEtypes_AId_t AId; +} MLAN_PACK_END IEEEtypes_ReAssocRsp_t; + +typedef MLAN_PACK_START struct { + IEEEtypes_SsIdElement_t SsId; + IEEEtypes_SuppRatesElement_t SuppRates; + IEEEtypes_ExtSuppRatesElement_t ExtSuppRates; +} MLAN_PACK_END IEEEtypes_ProbeRqst_t; + +typedef MLAN_PACK_START struct { + IEEEtypes_TimeStamp_t TimeStamp; + IEEEtypes_BcnInterval_t BcnInterval; + IEEEtypes_CapInfo_t CapInfo; + IEEEtypes_SsIdElement_t SsId; + IEEEtypes_SuppRatesElement_t SuppRates; + IEEEtypes_PhyParamElement_t PhyParamSet; + IEEEtypes_SsParamElement_t SsParamSet; + IEEEtypes_TimElement_t Tim; + IEEEtypes_ERPInfoElement_t ERPInfo; + IEEEtypes_ExtSuppRatesElement_t ExtSuppRates; +} MLAN_PACK_END IEEEtypes_ProbeRsp_t; + +typedef MLAN_PACK_START struct { + IEEEtypes_AuthAlg_t AuthAlg; + IEEEtypes_AuthTransSeq_t AuthTransSeq; + IEEEtypes_StatusCode_t StatusCode; + IEEEtypes_ChallengeText_t ChallengeText; +} MLAN_PACK_END IEEEtypes_Auth_t; + +typedef MLAN_PACK_START struct { + IEEEtypes_ReasonCode_t ReasonCode; +} MLAN_PACK_END IEEEtypes_Deauth_t; + +/*---------------------------------------------------------------------------*/ +/* IEEE 802.11 MLME SAP Interface Data Structures */ +/* */ +/* According to IEEE 802.11, services are provided by the MLME to the SME. */ +/* In the current architecture, the services are provided to the SME by the */ +/* MAC Management Service Task. This section describes data structures */ +/* needed for these services. */ +/*---------------------------------------------------------------------------*/ + +/* +** BSS Description Set +** +** A description of a BSS, providing the following: +** BssId: The ID of the BSS +** SsId: The SSID of the BSS +** BssType: The type of the BSS (INFRASTRUCTURE or INDEPENDENT) +** BcnPeriod: The beacon period (in time units) +** DtimPeriod: The DTIM period (in beacon periods) +** Tstamp: Timestamp of a received frame from the BSS; this is an 8 +** byte string from a probe response or beacon +** StartTs: The value of a station's timing synchronization function +** at the start of reception of the first octet of the +** timestamp field of a received frame (probe response or +** beacon) from a BSS; this is an 8 byte string +** PhyParamSet: The parameter set relevant to the PHY (empty if not +** needed by the PHY) +** SsParamSet: The service set parameters. These can consist of either +** the parameter set for CF periods or for an IBSS. +** Cap: The advertised capabilities of the BSS +** DataRates: The set of data rates that must be supported by all +** stations (the BSS basic rate set) +*/ +typedef MLAN_PACK_START struct { + IEEEtypes_MacAddr_t BssId; + IEEEtypes_SsId_t SsId; + IEEEtypes_Bss_e BssType; + IEEEtypes_BcnInterval_t BcnPeriod; + IEEEtypes_DtimPeriod_t DtimPeriod; + IEEEtypes_TimeStamp_t Tstamp; + IEEEtypes_TimeStamp_t StartTs; + IEEEtypes_PhyParamElement_t PhyParamSet; + IEEEtypes_SsParamElement_t SsParamSet; + IEEEtypes_CapInfo_t Cap; + IEEEtypes_DataRate_t DataRates[IEEEtypes_MAX_DATA_RATES_G]; + /* + ** DO NOT ADD ANY FIELDS TO THIS STRUCTURE. It is used in the middle of + ** the adhoc join command. Any changes will shift the binary layout + ** of the following fields in the command from the driver + */ +} MLAN_PACK_END IEEEtypes_BssDesc_t; + +typedef enum { + Band_2_4_GHz = 0, + Band_5_GHz = 1, + Band_4_GHz = 2, + +} ChanBand_e; + +#define NUM_CHAN_BAND_ENUMS 3 + +typedef enum { + ChanWidth_20_MHz = 0, + ChanWidth_10_MHz = 1, + ChanWidth_40_MHz = 2, + ChanWidth_80_MHz = 3, + +} ChanWidth_e; + +typedef enum { + SECONDARY_CHAN_NONE = 0, + SECONDARY_CHAN_ABOVE = 1, + SECONDARY_CHAN_BELOW = 3, + //reserved 2, 4~255 +} Chan2Offset_e; + +typedef enum { + MANUAL_MODE = 0, + ACS_MODE = 1, +} ScanMode_e; + +typedef MLAN_PACK_START struct { + IEEEtypes_ElementId_e ElementId; + IEEEtypes_Len_t Len; + UINT8 chan2Offset; +} MLAN_PACK_END IEEEtypes_SecondaryChannelOffset_t; + +/* This struct is used in ROM and existing fields should not be changed. */ +typedef MLAN_PACK_START struct { + ChanBand_e chanBand:2; + ChanWidth_e chanWidth:2; + Chan2Offset_e chan2Offset:2; + ScanMode_e scanMode:2; +} MLAN_PACK_END BandConfig_t; + +#define MASK_SCAN_MODE(pBandConfig) (*(UINT8*)(pBandConfig) &= 0x3f) + +#define BC_2_4_GHZ {Band_2_4_GHz, ChanWidth_20_MHz, SECONDARY_CHAN_NONE, MANUAL_MODE} +#define BC_5_GHZ {Band_5_GHz, ChanWidth_20_MHz, SECONDARY_CHAN_NONE, MANUAL_MODE} + +typedef MLAN_PACK_START struct { + BandConfig_t bandConfig; + UINT8 chanNum; + +} MLAN_PACK_END ChanBandInfo_t; + +typedef MLAN_PACK_START struct { + UINT8 passiveScan:1; + UINT8 disableChanFilt:1; + UINT8 multiDomainScan:1; + UINT8 rspTimeoutEn:1; + UINT8 hiddenSsidReport:1; + UINT8 firstScanCmd:1; + UINT8 reserved_6_7:2; + +} MLAN_PACK_END ChanScanMode_t; + +typedef MLAN_PACK_START struct { + BandConfig_t bandConfig; + UINT8 chanNum; + ChanScanMode_t scanMode; + UINT16 minScanTime; // unused - not removed since shared with + // host + UINT16 maxScanTime; + +} MLAN_PACK_END channelInfo_t; + +/* +** Join request message from the SME to establish synchronization with +** a BSS +*/ +typedef MLAN_PACK_START struct { + IEEEtypes_BssDesc_t BssDesc; +#ifdef OLD_DFS + IEEEtypes_QuietElement_t Quiet_Element; + IEEEtypes_IbssDfsElement_t IBSS_DFS_Element; +#endif + channelInfo_t JoinChannel; + + UINT8 *pIeBuf; + UINT16 ieBufLen; +} MLAN_PACK_END IEEEtypes_JoinCmd_t; + +/* +** Join confirm message sent from the MLME as a result of a join request; +** it reports the result of the join +*/ +typedef MLAN_PACK_START struct { + IEEEtypes_JoinResult_e Result; +} MLAN_PACK_END IEEEtypes_JoinCfrm_t; + +/* +** Authenticate request message sent from the SME to establish +** authentication with a specified peer MAC entity +*/ +typedef MLAN_PACK_START struct { + IEEEtypes_MacAddr_t PeerStaAddr; + IEEEtypes_AuthType_e AuthType; + IEEEtypes_AuthTransSeq_t AuthTransSeq; +} MLAN_PACK_END IEEEtypes_AuthCmd_t; + +/* +** Authenticate confirm message sent from the MLME as a result of an +** authenticate request; it reports the result of the authentication +*/ +typedef MLAN_PACK_START struct { + IEEEtypes_MacAddr_t PeerStaAddr; + IEEEtypes_AuthType_e AuthType; + IEEEtypes_AuthResult_e Result; +} MLAN_PACK_END IEEEtypes_AuthCfrm_t; + +/* +** Authenticate indication message sent from the MLME to report +** authentication with a peer MAC entity that resulted from an +** authentication procedure that was initiated by that MAC entity +*/ +typedef MLAN_PACK_START struct { + IEEEtypes_MacAddr_t PeerStaAddr; + IEEEtypes_AuthType_e AuthType; +} MLAN_PACK_END IEEEtypes_AuthInd_t; + +/* +** Deauthenticate request message sent from the SME to invalidate +** authentication with a specified peer MAC entity +*/ +typedef MLAN_PACK_START struct { + IEEEtypes_MacAddr_t PeerStaAddr; + channelInfo_t DeauthChanInfo; + IEEEtypes_ReasonCode_t Reason; +} MLAN_PACK_END IEEEtypes_DeauthCmd_t; + +/* +** Deauthenticate confirm message sent from the MLME as a result of a +** deauthenticate request message; it reports the result of the +** deauthentication +*/ +typedef MLAN_PACK_START struct { + IEEEtypes_MacAddr_t PeerStaAddr; + IEEEtypes_DeauthResult_e Result; +} MLAN_PACK_END IEEEtypes_DeauthCfrm_t; + +/* +** Deauthentication indication message sent from the MLME to report +** invalidation of an authentication with a peer MAC entity; the message +** is generated as a result of an invalidation of the authentication +*/ +typedef MLAN_PACK_START struct { + IEEEtypes_MacAddr_t PeerStaAddr; + IEEEtypes_ReasonCode_t Reason; +} MLAN_PACK_END IEEEtypes_DeauthInd_t; + +/* +** Internal Association command constructed from the TLV based host +** command struct +*/ +typedef struct { + IEEEtypes_MacAddr_t PeerStaAddr; + IEEEtypes_CapInfo_t CapInfo; + IEEEtypes_ListenInterval_t ListenInterval; + IEEEtypes_SsId_t SsId; + IEEEtypes_DataRate_t RateSet[IEEEtypes_MAX_DATA_RATES_G]; + + UINT8 *pIeBuf; + UINT16 ieBufLen; + +} IEEEtypes_AssocCmd_t; + +/* +** Association confirm message sent from the MLME as a result of an +** association request message; it reports the result of the assoication +*/ +typedef MLAN_PACK_START struct { + IEEEtypes_AssocResult_e Result; + IEEEtypes_AssocCmdFailurePoint_e FailurePoint; +} MLAN_PACK_END IEEEtypes_AssocCfrm_t; + +/* +** Disassociate request message sent from the SME to establish +** disassociation with an AP +*/ +typedef MLAN_PACK_START struct { + IEEEtypes_MacAddr_t PeerStaAddr; + IEEEtypes_ReasonCode_t Reason; +} MLAN_PACK_END IEEEtypes_DisassocCmd_t; + +/* +** Disassociate confirm message sent from the MLME as a result of a +** disassociate request message; it reports the result of the +** disassociation +*/ +typedef MLAN_PACK_START struct { + IEEEtypes_DisassocResult_e Result; +} MLAN_PACK_END IEEEtypes_DisassocCfrm_t; + +/* +** Disassociate indication message sent from the MLME to report the +** invalidation of an association relationship with a peer MAC entity; +** the message is generated as a result of an invalidation of an +** association relationship +*/ +typedef MLAN_PACK_START struct { + IEEEtypes_MacAddr_t PeerStaAddr; + IEEEtypes_ReasonCode_t Reason; +} MLAN_PACK_END IEEEtypes_DisassocInd_t; + +typedef MLAN_PACK_START struct { + IEEEtypes_ElementId_e ElementId; + IEEEtypes_Len_t Len; + IEEEtypes_MacAddr_t MacAddr; +} MLAN_PACK_END IEEEtypes_BssId_t; + +/* +** Start request message sent from the SME to start a new BSS; the BSS +** may be either an infrastructure BSS (with the MAC entity acting as the +** AP) or an independent BSS (with the MAC entity acting as the first +** station in the IBSS) +*/ +typedef MLAN_PACK_START struct { + IEEEtypes_SsId_t SsId; + IEEEtypes_Bss_e BssType; + IEEEtypes_BcnInterval_t BcnPeriod; + IEEEtypes_DtimPeriod_t DtimPeriod; + IEEEtypes_SsParamElement_t SsParamSet; + IEEEtypes_PhyParamElement_t PhyParamSet; + UINT16 Reserved; + IEEEtypes_CapInfo_t CapInfo; + IEEEtypes_DataRate_t OpRateSet[IEEEtypes_MAX_DATA_RATES_G]; +#ifdef OLD_DFS + IEEEtypes_QuietElement_t Quiet_Element; + IEEEtypes_IbssDfsElement_t IBSS_DFS_Element; +#endif + channelInfo_t JoinChannel; + UINT8 *pIeBuf; + UINT16 ieBufLen; + IEEEtypes_BssId_t BssId; +} MLAN_PACK_END IEEEtypes_StartCmd_t; + +/* +** Start confirm message sent from the MLME as a result of a start request +** message; it reports the results of the BSS creation procedure +*/ +typedef MLAN_PACK_START struct { + IEEEtypes_StartResult_e Result; +} MLAN_PACK_END IEEEtypes_StartCfrm_t; + +typedef MLAN_PACK_START struct { + IEEEtypes_MacAddr_t PeerStaAddr; +} MLAN_PACK_END IEEEtypes_TPCAdaptCmd_t; + +typedef MLAN_PACK_START struct { + IEEEtypes_TPCAdaptResult_e Result; +} MLAN_PACK_END IEEEtypes_TPCAdaptCfrm_t; + +typedef struct { + IEEEtypes_GenHdr_t Hdr; + UINT8 Body[8]; +} IEEEtypes_Frame_t; + +typedef MLAN_PACK_START struct { + IEEEtypes_MacAddr_t da; + IEEEtypes_MacAddr_t sa; + UINT16 type; +} MLAN_PACK_END ether_hdr_t; + +typedef MLAN_PACK_START struct { + ether_hdr_t Hdr; + UINT8 Body[1600 - 14]; +} MLAN_PACK_END IEEEtypes_8023_Frame_t; + +typedef MLAN_PACK_START struct { + IEEEtypes_ElementId_e ElemId; + IEEEtypes_Len_t Len; + UINT8 Oui[4]; + UINT8 Data[1]; +} MLAN_PACK_END IEEEtypes_WPSElement_t; + +/*---------------------------------*/ +/* IEEE 802.11 Management Messages */ +/*---------------------------------*/ + +#define WMM_STATS_PKTS_HIST_BINS 7 + +typedef MLAN_PACK_START enum { + AckPolicy_ImmediateAck = 0, + AckPolicy_NoAck = 1, + AckPolicy_ExplicitAck = 2, + AckPolicy_BlockAck = 3, + +} MLAN_PACK_END IEEEtypes_AckPolicy_e; + +typedef MLAN_PACK_START struct { + UINT16 userPriority:3; + UINT16 reserved1:1; + UINT16 eosp:1; + IEEEtypes_AckPolicy_e ackPolicy:2; + UINT16 amsdu:1; + UINT16 reserved2:8; + +} MLAN_PACK_END IEEEtypes_QosCtl_t; + +typedef MLAN_PACK_START struct { + UINT8 Version; + UINT8 SourceIpAddr[4]; + UINT8 DestIpAddr[4]; + UINT8 SourcePort[2]; + UINT8 DestPort[2]; + UINT8 DSCP; + UINT8 Protocol; + UINT8 Reserved; + +} MLAN_PACK_END IEEEtypes_TCLAS_IPv4_t; + +typedef MLAN_PACK_START struct { + IEEEtypes_ElementId_e ElementId; + IEEEtypes_Len_t Len; + + UINT8 UserPriority; + + UINT8 ClassifierType; + UINT8 ClassifierMask; + + MLAN_PACK_START union { + IEEEtypes_TCLAS_IPv4_t ipv4; + + } MLAN_PACK_END classifier; + +} MLAN_PACK_END IEEEtypes_TCLAS_t; + +typedef enum { + AC_BE = 0x0, + AC_BK, + AC_VI, + AC_VO, + + AC_MAX_TYPES +} IEEEtypes_WMM_AC_e; + +#define WMM_MAX_TIDS 8 +#define WMM_MAX_RX_PN_SUPPORTED 4 + +typedef MLAN_PACK_START struct { + UINT8 Aifsn:4; + UINT8 Acm:1; + UINT8 Aci:2; + UINT8 Rsvd1:1; + +} MLAN_PACK_END IEEEtypes_WMM_AC_ACI_AIFSN_t; + +typedef MLAN_PACK_START struct { + UINT8 EcwMin:4; + UINT8 EcwMax:4; + +} MLAN_PACK_END IEEEtypes_ECW_Min_Max_t; + +typedef MLAN_PACK_START struct { + IEEEtypes_WMM_AC_ACI_AIFSN_t AciAifsn; + IEEEtypes_ECW_Min_Max_t EcwMinMax; + UINT16 TxopLimit; + +} MLAN_PACK_END IEEEtypes_WMM_AC_Parameters_t; + +typedef MLAN_PACK_START struct { + UINT8 ParamSetCount:4; + UINT8 Reserved1:3; + UINT8 QosInfoUAPSD:1; + +} MLAN_PACK_END IEEEtypes_QAP_QOS_Info_t; + +typedef MLAN_PACK_START struct { + UINT8 AC_VO:1; + UINT8 AC_VI:1; + UINT8 AC_BK:1; + UINT8 AC_BE:1; + UINT8 QAck:1; + UINT8 MaxSP:2; + UINT8 MoreDataAck:1; + +} MLAN_PACK_END IEEEtypes_QSTA_QOS_Info_t; + +typedef MLAN_PACK_START union { + IEEEtypes_QAP_QOS_Info_t QAp; + IEEEtypes_QSTA_QOS_Info_t QSta; + +} MLAN_PACK_END IEEEtypes_QOS_Info_t; + +//added for TDLS +typedef MLAN_PACK_START struct { + IEEEtypes_ElementId_e ElementId; + IEEEtypes_Len_t Len; + IEEEtypes_QOS_Info_t QosInfo; +} MLAN_PACK_END IEEEtypes_QOS_Capability_t; + +typedef MLAN_PACK_START struct { + IEEEtypes_ElementId_e ElementId; + IEEEtypes_Len_t Len; + UINT8 OuiType[4]; /* 00:50:f2:02 */ + UINT8 OuiSubType; /* 01 */ + UINT8 Version; + + IEEEtypes_QOS_Info_t QosInfo; + UINT8 Reserved1; + IEEEtypes_WMM_AC_Parameters_t AcParams[AC_MAX_TYPES]; + +} MLAN_PACK_END IEEEtypes_WMM_ParamElement_t; + +typedef MLAN_PACK_START struct { + IEEEtypes_ElementId_e ElementId; + IEEEtypes_Len_t Len; + UINT8 OuiType[4]; /* 00:50:f2:02 */ + UINT8 OuiSubType; /* 00 */ + UINT8 Version; + + IEEEtypes_QOS_Info_t QosInfo; + +} MLAN_PACK_END IEEEtypes_WMM_InfoElement_t; + +typedef MLAN_PACK_START enum { + TSPEC_DIR_UPLINK = 0, + TSPEC_DIR_DOWNLINK = 1, + /* 2 is a reserved value */ + TSPEC_DIR_BIDIRECT = 3, + +} MLAN_PACK_END IEEEtypes_WMM_TSPEC_TS_Info_Direction_e; + +typedef MLAN_PACK_START enum { + TSPEC_PSB_LEGACY = 0, + TSPEC_PSB_TRIG = 1, + +} MLAN_PACK_END IEEEtypes_WMM_TSPEC_TS_Info_PSB_e; + +typedef MLAN_PACK_START enum { + /* 0 is reserved */ + TSPEC_ACCESS_EDCA = 1, + TSPEC_ACCESS_HCCA = 2, + TSPEC_ACCESS_HEMM = 3, + +} MLAN_PACK_END IEEEtypes_WMM_TSPEC_TS_Info_AccessPolicy_e; + +typedef MLAN_PACK_START enum { + TSPEC_ACKPOLICY_NORMAL = 0, + TSPEC_ACKPOLICY_NOACK = 1, + /* 2 is reserved */ + TSPEC_ACKPOLICY_BLOCKACK = 3, + +} MLAN_PACK_END IEEEtypes_WMM_TSPEC_TS_Info_AckPolicy_e; + +typedef MLAN_PACK_START enum { + TSPEC_TRAFFIC_APERIODIC = 0, + TSPEC_TRAFFIC_PERIODIC = 1, + +} MLAN_PACK_END IEEEtypes_WMM_TSPEC_TS_TRAFFIC_TYPE_e; + +typedef MLAN_PACK_START struct { + IEEEtypes_WMM_TSPEC_TS_TRAFFIC_TYPE_e TrafficType:1; + UINT8 TID:4; //! Unique identifier + IEEEtypes_WMM_TSPEC_TS_Info_Direction_e Direction:2; + UINT8 acp_1:1; + + UINT8 acp_2:1; + UINT8 Aggregation:1; + IEEEtypes_WMM_TSPEC_TS_Info_PSB_e PSB:1; //! Legacy/Trigg + UINT8 UserPriority:3; //! 802.1d User Priority + IEEEtypes_WMM_TSPEC_TS_Info_AckPolicy_e AckPolicy:2; + + UINT8 tsinfo_0:8; +} MLAN_PACK_END IEEEtypes_WMM_TSPEC_TS_Info_t; + +typedef MLAN_PACK_START struct { + UINT16 Size:15; //! Nominal size in octets + UINT16 Fixed:1; //! 1: Fixed size given in Size, 0: Var, size is nominal + +} MLAN_PACK_END IEEEtypes_WMM_TSPEC_NomMSDUSize_t; + +typedef MLAN_PACK_START struct { + UINT16 Fractional:13; //! Fractional portion + UINT16 Whole:3; //! Whole portion + +} MLAN_PACK_END IEEEtypes_WMM_TSPEC_SBWA; + +typedef MLAN_PACK_START struct { + IEEEtypes_WMM_TSPEC_TS_Info_t TSInfo; + IEEEtypes_WMM_TSPEC_NomMSDUSize_t NomMSDUSize; + UINT16 MaximumMSDUSize; + UINT32 MinServiceInterval; + UINT32 MaxServiceInterval; + UINT32 InactivityInterval; + UINT32 SuspensionInterval; + UINT32 ServiceStartTime; + UINT32 MinimumDataRate; + UINT32 MeanDataRate; + UINT32 PeakDataRate; + UINT32 MaxBurstSize; + UINT32 DelayBound; + UINT32 MinPHYRate; + IEEEtypes_WMM_TSPEC_SBWA SurplusBWAllowance; + UINT16 MediumTime; +} MLAN_PACK_END IEEEtypes_WMM_TSPEC_Body_t; + +typedef MLAN_PACK_START struct { + UINT8 ElementId; + UINT8 Len; + UINT8 OuiType[4]; /* 00:50:f2:02 */ + UINT8 OuiSubType; /* 01 */ + UINT8 Version; + + IEEEtypes_WMM_TSPEC_Body_t TspecBody; + +} MLAN_PACK_END IEEEtypes_WMM_TSPEC_t; + +typedef MLAN_PACK_START enum { + TSPEC_ACTION_CODE_ADDTS_REQ = 0, + TSPEC_ACTION_CODE_ADDTS_RSP = 1, + TSPEC_ACTION_CODE_DELTS = 2, + +} MLAN_PACK_END IEEEtypes_WMM_Tspec_Action_e; + +typedef MLAN_PACK_START struct { + IEEEtypes_ActionCategory_e category; + IEEEtypes_WMM_Tspec_Action_e action; + UINT8 dialogToken; + +} MLAN_PACK_END IEEEtypes_WMM_Tspec_Action_Base_Tspec_t; + +/* Allocate enough space for a V4 TCLASS + a small CCX or VendSpec IE */ +#define WMM_TSPEC_EXTRA_IE_BUF_MAX (sizeof(IEEEtypes_TCLAS_t) + 6) + +typedef MLAN_PACK_START struct { + IEEEtypes_WMM_Tspec_Action_Base_Tspec_t tspecAct; + UINT8 statusCode; + IEEEtypes_WMM_TSPEC_t tspecIE; + + /* Place holder for additional elements after the TSPEC */ + UINT8 subElem[WMM_TSPEC_EXTRA_IE_BUF_MAX]; + +} MLAN_PACK_END IEEEtypes_Action_WMM_AddTsReq_t; + +typedef MLAN_PACK_START struct { + IEEEtypes_WMM_Tspec_Action_Base_Tspec_t tspecAct; + UINT8 statusCode; + IEEEtypes_WMM_TSPEC_t tspecIE; + + /* Place holder for additional elements after the TSPEC */ + UINT8 subElem[256]; + +} MLAN_PACK_END IEEEtypes_Action_WMM_AddTsRsp_t; + +typedef MLAN_PACK_START struct { + IEEEtypes_WMM_Tspec_Action_Base_Tspec_t tspecAct; + UINT8 reasonCode; + IEEEtypes_WMM_TSPEC_t tspecIE; + +} MLAN_PACK_END IEEEtypes_Action_WMM_DelTs_t; + +typedef MLAN_PACK_START union { + IEEEtypes_WMM_Tspec_Action_Base_Tspec_t tspecAct; + + IEEEtypes_Action_WMM_AddTsReq_t addTsReq; + IEEEtypes_Action_WMM_AddTsRsp_t addTsRsp; + IEEEtypes_Action_WMM_DelTs_t delTs; + +} MLAN_PACK_END IEEEtypes_Action_WMMAC_t; + +typedef MLAN_PACK_START struct { + IEEEtypes_MgmtHdr_t Hdr; + MLAN_PACK_START union { + IEEEtypes_Bcn_t Bcn; + IEEEtypes_DisAssoc_t DisAssoc; + IEEEtypes_AssocRqst_t AssocRqst; + IEEEtypes_AssocRsp_t AssocRsp; + IEEEtypes_ReAssocRqst_t ReAssocRqst; + IEEEtypes_ReAssocRsp_t ReAssocRsp; + IEEEtypes_ProbeRqst_t ProbeRqst; + IEEEtypes_ProbeRsp_t ProbeRsp; + IEEEtypes_Auth_t Auth; + IEEEtypes_Deauth_t Deauth; + UINT8 BodyStart; + } MLAN_PACK_END Body; + + UINT32 FCS; +} MLAN_PACK_END dot11MgtFrame_t; + +#define FCS_SIZE (4) + +#define IV_SIZE (4) +#define EIV_SIZE (4) +#define MIC_SIZE (8) +#define MIC_KEY_SIZE (8) +#define ICV_SIZE (4) +#define EXT_IV (0x20) + +#define IEEE80211_HEADER_SIZE (24) +#define QOS_CTRL_SIZE (2) +#define IEEE80211_QOSHEADER_SIZE (IEEE80211_HEADER_SIZE+QOS_CTRL_SIZE) + +#define MACHDR_n_FCS_SIZE (IEEE80211_HEADER_SIZE+FCS_SIZE) +#define QOS_MACHDR_n_FCS_SIZE (IEEE80211_QOSHEADER_SIZE+FCS_SIZE) + +#define WEPOVERHEAD (IV_SIZE+ICV_SIZE) + +#define IEEE80211_SIFS_11b (10) /* us */ +#define IEEE80211_SIFS_11g (IEEE80211_SIFS_11b) /* us */ +#define IEEE80211_SIFS_11a (16) /* us */ + +#define IEEE80211b_SHORT_PREAM (96) /* us */ +#define IEEE80211b_LONG_PREAM (192) /* us */ + +#define SIGNAL_EXTENSION (6) /* us */ + +#define TPREAMBLE (16) /* us */ +#define TSIGNAL (4) /* us */ +#define IEEE80211g_PREAM (TPREAMBLE+TSIGNAL) /* us */ +#define IEEE80211a_PREAM (IEEE80211g_PREAM ) /* us */ +#define IEEE80211n_PREAM (40) /* us */ +#define IEEE80211ac_PREAM (40) /* us */ //TBD + +#define TSYM (4) /* us */ + +#define IEEE80211_BSS_CLOCK_PPM (100) // ppm + +enum { + OTHER = 0x00, + FCC = 0x10, + IC = 0x20, + ETSI = 0x30, + SPAIN = 0x31, + FRANCE = 0x32, + JAPAN = 0x40, + JAPAN1 = 0x41, + CHINA = 0x50 +}; + +typedef enum { + RegDomain_Null = 0x00, + + RegDomain_FCC = 0x01, + RegDomain_ETSI = 0x02, + RegDomain_MIC = 0x03, + + RegDomain_Other = 0xFF, + +} RegDomain_e; + +#define IEEE_PHY_RATE_CODE_1Mbps (10) // it's the same as IEEE_PHY_RATE_CODE_12Mbps +#define IEEE_PHY_RATE_CODE_2Mbps (20) +#define IEEE_PHY_RATE_CODE_5_5Mbps (55) +#define IEEE_PHY_RATE_CODE_11Mbps (110) +#define IEEE_PHY_RATE_CODE_22Mbps (220) +#define IEEE_PHY_RATE_CODE_6Mbps (0x0B) +#define IEEE_PHY_RATE_CODE_9Mbps (0x0F) +#define IEEE_PHY_RATE_CODE_12Mbps (0x0A) // it's the same as IEEE_PHY_RATE_CODE_1Mbps +#define IEEE_PHY_RATE_CODE_18Mbps (0x0E) +#define IEEE_PHY_RATE_CODE_24Mbps (0x09) +#define IEEE_PHY_RATE_CODE_36Mbps (0x0D) +#define IEEE_PHY_RATE_CODE_48Mbps (0x08) +#define IEEE_PHY_RATE_CODE_54Mbps (0x0C) +#define IEEE_PHY_RATE_CODE_72Mbps (0x07) + +#define IEEE_PHY_RATE_CODE_1Mbps (10) // it's the same as IEEE_PHY_RATE_CODE_12Mbps +#define IEEE_PHY_RATE_CODE_2Mbps (20) +#define IEEE_PHY_RATE_CODE_5_5Mbps (55) +#define IEEE_PHY_RATE_CODE_11Mbps (110) +#define IEEE_PHY_RATE_CODE_22Mbps (220) + +#define IEEE_PHY_RATE_CODE_6Mbps (0x0B) +#define IEEE_PHY_RATE_CODE_9Mbps (0x0F) +#define IEEE_PHY_RATE_CODE_12Mbps (0x0A) // it's the same as IEEE_PHY_RATE_CODE_1Mbps +#define IEEE_PHY_RATE_CODE_18Mbps (0x0E) +#define IEEE_PHY_RATE_CODE_24Mbps (0x09) +#define IEEE_PHY_RATE_CODE_36Mbps (0x0D) +#define IEEE_PHY_RATE_CODE_48Mbps (0x08) +#define IEEE_PHY_RATE_CODE_54Mbps (0x0C) +#define IEEE_PHY_RATE_CODE_72Mbps (0x07) + +#define IEEE_PHY_RATE_CODE_MCS0 (0x00) +#define IEEE_PHY_RATE_CODE_MCS1 (0x01) +#define IEEE_PHY_RATE_CODE_MCS2 (0x02) +#define IEEE_PHY_RATE_CODE_MCS3 (0x03) +#define IEEE_PHY_RATE_CODE_MCS4 (0x04) +#define IEEE_PHY_RATE_CODE_MCS5 (0x05) +#define IEEE_PHY_RATE_CODE_MCS6 (0x06) +#define IEEE_PHY_RATE_CODE_MCS7 (0x07) + +#define IEEE_PHY_RATE_CODE_MCS8 (0x08) +#define IEEE_PHY_RATE_CODE_MCS9 (0x09) +#define IEEE_PHY_RATE_CODE_MCS10 (0x0A) +#define IEEE_PHY_RATE_CODE_MCS11 (0x0B) +#define IEEE_PHY_RATE_CODE_MCS12 (0x0C) +#define IEEE_PHY_RATE_CODE_MCS13 (0x0D) +#define IEEE_PHY_RATE_CODE_MCS14 (0x0E) +#define IEEE_PHY_RATE_CODE_MCS15 (0x0F) + +#define IEEE_PHY_RATE_CODE_MCS32 (0x20) + +typedef enum { + MOD_CLASS_INFRA = 1, + MOD_CLASS_FHSS = 2, + MOD_CLASS_HR_DSSS = 3, + MOD_CLASS_ERP_PBCC = 4, + MOD_CLASS_DSSS_OFDM = 5, + MOD_CLASS_ERP_OFDM = 6, + MOD_CLASS_OFDM = 7, + MOD_CLASS_HT = 8, + MOD_CLASS_VHT = 9, +} MOD_CLASS_e; + +typedef enum { + DSSS_1Mbps = 0 + (MOD_CLASS_HR_DSSS << 8), + DSSS_2Mbps, + DSSS_5d5Mbps, + DSSS_11Mbps, +} HR_DSSS_e; + +typedef enum { + OFDM_6Mbps = 0 + (MOD_CLASS_OFDM << 8), + OFDM_9Mbps, + OFDM_12Mbps, + OFDM_18Mbps, + OFDM_24Mbps, + OFDM_36Mbps, + OFDM_48Mbps, + OFDM_54Mbps, +} OFDM_e; + +typedef enum { + // 1x1 EM + MCS_0 = 0 + (MOD_CLASS_HT << 8), + MCS_1, + MCS_2, + MCS_3, + MCS_4, + MCS_5, + MCS_6, + MCS_7, + + // 2x2 EM + MCS_8, + MCS_9, + MCS_10, + MCS_11, + MCS_12, + MCS_13, + MCS_14, + MCS_15, + + // 3x3 EM + MCS_16, + MCS_17, + MCS_18, + MCS_19, + MCS_20, + MCS_21, + MCS_22, + MCS_23, + + // 4x4 EM + MCS_24, + MCS_25, + MCS_26, + MCS_27, + MCS_28, + MCS_29, + MCS_30, + MCS_31, + + // 1x1 EM 40MHz only + MCS_32, + + // 2x2 UEM + MCS_33, + MCS_34, + MCS_35, + MCS_36, + MCS_37, + MCS_38, + + // 3x3 UEM + MCS_39, + MCS_40, + MCS_41, + MCS_42, + MCS_43, + MCS_44, + MCS_45, + MCS_46, + MCS_47, + MCS_48, + MCS_49, + MCS_50, + MCS_51, + MCS_52, + + // 4x4 UEM + MCS_53, + MCS_54, + MCS_55, + MCS_56, + MCS_57, + MCS_58, + MCS_59, + MCS_60, + MCS_61, + MCS_62, + MCS_63, + MCS_64, + MCS_65, + MCS_66, + MCS_67, + MCS_68, + MCS_69, + MCS_70, + MCS_71, + MCS_72, + MCS_73, + MCS_74, + MCS_75, + MCS_76, + +} MCS_e; + +typedef union { + UINT16 u16; + HR_DSSS_e dsss; + OFDM_e ofdm; + MCS_e mcs; +} RATE_CODE_u; + +#define RATECODE_to_RATEID(x) (x & 0xff) + +#define IEEEtypes_STATUS_INVALID_INFO_ELEMENT 40 +#define IEEEtypes_STATUS_INVALID_AKMP 43 +#define IEEEtypes_STATUS_CIPHER_POLICY_REJECT 46 +#define IEEEtypes_STATUS_INVALID_MCAST_CIPHER 47 +#define IEEEtypes_STATUS_INVALID_UCAST_CIPHER 48 +#define IEEEtypes_STATUS_UNSUPPORT_WAPI_VERSION 49 +#define IEEEtypes_STATUS_INVALID_WAPI_CAPS 50 + +typedef MLAN_PACK_START struct { + IEEEtypes_ElementId_e ElementId; + IEEEtypes_Len_t Len; + UINT8 Data[1]; +} MLAN_PACK_END IEEEtypes_WAPIElement_t; + +#endif /* _IEEE_TYPES_H_ */ diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/aes_cmac.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/aes_cmac.h new file mode 100644 index 000000000000..def7d4434d53 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/aes_cmac.h @@ -0,0 +1,34 @@ +/** @file aes_cmac.h + * + * @brief This file contains defines for aes_cmac + * + * Copyright (C) 2014-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 03/07/2014: Initial version +******************************************************/ +#ifndef _AES_CMAC_H_ +#define _AES_CMAC_H_ + +#include "wltypes.h" +#include "aes_cmac_rom.h" + +/* ROM Pointers Init */ +extern void aes_cmacRomInit(void); + +#endif diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/aes_cmac_rom.c b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/aes_cmac_rom.c new file mode 100644 index 000000000000..121964360640 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/aes_cmac_rom.c @@ -0,0 +1,183 @@ +/** @file aes_cmac_rom.c + * + * @brief This file defines aes cmac related function + * + * Copyright (C) 2014-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 03/07/2014: Initial version +******************************************************/ +#include "wl_macros.h" +#include "wltypes.h" +#include "hostsa_ext_def.h" +#include "authenticator.h" + +#include "crypt_new_rom.h" +#include "aes_cmac_rom.h" + +//#pragma diag_default 144 +//#pragma arm section rwdata +// End - patch table entries + +/* For CMAC Calculation */ +static UINT8 const_Rb[16] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87 +}; + +void +mrvl_aes_128(UINT8 *key, UINT8 *input, UINT8 *output) +{ +// BOOLEAN weuEnabled; + +#if 0 + if (IS_AEU_CLK_ENBLD()) { + weuEnabled = TRUE; + } else { + weuEnabled = FALSE; + } +#endif + /* keyLen = 128 bit / 8 bits/byte / 8 for MRVL API encoding == 2 */ + MRVL_AesEncrypt(key, (128 / 8) / 8, input, output); + +} + +/* Basic Functions */ +void +xor_128(UINT8 *a, UINT8 *b, UINT8 *out) +{ + int i; + for (i = 0; i < 16; i++) { + out[i] = a[i] ^ b[i]; + } +} + +/* AES-CMAC Generation Function */ + +void +leftshift_onebit(UINT8 *input, UINT8 *output) +{ + int i; + UINT8 overflow = 0; + + for (i = 15; i >= 0; i--) { + output[i] = input[i] << 1; + output[i] |= overflow; + overflow = (input[i] & 0x80) ? 1 : 0; + } +} + +void +generate_subkey(phostsa_private priv, UINT8 *key, UINT8 *K1, UINT8 *K2) +{ + hostsa_util_fns *util_fns = &priv->util_fns; + + UINT8 L[16]; + UINT8 Z[16]; + UINT8 tmp[16]; + + memset(util_fns, Z, 0x00, sizeof(Z)); + + mrvl_aes_128(key, Z, L); + + if ((L[0] & 0x80) == 0) { + /* If MSB(L) = 0, then K1 = L << 1 */ + leftshift_onebit(L, K1); + } else { + /* Else K1 = ( L << 1 ) (+) Rb */ + leftshift_onebit(L, tmp); + xor_128(tmp, const_Rb, K1); + } + + if ((K1[0] & 0x80) == 0) { + leftshift_onebit(K1, K2); + } else { + leftshift_onebit(K1, tmp); + xor_128(tmp, const_Rb, K2); + } +} + +void +padding(UINT8 *lastb, UINT8 *pad, int length) +{ + int j; + + /* original last block */ + for (j = 0; j < 16; j++) { + if (j < length) { + pad[j] = lastb[j]; + } else if (j == length) { + pad[j] = 0x80; + } else { + pad[j] = 0x00; + } + } +} + +void +mrvl_aes_cmac(phostsa_private priv, UINT8 *key, UINT8 *input, int length, + UINT8 *mac) +{ + hostsa_util_fns *util_fns = &priv->util_fns; + UINT8 X[16]; + UINT8 Y[16]; + UINT8 M_last[16]; + UINT8 padded[16]; + UINT8 K1[16]; + UINT8 K2[16]; + int n, i, flag; + + generate_subkey(priv, key, K1, K2); + + n = (length + 15) / 16; /* n is number of rounds */ + + if (n == 0) { + n = 1; + flag = 0; + } else { + if ((length % 16) == 0) { + /* last block is a complete block */ + flag = 1; + } else { + /* last block is not complete block */ + flag = 0; + } + } + + if (flag) { + /* last block is complete block */ + xor_128(&input[16 * (n - 1)], K1, M_last); + } else { + padding(&input[16 * (n - 1)], padded, length % 16); + xor_128(padded, K2, M_last); + } + + memset(util_fns, X, 0x00, sizeof(X)); + + for (i = 0; i < n - 1; i++) { + xor_128(X, &input[16 * i], Y); /* Y := Mi (+) X */ + mrvl_aes_128(key, Y, X); /* X := AES-128(KEY, Y); */ + } + + xor_128(X, M_last, Y); + mrvl_aes_128(key, Y, X); + + for (i = 0; i < 16; i++) { + mac[i] = X[i]; + } +} diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/aes_cmac_rom.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/aes_cmac_rom.h new file mode 100644 index 000000000000..09f56155b78b --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/aes_cmac_rom.h @@ -0,0 +1,46 @@ +/** @file aes_cmac_rom.h + * + * @brief This file contains the define for aes_cmac_rom + * + * Copyright (C) 2014-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 03/07/2014: Initial version +******************************************************/ +#ifndef _AES_CMAC_ROM_H_ +#define _AES_CMAC_ROM_H_ + +#include "wltypes.h" + +extern BOOLEAN (*mrvl_aes_cmac_hook) (UINT8 *key, UINT8 *input, int length, + UINT8 *mac); +extern void mrvl_aes_cmac(phostsa_private priv, UINT8 *key, UINT8 *input, + int length, UINT8 *mac); + +extern BOOLEAN (*mrvl_aes_128_hook) (UINT8 *key, UINT8 *input, UINT8 *output); +extern void mrvl_aes_128(UINT8 *key, UINT8 *input, UINT8 *output); + +/* RAM Linkages */ +extern void (*rom_hal_EnableWEU) (void); +extern void (*rom_hal_DisableWEU) (void); + +extern void xor_128(UINT8 *a, UINT8 *b, UINT8 *out); +extern void leftshift_onebit(UINT8 *input, UINT8 *output); +extern void padding(UINT8 *lastb, UINT8 *pad, int length); + +#endif diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/crypt_new.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/crypt_new.h new file mode 100644 index 000000000000..877a65fa0ed2 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/crypt_new.h @@ -0,0 +1,77 @@ +/** @file crypt_new.h + * + * @brief This file contains define for rc4 decrypt/encrypt + * + * Copyright (C) 2014-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 03/07/2014: Initial version +******************************************************/ +/* + * AES API header file + * + */ + +#ifndef _CRYPT_NEW_H_ +#define _CRYPT_NEW_H_ + +#include "crypt_new_rom.h" + +/* forward decl */ +typedef void (*MRVL_ENDECRYPT_FUNC_p) (MRVL_ENDECRYPT_t *enDeCrypt, int *pErr); +#if 0 +#if (HW_IP_AEU_VERSION < 100000) + +#ifdef WAPI_HW_SUPPORT +extern void MRVL_WapiEncrypt(MRVL_ENDECRYPT_t *crypt, int *pErr); +extern void MRVL_WapiDecrypt(MRVL_ENDECRYPT_t *crypt, int *pErr); + +#define MRVL_WAPI_ENCRYPT MRVL_WapiEncrypt +#define MRVL_WAPI_DECRYPT MRVL_WapiDecrypt +#endif + +#ifdef DIAG_AES_CCM +#define MRVL_AES_CCM_ENCRYPT MRVL_AesCCMEncrypt +#define MRVL_AES_CCM_DECRYPT MRVL_AesCCMDecrypt +#endif + +#endif /*(HW_IP_AEU_VERSION < 100000) */ +#endif +#define MRVL_AES_PRIMITIVE_ENCRYPT MRVL_AesPrimitiveEncrypt +#define MRVL_AES_PRIMITIVE_DECRYPT MRVL_AesPrimitiveDecrypt +#define MRVL_AES_WRAP_ENCRYPT MRVL_AesWrapEncrypt +#define MRVL_AES_WRAP_DECRYPT MRVL_AesWrapDecrypt + +#ifdef RC4 + +#define MRVL_RC4_ENCRYPT MRVL_Rc4Cryption +#define MRVL_RC4_DECRYPT MRVL_Rc4Cryption + +#endif /* RC4 */ + +typedef struct { + MRVL_ENDECRYPT_e action; + MRVL_ENDECRYPT_FUNC_p pActionFunc; + +} MRVL_ENDECRYPT_SETUP_t; + +extern MRVL_ENDECRYPT_SETUP_t mrvlEnDecryptSetup[2][6]; + +extern void cryptNewRomInit(void); + +#endif /* _CRYPT_NEW_H_ */ diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/crypt_new_rom.c b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/crypt_new_rom.c new file mode 100644 index 000000000000..1a5dae1b1e7c --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/crypt_new_rom.c @@ -0,0 +1,890 @@ +/** @file crypt_new_rom.c + * + * @brief This file defines AES based functions. + * + * Copyright (C) 2014-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 03/07/2014: Initial version +******************************************************/ +/* + * AES based functions + * + * - AES Key Wrap algorithm (128-bit KEK) (RFC-3394) + * - AES primitive algorithm + * - AES CCM algorithm + * + * Date: 11/01/2005 + */ +#include "wltypes.h" +#include "crypt_new_rom.h" +#include "rc4_rom.h" +#include "rijndael.h" + +#include "hostsa_ext_def.h" +#include "authenticator.h" + +static const UINT8 MRVL_DEFAULT_IV[8] = { 0xA6, 0xA6, 0xA6, 0xA6, + 0xA6, 0xA6, 0xA6, 0xA6 +}; + +UINT8 aesResult[32]; + +/* MRVL_AES_MEMCMP + * : similiar to memcmp in std c lib + * @dst : dst ptr to be compared to + * @src : src ptr to be compared from + * @len : size of the comparison + * + * ASSUMPTION : dst/src has to be valid data ptrs + */ +int +MRVL_AES_MEMCMP(UINT8 *dst, UINT8 *src, int len) +{ + int cnt = len; + + while (len--) { + if (*dst++ == *src++) { + cnt--; + } + } + + if (0 == cnt) { + return 0; /* dst == src */ + } + + return -1; /* dst != src */ +} + +/* MRVL_AES_MEMSET + * : similiar to memset in std c lib + * @dst : dst starting pointer + * @val : val to be set + * @size : size of dst buffer to be set + * + * ASSUMPTION : dst buffer must always have larger than the value of size + */ +void +MRVL_AES_MEMSET(UINT8 *dst, UINT8 val, int size) +{ + while (size--) { + *dst++ = val; + } +} + +/* MRVL_AES_MEMCPY + * : similar to memcpy in std c lib + * @dst : dst buffer starting ptr + * @src : src buffer starting ptr + * @size : size of copy + * + * ASSUMPTION : + * 1: dst buffer must be larger than src + size of copy + * 2: dst buffer isn't overlapping src buffer + */ +void +MRVL_AES_MEMCPY(UINT8 *dst, UINT8 *src, int size) +{ + if (dst < src) { + while (size) { + *dst++ = *src++; + size--; + } + } else { + while (size) { + *(dst + size - 1) = *(src + size - 1); + size--; + } + } +} + +int +MRVL_AesInterCheck(UINT8 *inter, UINT8 *d) +{ + if (0 == MRVL_AES_MEMCMP(inter, d, 16)) { + return 0; + } + + return -1; +} + +#ifdef WAR_ROM_BUG64609_SUPPORT_24_32_BYTES_KEY_LENGTH +extern const u8 Te4[256]; +extern const u32 rcon[]; +extern const u32 Td0[256]; +extern const u32 Td1[256]; +extern const u32 Td2[256]; +extern const u32 Td3[256]; +#define GETU32(pt) (((u32)(pt)[0] << 24) ^ ((u32)(pt)[1] << 16) ^ ((u32)(pt)[2] << 8) ^ ((u32)(pt)[3])) + +static int +rijndaelKeySetupEnc_2(u32 rk[ /*4*(Nr + 1) */ ], const u8 cipherKey[], + int keyBits) +{ + int i = 0; + u32 temp; + + rk[0] = GETU32(cipherKey); + rk[1] = GETU32(cipherKey + 4); + rk[2] = GETU32(cipherKey + 8); + rk[3] = GETU32(cipherKey + 12); + if (keyBits == 128) { + for (;;) { + temp = rk[3]; + rk[4] = rk[0] ^ + (Te4[(temp >> 16) & 0xff] << 24) ^ + (Te4[(temp >> 8) & 0xff] << 16) ^ + (Te4[(temp) & 0xff] << 8) ^ + (Te4[(temp >> 24)]) ^ rcon[i]; + rk[5] = rk[1] ^ rk[4]; + rk[6] = rk[2] ^ rk[5]; + rk[7] = rk[3] ^ rk[6]; + if (++i == 10) { + return 10; + } + rk += 4; + } + } + + /** Handle 24 bytes key length */ + rk[4] = GETU32(cipherKey + 16); + rk[5] = GETU32(cipherKey + 20); + if (keyBits == 192) { + for (;;) { + temp = rk[5]; + rk[6] = rk[0] ^ + (Te4[(temp >> 16) & 0xff] << 24) ^ + (Te4[(temp >> 8) & 0xff] << 16) ^ + (Te4[(temp) & 0xff] << 8) ^ + (Te4[(temp >> 24)]) ^ rcon[i]; + rk[7] = rk[1] ^ rk[6]; + rk[8] = rk[2] ^ rk[7]; + rk[9] = rk[3] ^ rk[8]; + if (++i == 8) { + return 12; + } + rk[10] = rk[4] ^ rk[9]; + rk[11] = rk[5] ^ rk[10]; + rk += 6; + } + } + + /** Handle 32 bytes key length */ + rk[6] = GETU32(cipherKey + 24); + rk[7] = GETU32(cipherKey + 28); + if (keyBits == 256) { + for (;;) { + temp = rk[7]; + rk[8] = rk[0] ^ + (Te4[(temp >> 16) & 0xff] << 24) ^ + (Te4[(temp >> 8) & 0xff] << 16) ^ + (Te4[(temp) & 0xff] << 8) ^ + (Te4[(temp >> 24)]) ^ rcon[i]; + rk[9] = rk[1] ^ rk[8]; + rk[10] = rk[2] ^ rk[9]; + rk[11] = rk[3] ^ rk[10]; + if (++i == 7) { + return 14; + } + temp = rk[11]; + rk[12] = rk[4] ^ + (Te4[(temp >> 24)] << 24) ^ + (Te4[(temp >> 16) & 0xff] << 16) ^ + (Te4[(temp >> 8) & 0xff] << 8) ^ + (Te4[(temp) & 0xff]); + rk[13] = rk[5] ^ rk[12]; + rk[14] = rk[6] ^ rk[13]; + rk[15] = rk[7] ^ rk[14]; + rk += 8; + } + } + return 0; +} + +static int +rijndaelKeySetupDec_2(u32 rk[ /*4*(Nr + 1) */ ], const u8 cipherKey[], + int keyBits, int have_encrypt) +{ + int Nr, i, j; + u32 temp; + + if (have_encrypt) { + Nr = have_encrypt; + } else { + /* expand the cipher key: */ + Nr = rijndaelKeySetupEnc_2(rk, cipherKey, keyBits); + } + /* invert the order of the round keys: */ + for (i = 0, j = 4 * Nr; i < j; i += 4, j -= 4) { + temp = rk[i]; + rk[i] = rk[j]; + rk[j] = temp; + temp = rk[i + 1]; + rk[i + 1] = rk[j + 1]; + rk[j + 1] = temp; + temp = rk[i + 2]; + rk[i + 2] = rk[j + 2]; + rk[j + 2] = temp; + temp = rk[i + 3]; + rk[i + 3] = rk[j + 3]; + rk[j + 3] = temp; + } + /* apply the inverse MixColumn transform to all round keys but the first and the last: */ + for (i = 1; i < Nr; i++) { + rk += 4; + rk[0] = Td0[Te4[(rk[0] >> 24)]] ^ + Td1[Te4[(rk[0] >> 16) & 0xff]] ^ + Td2[Te4[(rk[0] >> 8) & 0xff]] ^ + Td3[Te4[(rk[0]) & 0xff]]; + rk[1] = Td0[Te4[(rk[1] >> 24)]] ^ + Td1[Te4[(rk[1] >> 16) & 0xff]] ^ + Td2[Te4[(rk[1] >> 8) & 0xff]] ^ + Td3[Te4[(rk[1]) & 0xff]]; + rk[2] = Td0[Te4[(rk[2] >> 24)]] ^ + Td1[Te4[(rk[2] >> 16) & 0xff]] ^ + Td2[Te4[(rk[2] >> 8) & 0xff]] ^ + Td3[Te4[(rk[2]) & 0xff]]; + rk[3] = Td0[Te4[(rk[3] >> 24)]] ^ + Td1[Te4[(rk[3] >> 16) & 0xff]] ^ + Td2[Te4[(rk[3] >> 8) & 0xff]] ^ + Td3[Te4[(rk[3]) & 0xff]]; + } + return Nr; +} + +static void +rijndael_set_key_2(rijndael_ctx *ctx, u8 *key, int bits, int encrypt) +{ + ctx->Nr = rijndaelKeySetupEnc_2(ctx->key, key, bits); + if (encrypt) { + ctx->decrypt = 0; + } else { + ctx->decrypt = 1; + rijndaelKeySetupDec_2(ctx->key, key, bits, ctx->Nr); + /**bt_test :: TBD */ + } +} +#endif /** WAR_ROM_BUG64609_SUPPORT_24_32_BYTES_KEY_LENGTH */ + +/* + * AesEncrypt : aes primitive encryption + * + * @kek : key encryption key + * @kekLen : kek len + * @data : data pointer + * + * ASSUMPTION : both src and dst buffer has to be 16 bytes or 128-bit + */ +int +MRVL_AesEncrypt(UINT8 *kek, UINT8 kekLen, UINT8 *data, UINT8 *ret) +{ + //BufferDesc_t * pDesc = NULL; + UINT8 buf[400] = { 0 }; + rijndael_ctx *ctx; +#if 0 //!defined(REMOVE_PATCH_HOOKS) + int ptr_val; + + if (MRVL_AesEncrypt_hook(kek, kekLen, data, ret, &ptr_val)) { + return ptr_val; + } +#endif +#if 0 + /* Wait forever ensures a buffer */ + pDesc = (BufferDesc_t *) bml_AllocBuffer(ramHook_encrPoolConfig, + 400, BML_WAIT_FOREVER); +#endif + //ctx = (rijndael_ctx *)BML_DATA_PTR(pDesc); + ctx = (rijndael_ctx *)buf; +#ifdef WAR_ROM_BUG64609_SUPPORT_24_32_BYTES_KEY_LENGTH + rijndael_set_key_2(ctx, (UINT8 *)kek, kekLen * 64, 1); +#else + rijndael_set_key(ctx, (UINT8 *)kek, kekLen * 64, 1); +#endif /** WAR_ROM_BUG64609_SUPPORT_24_32_BYTES_KEY_LENGTH */ + rijndael_encrypt(ctx, data, ret); +// bml_FreeBuffer((UINT32)pDesc); + + return 0; +} + +/**************************************************************** + * AES_WRAP : AES_WRAP is specified by RFC 3394 section 2.2.1 + * + * Inputs : plaintest, n 64-bit values {P1, P2, ..., Pn}, and + * Key, K (the KEK) + * Outputs : ciphertext, (n+1) 64-bit values {C0, ..., Cn} + * + * NOTE: this function is ported over from WPA_SUPPLICANT + **************************************************************** + * + * @kek : key encryption key + * @kekLen : kek len, in unit of 64-bit, has to be 2, 3, or 4 + * @n : length of the wrapped key in 64-bit + * unit; e.g.: 2 = 128-bit = 16 bytes + * @plain : plaintext key to be wrapped, has to be n * (64-bit) + * or n * 8 bytes + * @cipher : wrapped key, (n + 1) * 64-bit or (n+1) * 8 bytes + */ + +/* debugging */ + +int +MRVL_AesWrap(UINT8 *kek, UINT8 kekLen, UINT32 n, + UINT8 *plain, UINT8 *keyIv, UINT8 *cipher) +{ + UINT8 a[8]; + UINT8 b[16]; + int i = 0; + int j = 0; + UINT8 *r = NULL; +#if 0 //!defined(REMOVE_PATCH_HOOKS) + int ptr_val; + + if (MRVL_AesWrap_hook(kek, kekLen, n, plain, keyIv, cipher, &ptr_val)) { + return ptr_val; + } +#endif + + /* 0: before everything, check n value + */ + if (1 > n) { + return -1; + } + + r = cipher + 8; + + /* 1: initialize variables */ + MRVL_AES_MEMSET(b, 0x0, 16); + if (keyIv) { + MRVL_AES_MEMCPY(a, keyIv, 8); + } else { + MRVL_AES_MEMCPY(a, (UINT8 *)MRVL_DEFAULT_IV, 8); + } + MRVL_AES_MEMCPY(r, plain, (8 * n)); + + /* 2: calculate intermediate values + * For j = 0 to 5 + * For i=1 to n + * B = AES(K, A | R[i]) + * A = MSB(64, B) ^ t where t = (n*j)+i + * R[i] = LSB(64, B) + */ + for (j = 0; j <= 5; j++) { + r = cipher + 8; + for (i = 1; i <= n; i++) { + MRVL_AES_MEMCPY(b, a, 8); + MRVL_AES_MEMCPY(b + 8, r, 8); + MRVL_AesEncrypt(kek, kekLen, b, b); + MRVL_AES_MEMCPY(a, b, 8); + a[7] ^= n * j + i; + MRVL_AES_MEMCPY(r, b + 8, 8); + r += 8; + } + } + + MRVL_AES_MEMCPY(cipher, a, 8); + + /* 3: output the results + * these are already in @cipher + */ + + return 0; +} + +/**************************************************************** + * AES_UNWRAP : AES_UNWRAP is specified by RFC 3394 section 2.2.2 + * + * Inputs : ciphertext, (n+1) 64-bit values {C0, ..., Cn}, and + * Key, K (the KEK) + * + * Outputs : plaintest, n 64-bit values {P1, P2, ..., Pn} + first 8 bytes + * for KEYIV + * + * + **************************************************************** + * + * @kek : key encryption key + * @kekLen : kek len, in unit of 64-bit, has to be 2, 3, or 4 + * @n : length of the wrapped key in 64-bit + * unit; e.g.: 2 = 128-bit = 16 bytes + * @cipher : wrapped data, (n + 1) * 64-bit or (n+1) * 8 bytes + * @plain : plaintext being unwrapped, has to be n * (64-bit) + * or n * 8 bytes + extra 8 bytes for KEYIV + */ +int +MRVL_AesUnWrap(UINT8 *kek, UINT8 kekLen, UINT32 n, + UINT8 *cipher, UINT8 *keyIv, UINT8 *plain) +{ + UINT8 b[16]; + int i = 0; + int j = 0; + UINT8 a[8]; + UINT8 *r = NULL; + //BufferDesc_t * pDesc = NULL; + UINT8 buf[400] = { 0 }; + + rijndael_ctx *ctx; +#if 0 //!defined(REMOVE_PATCH_HOOKS) + int ptr_val; + + if (MRVL_AesUnWrap_hook(kek, kekLen, n, cipher, keyIv, plain, &ptr_val)) { + return ptr_val; + } +#endif + + /* 0: before everything, check n value + */ + if (1 > n) { + return -1; + } + + /* 1: initialize variables */ + MRVL_AES_MEMSET(a, 0x0, 8); + MRVL_AES_MEMSET(b, 0x0, 16); + MRVL_AES_MEMCPY(a, cipher, 8); + r = plain; + MRVL_AES_MEMCPY(r, cipher + 8, 8 * n); +#if 0 + /* Wait forever ensures a buffer */ + pDesc = (BufferDesc_t *) bml_AllocBuffer(ramHook_encrPoolConfig, + 400, BML_WAIT_FOREVER); +#endif + //ctx = (rijndael_ctx *)BML_DATA_PTR(pDesc); + ctx = (rijndael_ctx *)buf; +#ifdef WAR_ROM_BUG64609_SUPPORT_24_32_BYTES_KEY_LENGTH + rijndael_set_key_2(ctx, (UINT8 *)kek, kekLen * 64, 0); +#else + rijndael_set_key(ctx, (UINT8 *)kek, kekLen * 64, 0); +#endif /** WAR_ROM_BUG64609_SUPPORT_24_32_BYTES_KEY_LENGTH */ + + /* 2: compute intermediate values + * For j = 5 to 0 + * For i = n to 1 + * B = AES-1(K, (A ^ t) | R[i]) where t = n*j+i + * A = MSB(64, B) + * R[i] = LSB(64, B) + */ + for (j = 5; j >= 0; j--) { + r = plain + (n - 1) * 8; + for (i = n; i >= 1; i--) { + MRVL_AES_MEMCPY(b, a, 8); + b[7] ^= n * j + i; + MRVL_AES_MEMCPY(b + 8, r, 8); + + rijndael_decrypt(ctx, b, b); + + MRVL_AES_MEMCPY(a, b, 8); + MRVL_AES_MEMCPY(r, b + 8, 8); + r -= 8; + } + } + +// bml_FreeBuffer((UINT32)pDesc); + + /* 3: copy decrypted KeyIV to keyIv array */ + if (keyIv) { + if (MRVL_AES_MEMCMP(keyIv, a, 8)) { + return -1; + } + } else { + if (MRVL_AES_MEMCMP((UINT8 *)MRVL_DEFAULT_IV, a, 8)) { + return -1; + } + } + + return 0; +} + +#if 0 +/***** + * AES CRYPTION HELPER FUNCTIONS + * + */ +int +MRVL_AesValidateHostRequest(UINT32 *pBitMap, UINT8 *pCmdPtr, + UINT8 *pCryptData, SINT8 *AESwrapEnc) +{ + host_MRVL_AES_CRYPT_t *pLocal = NULL; + MrvlIEParamSet_t *pLocalIEParam = NULL; +#if 0 //!defined(REMOVE_PATCH_HOOKS) + int ptr_val; + + if (MRVL_AesValidateHostRequest_hook(pBitMap, + pCmdPtr, + pCryptData, + AESwrapEnc, &ptr_val)) { + return ptr_val; + } +#endif + + if (NULL == pBitMap || NULL == pCmdPtr) { + return -1; + } + + pLocal = (host_MRVL_AES_CRYPT_t *) pCmdPtr; + pLocalIEParam = (MrvlIEParamSet_t *)&(pLocal->aesTlv); + if ((0 != pLocal->action) && (0x1 != pLocal->action)) { + *pBitMap |= MRVL_AES_NOT_EN_AND_DECRYPT; + } + + if (0 == pLocal->action) { + ((MRVL_ENDECRYPT_t *)pCryptData)->enDeAction = CRYPT_DECRYPT; + } else if (1 == pLocal->action) { + ((MRVL_ENDECRYPT_t *)pCryptData)->enDeAction = CRYPT_ENCRYPT; + } + switch (pLocal->algorithm) { + case MRVL_CRYPTO_TEST_RC4: + if ((1 > (pLocal->keyIVLen + pLocal->keyLen)) || + (256 < (pLocal->keyIVLen + pLocal->keyLen))) { + *pBitMap |= MRVL_AES_KEY_IV_INVALID_RC4; + } + break; + case MRVL_CRYPTO_TEST_AES_ECB: + if ((16 != pLocal->keyLen) && + (24 != pLocal->keyLen) && (32 != pLocal->keyLen)) { + *pBitMap |= MRVL_AES_KEY_SIZE_INVALID; + } + + if ((16 != pLocalIEParam->Length)) { + *pBitMap |= MRVL_AES_DATA_SIZE_INVALID; + } + break; + case MRVL_CRYPTO_TEST_AES_WRAP: + if (8 != pLocal->keyIVLen) { + *pBitMap |= MRVL_AES_KEY_IV_INVALID_AES_WRAP; + } + + if ((16 != pLocal->keyLen) && + (24 != pLocal->keyLen) && (32 != pLocal->keyLen)) { + *pBitMap |= MRVL_AES_KEY_SIZE_INVALID; + } + + if ((1016 < pLocalIEParam->Length) || + (8 > pLocalIEParam->Length)) { + *pBitMap |= MRVL_AES_DATA_SIZE_INVALID; + } + + if (1 == pLocal->action) { /* Encryption */ + *AESwrapEnc = 8; + } else if (0 == pLocal->action) { /* Decryption */ + *AESwrapEnc = -8; + } + break; +#ifdef DIAG_AES_CCM + case MRVL_CRYPTO_TEST_AEC_CCM: + { + host_MRVL_AES_CCM_CRYPT_t *pLocalCCM + = (host_MRVL_AES_CCM_CRYPT_t *) pCmdPtr; + + pLocalIEParam = + (MrvlIEParamSet_t *)&(pLocalCCM->aesTlv); + + /* key length should be 16 */ + if ((16 != pLocalCCM->keyLen) && + (24 != pLocalCCM->keyLen) && + (32 != pLocalCCM->keyLen)) { + *pBitMap |= MRVL_AES_KEY_SIZE_INVALID; + } + + /* nonce length 7 ~ 13 bytes */ + if ((pLocalCCM->nonceLen < 7) || + (pLocalCCM->nonceLen > 13)) { + *pBitMap |= MRVL_AES_NONCE_INVALID; + } + + /* AAD length 0 ~ 30 bytes */ + if (pLocalCCM->aadLen > 30) { + *pBitMap |= MRVL_AES_AAD_INVALID; + } + + /* payload length 0 ~ 32 bytes */ + if (40 < pLocalIEParam->Length) { + *pBitMap |= MRVL_AES_DATA_SIZE_INVALID; + } + } + break; +#endif +#ifdef WAPI_HW_SUPPORT + case MRVL_CRYPTO_TEST_WAPI: + { + host_MRVL_WAPI_CRYPT_t *pLocalWAPI + = (host_MRVL_WAPI_CRYPT_t *) pCmdPtr; + + /* key length should be 16 */ + if (pLocalWAPI->keyLen != 32) { + *pBitMap |= MRVL_AES_KEY_SIZE_INVALID; + } + + /* nonce length 16 bytes */ + if (pLocalWAPI->nonceLen != 16) { + *pBitMap |= MRVL_AES_NONCE_INVALID; + } + + /* AAD length 32 or 48 bytes */ + if ((pLocalWAPI->aadLen != 32) && + (pLocalWAPI->aadLen != 48)) { + *pBitMap |= MRVL_AES_AAD_INVALID; + } + } + break; +#endif + default: + *pBitMap |= MRVL_AES_ALGORITHM_INVALID; + break; + } + + /* put the buffer ptr to cryptdata */ + ((MRVL_ENDECRYPT_t *)pCryptData)->pData = pCmdPtr; + + return 0; +} + +/************** + * WRAPPER to do AES primitive encryption + */ +void +MRVL_AesPrimitiveEncrypt(MRVL_ENDECRYPT_t *crypt, int *pErr) +{ + UINT8 *kek = NULL; + UINT8 *data = NULL; + UINT8 kekLen = 0; + host_MRVL_AES_CRYPT_t *pLocal = NULL; + MrvlIEAesCrypt_t *pLocalIEParam = NULL; + +#if 0 //!defined(REMOVE_PATCH_HOOKS) + if (MRVL_AesPrimitiveEncrypt_hook(crypt, pErr)) { + return; + } +#endif + + if ((NULL == pErr) || (NULL == crypt)) { + *pErr = -1; + return; + } + + *pErr = 0; + + if ((CRYPT_ENCRYPT != crypt->enDeAction)) { + *pErr = -1; + return; + } + + pLocal = (host_MRVL_AES_CRYPT_t *) (crypt->pData); + pLocalIEParam = (MrvlIEAesCrypt_t *)&(pLocal->aesTlv); + + kek = (UINT8 *)(pLocal->key); + kekLen = pLocal->keyLen; + data = (UINT8 *)(pLocalIEParam->payload); + + if (-1 == MRVL_AesEncrypt(kek, kekLen / 8, data, data)) { + *pErr = -1; + return; + } +} + +void +MRVL_AesPrimitiveDecrypt(MRVL_ENDECRYPT_t *crypt, int *pErr) +{ + UINT8 *kek = NULL; + UINT8 *data = NULL; + UINT8 kekLen = 0; + host_MRVL_AES_CRYPT_t *pLocal = NULL; + MrvlIEAesCrypt_t *pLocalIEParam = NULL; + BufferDesc_t *pDesc; + rijndael_ctx *ctx; + +#if 0 //!defined(REMOVE_PATCH_HOOKS) + if (MRVL_AesPrimitiveDecrypt_hook(crypt, pErr)) { + return; + } +#endif + + if ((NULL == pErr) || (NULL == crypt)) { + *pErr = -1; + return; + } + + if ((CRYPT_DECRYPT != crypt->enDeAction)) { + *pErr = -1; + return; + } + + pLocal = (host_MRVL_AES_CRYPT_t *) (crypt->pData); + pLocalIEParam = (MrvlIEAesCrypt_t *)&(pLocal->aesTlv); + + kek = (UINT8 *)(pLocal->key); + kekLen = pLocal->keyLen; + data = (UINT8 *)(pLocalIEParam->payload); +#if 0 + /* Wait forever ensures a buffer */ + pDesc = (BufferDesc_t *) bml_AllocBuffer(ramHook_encrPoolConfig, + 400, BML_WAIT_FOREVER); +#endif + ctx = (rijndael_ctx *)BML_DATA_PTR(pDesc); + +#ifdef WAR_ROM_BUG64609_SUPPORT_24_32_BYTES_KEY_LENGTH + rijndael_set_key_2(ctx, (UINT8 *)kek, kekLen * 8, 0); +#else + rijndael_set_key(ctx, (UINT8 *)kek, kekLen * 8, 0); +#endif /** WAR_ROM_BUG64609_SUPPORT_24_32_BYTES_KEY_LENGTH */ + + rijndael_decrypt(ctx, data, data); + bml_FreeBuffer((UINT32)pDesc); + + *pErr = 0; + +} + +void +MRVL_AesWrapEncrypt(MRVL_ENDECRYPT_t *crypt, int *pErr) +{ + UINT8 *kek = NULL; + UINT8 *data = NULL; + UINT8 *keyIV = NULL; + UINT8 kekLen = 0; + UINT32 dataLen = 0; + host_MRVL_AES_CRYPT_t *pLocal = NULL; + MrvlIEAesCrypt_t *pLocalIEParam = NULL; + +#if 0 //!defined(REMOVE_PATCH_HOOKS) + if (MRVL_AesWrapEncrypt_hook(crypt, pErr)) { + return; + } +#endif + + if ((NULL == pErr) || (NULL == crypt)) { + *pErr = -1; + return; + } + + *pErr = 0; + + if ((CRYPT_ENCRYPT != crypt->enDeAction)) { + *pErr = -1; + return; + } + + pLocal = (host_MRVL_AES_CRYPT_t *) (crypt->pData); + pLocalIEParam = (MrvlIEAesCrypt_t *)&(pLocal->aesTlv); + + kek = (UINT8 *)(pLocal->key); + keyIV = (UINT8 *)(pLocal->keyIV); + kekLen = pLocal->keyLen; + data = (UINT8 *)(pLocalIEParam->payload); + dataLen = pLocalIEParam->hdr.Length; + /* need to add one more 8-bytes for return length */ + pLocalIEParam->hdr.Length = dataLen + 8; + + if (-1 == + MRVL_AesWrap(kek, kekLen / 8, dataLen / 8, data, keyIV, + aesResult)) { + *pErr = -2; + return; + } + + MRVL_AES_MEMCPY(data, aesResult, pLocalIEParam->hdr.Length); +} + +void +MRVL_AesWrapDecrypt(MRVL_ENDECRYPT_t *crypt, int *pErr) +{ + UINT8 *kek = NULL; + UINT8 *keyIV = NULL; + UINT8 *data = NULL; + UINT8 kekLen = 0; + UINT32 dataLen = 0; + host_MRVL_AES_CRYPT_t *pLocal = NULL; + MrvlIEAesCrypt_t *pLocalIEParam = NULL; + +#if 0 //!defined(REMOVE_PATCH_HOOKS) + if (MRVL_AesWrapDecrypt_hook(crypt, pErr)) { + return; + } +#endif + + if ((NULL == pErr) || (NULL == crypt)) { + *pErr = -1; + return; + } + + *pErr = 0; + + if ((CRYPT_DECRYPT != crypt->enDeAction)) { + *pErr = -1; + return; + } + + pLocal = (host_MRVL_AES_CRYPT_t *) (crypt->pData); + pLocalIEParam = (MrvlIEAesCrypt_t *)&(pLocal->aesTlv); + + kek = (UINT8 *)(pLocal->key); + keyIV = (UINT8 *)(pLocal->keyIV); + kekLen = pLocal->keyLen; + data = (UINT8 *)(pLocalIEParam->payload); + dataLen = pLocalIEParam->hdr.Length; + + if (-1 == MRVL_AesUnWrap(kek, kekLen / 8, dataLen / 8 - 1, + data, keyIV, aesResult)) { + *pErr = -2; + return; + } + + dataLen -= 8; + pLocalIEParam->hdr.Length = dataLen; + MRVL_AES_MEMCPY(data, aesResult, dataLen); + +} +#endif +#ifdef RC4 + +void +MRVL_Rc4Cryption(void *priv, MRVL_ENDECRYPT_t *crypt, int *pErr) +{ + host_MRVL_AES_CRYPT_t *pLocal = NULL; + MrvlIEAesCrypt_t *pLocalIEParam = NULL; + UINT8 *key = NULL; + UINT8 *keyIV = NULL; + UINT8 *data = NULL; + UINT32 dataLen = 0; + + if ((NULL == pErr) || (NULL == crypt)) { + *pErr = -1; + return; + } + + *pErr = 0; + + /* since RC4 encrypt/decrypt are the same */ + if ((CRYPT_DECRYPT != crypt->enDeAction) && + (CRYPT_ENCRYPT != crypt->enDeAction)) { + *pErr = -2; + return; + } + + pLocal = (host_MRVL_AES_CRYPT_t *) (crypt->pData); + pLocalIEParam = (MrvlIEAesCrypt_t *)&(pLocal->aesTlv); + + key = (UINT8 *)(pLocal->key); + data = (UINT8 *)(pLocalIEParam->payload); + keyIV = (UINT8 *)(pLocal->keyIV); + dataLen = pLocalIEParam->hdr.Length; + + RC4_Encrypt(priv, key, keyIV, pLocal->keyIVLen, data, dataLen, 0); + + return; +} +#endif /* RC4 */ diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/crypt_new_rom.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/crypt_new_rom.h new file mode 100644 index 000000000000..c087bb22e900 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/crypt_new_rom.h @@ -0,0 +1,167 @@ +/** @file crypt_new_rom.c + * + * @brief This file contains the api for AES based functions + * + * Copyright (C) 2014-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 03/07/2014: Initial version +******************************************************/ +#ifndef _CRYPT_NEW_ROM_H_ +#define _CRYPT_NEW_ROM_H_ + +#define MRVL_AES_CMD_REQUEST_TYPE_INVALID (1) +#define MRVL_AES_ALGORITHM_INVALID (1<<1) +#define MRVL_AES_KEY_SIZE_INVALID (1<<2) +#define MRVL_AES_KEY_IV_SIZE_INVALID (1<<3) +#define MRVL_AES_ENCRYPT_DATA_OVER_127 (1<<4) +#define MRVL_AES_ENCRYPT_DATA_LESS_2 (1<<5) +#define MRVL_AES_NOT_EN_AND_DECRYPT (1<<6) +#define MRVL_AES_KEY_IV_INVALID_AES (1<<7) +#define MRVL_AES_KEY_IV_INVALID_AES_WRAP (1<<8) +#define MRVL_AES_KEY_IV_INVALID_RC4 (1<<9) +#define MRVL_AES_DATA_SIZE_INVALID (1<<10) + +#define MRVL_AES_NONCE_INVALID (1<<11) +#define MRVL_AES_AAD_INVALID (1<<12) + +#define host_AES_ENCRYPT 0x1 +#define host_AES_DECRYPT 0x0 + +#define MRVL_CRYPTO_TEST_RC4 1 +#define MRVL_CRYPTO_TEST_AES_ECB 2 +#define MRVL_CRYPTO_TEST_AES_WRAP 3 +#define MRVL_CRYPTO_TEST_AEC_CCM 4 +#define MRVL_CRYPTO_TEST_WAPI 5 + +/* basic data structs to support AES feature */ +/* enum for encrypt/decrypt */ +typedef enum { + CRYPT_DECRYPT = 0, + CRYPT_ENCRYPT = 1, + CRYPT_UNKNOWN +} MRVL_ENDECRYPT_e; + +/* data strcut to hold action type and data to be processed */ +typedef struct { + UINT8 enDeAction; /* encrypt or decrypt */ + UINT8 *pData; +} MRVL_ENDECRYPT_t; + +#ifdef WAPI_HW_SUPPORT +extern void MRVL_WapiEncrypt(MRVL_ENDECRYPT_t *crypt, int *pErr); +extern void MRVL_WapiDecrypt(MRVL_ENDECRYPT_t *crypt, int *pErr); +#endif + +extern BOOLEAN (*MRVL_AesPrimitiveEncrypt_hook) (MRVL_ENDECRYPT_t *crypt, + int *pErr); +extern void MRVL_AesPrimitiveEncrypt(MRVL_ENDECRYPT_t *crypt, int *pErr); + +extern BOOLEAN (*MRVL_AesPrimitiveDecrypt_hook) (MRVL_ENDECRYPT_t *crypt, + int *pErr); +extern void MRVL_AesPrimitiveDecrypt(MRVL_ENDECRYPT_t *crypt, int *pErr); + +extern BOOLEAN (*MRVL_AesWrapEncrypt_hook) (MRVL_ENDECRYPT_t *crypt, int *pErr); +extern void MRVL_AesWrapEncrypt(MRVL_ENDECRYPT_t *crypt, int *pErr); + +extern BOOLEAN (*MRVL_AesWrapDecrypt_hook) (MRVL_ENDECRYPT_t *crypt, int *pErr); +extern void MRVL_AesWrapDecrypt(MRVL_ENDECRYPT_t *crypt, int *pErr); + +#ifdef DIAG_AES_CCM +extern BOOLEAN (*MRVL_AesCCMPollingMode_hook) (MRVL_ENDECRYPT_t *crypt, + int *pErr, int decEnable); +extern void MRVL_AesCCMPollingMode(MRVL_ENDECRYPT_t *crypt, int *pErr, + int decEnable); +#endif + +extern BOOLEAN (*MRVL_AesEncrypt_hook) (UINT8 *kek, UINT8 kekLen, UINT8 *data, + UINT8 *ret, int *ptr_val); +extern int MRVL_AesEncrypt(UINT8 *kek, UINT8 kekLen, UINT8 *data, UINT8 *ret); + +extern BOOLEAN (*MRVL_AesValidateHostRequest_hook) (UINT32 *pBitMap, + UINT8 *pCmdPtr, + UINT8 *pCryptData, + SINT8 *pAESWrapEnc, + int *ptr_val); +extern int MRVL_AesValidateHostRequest(UINT32 *pBitMap, UINT8 *pCmdPtr, + UINT8 *pCryptData, SINT8 *pAESWrapEnc); + +#ifdef RC4 +extern BOOLEAN (*MRVL_Rc4Cryption_hook) (MRVL_ENDECRYPT_t *crypt, int *pErr); +extern void MRVL_Rc4Cryption(void *priv, MRVL_ENDECRYPT_t *crypt, int *pErr); +#endif + +extern BOOLEAN (*MRVL_AesWrap_hook) (UINT8 *kek, UINT8 kekLen, UINT32 n, + UINT8 *plain, UINT8 *keyIv, UINT8 *cipher, + int *ptr_val); +extern int MRVL_AesWrap(UINT8 *kek, UINT8 kekLen, UINT32 n, UINT8 *plain, + UINT8 *keyIv, UINT8 *cipher); + +extern BOOLEAN (*MRVL_AesUnWrap_hook) (UINT8 *kek, UINT8 kekLen, UINT32 n, + UINT8 *cipher, UINT8 *keyIv, + UINT8 *plain, int *ptr_val); +extern int MRVL_AesUnWrap(UINT8 *kek, UINT8 kekLen, UINT32 n, UINT8 *cipher, + UINT8 *keyIv, UINT8 *plain); + +extern BOOLEAN (*MRVL_AesSetKey_hook) (const UINT8 *kek, UINT8 kekLen, + int *ptr_val); +extern int MRVL_AesSetKey(const UINT8 *kek, UINT8 kekLen); + +/* AES related defines */ +#define MRVL_AES_KEY_UPDATE_DONE 5 +#define MRVL_AES_DONE 6 +#define MRVL_AES_ENGINE_BITS_POS 12 + +#define MRVL_AES_KEYUPDATE_LOC 0x00000900 +#define MRVL_AES_ENABLE_ENCR_LOC 0x00000500 +#define MRVL_AES_ENABLE_DECR_LOC 0x00000700 + +/* convert 4 individual bytes into a 4 byte unsigned int */ +#define MRVL_AES_GET_UINT32(x) ((x[3]<<24)|(x[2]<<16)|(x[1]<<8)|x[0]) + +/* convert 4 byte unsigned int back to a 4 individual bytes */ +#define MRVL_AES_CONVERT_UINT32_UINT8(x,u) \ + *u = (UINT8)((((UINT32)x)&0x000000ff)); \ + *(u + 1)= (UINT8)((((UINT32)x)&0x0000ff00)>>8); \ + *(u + 2)= (UINT8)((((UINT32)x)&0x00ff0000)>>16); \ + *(u + 3)= (UINT8)((((UINT32)x)&0xff000000)>>24) \ + + +/* HW register read macros */ +#define MRVL_AES_READ32(x) (WL_REGS32(x)) +#define MRVL_AES_READ16(x) (WL_REGS16(x)) +#define MRVL_AES_READ8(x) (WL_REGS8(x) ) + +/* HW register write macros */ +#define MRVL_AES_WRITE32(reg,val) (WL_WRITE_REGS32(reg,val)) +#define MRVL_AES_WRITE16(reg,val) (WL_WRITE_REGS16(reg,val)) +#define MRVL_AES_WRITE8(reg,val) (WL_WRITE_REGS8(reg,val)) + +extern UINT32 (*ramHook_CryptNew_EnterCritical) (void); +extern void (*ramHook_CryptNew_ExitCritical) (UINT32 intSave); + +extern int MRVL_AES_MEMCMP(UINT8 *dst, UINT8 *src, int len); +extern void MRVL_AES_MEMSET(UINT8 *dst, UINT8 val, int size); +extern void MRVL_AES_MEMCPY(UINT8 *dst, UINT8 *src, int size); +extern int MRVL_AesInterCheck(UINT8 *inter, UINT8 *d); +#ifdef DIAG_AES_CCM +extern void MRVL_AesCCMEncrypt(MRVL_ENDECRYPT_t *crypt, int *pErr); +extern void MRVL_AesCCMDecrypt(MRVL_ENDECRYPT_t *crypt, int *pErr); +#endif + +#endif /* _CRYPT_NEW_ROM_H_ */ diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/hmac_md5.c b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/hmac_md5.c new file mode 100644 index 000000000000..57ea7ca05af9 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/hmac_md5.c @@ -0,0 +1,106 @@ +/** @file hmac_md5.c + * + * @brief This file defines algorithm for hmac md5 + * + * Copyright (C) 2014-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 03/07/2014: Initial version +******************************************************/ + +#include "wltypes.h" +#include "md5.h" + +#include "hostsa_ext_def.h" +#include "authenticator.h" + +void +Mrvl_hmac_md5(void *priv, UINT8 *text_data, int text_len, UINT8 *key, + int key_len, void *digest) +{ + phostsa_private psapriv = (phostsa_private)priv; + hostsa_util_fns *util_fns = &psapriv->util_fns; + //BufferDesc_t * pDesc = NULL; + UINT8 buf[400] = { 0 }; + Mrvl_MD5_CTX *context; + unsigned char *k_pad; /* padding - key XORd with i/opad */ + int i; +#if 0 + /* Wait forever ensures a buffer */ + pDesc = (BufferDesc_t *) bml_AllocBuffer(ramHook_encrPoolConfig, + 400, BML_WAIT_FOREVER); +#endif + /* WLAN buffers are aligned, so k_pad start at a UINT32 boundary */ + //k_pad = (unsigned char *)BML_DATA_PTR(pDesc); + k_pad = (unsigned char *)buf; + context = (Mrvl_MD5_CTX *)(k_pad + 64); + + /* if key is longer than 64 bytes reset it to key=MD5(key) */ + if (key_len > 64) { + Mrvl_MD5_CTX tctx; + + wpa_MD5Init(&tctx); + wpa_MD5Update((void *)priv, &tctx, key, key_len); + wpa_MD5Final((void *)priv, context->buffer, &tctx); + + key = context->buffer; + key_len = 16; + } + + /* the HMAC_MD5 transform looks like: */ + /* */ + /* MD5(K XOR opad, MD5(K XOR ipad, text)) */ + /* */ + /* where K is an n byte key */ + /* ipad is the byte 0x36 repeated 64 times */ + /* opad is the byte 0x5c repeated 64 times */ + /* and text is the data being protected */ + + /* start out by storing key in pads */ + memset(util_fns, k_pad, 0, 64); + memcpy(util_fns, k_pad, key, key_len); + + /* XOR key with ipad and opad values */ + for (i = 0; i < 16; i++) { + ((UINT32 *)k_pad)[i] ^= 0x36363636; + } + + /* perform inner MD5 */ + wpa_MD5Init(context); /* init context for 1st pass */ + wpa_MD5Update((void *)priv, context, k_pad, 64); /* start with inner pad */ + wpa_MD5Update((void *)priv, context, text_data, text_len); /* then text of datagram */ + wpa_MD5Final((void *)priv, digest, context); /* finish up 1st pass */ + + /* start out by storing key in pads */ + memset(util_fns, k_pad, 0, 64); + memcpy(util_fns, k_pad, key, key_len); + + for (i = 0; i < 16; i++) { + ((UINT32 *)k_pad)[i] ^= 0x5c5c5c5c; + } + + /* perform outer MD5 */ + wpa_MD5Init(context); /* init context for 2nd pass */ + wpa_MD5Update((void *)priv, context, k_pad, 64); + /* start with outer pad */ + wpa_MD5Update((void *)priv, context, digest, 16); + /* then results of 1st hash */ + wpa_MD5Final((void *)priv, digest, context); /* finish up 2nd pass */ + +// bml_FreeBuffer((UINT32)pDesc); +} diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/hmac_sha1.c b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/hmac_sha1.c new file mode 100644 index 000000000000..dac53ebaab59 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/hmac_sha1.c @@ -0,0 +1,163 @@ +/** @file hmac_sha1.c + * + * @brief This file defines algorithm for hmac sha1 + * + * Copyright (C) 2014-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 03/07/2014: Initial version +******************************************************/ +#include "wltypes.h" +#include "wl_macros.h" +#include "sha1.h" +#include "hostsa_ext_def.h" +#include "authenticator.h" + +void +Mrvl_hmac_sha1(void *priv, unsigned char **ppText, + int *pTextLen, + int textNum, + unsigned char *key, + int key_len, unsigned char *output, int outputLen) +{ + /* + Note- Some of the variables are made static in this function + becuase currently this function executes in the idle task. + The idle task dosent have enough stack space to accomodate + these varables. In the future if this function in executed in + a different task or if we increase the stack size of the idle + task then we can put these variables on the stack + */ + //BufferDesc_t * pDesc = NULL; + phostsa_private psapriv = (phostsa_private)priv; + hostsa_util_fns *util_fns = &psapriv->util_fns; + UINT8 buf[400] = { 0 }; + Mrvl_SHA1_CTX *context; + unsigned char *k_pad; /* padding - key XORd with i/opad */ + unsigned char *digest; + int i; + + k_pad = (unsigned char *)buf; + digest = k_pad + 64; + context = (Mrvl_SHA1_CTX *)(k_pad + 64 + 20); + + /* if key is longer than 64 bytes reset it to key=SHA1(key) */ + if (key_len > 64) { + Mrvl_SHA1Init(context); + Mrvl_SHA1Update(context, key, key_len); + Mrvl_SHA1Final((void *)priv, context, key); + + key_len = 20; + } + + /* + * the HMAC_SHA1 transform looks like: + * + * SHA1(K XOR opad, SHA1(K XOR ipad, text)) + * + * where K is an n byte key + * ipad is the byte 0x36 repeated 64 times + * opad is the byte 0x5c repeated 64 times + * and text is the data being protected + */ + + /* perform inner SHA1 */ + /* start out by storing key in pads */ + memset(util_fns, k_pad, 0, 64); + memcpy(util_fns, k_pad, key, key_len); + + /* XOR key with ipad and opad values */ + for (i = 0; i < 8; i++) { + ((UINT64 *)k_pad)[i] ^= 0x3636363636363636ULL; + } + + Mrvl_SHA1Init(context); /* init context for 1st pass */ + Mrvl_SHA1Update(context, k_pad, 64); /* start with inner pad */ + for (i = 0; i < textNum; i++) { + /* then text of datagram */ + Mrvl_SHA1Update(context, ppText[i], pTextLen[i]); + } + + Mrvl_SHA1Final((void *)priv, context, digest); /* finish up 1st pass */ + + /* perform outer SHA1 */ + /* start out by storing key in pads */ + memset(util_fns, k_pad, 0, 64); + memcpy(util_fns, k_pad, key, key_len); + + /* XOR key with ipad and opad values */ + for (i = 0; i < 8; i++) { + ((UINT64 *)k_pad)[i] ^= 0x5c5c5c5c5c5c5c5cULL; + } + + Mrvl_SHA1Init(context); /* init context for 2nd pass */ + Mrvl_SHA1Update(context, k_pad, 64); /* start with outer pad */ + Mrvl_SHA1Update(context, digest, 20); /* then results of 1st hash */ + Mrvl_SHA1Final((void *)priv, context, digest); /* finish up 2nd pass */ + memcpy(util_fns, output, digest, outputLen); + +// bml_FreeBuffer((UINT32)pDesc); +} + +/* + * PRF -- Length of output is in octets rather than bits + * since length is always a multiple of 8 output array is + * organized so first N octets starting from 0 contains PRF output + * + * supported inputs are 16, 32, 48, 64 + * output array must be 80 octets to allow for sha1 overflow + */ +void +Mrvl_PRF(void *priv, unsigned char *key, int key_len, + unsigned char *prefix, int prefix_len, + unsigned char *data, int data_len, unsigned char *output, int len) +{ + phostsa_private psapriv = (phostsa_private)priv; + hostsa_util_fns *util_fns = &psapriv->util_fns; + int i; + int currentindex = 0; + int total_len; + UINT8 prf_input[120]; /* concatenated input */ + unsigned char *pText = prf_input; + SINT8 remain = len; + + memset(util_fns, prf_input, 0x00, sizeof(prf_input)); + + if (prefix) { + memcpy(util_fns, prf_input, prefix, prefix_len); + prf_input[prefix_len] = 0; /* single octet 0 */ + memcpy(util_fns, &prf_input[prefix_len + 1], data, data_len); + total_len = prefix_len + 1 + data_len; + } else { + memcpy(util_fns, prf_input, data, data_len); + total_len = data_len; + } + + prf_input[total_len] = 0; /* single octet count, starts at 0 */ + total_len++; + for (i = 0; i < (len + 19) / 20; i++) { + Mrvl_hmac_sha1(priv, (UINT8 **)&pText, + &total_len, + 1, + key, + key_len, &output[currentindex], MIN(20, remain)); + currentindex += MIN(20, remain); /* next concatenation location */ + remain -= 20; + prf_input[total_len - 1]++; /* increment octet count */ + } +} diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/md5.c b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/md5.c new file mode 100644 index 000000000000..84805cb9288d --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/md5.c @@ -0,0 +1,277 @@ +/** @file md5.c + * + * @brief This file defines the function for md5 + * + * Copyright (C) 2014-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 03/07/2014: Initial version +******************************************************/ +#include "wltypes.h" +#include "md5.h" + +#include "hostsa_ext_def.h" +#include "authenticator.h" + +/* Constants for wpa_MD5Transform routine. + */ +#define S11 7 +#define S12 12 +#define S13 17 +#define S14 22 +#define S21 5 +#define S22 9 +#define S23 14 +#define S24 20 +#define S31 4 +#define S32 11 +#define S33 16 +#define S34 23 +#define S41 6 +#define S42 10 +#define S43 15 +#define S44 21 + +#define Mrvl_MD5_memcpy(utils, output, input, len) memcpy(utils, output, input, len) +#define Mrvl_MD5_memset(utils, output, value, len) memset(utils, output, value, len) +#define Encode(utils, output, input, len) memcpy(utils, output, input, len) + +static void wpa_MD5Transform(void *priv, UINT32 *state, unsigned int *block); + +static const unsigned char PADDING[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* F, G, H and I are basic MD5 functions. + */ +#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | (~z))) + +/* ROTATE_LEFT rotates x left n bits. + */ +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + +/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. +Rotation is separate from addition to prevent recomputation. + */ +#define FF(a, b, c, d, x, s, ac) { \ + (a) += F ((b), (c), (d)) + (x) + (UINT32)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b);} + +#define GG(a, b, c, d, x, s, ac) { \ + (a) += G ((b), (c), (d)) + (x) + (UINT32)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define HH(a, b, c, d, x, s, ac) { \ + (a) += H ((b), (c), (d)) + (x) + (UINT32)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define II(a, b, c, d, x, s, ac) { \ + (a) += I ((b), (c), (d)) + (x) + (UINT32)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } + +/* MD5 initialization. Begins an MD5 operation, writing a new context. + */ +void +wpa_MD5Init(Mrvl_MD5_CTX *context) +{ + context->count[0] = context->count[1] = 0; + /* Load magic initialization constants. */ + context->state[0] = 0x67452301; + context->state[1] = 0xefcdab89; + context->state[2] = 0x98badcfe; + context->state[3] = 0x10325476; +} + +/* MD5 block update operation. Continues an MD5 message-digest + operation, processing another message block, and updating the + context. + */ +void +wpa_MD5Update(void *priv, Mrvl_MD5_CTX *context, UINT8 *input, UINT32 inputLen) +{ + phostsa_private psapriv = (phostsa_private)priv; + hostsa_util_fns *util_fns = &psapriv->util_fns; + unsigned int i, index, partLen; + + /* Compute number of bytes mod 64 */ + index = (unsigned int)((context->count[0] >> 3) & 0x3F); + + /* Update number of bits */ + if ((context->count[0] += ((UINT32)inputLen << 3)) + < ((UINT32)inputLen << 3)) { + context->count[1]++; + } + context->count[1] += ((UINT32)inputLen >> 29); + + partLen = 64 - index; + + /* Transform as many times as possible. */ + if (inputLen >= partLen) { + Mrvl_MD5_memcpy(util_fns, &context->buffer[index], input, + partLen); + wpa_MD5Transform((void *)priv, context->state, + (UINT32 *)context->buffer); + + for (i = partLen; i + 63 < inputLen; i += 64) { + Mrvl_MD5_memcpy(util_fns, context->scratch, (input + i), + 64); + wpa_MD5Transform((void *)priv, context->state, + context->scratch); + } + index = 0; + } else { + i = 0; + } + + /* Buffer remaining input */ + Mrvl_MD5_memcpy(util_fns, &context->buffer[index], &input[i], + inputLen - i); +} + +/* MD5 finalization. Ends an MD5 message-digest operation, writing the + the message digest and zeroizing the context. + */ +void +wpa_MD5Final(void *priv, unsigned char digest[16], Mrvl_MD5_CTX *context) +{ + phostsa_private psapriv = (phostsa_private)priv; + hostsa_util_fns *util_fns = &psapriv->util_fns; + unsigned char bits[8]; + unsigned int index, padLen; + + /* Save number of bits */ + Encode(util_fns, bits, context->count, 8); + + /* Pad out to 56 mod 64. */ + index = (unsigned int)((context->count[0] >> 3) & 0x3f); + padLen = (index < 56) ? (56 - index) : (120 - index); + wpa_MD5Update(priv, context, (UINT8 *)PADDING, padLen); + + /* Append length (before padding) */ + wpa_MD5Update(priv, context, bits, 8); + + /* Store state in digest */ + Encode(util_fns, digest, context->state, 16); + + /* Zeroize sensitive information. */ + Mrvl_MD5_memset(util_fns, context, 0, sizeof(*context)); +} + +/* MD5 basic transformation. Transforms state based on block. + */ +static void +wpa_MD5Transform(void *priv, UINT32 *state, unsigned int *block) +{ + phostsa_private psapriv = (phostsa_private)priv; + hostsa_util_fns *util_fns = &psapriv->util_fns; + UINT32 a = state[0], b = state[1], c = state[2], d = state[3], *x; + + /* Decode (x, block, 64); */ + x = block; + + /* Round 1 */ + FF(a, b, c, d, x[0], S11, 0xd76aa478); /* 1 */ + FF(d, a, b, c, x[1], S12, 0xe8c7b756); /* 2 */ + FF(c, d, a, b, x[2], S13, 0x242070db); /* 3 */ + FF(b, c, d, a, x[3], S14, 0xc1bdceee); /* 4 */ + FF(a, b, c, d, x[4], S11, 0xf57c0faf); /* 5 */ + FF(d, a, b, c, x[5], S12, 0x4787c62a); /* 6 */ + FF(c, d, a, b, x[6], S13, 0xa8304613); /* 7 */ + FF(b, c, d, a, x[7], S14, 0xfd469501); /* 8 */ + FF(a, b, c, d, x[8], S11, 0x698098d8); /* 9 */ + FF(d, a, b, c, x[9], S12, 0x8b44f7af); /* 10 */ + FF(c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ + FF(b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ + FF(a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ + FF(d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ + FF(c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ + FF(b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ + + /* Round 2 */ + GG(a, b, c, d, x[1], S21, 0xf61e2562); /* 17 */ + GG(d, a, b, c, x[6], S22, 0xc040b340); /* 18 */ + GG(c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ + GG(b, c, d, a, x[0], S24, 0xe9b6c7aa); /* 20 */ + GG(a, b, c, d, x[5], S21, 0xd62f105d); /* 21 */ + GG(d, a, b, c, x[10], S22, 0x2441453); /* 22 */ + GG(c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ + GG(b, c, d, a, x[4], S24, 0xe7d3fbc8); /* 24 */ + GG(a, b, c, d, x[9], S21, 0x21e1cde6); /* 25 */ + GG(d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ + GG(c, d, a, b, x[3], S23, 0xf4d50d87); /* 27 */ + + GG(b, c, d, a, x[8], S24, 0x455a14ed); /* 28 */ + GG(a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ + GG(d, a, b, c, x[2], S22, 0xfcefa3f8); /* 30 */ + GG(c, d, a, b, x[7], S23, 0x676f02d9); /* 31 */ + GG(b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ + + /* Round 3 */ + HH(a, b, c, d, x[5], S31, 0xfffa3942); /* 33 */ + HH(d, a, b, c, x[8], S32, 0x8771f681); /* 34 */ + HH(c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ + HH(b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ + HH(a, b, c, d, x[1], S31, 0xa4beea44); /* 37 */ + HH(d, a, b, c, x[4], S32, 0x4bdecfa9); /* 38 */ + HH(c, d, a, b, x[7], S33, 0xf6bb4b60); /* 39 */ + HH(b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ + HH(a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ + HH(d, a, b, c, x[0], S32, 0xeaa127fa); /* 42 */ + HH(c, d, a, b, x[3], S33, 0xd4ef3085); /* 43 */ + HH(b, c, d, a, x[6], S34, 0x4881d05); /* 44 */ + HH(a, b, c, d, x[9], S31, 0xd9d4d039); /* 45 */ + HH(d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ + HH(c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ + HH(b, c, d, a, x[2], S34, 0xc4ac5665); /* 48 */ + + /* Round 4 */ + II(a, b, c, d, x[0], S41, 0xf4292244); /* 49 */ + II(d, a, b, c, x[7], S42, 0x432aff97); /* 50 */ + II(c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ + II(b, c, d, a, x[5], S44, 0xfc93a039); /* 52 */ + II(a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ + II(d, a, b, c, x[3], S42, 0x8f0ccc92); /* 54 */ + II(c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ + II(b, c, d, a, x[1], S44, 0x85845dd1); /* 56 */ + II(a, b, c, d, x[8], S41, 0x6fa87e4f); /* 57 */ + II(d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ + II(c, d, a, b, x[6], S43, 0xa3014314); /* 59 */ + II(b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ + II(a, b, c, d, x[4], S41, 0xf7537e82); /* 61 */ + II(d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ + II(c, d, a, b, x[2], S43, 0x2ad7d2bb); /* 63 */ + II(b, c, d, a, x[9], S44, 0xeb86d391); /* 64 */ + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + + /* Zeroize sensitive information. */ + Mrvl_MD5_memset(util_fns, x, 0, sizeof(x)); +} diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/md5.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/md5.h new file mode 100644 index 000000000000..f6545ae84b67 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/md5.h @@ -0,0 +1,45 @@ +/** @file md5.h + * + * @brief This file contains define for md5. + * + * Copyright (C) 2014-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 03/07/2014: Initial version +******************************************************/ +#ifndef _MD5_H_ +#define _MD5_H_ + +typedef struct { + unsigned int state[4]; /* state (ABCD) */ + unsigned int count[2]; /* number of bits, modulo 2^64 (lsb first) */ + unsigned int scratch[16]; /* This is used to reduce the memory + ** requirements of the transform + ** function + */ + unsigned char buffer[64]; /* input buffer */ +} Mrvl_MD5_CTX; + +void wpa_MD5Init(Mrvl_MD5_CTX *context); +void wpa_MD5Update(void *priv, Mrvl_MD5_CTX *context, UINT8 *input, + UINT32 inputLen); +void wpa_MD5Final(void *priv, unsigned char digest[16], Mrvl_MD5_CTX *context); +void Mrvl_hmac_md5(void *priv, UINT8 *text, int text_len, UINT8 *key, + int key_len, void *digest); + +#endif diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/mrvl_sha256_crypto.c b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/mrvl_sha256_crypto.c new file mode 100644 index 000000000000..12233ef3c0d3 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/mrvl_sha256_crypto.c @@ -0,0 +1,118 @@ +/** @file Mrvl_sha256_crypto.c + * + * @brief This file defines sha256 crypto + * + * Copyright (C) 2014-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 03/07/2014: Initial version +******************************************************/ +#include + +#include "wltypes.h" +#include "wl_macros.h" + +#include "sha_256.h" +#include "mrvl_sha256_crypto.h" + +#include "hostsa_ext_def.h" +#include "authenticator.h" + +/* Helper function to allocate scratch memory and call sha256_vector() */ +void +mrvl_sha256_crypto_vector(void *priv, size_t num_elem, + UINT8 *addr[], size_t * len, UINT8 *mac) +{ + + //BufferDesc_t* pBufDesc = NULL; + UINT8 buf[SHA256_MIN_SCRATCH_BUF] = { 0 }; + + sha256_vector((void *)priv, num_elem, addr, len, mac, (UINT8 *)buf); + +// bml_FreeBuffer((UINT32)pBufDesc); +} + +void +mrvl_sha256_crypto_kdf(void *priv, UINT8 *pKey, + UINT8 key_len, + char *label, + UINT8 label_len, + UINT8 *pContext, + UINT16 context_len, UINT8 *pOutput, UINT16 output_len) +{ + phostsa_private psapriv = (phostsa_private)priv; + hostsa_util_fns *util_fns = &psapriv->util_fns; + UINT8 *vectors[4 + 1]; + size_t vectLen[NELEMENTS(vectors)]; + UINT8 *pResult; + UINT8 *pLoopResult; + UINT8 iterations; + UINT16 i; + //BufferDesc_t* pBufDesc = NULL; + UINT8 buf[HMAC_SHA256_MIN_SCRATCH_BUF] = { 0 }; + + pResult = pContext + context_len; + + /* + ** Working memory layout: + ** | KDF-Len output --- > + ** | + ** [KDF Input][HMAC output#1][HMAC output#2][...] + ** + */ + + /* Number of SHA256 digests needed to meet the bit length output_len */ + iterations = (output_len + 255) / 256; + + pLoopResult = pResult; + + for (i = 1; i <= iterations; i++) { + /* Skip vectors[0]; Used internally in hmac_sha256_vector function */ + vectors[1] = (UINT8 *)&i; + vectLen[1] = sizeof(i); + + vectors[2] = (UINT8 *)label; + vectLen[2] = label_len; + + vectors[3] = pContext; + vectLen[3] = context_len; + + vectors[4] = (UINT8 *)&output_len; + vectLen[4] = sizeof(output_len); + + /* + ** + ** KDF input = (K, i || label || Context || Length) + ** + */ + hmac_sha256_vector(priv, pKey, key_len, + NELEMENTS(vectors), vectors, vectLen, + pLoopResult, (UINT8 *)buf); + + /* Move the hmac output pointer so another digest can be appended + ** if more loop iterations are needed to get the output_len key + ** bit total + */ + pLoopResult += SHA256_MAC_LEN; + } + +// bml_FreeBuffer((UINT32)pBufDesc); + + memcpy(util_fns, pOutput, pResult, output_len / 8); + +} diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/mrvl_sha256_crypto.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/mrvl_sha256_crypto.h new file mode 100644 index 000000000000..74f76a951b8a --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/mrvl_sha256_crypto.h @@ -0,0 +1,40 @@ +/** @file Mrvl_sha256_crypto.h + * + * @brief This file contains the define for sha256 + * + * Copyright (C) 2014-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 03/07/2014: Initial version +******************************************************/ + +#ifndef _MRVL_SHA256_CRYPTO_H +#define _MRVL_SHA256_CRYPTO_H + +extern void mrvl_sha256_crypto_vector(void *priv, size_t num_elem, + UINT8 *addr[], size_t * len, UINT8 *mac); + +extern void mrvl_sha256_crypto_kdf(void *priv, UINT8 *pKey, + UINT8 key_len, + char *label, + UINT8 label_len, + UINT8 *pContext, + UINT16 context_len, + UINT8 *pOutput, UINT16 output_len); + +#endif diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/packetType.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/packetType.h new file mode 100644 index 000000000000..491047c23bba --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/packetType.h @@ -0,0 +1,76 @@ +/** @file packetType.h + * + * @brief This file defines Packet Type enumeration used for PacketType fields in RX and TX + * packet descriptors + * + * Copyright (C) 2014-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 03/07/2014: Initial version +******************************************************/ +#ifndef _PACKETTYPE_H_ +#define _PACKETTYPE_H_ + +/** +*** @brief Enumeration of different Packets Types. +**/ + +typedef enum { + PKT_TYPE_802DOT3_DEFAULT = 0, /*!< For RX packets it represents + ** IEEE 802.3 SNAP frame . For + ** TX Packets. This Field is for + ** backwards compatibility only and + ** should not be used going + ** forward. + */ + PKT_TYPE_802DOT3_LLC = 1, //!< IEEE 802.3 frame with LLC header + PKT_TYPE_ETHERNET_V2 = 2, //!< Ethernet version 2 frame + PKT_TYPE_802DOT3_SNAP = 3, //!< IEEE 802.3 SNAP frame + PKT_TYPE_802DOT3 = 4, //!< IEEE 802.3 frame + PKT_TYPE_802DOT11 = 5, //!< IEEE 802.11 frame + PKT_TYPE_ETCP_SOCKET_DATA = 7, //!< eTCP Socket Data + PKT_TYPE_RAW_DATA = 8, //!< Non socket data when using eTCP + PKT_TYPE_MRVL_MESH = 9, //!< Marvell Mesh frame + + /* Marvell Internal firmware packet types + ** Range from 0x0E to 0xEE + ** These internal Packet types should grow from + ** 0xEE down. This will leave room incase the packet + ** types between the driver & firmware need to be expanded + */ + PKT_TYPE_MRVL_EAPOL_MSG = 0xDF, + PKT_TYPE_MRVL_BT_AMP = 0xE0, + PKT_TYPE_FWD_MGT = 0xE2, + PKT_TYPE_MGT = 0xE5, + PKT_TYPE_MRVL_AMSDU = 0xE6, + PKT_TYPE_MRVL_BAR = 0xE7, + PKT_TYPE_MRVL_LOOPBACK = 0xE8, + PKT_TYPE_MRVL_DATA_MORE = 0xE9, + PKT_TYPE_MRVL_DATA_LAST = 0xEA, + PKT_TYPE_MRVL_DATA_NULL = 0xEB, + PKT_TYPE_MRVL_UNKNOWN = 0xEC, + PKT_TYPE_MRVL_SEND_TO_DATASWITCH = 0xED, + PKT_TYPE_MRVL_SEND_TO_HOST = 0xEE, + + PKT_TYPE_DEBUG = 0xEF, + + // Customer range for Packet Types + // Range 0xF0 to 0xFF +} PacketType; + +#endif diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/parser.c b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/parser.c new file mode 100644 index 000000000000..87837806173a --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/parser.c @@ -0,0 +1,285 @@ +/** @file parser.c + * + * @brief This file defines function for 802.11 Management Frames Parsing + * + * Copyright (C) 2014-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 03/07/2014: Initial version +******************************************************/ +/***************************************************************************** +* +* File: parser.c +* +* +* +* Author(s): Rajesh Bhagwat +* Date: 2005-02-04 +* Description: 802.11 Management Frames Parsing +* +******************************************************************************/ +#include "wltypes.h" +#include "wl_mib.h" +#include "IEEE_types.h" +#include "parser.h" +#include "parser_rom.h" + +#include "hostsa_ext_def.h" +#include "authenticator.h" + +VendorSpecificIEType_e +IsEpigramHTElement(void *priv, uint8 *pBuffer) +{ + phostsa_private psapriv = (phostsa_private)priv; + hostsa_util_fns *util_fns = &psapriv->util_fns; + VendorSpecificIEType_e retVal = VendSpecIE_Other; + const uint8 szMatchingCapElement[] = { 0x00, 0x90, 0x4c, 0x33 }; + const uint8 szMatchingInfoElement[] = { 0x00, 0x90, 0x4c, 0x34 }; + + if (!memcmp(util_fns, pBuffer, + szMatchingInfoElement, sizeof(szMatchingInfoElement))) { + retVal = VendSpecIE_HT_Info; + } else if (!memcmp(util_fns, pBuffer, + szMatchingCapElement, + sizeof(szMatchingCapElement))) { + retVal = VendSpecIE_HT_Cap; + } + + return retVal; +} + +VendorSpecificIEType_e +IsWPSElement(void *priv, UINT8 *pBuffer) +{ + phostsa_private psapriv = (phostsa_private)priv; + hostsa_util_fns *util_fns = &psapriv->util_fns; + VendorSpecificIEType_e retVal = VendSpecIE_Other; + const UINT8 szMatchingInfoElement[] = { 0x00, 0x50, 0xf2, 0x04 }; + + if (!memcmp(util_fns, pBuffer, + szMatchingInfoElement, sizeof(szMatchingInfoElement))) { + retVal = VendSpecIE_WPS; + } + + return retVal; +} + +VendorSpecificIEType_e +IsSsIdLElement(void *priv, UINT8 *pBuffer) +{ + phostsa_private psapriv = (phostsa_private)priv; + hostsa_util_fns *util_fns = &psapriv->util_fns; + VendorSpecificIEType_e retVal = VendSpecIE_Other; + const UINT8 szMatchingInfoElement[] = { 0x00, 0x50, 0xf2, 0x05, 0x00 }; + + if (!memcmp(util_fns, pBuffer, + szMatchingInfoElement, sizeof(szMatchingInfoElement))) { + retVal = VendSpecIE_SsIdL; + } + + return retVal; +} + +int +ieBufValidate(UINT8 *pIe, int bufLen) +{ + while (bufLen) { + UINT8 ieLen = *(pIe + 1); + if (bufLen < (ieLen + 2)) { + return MLME_FAILURE; + } + bufLen -= ieLen + 2; + pIe += ieLen + 2; + } + + return MLME_SUCCESS; +} + +int +GetIEPointers(void *priv, UINT8 *pIe, int bufLen, IEPointers_t *pIePointers) +{ + phostsa_private psapriv = (phostsa_private)priv; + hostsa_util_fns *util_fns = &psapriv->util_fns; + memset(util_fns, pIePointers, 0x00, sizeof(IEPointers_t)); + + while (bufLen) { + if (bufLen < (*(pIe + 1) + 2)) { + break; + } + + /* Handle IEs not processed by ROM functions. */ + switch (*pIe) { + case ELEM_ID_RSN: + pIePointers->pRsn = (IEEEtypes_RSNElement_t *)pIe; + break; + case ELEM_ID_WAPI: + pIePointers->pWapi = (IEEEtypes_WAPIElement_t *)pIe; + break; + + /* Add element not handled by ROM_parser_getIEPtr or + ** override element processing in ROM_parser_getIEPtr + ** here. + */ + case ELEM_ID_VENDOR_SPECIFIC: + default: + if (ROM_parser_getIEPtr(priv, pIe, pIePointers) == + FALSE) { + if ((*pIe) == ELEM_ID_VENDOR_SPECIFIC) { + if (IsWPSElement(priv, (pIe + 2))) { + pIePointers->pWps = + (IEEEtypes_WPSElement_t + *)pIe; + } + } + // Add your code to process vendor specific elements not + // processed by above ROM_paser_getAssocIEPtr function. + } + break; + } + bufLen -= *(pIe + 1) + 2; + pIe += *(pIe + 1) + 2; + } + return bufLen; +} + +BOOLEAN +parser_getAssocIEs(void *priv, UINT8 *pIe, + int bufLen, AssocIePointers_t *pIePointers) +{ + phostsa_private psapriv = (phostsa_private)priv; + hostsa_util_fns *util_fns = &psapriv->util_fns; + BOOLEAN ieParseSuccessful = TRUE; + + memset(util_fns, pIePointers, 0x00, sizeof(AssocIePointers_t)); + + while (bufLen) { + UINT8 ieType = *pIe; + UINT8 ieLen = *(pIe + 1); + + if (bufLen < (ieLen + 2)) { + ieParseSuccessful = FALSE; + break; + } + + switch (ieType) { + // add code for elements not handled in ROM function. + case ELEM_ID_AP_CHANNEL_REPORT: + pIePointers->pApChanRpt = + (IEEEtypes_ApChanRptElement_t *)pIe; + break; +#ifdef TDLS + case ELEM_ID_SUPPORTED_REGCLASS: + pIePointers->pSuppRegClass = + (IEEEtypes_SupportedRegClasses_t *)pIe; + break; +#endif + + /* The following 5 elements, HT CAP, HT INFO, 20/40 Coex, + OBSS SCAN PARAM, and EXTENDED CAP, are ignored + here if 11n is not compiled. When 11n is compiled these + 5 elements would be handled in ROM_parser_getAssocIEPtr + routine. + + */ + case ELEM_ID_HT_CAPABILITY: + case ELEM_ID_HT_INFORMATION: + case ELEM_ID_2040_BSS_COEXISTENCE: + case ELEM_ID_OBSS_SCAN_PARAM: + case ELEM_ID_EXT_CAPABILITIES: + /* Do not process these elements in ROM routine + ROM_parser_getAssocIEPtr + Note: a break here. + */ + break; + + /* Add element not handled by ROM_parser_getAssocIEPtr or + override element processing in ROM_parser_getAssocIEPtr + here. + \ + */ + + case ELEM_ID_VENDOR_SPECIFIC: + default: + if (ROM_parser_getAssocIEPtr(priv, pIe, pIePointers) == + FALSE) { + // Add your code to process vendor specific elements not + // processed by above ROM_paser_getAssocIEPtr function. + if (!pIePointers->pHtCap || + !pIePointers->pHtInfo) { + switch (IsEpigramHTElement + (priv, (pIe + 2))) { + + case VendSpecIE_HT_Cap: + if (!pIePointers->pHtCap) { + *(pIe + 4) = + ELEM_ID_HT_CAPABILITY; + *(pIe + 5) = + sizeof + (IEEEtypes_HT_Capability_t); + pIePointers->pHtCap = + (IEEEtypes_HT_Capability_t + *)(pIe + 4); + } + break; + + case VendSpecIE_HT_Info: + if (!pIePointers->pHtInfo) { + *(pIe + 4) = + ELEM_ID_HT_INFORMATION; + *(pIe + 5) = + sizeof + (IEEEtypes_HT_Information_t); + pIePointers->pHtInfo = + (IEEEtypes_HT_Information_t + *)(pIe + 4); + } + break; + + case VendSpecIE_Other: + default: + break; + } + } + } + break; + + } + bufLen -= ieLen + 2; + pIe += ieLen + 2; + } + return ieParseSuccessful; +} + +UINT8 +parser_countNumInfoElements(UINT8 *pIe, int bufLen) +{ + UINT8 ieCount = 0; + + while (bufLen) { + if (bufLen < (*(pIe + 1) + 2)) { + break; + } + + ieCount++; + + bufLen -= *(pIe + 1) + 2; + pIe += *(pIe + 1) + 2; + } + + return ieCount; +} diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/parser.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/parser.h new file mode 100644 index 000000000000..31016b98e68a --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/parser.h @@ -0,0 +1,74 @@ +/** @file parser.h + * + * @brief This file contains definitions of 802.11 Management Frames + * and Information Element Parsing + * + * Copyright (C) 2014-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 03/07/2014: Initial version +******************************************************/ +#ifndef _PARSER_H_ +#define _PARSER_H_ + +#include "IEEE_types.h" +#include "parser_rom.h" + +typedef struct { + IEEEtypes_HT_Capability_t *pHtCap; + IEEEtypes_HT_Information_t *pHtInfo; + IEEEtypes_20N40_BSS_Coexist_t *p2040Coexist; + IEEEtypes_OBSS_ScanParam_t *pHtScanParam; + IEEEtypes_ExtCapability_t *pHtExtCap; +} dot11nIEPointers_t; + +#ifdef DOT11AC +typedef struct { + IEEEtypes_VHT_Capability_t *pVhtCap; + IEEEtypes_VHT_Operation_t *pVhtOper; +} dot11acIEPointers_t; +#endif + +#ifdef TDLS +typedef struct { + IEEEtypes_MobilityDomainElement_t *pMdie; + IEEEtypes_FastBssTransElement_t *pFtie; + IEEEtypes_RSNElement_t *pRsn; + IEEEtypes_TimeoutIntervalElement_t *pTie[2]; + IEEEtypes_RICDataElement_t *pFirstRdie; + +} Dot11rIePointers_t; +#endif +extern VendorSpecificIEType_e IsWMMElement(void *priv, UINT8 *pBuffer); +extern VendorSpecificIEType_e IsWPAElement(void *priv, UINT8 *pBuffer); + +extern int ieBufValidate(UINT8 *pIe, int bufLen); + +extern int GetIEPointers(void *priv, UINT8 *pIe, + int bufLen, IEPointers_t *pIePointers); + +extern BOOLEAN parser_getAssocIEs(void *priv, UINT8 *pIe, + int bufLen, AssocIePointers_t *pIePointers); + +extern +IEEEtypes_InfoElementHdr_t *parser_getSpecificIE(IEEEtypes_ElementId_e elemId, + UINT8 *pIe, int bufLen); + +extern UINT8 parser_countNumInfoElements(UINT8 *pIe, int bufLen); + +#endif // _PARSER_H_ diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/parser_rom.c b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/parser_rom.c new file mode 100644 index 000000000000..4ea1b1548914 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/parser_rom.c @@ -0,0 +1,243 @@ +/** @file parser_rom.c + * + * @brief This file define the function for 802.11 Management Frames Parsing + * + * Copyright (C) 2014-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 03/07/2014: Initial version +******************************************************/ +#include "parser_rom.h" + +#include "hostsa_ext_def.h" +#include "authenticator.h" + +#define WMM_IE_MIN_LEN 7 + +VendorSpecificIEType_e +IsWMMElement(void *priv, uint8 *pBuffer) +{ + phostsa_private psapriv = (phostsa_private)priv; + hostsa_util_fns *util_fns = &psapriv->util_fns; + + VendorSpecificIEType_e retVal = VendSpecIE_Other; + const uint8 szMatchingInfoElement[] = { 0x00, 0x50, 0xf2, + 0x02, 0x00, 0x01 + }; + const uint8 szMatchingParamElement[] = { 0x00, 0x50, 0xf2, + 0x02, 0x01, 0x01 + }; + const uint8 szMatchingTspecElement[] = { 0x00, 0x50, 0xf2, + 0x02, 0x02, 0x01 + }; + + if (!memcmp(util_fns, pBuffer, + szMatchingInfoElement, sizeof(szMatchingInfoElement))) { + retVal = VendSpecIE_WMM_Info; + } else if (!memcmp(util_fns, pBuffer, + szMatchingParamElement, + sizeof(szMatchingParamElement))) { + retVal = VendSpecIE_WMM_Param; + } else if (!memcmp(util_fns, pBuffer, + szMatchingTspecElement, + sizeof(szMatchingTspecElement))) { + retVal = VendSpecIE_TSPEC; + } + + return retVal; +} + +VendorSpecificIEType_e +IsWPAElement(void *priv, uint8 *pBuffer) +{ + phostsa_private psapriv = (phostsa_private)priv; + hostsa_util_fns *util_fns = &psapriv->util_fns; + + VendorSpecificIEType_e retVal = VendSpecIE_Other; + const uint8 szMatchingInfoElement[] = { 0x00, 0x50, 0xf2, + 0x01, 0x01, 0x00 + }; + + if (!memcmp(util_fns, pBuffer, + szMatchingInfoElement, sizeof(szMatchingInfoElement))) { + retVal = VendSpecIE_WPA; + } + + return retVal; +} + +BOOLEAN +ROM_parser_getIEPtr(void *priv, uint8 *pIe, IEPointers_t *pIePointers) +{ + BOOLEAN status = TRUE; + switch (*pIe) { + case ELEM_ID_SSID: + pIePointers->pSsid = (IEEEtypes_SsIdElement_t *)pIe; + break; + + case ELEM_ID_DS_PARAM_SET: + pIePointers->pDsParam = (IEEEtypes_DsParamElement_t *)pIe; + break; + + case ELEM_ID_TIM: + pIePointers->pTim = (IEEEtypes_TimElement_t *)pIe; + break; + + case ELEM_ID_SUPPORTED_RATES: + pIePointers->pSupportedRates = + (IEEEtypes_SuppRatesElement_t *)pIe; + break; + + case ELEM_ID_EXT_SUPPORTED_RATES: + pIePointers->pExtSupportedRates + = (IEEEtypes_ExtSuppRatesElement_t *)pIe; + break; + + case ELEM_ID_ERP_INFO: + pIePointers->pErpInfo = (IEEEtypes_ERPInfoElement_t *)pIe; + break; + + case ELEM_ID_IBSS_PARAM_SET: + pIePointers->pIbssParam = (IEEEtypes_IbssParamElement_t *)pIe; + break; + + case ELEM_ID_COUNTRY: + pIePointers->pCountry = (IEEEtypes_CountryInfoElement_t *)pIe; + break; + + case ELEM_ID_RSN: + pIePointers->pRsn = (IEEEtypes_RSNElement_t *)pIe; + break; + + case ELEM_ID_VENDOR_SPECIFIC: + + if (IsWPAElement(priv, (pIe + 2))) { + pIePointers->pWpa = (IEEEtypes_WPAElement_t *)pIe; + } else { + switch (IsWMMElement(priv, (pIe + 2))) { + case VendSpecIE_Other: + case VendSpecIE_TSPEC: + default: + status = FALSE; + break; + + case VendSpecIE_WMM_Info: + pIePointers->pWmmInfo = + (IEEEtypes_WMM_InfoElement_t *)pIe; + break; + + case VendSpecIE_WMM_Param: + pIePointers->pWmmParam + = (IEEEtypes_WMM_ParamElement_t *)pIe; + break; + } + + } + break; + default: + status = FALSE; + break; + } + + return status; +} + +BOOLEAN +ROM_parser_getAssocIEPtr(void *priv, uint8 *pIe, AssocIePointers_t *pIePointers) +{ + BOOLEAN status = TRUE; + switch (*pIe) { + case ELEM_ID_SSID: + pIePointers->pSsid = (IEEEtypes_SsIdElement_t *)pIe; + break; + + case ELEM_ID_COUNTRY: + pIePointers->pCountry = (IEEEtypes_CountryInfoElement_t *)pIe; + break; + + case ELEM_ID_DS_PARAM_SET: + pIePointers->pDsParam = (IEEEtypes_DsParamElement_t *)pIe; + break; + + case ELEM_ID_SUPPORTED_RATES: + pIePointers->pSupportedRates = + (IEEEtypes_SuppRatesElement_t *)pIe; + break; + + case ELEM_ID_EXT_SUPPORTED_RATES: + pIePointers->pExtSupportedRates + = (IEEEtypes_ExtSuppRatesElement_t *)pIe; + break; + + case ELEM_ID_RSN: + pIePointers->pRsn = (IEEEtypes_RSNElement_t *)pIe; + break; + + case ELEM_ID_VENDOR_SPECIFIC: + if (IsWPAElement(priv, (pIe + 2))) { + pIePointers->pWpa = (IEEEtypes_WPAElement_t *)pIe; + } else { + switch (IsWMMElement(priv, (pIe + 2))) { + case VendSpecIE_Other: + case VendSpecIE_TSPEC: + default: + status = FALSE; + break; + + case VendSpecIE_WMM_Info: + pIePointers->pWmmInfo + = (IEEEtypes_WMM_InfoElement_t *)pIe; + break; + + case VendSpecIE_WMM_Param: + pIePointers->pWmmParam + = (IEEEtypes_WMM_ParamElement_t *)pIe; + break; + } + } + break; + default: + status = FALSE; + break; + } + + return status; +} + +IEEEtypes_InfoElementHdr_t * +parser_getSpecificIE(IEEEtypes_ElementId_e elemId, UINT8 *pIe, int bufLen) +{ + if (!pIe) { + return NULL; + } + + while (bufLen) { + if (bufLen < (*(pIe + 1) + 2)) { + break; + } + + if (*pIe == elemId) { + return (IEEEtypes_InfoElementHdr_t *)pIe; + } + + bufLen -= *(pIe + 1) + 2; + pIe += *(pIe + 1) + 2; + } + + return NULL; +} diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/parser_rom.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/parser_rom.h new file mode 100644 index 000000000000..5caf5ac9e1a2 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/parser_rom.h @@ -0,0 +1,135 @@ +/** @file parser_rom.h + * + * @brief This file contains the data structrue for iepointer and declare the parse function + * + * Copyright (C) 2014-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 03/07/2014: Initial version +******************************************************/ +#ifndef PARSER_ROM_H__ +#define PARSER_ROM_H__ +#include "IEEE_types.h" + +typedef enum { + VendSpecIE_Other = 0, + VendSpecIE_WMM_Info, + VendSpecIE_WMM_Param, + VendSpecIE_WPA, + VendSpecIE_WPS, + VendSpecIE_TSPEC, + VendSpecIE_SsIdL, + VendSpecIE_WFD, + + VendSpecIE_HT_Cap, + VendSpecIE_HT_Info, + +} VendorSpecificIEType_e; + +typedef struct { + /* IMPORTANT: please read before you modify this struct: + Some of the members of this struct are used in ROM code. + Therefore, please do not change any existing field, including + its name and type. If you want to add a new element into + this struct add it at the end. + */ + IEEEtypes_SsIdElement_t *pSsid; + IEEEtypes_TimElement_t *pTim; + IEEEtypes_WPAElement_t *pWpa; + IEEEtypes_WMM_InfoElement_t *pWmmInfo; + IEEEtypes_WMM_ParamElement_t *pWmmParam; + IEEEtypes_DsParamElement_t *pDsParam; + IEEEtypes_SuppRatesElement_t *pSupportedRates; + IEEEtypes_ExtSuppRatesElement_t *pExtSupportedRates; + IEEEtypes_ERPInfoElement_t *pErpInfo; + IEEEtypes_IbssParamElement_t *pIbssParam; + IEEEtypes_CountryInfoElement_t *pCountry; + + IEEEtypes_MobilityDomainElement_t *pMdie; + + IEEEtypes_RSNElement_t *pRsn; + + IEEEtypes_HT_Capability_t *pHtCap; + IEEEtypes_HT_Information_t *pHtInfo; + IEEEtypes_20N40_BSS_Coexist_t *p2040Coexist; + IEEEtypes_OBSS_ScanParam_t *pHtScanParam; + IEEEtypes_ExtCapability_t *pExtCap; + + IEEEtypes_WPSElement_t *pWps; + IEEEtypes_WAPIElement_t *pWapi; + +} IEPointers_t; + +typedef struct { + /* IMPORTANT: please read before you modify this struct: + Some of the members of this struct are used in ROM code. + Therefore, please do not change any existing field, including + its name and type. If you want to add a new element into + this struct add it at the end. + */ + IEEEtypes_SsIdElement_t *pSsid; + IEEEtypes_TimElement_t *pTim; + IEEEtypes_DsParamElement_t *pDsParam; + + IEEEtypes_CountryInfoElement_t *pCountry; + + UINT8 numSsIdLs; + IEEEtypes_SsIdLElement_t *pSsIdL; /* Only the first SSIDL found, + ** need iterator to get next since + ** multiple may be in beacon + */ +} ScanIePointers_t; + +typedef struct { + /* IMPORTANT: please read before you modify this struct: + Some of the members of this struct are used in ROM code. + Therefore, please do not change any existing field, including + its name and type. If you want to add a new element into + this struct add it at the end. + */ + IEEEtypes_SsIdElement_t *pSsid; + IEEEtypes_DsParamElement_t *pDsParam; + + IEEEtypes_CountryInfoElement_t *pCountry; + IEEEtypes_ApChanRptElement_t *pApChanRpt; + IEEEtypes_PowerConstraintElement_t *pPwrCon; + + IEEEtypes_SuppRatesElement_t *pSupportedRates; + IEEEtypes_ExtSuppRatesElement_t *pExtSupportedRates; + + IEEEtypes_WPAElement_t *pWpa; + IEEEtypes_WMM_InfoElement_t *pWmmInfo; + IEEEtypes_WMM_ParamElement_t *pWmmParam; + + IEEEtypes_MobilityDomainElement_t *pMdie; + + IEEEtypes_RSNElement_t *pRsn; + + IEEEtypes_HT_Information_t *pHtInfo; + IEEEtypes_HT_Capability_t *pHtCap; + IEEEtypes_20N40_BSS_Coexist_t *p2040Coexist; + IEEEtypes_OBSS_ScanParam_t *pHtScanParam; + IEEEtypes_ExtCapability_t *pExtCap; + +} AssocIePointers_t; +extern BOOLEAN ROM_parser_getIEPtr(void *priv, uint8 *pIe, + IEPointers_t *pIePointers); +extern BOOLEAN ROM_parser_getAssocIEPtr(void *priv, uint8 *pIe, + AssocIePointers_t *pIePointers); + +#endif // _PARSER_ROM_H_ diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/pass_phrase.c b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/pass_phrase.c new file mode 100644 index 000000000000..16b5aa7509cb --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/pass_phrase.c @@ -0,0 +1,115 @@ +/** @file pass_phrase.c + * + * @brief This file defines passphase hash + * + * Copyright (C) 2014-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 03/07/2014: Initial version +******************************************************/ +#include "wl_macros.h" +#include "pass_phrase.h" +//#include "keyMgmtSta.h" +#include "sha1.h" +#include "hostsa_ext_def.h" +#include "authenticator.h" + +static INLINE t_u32 +pass_strlen(const char *str) +{ + t_u32 i; + + for (i = 0; str[i] != 0; i++) { + } + return i; +} + +/* + * F(P, S, c, i) = U1 xor U2 xor ... Uc + * U1 = PRF(P, S || Int(i)) + * U2 = PRF(P, U1) + * Uc = PRF(P, Uc-1) + */ +void +Mrvl_F(void *priv, char *password, unsigned char *ssid, int ssidlength, + int iterations, int count, unsigned char *output) +{ + phostsa_private psapriv = (phostsa_private)priv; + hostsa_util_fns *util_fns = &psapriv->util_fns; + static unsigned char digest[36], digest1[A_SHA_DIGEST_LEN]; + int i, j; + int len = pass_strlen(password); + int tmpLen = ssidlength + 4; + unsigned char *pTemp = digest; + + /* U1 = PRF(P, S || int(i)) */ + memcpy(util_fns, digest, ssid, ssidlength); + digest[ssidlength] = (unsigned char)((count >> 24) & 0xff); + digest[ssidlength + 1] = (unsigned char)((count >> 16) & 0xff); + digest[ssidlength + 2] = (unsigned char)((count >> 8) & 0xff); + digest[ssidlength + 3] = (unsigned char)(count & 0xff); + + Mrvl_hmac_sha1((void *)priv, &pTemp, + &tmpLen, + 1, + (unsigned char *)password, + len, digest1, A_SHA_DIGEST_LEN); + + /* output = U1 */ + memcpy(util_fns, output, digest1, A_SHA_DIGEST_LEN); + pTemp = digest1; + for (i = 1; i < iterations; i++) { + tmpLen = A_SHA_DIGEST_LEN; + + /* Un = PRF(P, Un-1) */ + Mrvl_hmac_sha1((void *)priv, &pTemp, + &tmpLen, + 1, + (unsigned char *)password, + len, digest, A_SHA_DIGEST_LEN); + + memcpy(util_fns, digest1, digest, A_SHA_DIGEST_LEN); + + /* output = output xor Un */ + for (j = 0; j < A_SHA_DIGEST_LEN; j++) { + output[j] ^= digest[j]; + } + } +} + +/* + * password - ascii string up to 63 characters in length + * ssid - octet string up to 32 octets + * ssidlength - length of ssid in octets + * output must be 32 octets in length and outputs 256 bits of key + */ +int +Mrvl_PasswordHash(void *priv, char *password, unsigned char *ssid, + int ssidlength, unsigned char *output) +{ + phostsa_private psapriv = (phostsa_private)priv; + hostsa_util_fns *util_fns = &psapriv->util_fns; + if ((pass_strlen(password) > 63) || (ssidlength > 32)) { + return 0; + } + + Mrvl_F((void *)priv, password, ssid, ssidlength, 4096, 2, output); + memcpy(util_fns, output + A_SHA_DIGEST_LEN, output, 12); + Mrvl_F((void *)priv, password, ssid, ssidlength, 4096, 1, output); + return 1; +} diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/pass_phrase.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/pass_phrase.h new file mode 100644 index 000000000000..53a52059f8aa --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/pass_phrase.h @@ -0,0 +1,31 @@ +/** @file pass_phrase.h + * + * @brief This file contains define for passphase hash + * + * Copyright (C) 2014-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 03/07/2014: Initial version +******************************************************/ +#ifndef PASS_PHRASE_H__ +#define PASS_PHRASE_H__ + +extern int Mrvl_PasswordHash(void *priv, char *password, unsigned char *ssid, + int ssidlength, unsigned char *output); + +#endif diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/pmkCache.c b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/pmkCache.c new file mode 100644 index 000000000000..3acb216fdc2c --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/pmkCache.c @@ -0,0 +1,303 @@ +/** @file pmkcache.c + * + * @brief This file defines pmk cache functions + * + * Copyright (C) 2014-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 03/07/2014: Initial version +******************************************************/ +#include "wl_macros.h" +#include "pass_phrase.h" +#include "pmkCache.h" +#include "hostsa_ext_def.h" +#include "authenticator.h" +#include "tlv.h" +#include "keyMgmtApStaCommon.h" + +#define MAX_PMK_CACHE_ENTRIES 10 + +pmkElement_t pmkCache[MAX_PMK_CACHE_ENTRIES]; + +char PSKPassPhrase[PSK_PASS_PHRASE_LEN_MAX]; +/* +** Replacement rank is used to determine which cache entry to replace +** once the cache is full. The rank order is determined by usage. The +** least recently used cache element is the first to be replaced. +** +** replacementRankMax is an indication of the number of cache entries used. +** It is used to determine the rank of the current cache entry used. +** +** Rank order goes from 1 to MAX_PMK_CACHE_ENTRIES. If the cache is full, +** the element with rank 1 is first to be replaced. +** +** Replacement rank of zero indicates that the entry is invalid. +*/ + +UINT8 * +pmkCacheFindPSK(void *priv, UINT8 *pSsid, UINT8 ssidLen) +{ + UINT8 *pPMK = NULL; + pmkElement_t *pPMKElement; + + if (!pPMK) { + /* extract the PSK from the cache entry */ + pPMKElement = + pmkCacheFindPSKElement((void *)priv, pSsid, ssidLen); + if (pPMKElement) { + pPMK = pPMKElement->PMK; + } else if ('\0' != PSKPassPhrase[0]) { + /* Generate a new PSK entry with the + ** provided passphrase. + */ + pmkCacheAddPSK((void *)priv, pSsid, ssidLen, NULL, + PSKPassPhrase); + pPMKElement = + pmkCacheFindPSKElement((void *)priv, pSsid, + ssidLen); + pmkCacheGeneratePSK((void *)priv, pSsid, ssidLen, + PSKPassPhrase, pPMKElement->PMK); + pPMK = pPMKElement->PMK; + } + } + + return pPMK; +} + +UINT8 * +pmkCacheFindPassphrase(void *priv, UINT8 *pSsid, UINT8 ssidLen) +{ + UINT8 *pPassphrase = NULL; + pmkElement_t *pPMKElement; + + if (!pPassphrase) { + /* extract the PSK from the cache entry */ + pPMKElement = + pmkCacheFindPSKElement((void *)priv, pSsid, ssidLen); + if (pPMKElement) { + pPassphrase = pPMKElement->passphrase; + } + + } + + return pPassphrase; +} + +void +pmkCacheSetPassphrase(void *priv, char *pPassphrase) +{ + phostsa_private psapriv = (phostsa_private)priv; + hostsa_util_fns *util_fns = &psapriv->util_fns; + + if (pPassphrase != NULL) { + memcpy(util_fns, PSKPassPhrase, + pPassphrase, sizeof(PSKPassPhrase)); + } +} + +void +pmkCacheGetPassphrase(void *priv, char *pPassphrase) +{ + phostsa_private psapriv = (phostsa_private)priv; + hostsa_util_fns *util_fns = &psapriv->util_fns; + + if (pPassphrase != NULL) { + memcpy(util_fns, pPassphrase, + PSKPassPhrase, sizeof(PSKPassPhrase)); + } +} + +void +pmkCacheInit(void *priv) +{ + phostsa_private psapriv = (phostsa_private)priv; + hostsa_util_fns *util_fns = &psapriv->util_fns; + + memset(util_fns, pmkCache, 0x00, sizeof(pmkCache)); + memset(util_fns, PSKPassPhrase, 0x00, sizeof(PSKPassPhrase)); + replacementRankMax = 0; +} + +void +pmkCacheFlush(void *priv) +{ + phostsa_private psapriv = (phostsa_private)priv; + hostsa_util_fns *util_fns = &psapriv->util_fns; + + memset(util_fns, pmkCache, 0x00, sizeof(pmkCache)); + replacementRankMax = 0; +} + +//#pragma arm section code = ".init" +void +pmkCacheRomInit(void) +{ + ramHook_MAX_PMK_CACHE_ENTRIES = MAX_PMK_CACHE_ENTRIES; + ramHook_pmkCache = &pmkCache[0]; + ramHook_PSKPassPhrase = &PSKPassPhrase[0]; +// ramHook_hal_SetCpuMaxSpeed = hal_SetCpuOpToSecuritySpeed; +// ramHook_hal_RestoreCpuSpeed = cm_SetPerformanceParams; +} + +//#pragma arm section code + +#ifdef DRV_EMBEDDED_SUPPLICANT + +t_u16 +SupplicantSetPassphrase(void *priv, void *pPassphraseBuf) +{ + phostsa_private psapriv = (phostsa_private)priv; + hostsa_util_fns *util_fns = NULL; + mlan_ds_passphrase *psk = (mlan_ds_passphrase *)pPassphraseBuf; + IEEEtypes_MacAddr_t *pBssid = NULL; + UINT8 *pPMK = NULL; + UINT8 Passphrase[PSK_PASS_PHRASE_LEN_MAX], *pPassphrase = NULL; + UINT8 *pSsid = NULL; + UINT8 ssidLen = 0; + UINT16 retVal = 0; + t_u8 zero_mac[] = { 0, 0, 0, 0, 0, 0 }; + + if (!psapriv) + return retVal; + util_fns = &psapriv->util_fns; + + if (memcmp(util_fns, (t_u8 *)&psk->bssid, zero_mac, sizeof(zero_mac))) + pBssid = (IEEEtypes_MacAddr_t *)&psk->bssid; + + ssidLen = psk->ssid.ssid_len; + if (ssidLen > 0) + pSsid = psk->ssid.ssid; + + if (psk->psk_type == MLAN_PSK_PASSPHRASE) { + pPassphrase = psk->psk.passphrase.passphrase; + memset(util_fns, Passphrase, 0x00, sizeof(Passphrase)); + memcpy(util_fns, Passphrase, pPassphrase, + MIN(MLAN_MAX_PASSPHRASE_LENGTH, + psk->psk.passphrase.passphrase_len)); + } + + if (psk->psk_type == MLAN_PSK_PMK) + pPMK = psk->psk.pmk.pmk; + + /* Always enable the supplicant on a set */ + // supplicantEnable(priv); + + if (pBssid && pPMK) { + pmkCacheAddPMK(priv, pBssid, pPMK); + } else if (pSsid) { + if (pPMK) { + pmkCacheAddPSK(priv, pSsid, ssidLen, pPMK, NULL); + } else if (pPassphrase) { + pmkCacheAddPSK(priv, pSsid, ssidLen, NULL, Passphrase); + pPMK = pmkCacheFindPSK(priv, pSsid, ssidLen); + pmkCacheGeneratePSK(priv, pSsid, ssidLen, + (char *)Passphrase, pPMK); + } else { + /* Just an SSID so we can't set anything in the cache */ + retVal = 1; + } + } else if (pPassphrase) { + memcpy(util_fns, PSKPassPhrase, Passphrase, sizeof(Passphrase)); + } else { + /* Not enough data to set anything in the cache */ + retVal = 1; + } + + return retVal; +} + +BOOLEAN +SupplicantClearPMK_internal(void *priv, void *pPassphraseBuf) +{ + phostsa_private psapriv = (phostsa_private)priv; + hostsa_util_fns *util_fns = &psapriv->util_fns; + mlan_ds_passphrase *psk = (mlan_ds_passphrase *)pPassphraseBuf; + IEEEtypes_MacAddr_t *pBssid = NULL; + UINT8 *pPassphrase = NULL; + UINT8 *pSsid = NULL; + UINT8 ssidLen = 0; + t_u8 zero_mac[] = { 0, 0, 0, 0, 0, 0 }; + + if (memcmp(util_fns, (t_u8 *)&psk->bssid, zero_mac, sizeof(zero_mac))) + pBssid = (IEEEtypes_MacAddr_t *)&psk->bssid; + + ssidLen = psk->ssid.ssid_len; + if (ssidLen > 0) + pSsid = psk->ssid.ssid; + + if (psk->psk_type == MLAN_PSK_PASSPHRASE) + pPassphrase = psk->psk.passphrase.passphrase; + + if (pBssid) { + pmkCacheDeletePMK(priv, (UINT8 *)pBssid); + } else if (pSsid) { + pmkCacheDeletePSK(priv, pSsid, ssidLen); + } else if (pPassphrase) { + /* Clear the global passphrase by setting it to blank */ + memset(util_fns, ramHook_PSKPassPhrase, 0x00, + PSK_PASS_PHRASE_LEN_MAX); + } else { + return FALSE; + } + + return TRUE; +} + +void +SupplicantClearPMK(void *priv, void *pPassphrase) +{ + if (!priv) + return; + + if (!SupplicantClearPMK_internal(priv, pPassphrase)) { + /* Always disable the supplicant on a flush */ + supplicantDisable(priv); + pmkCacheFlush(priv); + } +} + +void +SupplicantQueryPassphrase(void *priv, void *pPassphraseBuf) +{ + phostsa_private psapriv = (phostsa_private)priv; + hostsa_util_fns *util_fns = NULL; + mlan_ds_passphrase *psk = (mlan_ds_passphrase *)pPassphraseBuf; + UINT8 *pPassphrase = NULL; + UINT8 *pSsid = NULL; + UINT8 ssidLen = 0; + + if (!psapriv) + return; + + util_fns = &psapriv->util_fns; + ssidLen = psk->ssid.ssid_len; + pSsid = psk->ssid.ssid; + + if (ssidLen) { + pPassphrase = pmkCacheFindPassphrase(priv, pSsid, ssidLen); + if (pPassphrase) { + psk->psk_type = MLAN_PSK_PASSPHRASE; + memcpy(util_fns, psk->psk.passphrase.passphrase, + pPassphrase, PSK_PASS_PHRASE_LEN_MAX); + psk->psk.passphrase.passphrase_len = + wlan_strlen(pPassphrase); + } + } + +} +#endif diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/pmkCache.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/pmkCache.h new file mode 100644 index 000000000000..96d8d6ef86cb --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/pmkCache.h @@ -0,0 +1,58 @@ +/** @file pmkcache.h + * + * @brief This file contains define for pmk cache + * + * Copyright (C) 2014-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 03/07/2014: Initial version +******************************************************/ +#ifndef PMK_CACHE_H__ +#define PMK_CACHE_H__ + +#include "wltypes.h" +#include "IEEE_types.h" +#include "pmkCache_rom.h" + +/*! +** \brief If a matching SSID entry is present in the PMK Cache, returns a +** pointer to the PSK. If no entry is found in the cache, a +** new PSK entry will be generated if a PassPhrase is set. +** \param pSsid pointer to string containing desired SSID. +** \param ssidLen length of the SSID string *pSsid. +** \exception Does not handle the case when multiple matching SSID entries are +** found. Returns the first match. +** \return pointer to PSK with matching SSID entry from PMK cache, +** NULL if no matching entry found. +*/ +extern UINT8 *pmkCacheFindPSK(void *priv, UINT8 *pSsid, UINT8 ssidLen); + +/*! +** \brief Flushes all entries in PMK cache +*/ +extern void pmkCacheFlush(void *priv); + +extern void pmkCacheGetPassphrase(void *priv, char *pPassphrase); + +extern void pmkCacheSetPassphrase(void *priv, char *pPassphrase); +extern void pmkCacheInit(void *priv); +extern void pmkCacheRomInit(void); + +extern void supplicantDisable(void *priv); + +#endif diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/pmkCache_rom.c b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/pmkCache_rom.c new file mode 100644 index 000000000000..0e562a10cf81 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/pmkCache_rom.c @@ -0,0 +1,381 @@ +/** @file pmkcache_rom.c + * + * @brief This file defines function for pmk cache + * + * Copyright (C) 2014-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 03/07/2014: Initial version +******************************************************/ +#include "wl_macros.h" +#include "wltypes.h" +#include "pass_phrase.h" +#include "pmkCache_rom.h" + +#include "hostsa_ext_def.h" +#include "authenticator.h" + +#define BSSID_FLAG 0xff + +SINT8 replacementRankMax; + +SINT32 ramHook_MAX_PMK_CACHE_ENTRIES; + +pmkElement_t *ramHook_pmkCache; +char *ramHook_PSKPassPhrase; + +//void (*ramHook_hal_SetCpuMaxSpeed)(void); +//void (*ramHook_hal_RestoreCpuSpeed)(void); + +/*! +** \brief creates a new PMK cache entry with given SSID. +** \param pSsid pointer to desired SSID. +** \param ssidLen length of the desired SSID string. +** \return pointer to newly created PMK cache entry, +** NULL if PMK cache is full. +*/ +pmkElement_t * +pmkCacheNewElement(void *priv) +{ + phostsa_private psapriv = (phostsa_private)priv; + hostsa_util_fns *util_fns = &psapriv->util_fns; + UINT8 index; + pmkElement_t *pPMK = NULL; + +#if 0 //!defined(REMOVE_PATCH_HOOKS) + if (pmkCacheNewElement_hook(&pPMK)) { + return pPMK; + } +#endif + + for (index = 0; index < ramHook_MAX_PMK_CACHE_ENTRIES; index++) { + /* If the cache is full the least recently used entry + ** will be replaced. Decrement all the replacement ranks + ** to have a free cache entry. + */ + if (ramHook_MAX_PMK_CACHE_ENTRIES == replacementRankMax) { + (ramHook_pmkCache[index].replacementRank)--; + } + + /* Either the entry is free or it can be replaced */ + if (NULL == pPMK && + 0 == ramHook_pmkCache[index].replacementRank) { + /* empty entry found */ + pPMK = &ramHook_pmkCache[index]; + + /* clear the entry in case this is a replacement */ + memset(util_fns, pPMK, 0x00, sizeof(pmkElement_t)); + + if (ramHook_MAX_PMK_CACHE_ENTRIES > replacementRankMax) { + /* Cache isn't full so increment the max possible rank */ + replacementRankMax++; + } + + /* Set the rank so it is the last to be replaced */ + ramHook_pmkCache[index].replacementRank = + replacementRankMax; + } + } + + return pPMK; +} + +void +pmkCacheUpdateReplacementRank(pmkElement_t *pPMKElement) +{ + UINT8 index; + + /* Update the replacementRank field if the PMK is found */ + if (pPMKElement && pPMKElement->replacementRank != replacementRankMax) { + /* + ** The cache entry with a larger rank value needs to + ** to be adjusted. The cache entry given will have the + ** largest rank value + */ + for (index = 0; index < ramHook_MAX_PMK_CACHE_ENTRIES; index++) { + if (ramHook_pmkCache[index].replacementRank + > pPMKElement->replacementRank) { + (ramHook_pmkCache[index].replacementRank)--; + } + } + + pPMKElement->replacementRank = replacementRankMax; + } +} + +/*! +** \brief Finds a PMK entry matching given BSSID +** \param pBssid pointer to the desired BSSID +** \return pointer to key data field of the matching PMK cache entry. +** NULL, if no matching PMK entry is found +*/ +pmkElement_t * +pmkCacheFindPMKElement(void *priv, IEEEtypes_MacAddr_t *pBssid) +{ + phostsa_private psapriv = (phostsa_private)priv; + hostsa_util_fns *util_fns = &psapriv->util_fns; + UINT8 index = 0; + pmkElement_t *pPMKElement = NULL; + +#if 0 //!defined(REMOVE_PATCH_HOOKS) + if (pmkCacheFindPMKElement_hook(pBssid, &pPMKElement)) { + return pPMKElement; + } +#endif + + for (index = 0; index < ramHook_MAX_PMK_CACHE_ENTRIES; index++) { + /* See if the entry is valid. + ** See if the entry is a PMK + ** See if the BSSID matches + */ + if (ramHook_pmkCache[index].replacementRank > 0 + && ramHook_pmkCache[index].length == BSSID_FLAG + && (0 == memcmp(util_fns, ramHook_pmkCache[index].key.Bssid, + pBssid, + sizeof(ramHook_pmkCache[index].key.Bssid)))) + { + pPMKElement = (ramHook_pmkCache + index); + } + } + + /* Update the rank if an entry is found. Null is an accepted + ** input for the function + */ + pmkCacheUpdateReplacementRank(pPMKElement); + + return pPMKElement; +} + +/*! +** \brief If a matching SSID entry is present in the PMK Cache, returns a +** pointer to its key field. +** \param pSsid pointer to string containing desired SSID. +** \param ssidLen length of the SSID string *pSsid. +** \exception Does not handle the case when multiple matching SSID entries are +** found. Returns the first match. +** \return pointer to pmkElement with matching SSID entry from PMK cache, +** NULL if no matching entry found. +*/ +pmkElement_t * +pmkCacheFindPSKElement(void *priv, UINT8 *pSsid, UINT8 ssidLen) +{ + phostsa_private psapriv = (phostsa_private)priv; + hostsa_util_fns *util_fns = &psapriv->util_fns; + UINT8 index = 0; + pmkElement_t *pPMKElement = NULL; + +#if 0 //!defined(REMOVE_PATCH_HOOKS) + if (pmkCacheFindPSKElement_hook(pSsid, ssidLen, &pPMKElement)) { + return pPMKElement; + } +#endif + + for (index = 0; index < ramHook_MAX_PMK_CACHE_ENTRIES; index++) { + /* See if the entry is valid. + ** See if the entry is a PSK + ** See if the SSID matches + */ + if (ramHook_pmkCache[index].replacementRank + && ramHook_pmkCache[index].length == ssidLen + && (0 == memcmp(util_fns, ramHook_pmkCache[index].key.Ssid, + pSsid, ssidLen))) { + pPMKElement = (ramHook_pmkCache + index); + } + } + + /* Update the rank if an entry is found. Null is an accepted + ** input for the function + */ + pmkCacheUpdateReplacementRank(pPMKElement); + + return pPMKElement; +} + +UINT8 * +pmkCacheFindPMK(void *priv, IEEEtypes_MacAddr_t *pBssid) +{ + UINT8 *pPMK = NULL; + pmkElement_t *pPMKElement = pmkCacheFindPMKElement(priv, pBssid); + + /* extract the PMK from the cache entry */ + if (pPMKElement) { + pPMK = pPMKElement->PMK; + } + + return pPMK; +} + +void +pmkCacheAddPMK(void *priv, IEEEtypes_MacAddr_t *pBssid, UINT8 *pPMK) +{ + phostsa_private psapriv = (phostsa_private)priv; + hostsa_util_fns *util_fns = &psapriv->util_fns; + pmkElement_t *pPMKElement; + +#if 0 //!defined(REMOVE_PATCH_HOOKS) + if (pmkCacheAddPMK_hook(pBssid, pPMK)) { + return; + } +#endif + + pPMKElement = pmkCacheFindPMKElement(priv, pBssid); + + if (!pPMKElement) { + /* Entry not found. Create a new entry and fill it in */ + pPMKElement = pmkCacheNewElement(priv); + + /* Update the key union with the BSSID */ + memcpy(util_fns, pPMKElement->key.Bssid, + pBssid, sizeof(pPMKElement->key.Bssid)); + + /* Set the length to a value that is invalid for + ** an SSID. The invalid value will flag the entry as a PMK + */ + pPMKElement->length = BSSID_FLAG; + } + + if (pPMK) { + memcpy(util_fns, pPMKElement->PMK, pPMK, MAX_PMK_SIZE); + } +} + +void +pmkCacheAddPSK(void *priv, UINT8 *pSsid, UINT8 ssidLen, UINT8 *pPSK, + UINT8 *pPassphrase) +{ + phostsa_private psapriv = (phostsa_private)priv; + hostsa_util_fns *util_fns = &psapriv->util_fns; + pmkElement_t *pPMKElement; + +#if 0 //!defined(REMOVE_PATCH_HOOKS) + if (pmkCacheAddPSK_hook(pSsid, ssidLen, pPSK)) { + return; + } +#endif + + pPMKElement = pmkCacheFindPSKElement(priv, pSsid, ssidLen); + + if (NULL == pPMKElement) { + /* Entry not found. Create a new entry and fill it in */ + pPMKElement = pmkCacheNewElement(priv); + + /* Update the key portion with the SSID */ + memcpy(util_fns, pPMKElement->key.Ssid, pSsid, ssidLen); + + pPMKElement->length = ssidLen; + } + + if (pPSK) { + memcpy(util_fns, pPMKElement->PMK, pPSK, MAX_PMK_SIZE); + } + + if (pPassphrase) + memcpy(util_fns, pPMKElement->passphrase, pPassphrase, + PSK_PASS_PHRASE_LEN_MAX); + +} + +void +pmkCacheDeletePMK(void *priv, t_u8 *pBssid) +{ + phostsa_private psapriv = (phostsa_private)priv; + hostsa_util_fns *util_fns = &psapriv->util_fns; + pmkElement_t *pPMKElement = + pmkCacheFindPMKElement(priv, (IEEEtypes_MacAddr_t *)pBssid); + + if (pPMKElement) { + /* Invalidate the enrty by setting the memory for the + ** cache entry to zero. + ** This will ensure that the replacementRank is zero + */ + memset(util_fns, pPMKElement, 0x00, sizeof(pmkElement_t)); + replacementRankMax--; + } +} + +void +pmkCacheDeletePSK(void *priv, UINT8 *pSsid, UINT8 ssidLen) +{ + phostsa_private psapriv = (phostsa_private)priv; + hostsa_util_fns *util_fns = &psapriv->util_fns; + pmkElement_t *pPMKElement = + pmkCacheFindPSKElement(priv, pSsid, ssidLen); + + if (pPMKElement) { + /* Invalidate the enrty by setting the memory for the + ** cache entry to zero. + ** This will ensure that the replacementRank is zero + */ + memset(util_fns, pPMKElement, 0x00, sizeof(pmkElement_t)); + replacementRankMax--; + } +} + +UINT8 +pmkCacheGetHexNibble(UINT8 nibble) +{ + if (nibble >= 'a') { + return (nibble - 'a' + 10); + } + + if (nibble >= 'A') { + return (nibble - 'A' + 10); + } + + return (nibble - '0'); +} + +void +pmkCacheGeneratePSK(void *priv, UINT8 *pSsid, + UINT8 ssidLen, char *pPassphrase, UINT8 *pPSK) +{ + int i; + +#if 0 //!defined(REMOVE_PATCH_HOOKS) + if (pmkCacheGeneratePSK_hook(pSsid, ssidLen, pPassphrase, pPSK)) { + return; + } +#endif + + if (pPSK && pPassphrase) { + for (i = 0; i < PSK_PASS_PHRASE_LEN_MAX; i++) { + if (pPassphrase[i] == 0) { + break; + } + } + + if (i > 7 && i < PSK_PASS_PHRASE_LEN_MAX) { + /* bump the CPU speed for the PSK generation */ + //ramHook_hal_SetCpuMaxSpeed(); + Mrvl_PasswordHash((void *)priv, pPassphrase, + (UINT8 *)pSsid, ssidLen, pPSK); + //ramHook_hal_RestoreCpuSpeed(); + } else if (i == PSK_PASS_PHRASE_LEN_MAX) { + /* Convert ASCII to binary */ + for (i = 0; i < PSK_PASS_PHRASE_LEN_MAX; i += 2) { + pPSK[i / 2] = + ((pmkCacheGetHexNibble(pPassphrase[i]) + << 4) + | + pmkCacheGetHexNibble(pPassphrase + [i + 1])); + + } + } + } +} diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/pmkCache_rom.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/pmkCache_rom.h new file mode 100644 index 000000000000..98d105674df7 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/pmkCache_rom.h @@ -0,0 +1,125 @@ +/** @file pmkcache_rom.h + * + * @brief This file contains the defien for pmk cache + * + * Copyright (C) 2014-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 03/07/2014: Initial version +******************************************************/ +#ifndef PMK_CACHE_ROM_H__ +#define PMK_CACHE_ROM_H__ + +#include "wltypes.h" +#include "IEEE_types.h" +#include "hostsa_ext_def.h" + +#define PSK_PASS_PHRASE_LEN_MAX 64 +#define PMK_LEN_MAX 32 +#define MAX_PMK_SIZE 32 + +typedef struct { + union { + IEEEtypes_MacAddr_t Bssid; + char Ssid[32]; + } key; + UINT8 PMK[MAX_PMK_SIZE]; /* PMK / PSK */ + UINT8 length; + UINT8 passphrase[PSK_PASS_PHRASE_LEN_MAX]; + SINT8 replacementRank; +} pmkElement_t; + +/*! +** \brief Finds a PMK matching a given BSSID +** \param pBssid pointer to the desired BSSID +** \return pointer to the matching PMK. +** NULL, if no matching PMK entry is found +*/ +extern UINT8 *pmkCacheFindPMK(void *priv, IEEEtypes_MacAddr_t *pBssid); +#if 0 +extern BOOLEAN (*pmkCacheFindPSKElement_hook) (UINT8 *pSsid, + UINT8 ssidLen, + pmkElement_t **ptr_val); +#endif +extern pmkElement_t *pmkCacheFindPSKElement(void *priv, UINT8 *pSsid, + UINT8 ssidLen); + +/*! +** \brief adds a new PMK entry to PMK cache. +** \param pBssid pointer to Bssid for which to add the PMK +** \param pPMK pointer to PMK data +*/ +//extern BOOLEAN (*pmkCacheAddPMK_hook)(IEEEtypes_MacAddr_t * pBssid, +// UINT8 *pPMK); +extern void pmkCacheAddPMK(void *priv, IEEEtypes_MacAddr_t *pBssid, + UINT8 *pPMK); + +/*! +** \brief Adds a new PSK to PMK cache. +** \param pSsid pointer to desired SSID for which to add the PSK entry. +** \param ssidLen length of the SSID string. +** \param pPSK pointer to PSK to store. +*/ +#if 0 +extern BOOLEAN (*pmkCacheAddPSK_hook) (UINT8 *pSsid, + UINT8 ssidLen, UINT8 *pPSK); +#endif +extern void pmkCacheAddPSK(void *priv, UINT8 *pSsid, + UINT8 ssidLen, UINT8 *pPSK, UINT8 *pPassphrase); + +/*! +** \brief Delete a particular PMK entry from PMK cache. +** \param pBssid pointer to BSSID that needs to be deleted +*/ +extern void pmkCacheDeletePMK(void *priv, t_u8 *pBssid); + +/*! +** \brief delete a particular PSK entry from PMK cache +** \param Ssid pointer to SSID that needs to be deleted +** \param ssidLen length of the string pointed to by Ssid +*/ +extern void pmkCacheDeletePSK(void *priv, UINT8 *ssid, UINT8 ssidLen); +#if 0 +extern BOOLEAN (*pmkCacheGeneratePSK_hook) (UINT8 *pSsid, + UINT8 ssidLen, + char *pPassphrase, UINT8 *pPSK); +#endif +extern void pmkCacheGeneratePSK(void *priv, UINT8 *pSsid, + UINT8 ssidLen, char *pPassphrase, UINT8 *pPSK); + +//extern BOOLEAN (*pmkCacheNewElement_hook)(pmkElement_t ** ptr_val); +extern pmkElement_t *pmkCacheNewElement(void *priv); + +//extern BOOLEAN (*pmkCacheFindPMKElement_hook)(IEEEtypes_MacAddr_t * pBssid, +// pmkElement_t ** ptr_val); +extern pmkElement_t *pmkCacheFindPMKElement(void *priv, + IEEEtypes_MacAddr_t *pBssid); + +extern void pmkCacheUpdateReplacementRank(pmkElement_t *pPMKElement); + +extern SINT8 replacementRankMax; + +/* ROM linkages */ +extern SINT32 ramHook_MAX_PMK_CACHE_ENTRIES; +extern pmkElement_t *ramHook_pmkCache; +extern char *ramHook_PSKPassPhrase; + +//extern void (*ramHook_hal_SetCpuMaxSpeed)(void); +//extern void (*ramHook_hal_RestoreCpuSpeed)(void); + +#endif diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/rc4.c b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/rc4.c new file mode 100644 index 000000000000..be941ee73ea9 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/rc4.c @@ -0,0 +1,127 @@ +/** @file rc4.c + * + * @brief This file defines rc4 encrypt algorithm + * + * Copyright (C) 2014-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 03/07/2014: Initial version +******************************************************/ +#include "wltypes.h" +#include "rc4_rom.h" +#include "hostsa_ext_def.h" +#include "authenticator.h" + +typedef struct rc4_key { + unsigned char state[256]; + unsigned char x; + unsigned char y; +} rc4_key; + +static rc4_key rc4key; + +static void swap_byte(unsigned char *a, unsigned char *b); + +void +prepare_key(unsigned char *key_data_ptr, int key_data_len, rc4_key *key) +{ + unsigned char index1; + unsigned char index2; + unsigned char *state; + short counter; + + state = &key->state[0]; + for (counter = 0; counter < 256; counter++) { + state[counter] = counter; + } + key->x = 0; + key->y = 0; + index1 = 0; + index2 = 0; + for (counter = 0; counter < 256; counter++) { + index2 = (key_data_ptr[index1] + state[counter] + index2) % 256; + swap_byte(&state[counter], &state[index2]); + + index1 = (index1 + 1) % key_data_len; + } +} + +void +rc4(unsigned char *buffer_ptr, int buffer_len, int skip, rc4_key *key) +{ + unsigned char x; + unsigned char y; + unsigned char *state; + unsigned char xorIndex; + short counter; + + x = key->x; + y = key->y; + + state = &key->state[0]; + + for (counter = 0; counter < skip; counter++) { + x = (x + 1) % 256; + y = (state[x] + y) % 256; + swap_byte(&state[x], &state[y]); + } + + for (counter = 0; counter < buffer_len; counter++) { + x = (x + 1) % 256; + y = (state[x] + y) % 256; + swap_byte(&state[x], &state[y]); + + xorIndex = (state[x] + state[y]) % 256; + + buffer_ptr[counter] ^= state[xorIndex]; + } + key->x = x; + key->y = y; +} + +static void +swap_byte(unsigned char *a, unsigned char *b) +{ + unsigned char swapByte; + + swapByte = *a; + *a = *b; + *b = swapByte; +} + +void +RC4_Encrypt(void *priv, unsigned char *Encr_Key, + unsigned char *IV, + unsigned short iv_length, + unsigned char *Data, + unsigned short data_length, unsigned short skipBytes) +{ + phostsa_private psapriv = (phostsa_private)priv; + hostsa_util_fns *util_fns = &psapriv->util_fns; + unsigned char key[32]; + + if (iv_length + 16 > sizeof(key)) { + return; + } + + memcpy(util_fns, key, IV, iv_length); + memcpy(util_fns, key + iv_length, Encr_Key, 16); + + prepare_key(key, iv_length + 16, &rc4key); + rc4(Data, data_length, skipBytes, &rc4key); +} diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/rc4.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/rc4.h new file mode 100644 index 000000000000..ad1d38acdd4d --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/rc4.h @@ -0,0 +1,30 @@ +/** @file rc4.h + * + * @brief This file include rc4_rom.h + * + * Copyright (C) 2014-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 03/07/2014: Initial version +******************************************************/ +#ifndef _RC4_H +#define _RC4_H + +#include "rc4_rom.h" + +#endif diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/rc4_rom.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/rc4_rom.h new file mode 100644 index 000000000000..e6ed8d4ebf12 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/rc4_rom.h @@ -0,0 +1,33 @@ +/** @file rc4._rom.h + * + * @brief This file contains the define for rc4 + * + * Copyright (C) 2014-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 03/07/2014: Initial version +******************************************************/ +#ifndef _RC4_ROM_H +#define _RC4_ROM_H + +extern void RC4_Encrypt(void *pmpriv, unsigned char *Encr_Key, + unsigned char *IV, + unsigned short iv_length, + unsigned char *Data, + unsigned short data_length, unsigned short skipBytes); +#endif diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/rijndael.c b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/rijndael.c new file mode 100644 index 000000000000..a2f3b612ea8a --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/rijndael.c @@ -0,0 +1,1351 @@ +/** @file rijndael.c + * + * @brief This file Optimised ANSI C code for the Rijndael cipher (now AES) + * + * Copyright (C) 2014-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 03/07/2014: Initial version +******************************************************/ + +#include + +#include "rijndael.h" +#include "wltypes.h" +//#define FULL_UNROLL + +/* +Te0[x] = S [x].[02, 01, 01, 03]; +Te1[x] = S [x].[03, 02, 01, 01]; +Te2[x] = S [x].[01, 03, 02, 01]; +Te3[x] = S [x].[01, 01, 03, 02]; +Te4[x] = S [x].[01, 01, 01, 01]; + +Td0[x] = Si[x].[0e, 09, 0d, 0b]; +Td1[x] = Si[x].[0b, 0e, 09, 0d]; +Td2[x] = Si[x].[0d, 0b, 0e, 09]; +Td3[x] = Si[x].[09, 0d, 0b, 0e]; +Td4[x] = Si[x].[01, 01, 01, 01]; +*/ + +//#ifdef AES_WRAP_FUNCTION +static const u32 Te0[256] = { + 0xc66363a5U, 0xf87c7c84U, 0xee777799U, 0xf67b7b8dU, + 0xfff2f20dU, 0xd66b6bbdU, 0xde6f6fb1U, 0x91c5c554U, + 0x60303050U, 0x02010103U, 0xce6767a9U, 0x562b2b7dU, + 0xe7fefe19U, 0xb5d7d762U, 0x4dababe6U, 0xec76769aU, + 0x8fcaca45U, 0x1f82829dU, 0x89c9c940U, 0xfa7d7d87U, + 0xeffafa15U, 0xb25959ebU, 0x8e4747c9U, 0xfbf0f00bU, + 0x41adadecU, 0xb3d4d467U, 0x5fa2a2fdU, 0x45afafeaU, + 0x239c9cbfU, 0x53a4a4f7U, 0xe4727296U, 0x9bc0c05bU, + 0x75b7b7c2U, 0xe1fdfd1cU, 0x3d9393aeU, 0x4c26266aU, + 0x6c36365aU, 0x7e3f3f41U, 0xf5f7f702U, 0x83cccc4fU, + 0x6834345cU, 0x51a5a5f4U, 0xd1e5e534U, 0xf9f1f108U, + 0xe2717193U, 0xabd8d873U, 0x62313153U, 0x2a15153fU, + 0x0804040cU, 0x95c7c752U, 0x46232365U, 0x9dc3c35eU, + 0x30181828U, 0x379696a1U, 0x0a05050fU, 0x2f9a9ab5U, + 0x0e070709U, 0x24121236U, 0x1b80809bU, 0xdfe2e23dU, + 0xcdebeb26U, 0x4e272769U, 0x7fb2b2cdU, 0xea75759fU, + 0x1209091bU, 0x1d83839eU, 0x582c2c74U, 0x341a1a2eU, + 0x361b1b2dU, 0xdc6e6eb2U, 0xb45a5aeeU, 0x5ba0a0fbU, + 0xa45252f6U, 0x763b3b4dU, 0xb7d6d661U, 0x7db3b3ceU, + 0x5229297bU, 0xdde3e33eU, 0x5e2f2f71U, 0x13848497U, + 0xa65353f5U, 0xb9d1d168U, 0x00000000U, 0xc1eded2cU, + 0x40202060U, 0xe3fcfc1fU, 0x79b1b1c8U, 0xb65b5bedU, + 0xd46a6abeU, 0x8dcbcb46U, 0x67bebed9U, 0x7239394bU, + 0x944a4adeU, 0x984c4cd4U, 0xb05858e8U, 0x85cfcf4aU, + 0xbbd0d06bU, 0xc5efef2aU, 0x4faaaae5U, 0xedfbfb16U, + 0x864343c5U, 0x9a4d4dd7U, 0x66333355U, 0x11858594U, + 0x8a4545cfU, 0xe9f9f910U, 0x04020206U, 0xfe7f7f81U, + 0xa05050f0U, 0x783c3c44U, 0x259f9fbaU, 0x4ba8a8e3U, + 0xa25151f3U, 0x5da3a3feU, 0x804040c0U, 0x058f8f8aU, + 0x3f9292adU, 0x219d9dbcU, 0x70383848U, 0xf1f5f504U, + 0x63bcbcdfU, 0x77b6b6c1U, 0xafdada75U, 0x42212163U, + 0x20101030U, 0xe5ffff1aU, 0xfdf3f30eU, 0xbfd2d26dU, + 0x81cdcd4cU, 0x180c0c14U, 0x26131335U, 0xc3ecec2fU, + 0xbe5f5fe1U, 0x359797a2U, 0x884444ccU, 0x2e171739U, + 0x93c4c457U, 0x55a7a7f2U, 0xfc7e7e82U, 0x7a3d3d47U, + 0xc86464acU, 0xba5d5de7U, 0x3219192bU, 0xe6737395U, + 0xc06060a0U, 0x19818198U, 0x9e4f4fd1U, 0xa3dcdc7fU, + 0x44222266U, 0x542a2a7eU, 0x3b9090abU, 0x0b888883U, + 0x8c4646caU, 0xc7eeee29U, 0x6bb8b8d3U, 0x2814143cU, + 0xa7dede79U, 0xbc5e5ee2U, 0x160b0b1dU, 0xaddbdb76U, + 0xdbe0e03bU, 0x64323256U, 0x743a3a4eU, 0x140a0a1eU, + 0x924949dbU, 0x0c06060aU, 0x4824246cU, 0xb85c5ce4U, + 0x9fc2c25dU, 0xbdd3d36eU, 0x43acacefU, 0xc46262a6U, + 0x399191a8U, 0x319595a4U, 0xd3e4e437U, 0xf279798bU, + 0xd5e7e732U, 0x8bc8c843U, 0x6e373759U, 0xda6d6db7U, + 0x018d8d8cU, 0xb1d5d564U, 0x9c4e4ed2U, 0x49a9a9e0U, + 0xd86c6cb4U, 0xac5656faU, 0xf3f4f407U, 0xcfeaea25U, + 0xca6565afU, 0xf47a7a8eU, 0x47aeaee9U, 0x10080818U, + 0x6fbabad5U, 0xf0787888U, 0x4a25256fU, 0x5c2e2e72U, + 0x381c1c24U, 0x57a6a6f1U, 0x73b4b4c7U, 0x97c6c651U, + 0xcbe8e823U, 0xa1dddd7cU, 0xe874749cU, 0x3e1f1f21U, + 0x964b4bddU, 0x61bdbddcU, 0x0d8b8b86U, 0x0f8a8a85U, + 0xe0707090U, 0x7c3e3e42U, 0x71b5b5c4U, 0xcc6666aaU, + 0x904848d8U, 0x06030305U, 0xf7f6f601U, 0x1c0e0e12U, + 0xc26161a3U, 0x6a35355fU, 0xae5757f9U, 0x69b9b9d0U, + 0x17868691U, 0x99c1c158U, 0x3a1d1d27U, 0x279e9eb9U, + 0xd9e1e138U, 0xebf8f813U, 0x2b9898b3U, 0x22111133U, + 0xd26969bbU, 0xa9d9d970U, 0x078e8e89U, 0x339494a7U, + 0x2d9b9bb6U, 0x3c1e1e22U, 0x15878792U, 0xc9e9e920U, + 0x87cece49U, 0xaa5555ffU, 0x50282878U, 0xa5dfdf7aU, + 0x038c8c8fU, 0x59a1a1f8U, 0x09898980U, 0x1a0d0d17U, + 0x65bfbfdaU, 0xd7e6e631U, 0x844242c6U, 0xd06868b8U, + 0x824141c3U, 0x299999b0U, 0x5a2d2d77U, 0x1e0f0f11U, + 0x7bb0b0cbU, 0xa85454fcU, 0x6dbbbbd6U, 0x2c16163aU, +}; + +static const u32 Te1[256] = { + 0xa5c66363U, 0x84f87c7cU, 0x99ee7777U, 0x8df67b7bU, + 0x0dfff2f2U, 0xbdd66b6bU, 0xb1de6f6fU, 0x5491c5c5U, + 0x50603030U, 0x03020101U, 0xa9ce6767U, 0x7d562b2bU, + 0x19e7fefeU, 0x62b5d7d7U, 0xe64dababU, 0x9aec7676U, + 0x458fcacaU, 0x9d1f8282U, 0x4089c9c9U, 0x87fa7d7dU, + 0x15effafaU, 0xebb25959U, 0xc98e4747U, 0x0bfbf0f0U, + 0xec41adadU, 0x67b3d4d4U, 0xfd5fa2a2U, 0xea45afafU, + 0xbf239c9cU, 0xf753a4a4U, 0x96e47272U, 0x5b9bc0c0U, + 0xc275b7b7U, 0x1ce1fdfdU, 0xae3d9393U, 0x6a4c2626U, + 0x5a6c3636U, 0x417e3f3fU, 0x02f5f7f7U, 0x4f83ccccU, + 0x5c683434U, 0xf451a5a5U, 0x34d1e5e5U, 0x08f9f1f1U, + 0x93e27171U, 0x73abd8d8U, 0x53623131U, 0x3f2a1515U, + 0x0c080404U, 0x5295c7c7U, 0x65462323U, 0x5e9dc3c3U, + 0x28301818U, 0xa1379696U, 0x0f0a0505U, 0xb52f9a9aU, + 0x090e0707U, 0x36241212U, 0x9b1b8080U, 0x3ddfe2e2U, + 0x26cdebebU, 0x694e2727U, 0xcd7fb2b2U, 0x9fea7575U, + 0x1b120909U, 0x9e1d8383U, 0x74582c2cU, 0x2e341a1aU, + 0x2d361b1bU, 0xb2dc6e6eU, 0xeeb45a5aU, 0xfb5ba0a0U, + 0xf6a45252U, 0x4d763b3bU, 0x61b7d6d6U, 0xce7db3b3U, + 0x7b522929U, 0x3edde3e3U, 0x715e2f2fU, 0x97138484U, + 0xf5a65353U, 0x68b9d1d1U, 0x00000000U, 0x2cc1ededU, + 0x60402020U, 0x1fe3fcfcU, 0xc879b1b1U, 0xedb65b5bU, + 0xbed46a6aU, 0x468dcbcbU, 0xd967bebeU, 0x4b723939U, + 0xde944a4aU, 0xd4984c4cU, 0xe8b05858U, 0x4a85cfcfU, + 0x6bbbd0d0U, 0x2ac5efefU, 0xe54faaaaU, 0x16edfbfbU, + 0xc5864343U, 0xd79a4d4dU, 0x55663333U, 0x94118585U, + 0xcf8a4545U, 0x10e9f9f9U, 0x06040202U, 0x81fe7f7fU, + 0xf0a05050U, 0x44783c3cU, 0xba259f9fU, 0xe34ba8a8U, + 0xf3a25151U, 0xfe5da3a3U, 0xc0804040U, 0x8a058f8fU, + 0xad3f9292U, 0xbc219d9dU, 0x48703838U, 0x04f1f5f5U, + 0xdf63bcbcU, 0xc177b6b6U, 0x75afdadaU, 0x63422121U, + 0x30201010U, 0x1ae5ffffU, 0x0efdf3f3U, 0x6dbfd2d2U, + 0x4c81cdcdU, 0x14180c0cU, 0x35261313U, 0x2fc3ececU, + 0xe1be5f5fU, 0xa2359797U, 0xcc884444U, 0x392e1717U, + 0x5793c4c4U, 0xf255a7a7U, 0x82fc7e7eU, 0x477a3d3dU, + 0xacc86464U, 0xe7ba5d5dU, 0x2b321919U, 0x95e67373U, + 0xa0c06060U, 0x98198181U, 0xd19e4f4fU, 0x7fa3dcdcU, + 0x66442222U, 0x7e542a2aU, 0xab3b9090U, 0x830b8888U, + 0xca8c4646U, 0x29c7eeeeU, 0xd36bb8b8U, 0x3c281414U, + 0x79a7dedeU, 0xe2bc5e5eU, 0x1d160b0bU, 0x76addbdbU, + 0x3bdbe0e0U, 0x56643232U, 0x4e743a3aU, 0x1e140a0aU, + 0xdb924949U, 0x0a0c0606U, 0x6c482424U, 0xe4b85c5cU, + 0x5d9fc2c2U, 0x6ebdd3d3U, 0xef43acacU, 0xa6c46262U, + 0xa8399191U, 0xa4319595U, 0x37d3e4e4U, 0x8bf27979U, + 0x32d5e7e7U, 0x438bc8c8U, 0x596e3737U, 0xb7da6d6dU, + 0x8c018d8dU, 0x64b1d5d5U, 0xd29c4e4eU, 0xe049a9a9U, + 0xb4d86c6cU, 0xfaac5656U, 0x07f3f4f4U, 0x25cfeaeaU, + 0xafca6565U, 0x8ef47a7aU, 0xe947aeaeU, 0x18100808U, + 0xd56fbabaU, 0x88f07878U, 0x6f4a2525U, 0x725c2e2eU, + 0x24381c1cU, 0xf157a6a6U, 0xc773b4b4U, 0x5197c6c6U, + 0x23cbe8e8U, 0x7ca1ddddU, 0x9ce87474U, 0x213e1f1fU, + 0xdd964b4bU, 0xdc61bdbdU, 0x860d8b8bU, 0x850f8a8aU, + 0x90e07070U, 0x427c3e3eU, 0xc471b5b5U, 0xaacc6666U, + 0xd8904848U, 0x05060303U, 0x01f7f6f6U, 0x121c0e0eU, + 0xa3c26161U, 0x5f6a3535U, 0xf9ae5757U, 0xd069b9b9U, + 0x91178686U, 0x5899c1c1U, 0x273a1d1dU, 0xb9279e9eU, + 0x38d9e1e1U, 0x13ebf8f8U, 0xb32b9898U, 0x33221111U, + 0xbbd26969U, 0x70a9d9d9U, 0x89078e8eU, 0xa7339494U, + 0xb62d9b9bU, 0x223c1e1eU, 0x92158787U, 0x20c9e9e9U, + 0x4987ceceU, 0xffaa5555U, 0x78502828U, 0x7aa5dfdfU, + 0x8f038c8cU, 0xf859a1a1U, 0x80098989U, 0x171a0d0dU, + 0xda65bfbfU, 0x31d7e6e6U, 0xc6844242U, 0xb8d06868U, + 0xc3824141U, 0xb0299999U, 0x775a2d2dU, 0x111e0f0fU, + 0xcb7bb0b0U, 0xfca85454U, 0xd66dbbbbU, 0x3a2c1616U, +}; + +static const u32 Te2[256] = { + 0x63a5c663U, 0x7c84f87cU, 0x7799ee77U, 0x7b8df67bU, + 0xf20dfff2U, 0x6bbdd66bU, 0x6fb1de6fU, 0xc55491c5U, + 0x30506030U, 0x01030201U, 0x67a9ce67U, 0x2b7d562bU, + 0xfe19e7feU, 0xd762b5d7U, 0xabe64dabU, 0x769aec76U, + 0xca458fcaU, 0x829d1f82U, 0xc94089c9U, 0x7d87fa7dU, + 0xfa15effaU, 0x59ebb259U, 0x47c98e47U, 0xf00bfbf0U, + 0xadec41adU, 0xd467b3d4U, 0xa2fd5fa2U, 0xafea45afU, + 0x9cbf239cU, 0xa4f753a4U, 0x7296e472U, 0xc05b9bc0U, + 0xb7c275b7U, 0xfd1ce1fdU, 0x93ae3d93U, 0x266a4c26U, + 0x365a6c36U, 0x3f417e3fU, 0xf702f5f7U, 0xcc4f83ccU, + 0x345c6834U, 0xa5f451a5U, 0xe534d1e5U, 0xf108f9f1U, + 0x7193e271U, 0xd873abd8U, 0x31536231U, 0x153f2a15U, + 0x040c0804U, 0xc75295c7U, 0x23654623U, 0xc35e9dc3U, + 0x18283018U, 0x96a13796U, 0x050f0a05U, 0x9ab52f9aU, + 0x07090e07U, 0x12362412U, 0x809b1b80U, 0xe23ddfe2U, + 0xeb26cdebU, 0x27694e27U, 0xb2cd7fb2U, 0x759fea75U, + 0x091b1209U, 0x839e1d83U, 0x2c74582cU, 0x1a2e341aU, + 0x1b2d361bU, 0x6eb2dc6eU, 0x5aeeb45aU, 0xa0fb5ba0U, + 0x52f6a452U, 0x3b4d763bU, 0xd661b7d6U, 0xb3ce7db3U, + 0x297b5229U, 0xe33edde3U, 0x2f715e2fU, 0x84971384U, + 0x53f5a653U, 0xd168b9d1U, 0x00000000U, 0xed2cc1edU, + 0x20604020U, 0xfc1fe3fcU, 0xb1c879b1U, 0x5bedb65bU, + 0x6abed46aU, 0xcb468dcbU, 0xbed967beU, 0x394b7239U, + 0x4ade944aU, 0x4cd4984cU, 0x58e8b058U, 0xcf4a85cfU, + 0xd06bbbd0U, 0xef2ac5efU, 0xaae54faaU, 0xfb16edfbU, + 0x43c58643U, 0x4dd79a4dU, 0x33556633U, 0x85941185U, + 0x45cf8a45U, 0xf910e9f9U, 0x02060402U, 0x7f81fe7fU, + 0x50f0a050U, 0x3c44783cU, 0x9fba259fU, 0xa8e34ba8U, + 0x51f3a251U, 0xa3fe5da3U, 0x40c08040U, 0x8f8a058fU, + 0x92ad3f92U, 0x9dbc219dU, 0x38487038U, 0xf504f1f5U, + 0xbcdf63bcU, 0xb6c177b6U, 0xda75afdaU, 0x21634221U, + 0x10302010U, 0xff1ae5ffU, 0xf30efdf3U, 0xd26dbfd2U, + 0xcd4c81cdU, 0x0c14180cU, 0x13352613U, 0xec2fc3ecU, + 0x5fe1be5fU, 0x97a23597U, 0x44cc8844U, 0x17392e17U, + 0xc45793c4U, 0xa7f255a7U, 0x7e82fc7eU, 0x3d477a3dU, + 0x64acc864U, 0x5de7ba5dU, 0x192b3219U, 0x7395e673U, + 0x60a0c060U, 0x81981981U, 0x4fd19e4fU, 0xdc7fa3dcU, + 0x22664422U, 0x2a7e542aU, 0x90ab3b90U, 0x88830b88U, + 0x46ca8c46U, 0xee29c7eeU, 0xb8d36bb8U, 0x143c2814U, + 0xde79a7deU, 0x5ee2bc5eU, 0x0b1d160bU, 0xdb76addbU, + 0xe03bdbe0U, 0x32566432U, 0x3a4e743aU, 0x0a1e140aU, + 0x49db9249U, 0x060a0c06U, 0x246c4824U, 0x5ce4b85cU, + 0xc25d9fc2U, 0xd36ebdd3U, 0xacef43acU, 0x62a6c462U, + 0x91a83991U, 0x95a43195U, 0xe437d3e4U, 0x798bf279U, + 0xe732d5e7U, 0xc8438bc8U, 0x37596e37U, 0x6db7da6dU, + 0x8d8c018dU, 0xd564b1d5U, 0x4ed29c4eU, 0xa9e049a9U, + 0x6cb4d86cU, 0x56faac56U, 0xf407f3f4U, 0xea25cfeaU, + 0x65afca65U, 0x7a8ef47aU, 0xaee947aeU, 0x08181008U, + 0xbad56fbaU, 0x7888f078U, 0x256f4a25U, 0x2e725c2eU, + 0x1c24381cU, 0xa6f157a6U, 0xb4c773b4U, 0xc65197c6U, + 0xe823cbe8U, 0xdd7ca1ddU, 0x749ce874U, 0x1f213e1fU, + 0x4bdd964bU, 0xbddc61bdU, 0x8b860d8bU, 0x8a850f8aU, + 0x7090e070U, 0x3e427c3eU, 0xb5c471b5U, 0x66aacc66U, + 0x48d89048U, 0x03050603U, 0xf601f7f6U, 0x0e121c0eU, + 0x61a3c261U, 0x355f6a35U, 0x57f9ae57U, 0xb9d069b9U, + 0x86911786U, 0xc15899c1U, 0x1d273a1dU, 0x9eb9279eU, + 0xe138d9e1U, 0xf813ebf8U, 0x98b32b98U, 0x11332211U, + 0x69bbd269U, 0xd970a9d9U, 0x8e89078eU, 0x94a73394U, + 0x9bb62d9bU, 0x1e223c1eU, 0x87921587U, 0xe920c9e9U, + 0xce4987ceU, 0x55ffaa55U, 0x28785028U, 0xdf7aa5dfU, + 0x8c8f038cU, 0xa1f859a1U, 0x89800989U, 0x0d171a0dU, + 0xbfda65bfU, 0xe631d7e6U, 0x42c68442U, 0x68b8d068U, + 0x41c38241U, 0x99b02999U, 0x2d775a2dU, 0x0f111e0fU, + 0xb0cb7bb0U, 0x54fca854U, 0xbbd66dbbU, 0x163a2c16U, +}; + +static const u32 Te3[256] = { + + 0x6363a5c6U, 0x7c7c84f8U, 0x777799eeU, 0x7b7b8df6U, + 0xf2f20dffU, 0x6b6bbdd6U, 0x6f6fb1deU, 0xc5c55491U, + 0x30305060U, 0x01010302U, 0x6767a9ceU, 0x2b2b7d56U, + 0xfefe19e7U, 0xd7d762b5U, 0xababe64dU, 0x76769aecU, + 0xcaca458fU, 0x82829d1fU, 0xc9c94089U, 0x7d7d87faU, + 0xfafa15efU, 0x5959ebb2U, 0x4747c98eU, 0xf0f00bfbU, + 0xadadec41U, 0xd4d467b3U, 0xa2a2fd5fU, 0xafafea45U, + 0x9c9cbf23U, 0xa4a4f753U, 0x727296e4U, 0xc0c05b9bU, + 0xb7b7c275U, 0xfdfd1ce1U, 0x9393ae3dU, 0x26266a4cU, + 0x36365a6cU, 0x3f3f417eU, 0xf7f702f5U, 0xcccc4f83U, + 0x34345c68U, 0xa5a5f451U, 0xe5e534d1U, 0xf1f108f9U, + 0x717193e2U, 0xd8d873abU, 0x31315362U, 0x15153f2aU, + 0x04040c08U, 0xc7c75295U, 0x23236546U, 0xc3c35e9dU, + 0x18182830U, 0x9696a137U, 0x05050f0aU, 0x9a9ab52fU, + 0x0707090eU, 0x12123624U, 0x80809b1bU, 0xe2e23ddfU, + 0xebeb26cdU, 0x2727694eU, 0xb2b2cd7fU, 0x75759feaU, + 0x09091b12U, 0x83839e1dU, 0x2c2c7458U, 0x1a1a2e34U, + 0x1b1b2d36U, 0x6e6eb2dcU, 0x5a5aeeb4U, 0xa0a0fb5bU, + 0x5252f6a4U, 0x3b3b4d76U, 0xd6d661b7U, 0xb3b3ce7dU, + 0x29297b52U, 0xe3e33eddU, 0x2f2f715eU, 0x84849713U, + 0x5353f5a6U, 0xd1d168b9U, 0x00000000U, 0xeded2cc1U, + 0x20206040U, 0xfcfc1fe3U, 0xb1b1c879U, 0x5b5bedb6U, + 0x6a6abed4U, 0xcbcb468dU, 0xbebed967U, 0x39394b72U, + 0x4a4ade94U, 0x4c4cd498U, 0x5858e8b0U, 0xcfcf4a85U, + 0xd0d06bbbU, 0xefef2ac5U, 0xaaaae54fU, 0xfbfb16edU, + 0x4343c586U, 0x4d4dd79aU, 0x33335566U, 0x85859411U, + 0x4545cf8aU, 0xf9f910e9U, 0x02020604U, 0x7f7f81feU, + 0x5050f0a0U, 0x3c3c4478U, 0x9f9fba25U, 0xa8a8e34bU, + 0x5151f3a2U, 0xa3a3fe5dU, 0x4040c080U, 0x8f8f8a05U, + 0x9292ad3fU, 0x9d9dbc21U, 0x38384870U, 0xf5f504f1U, + 0xbcbcdf63U, 0xb6b6c177U, 0xdada75afU, 0x21216342U, + 0x10103020U, 0xffff1ae5U, 0xf3f30efdU, 0xd2d26dbfU, + 0xcdcd4c81U, 0x0c0c1418U, 0x13133526U, 0xecec2fc3U, + 0x5f5fe1beU, 0x9797a235U, 0x4444cc88U, 0x1717392eU, + 0xc4c45793U, 0xa7a7f255U, 0x7e7e82fcU, 0x3d3d477aU, + 0x6464acc8U, 0x5d5de7baU, 0x19192b32U, 0x737395e6U, + 0x6060a0c0U, 0x81819819U, 0x4f4fd19eU, 0xdcdc7fa3U, + 0x22226644U, 0x2a2a7e54U, 0x9090ab3bU, 0x8888830bU, + 0x4646ca8cU, 0xeeee29c7U, 0xb8b8d36bU, 0x14143c28U, + 0xdede79a7U, 0x5e5ee2bcU, 0x0b0b1d16U, 0xdbdb76adU, + 0xe0e03bdbU, 0x32325664U, 0x3a3a4e74U, 0x0a0a1e14U, + 0x4949db92U, 0x06060a0cU, 0x24246c48U, 0x5c5ce4b8U, + 0xc2c25d9fU, 0xd3d36ebdU, 0xacacef43U, 0x6262a6c4U, + 0x9191a839U, 0x9595a431U, 0xe4e437d3U, 0x79798bf2U, + 0xe7e732d5U, 0xc8c8438bU, 0x3737596eU, 0x6d6db7daU, + 0x8d8d8c01U, 0xd5d564b1U, 0x4e4ed29cU, 0xa9a9e049U, + 0x6c6cb4d8U, 0x5656faacU, 0xf4f407f3U, 0xeaea25cfU, + 0x6565afcaU, 0x7a7a8ef4U, 0xaeaee947U, 0x08081810U, + 0xbabad56fU, 0x787888f0U, 0x25256f4aU, 0x2e2e725cU, + 0x1c1c2438U, 0xa6a6f157U, 0xb4b4c773U, 0xc6c65197U, + 0xe8e823cbU, 0xdddd7ca1U, 0x74749ce8U, 0x1f1f213eU, + 0x4b4bdd96U, 0xbdbddc61U, 0x8b8b860dU, 0x8a8a850fU, + 0x707090e0U, 0x3e3e427cU, 0xb5b5c471U, 0x6666aaccU, + 0x4848d890U, 0x03030506U, 0xf6f601f7U, 0x0e0e121cU, + 0x6161a3c2U, 0x35355f6aU, 0x5757f9aeU, 0xb9b9d069U, + 0x86869117U, 0xc1c15899U, 0x1d1d273aU, 0x9e9eb927U, + 0xe1e138d9U, 0xf8f813ebU, 0x9898b32bU, 0x11113322U, + 0x6969bbd2U, 0xd9d970a9U, 0x8e8e8907U, 0x9494a733U, + 0x9b9bb62dU, 0x1e1e223cU, 0x87879215U, 0xe9e920c9U, + 0xcece4987U, 0x5555ffaaU, 0x28287850U, 0xdfdf7aa5U, + 0x8c8c8f03U, 0xa1a1f859U, 0x89898009U, 0x0d0d171aU, + 0xbfbfda65U, 0xe6e631d7U, 0x4242c684U, 0x6868b8d0U, + 0x4141c382U, 0x9999b029U, 0x2d2d775aU, 0x0f0f111eU, + 0xb0b0cb7bU, 0x5454fca8U, 0xbbbbd66dU, 0x16163a2cU, +}; + +//#endif +static const u8 Te4[256] = { + 0x63U, 0x7cU, 0x77U, 0x7bU, + 0xf2U, 0x6bU, 0x6fU, 0xc5U, + 0x30U, 0x01U, 0x67U, 0x2bU, + 0xfeU, 0xd7U, 0xabU, 0x76U, + 0xcaU, 0x82U, 0xc9U, 0x7dU, + 0xfaU, 0x59U, 0x47U, 0xf0U, + 0xadU, 0xd4U, 0xa2U, 0xafU, + 0x9cU, 0xa4U, 0x72U, 0xc0U, + 0xb7U, 0xfdU, 0x93U, 0x26U, + 0x36U, 0x3fU, 0xf7U, 0xccU, + 0x34U, 0xa5U, 0xe5U, 0xf1U, + 0x71U, 0xd8U, 0x31U, 0x15U, + 0x04U, 0xc7U, 0x23U, 0xc3U, + 0x18U, 0x96U, 0x05U, 0x9aU, + 0x07U, 0x12U, 0x80U, 0xe2U, + 0xebU, 0x27U, 0xb2U, 0x75U, + 0x09U, 0x83U, 0x2cU, 0x1aU, + 0x1bU, 0x6eU, 0x5aU, 0xa0U, + 0x52U, 0x3bU, 0xd6U, 0xb3U, + 0x29U, 0xe3U, 0x2fU, 0x84U, + 0x53U, 0xd1U, 0x00U, 0xedU, + 0x20U, 0xfcU, 0xb1U, 0x5bU, + 0x6aU, 0xcbU, 0xbeU, 0x39U, + 0x4aU, 0x4cU, 0x58U, 0xcfU, + 0xd0U, 0xefU, 0xaaU, 0xfbU, + 0x43U, 0x4dU, 0x33U, 0x85U, + 0x45U, 0xf9U, 0x02U, 0x7fU, + 0x50U, 0x3cU, 0x9fU, 0xa8U, + 0x51U, 0xa3U, 0x40U, 0x8fU, + 0x92U, 0x9dU, 0x38U, 0xf5U, + 0xbcU, 0xb6U, 0xdaU, 0x21U, + 0x10U, 0xffU, 0xf3U, 0xd2U, + 0xcdU, 0x0cU, 0x13U, 0xecU, + 0x5fU, 0x97U, 0x44U, 0x17U, + 0xc4U, 0xa7U, 0x7eU, 0x3dU, + 0x64U, 0x5dU, 0x19U, 0x73U, + 0x60U, 0x81U, 0x4fU, 0xdcU, + 0x22U, 0x2aU, 0x90U, 0x88U, + 0x46U, 0xeeU, 0xb8U, 0x14U, + 0xdeU, 0x5eU, 0x0bU, 0xdbU, + 0xe0U, 0x32U, 0x3aU, 0x0aU, + 0x49U, 0x06U, 0x24U, 0x5cU, + 0xc2U, 0xd3U, 0xacU, 0x62U, + 0x91U, 0x95U, 0xe4U, 0x79U, + 0xe7U, 0xc8U, 0x37U, 0x6dU, + 0x8dU, 0xd5U, 0x4eU, 0xa9U, + 0x6cU, 0x56U, 0xf4U, 0xeaU, + 0x65U, 0x7aU, 0xaeU, 0x08U, + 0xbaU, 0x78U, 0x25U, 0x2eU, + 0x1cU, 0xa6U, 0xb4U, 0xc6U, + 0xe8U, 0xddU, 0x74U, 0x1fU, + 0x4bU, 0xbdU, 0x8bU, 0x8aU, + 0x70U, 0x3eU, 0xb5U, 0x66U, + 0x48U, 0x03U, 0xf6U, 0x0eU, + 0x61U, 0x35U, 0x57U, 0xb9U, + 0x86U, 0xc1U, 0x1dU, 0x9eU, + 0xe1U, 0xf8U, 0x98U, 0x11U, + 0x69U, 0xd9U, 0x8eU, 0x94U, + 0x9bU, 0x1eU, 0x87U, 0xe9U, + 0xceU, 0x55U, 0x28U, 0xdfU, + 0x8cU, 0xa1U, 0x89U, 0x0dU, + 0xbfU, 0xe6U, 0x42U, 0x68U, + 0x41U, 0x99U, 0x2dU, 0x0fU, + 0xb0U, 0x54U, 0xbbU, 0x16U, +}; + +static const u32 Td0[256] = { + 0x51f4a750U, 0x7e416553U, 0x1a17a4c3U, 0x3a275e96U, + 0x3bab6bcbU, 0x1f9d45f1U, 0xacfa58abU, 0x4be30393U, + 0x2030fa55U, 0xad766df6U, 0x88cc7691U, 0xf5024c25U, + 0x4fe5d7fcU, 0xc52acbd7U, 0x26354480U, 0xb562a38fU, + 0xdeb15a49U, 0x25ba1b67U, 0x45ea0e98U, 0x5dfec0e1U, + 0xc32f7502U, 0x814cf012U, 0x8d4697a3U, 0x6bd3f9c6U, + 0x038f5fe7U, 0x15929c95U, 0xbf6d7aebU, 0x955259daU, + 0xd4be832dU, 0x587421d3U, 0x49e06929U, 0x8ec9c844U, + 0x75c2896aU, 0xf48e7978U, 0x99583e6bU, 0x27b971ddU, + 0xbee14fb6U, 0xf088ad17U, 0xc920ac66U, 0x7dce3ab4U, + 0x63df4a18U, 0xe51a3182U, 0x97513360U, 0x62537f45U, + 0xb16477e0U, 0xbb6bae84U, 0xfe81a01cU, 0xf9082b94U, + 0x70486858U, 0x8f45fd19U, 0x94de6c87U, 0x527bf8b7U, + 0xab73d323U, 0x724b02e2U, 0xe31f8f57U, 0x6655ab2aU, + 0xb2eb2807U, 0x2fb5c203U, 0x86c57b9aU, 0xd33708a5U, + 0x302887f2U, 0x23bfa5b2U, 0x02036abaU, 0xed16825cU, + 0x8acf1c2bU, 0xa779b492U, 0xf307f2f0U, 0x4e69e2a1U, + 0x65daf4cdU, 0x0605bed5U, 0xd134621fU, 0xc4a6fe8aU, + 0x342e539dU, 0xa2f355a0U, 0x058ae132U, 0xa4f6eb75U, + 0x0b83ec39U, 0x4060efaaU, 0x5e719f06U, 0xbd6e1051U, + 0x3e218af9U, 0x96dd063dU, 0xdd3e05aeU, 0x4de6bd46U, + 0x91548db5U, 0x71c45d05U, 0x0406d46fU, 0x605015ffU, + 0x1998fb24U, 0xd6bde997U, 0x894043ccU, 0x67d99e77U, + 0xb0e842bdU, 0x07898b88U, 0xe7195b38U, 0x79c8eedbU, + 0xa17c0a47U, 0x7c420fe9U, 0xf8841ec9U, 0x00000000U, + 0x09808683U, 0x322bed48U, 0x1e1170acU, 0x6c5a724eU, + 0xfd0efffbU, 0x0f853856U, 0x3daed51eU, 0x362d3927U, + 0x0a0fd964U, 0x685ca621U, 0x9b5b54d1U, 0x24362e3aU, + 0x0c0a67b1U, 0x9357e70fU, 0xb4ee96d2U, 0x1b9b919eU, + 0x80c0c54fU, 0x61dc20a2U, 0x5a774b69U, 0x1c121a16U, + 0xe293ba0aU, 0xc0a02ae5U, 0x3c22e043U, 0x121b171dU, + 0x0e090d0bU, 0xf28bc7adU, 0x2db6a8b9U, 0x141ea9c8U, + 0x57f11985U, 0xaf75074cU, 0xee99ddbbU, 0xa37f60fdU, + 0xf701269fU, 0x5c72f5bcU, 0x44663bc5U, 0x5bfb7e34U, + 0x8b432976U, 0xcb23c6dcU, 0xb6edfc68U, 0xb8e4f163U, + 0xd731dccaU, 0x42638510U, 0x13972240U, 0x84c61120U, + 0x854a247dU, 0xd2bb3df8U, 0xaef93211U, 0xc729a16dU, + 0x1d9e2f4bU, 0xdcb230f3U, 0x0d8652ecU, 0x77c1e3d0U, + 0x2bb3166cU, 0xa970b999U, 0x119448faU, 0x47e96422U, + 0xa8fc8cc4U, 0xa0f03f1aU, 0x567d2cd8U, 0x223390efU, + 0x87494ec7U, 0xd938d1c1U, 0x8ccaa2feU, 0x98d40b36U, + 0xa6f581cfU, 0xa57ade28U, 0xdab78e26U, 0x3fadbfa4U, + 0x2c3a9de4U, 0x5078920dU, 0x6a5fcc9bU, 0x547e4662U, + 0xf68d13c2U, 0x90d8b8e8U, 0x2e39f75eU, 0x82c3aff5U, + 0x9f5d80beU, 0x69d0937cU, 0x6fd52da9U, 0xcf2512b3U, + 0xc8ac993bU, 0x10187da7U, 0xe89c636eU, 0xdb3bbb7bU, + 0xcd267809U, 0x6e5918f4U, 0xec9ab701U, 0x834f9aa8U, + 0xe6956e65U, 0xaaffe67eU, 0x21bccf08U, 0xef15e8e6U, + 0xbae79bd9U, 0x4a6f36ceU, 0xea9f09d4U, 0x29b07cd6U, + 0x31a4b2afU, 0x2a3f2331U, 0xc6a59430U, 0x35a266c0U, + 0x744ebc37U, 0xfc82caa6U, 0xe090d0b0U, 0x33a7d815U, + 0xf104984aU, 0x41ecdaf7U, 0x7fcd500eU, 0x1791f62fU, + 0x764dd68dU, 0x43efb04dU, 0xccaa4d54U, 0xe49604dfU, + 0x9ed1b5e3U, 0x4c6a881bU, 0xc12c1fb8U, 0x4665517fU, + 0x9d5eea04U, 0x018c355dU, 0xfa877473U, 0xfb0b412eU, + 0xb3671d5aU, 0x92dbd252U, 0xe9105633U, 0x6dd64713U, + 0x9ad7618cU, 0x37a10c7aU, 0x59f8148eU, 0xeb133c89U, + 0xcea927eeU, 0xb761c935U, 0xe11ce5edU, 0x7a47b13cU, + 0x9cd2df59U, 0x55f2733fU, 0x1814ce79U, 0x73c737bfU, + 0x53f7cdeaU, 0x5ffdaa5bU, 0xdf3d6f14U, 0x7844db86U, + 0xcaaff381U, 0xb968c43eU, 0x3824342cU, 0xc2a3405fU, + 0x161dc372U, 0xbce2250cU, 0x283c498bU, 0xff0d9541U, + 0x39a80171U, 0x080cb3deU, 0xd8b4e49cU, 0x6456c190U, + 0x7bcb8461U, 0xd532b670U, 0x486c5c74U, 0xd0b85742U, +}; + +static const u32 Td1[256] = { + 0x5051f4a7U, 0x537e4165U, 0xc31a17a4U, 0x963a275eU, + 0xcb3bab6bU, 0xf11f9d45U, 0xabacfa58U, 0x934be303U, + 0x552030faU, 0xf6ad766dU, 0x9188cc76U, 0x25f5024cU, + 0xfc4fe5d7U, 0xd7c52acbU, 0x80263544U, 0x8fb562a3U, + 0x49deb15aU, 0x6725ba1bU, 0x9845ea0eU, 0xe15dfec0U, + 0x02c32f75U, 0x12814cf0U, 0xa38d4697U, 0xc66bd3f9U, + 0xe7038f5fU, 0x9515929cU, 0xebbf6d7aU, 0xda955259U, + 0x2dd4be83U, 0xd3587421U, 0x2949e069U, 0x448ec9c8U, + 0x6a75c289U, 0x78f48e79U, 0x6b99583eU, 0xdd27b971U, + 0xb6bee14fU, 0x17f088adU, 0x66c920acU, 0xb47dce3aU, + 0x1863df4aU, 0x82e51a31U, 0x60975133U, 0x4562537fU, + 0xe0b16477U, 0x84bb6baeU, 0x1cfe81a0U, 0x94f9082bU, + 0x58704868U, 0x198f45fdU, 0x8794de6cU, 0xb7527bf8U, + 0x23ab73d3U, 0xe2724b02U, 0x57e31f8fU, 0x2a6655abU, + 0x07b2eb28U, 0x032fb5c2U, 0x9a86c57bU, 0xa5d33708U, + 0xf2302887U, 0xb223bfa5U, 0xba02036aU, 0x5ced1682U, + 0x2b8acf1cU, 0x92a779b4U, 0xf0f307f2U, 0xa14e69e2U, + 0xcd65daf4U, 0xd50605beU, 0x1fd13462U, 0x8ac4a6feU, + 0x9d342e53U, 0xa0a2f355U, 0x32058ae1U, 0x75a4f6ebU, + 0x390b83ecU, 0xaa4060efU, 0x065e719fU, 0x51bd6e10U, + 0xf93e218aU, 0x3d96dd06U, 0xaedd3e05U, 0x464de6bdU, + 0xb591548dU, 0x0571c45dU, 0x6f0406d4U, 0xff605015U, + 0x241998fbU, 0x97d6bde9U, 0xcc894043U, 0x7767d99eU, + 0xbdb0e842U, 0x8807898bU, 0x38e7195bU, 0xdb79c8eeU, + 0x47a17c0aU, 0xe97c420fU, 0xc9f8841eU, 0x00000000U, + 0x83098086U, 0x48322bedU, 0xac1e1170U, 0x4e6c5a72U, + 0xfbfd0effU, 0x560f8538U, 0x1e3daed5U, 0x27362d39U, + 0x640a0fd9U, 0x21685ca6U, 0xd19b5b54U, 0x3a24362eU, + 0xb10c0a67U, 0x0f9357e7U, 0xd2b4ee96U, 0x9e1b9b91U, + 0x4f80c0c5U, 0xa261dc20U, 0x695a774bU, 0x161c121aU, + 0x0ae293baU, 0xe5c0a02aU, 0x433c22e0U, 0x1d121b17U, + 0x0b0e090dU, 0xadf28bc7U, 0xb92db6a8U, 0xc8141ea9U, + 0x8557f119U, 0x4caf7507U, 0xbbee99ddU, 0xfda37f60U, + 0x9ff70126U, 0xbc5c72f5U, 0xc544663bU, 0x345bfb7eU, + 0x768b4329U, 0xdccb23c6U, 0x68b6edfcU, 0x63b8e4f1U, + 0xcad731dcU, 0x10426385U, 0x40139722U, 0x2084c611U, + 0x7d854a24U, 0xf8d2bb3dU, 0x11aef932U, 0x6dc729a1U, + 0x4b1d9e2fU, 0xf3dcb230U, 0xec0d8652U, 0xd077c1e3U, + 0x6c2bb316U, 0x99a970b9U, 0xfa119448U, 0x2247e964U, + 0xc4a8fc8cU, 0x1aa0f03fU, 0xd8567d2cU, 0xef223390U, + 0xc787494eU, 0xc1d938d1U, 0xfe8ccaa2U, 0x3698d40bU, + 0xcfa6f581U, 0x28a57adeU, 0x26dab78eU, 0xa43fadbfU, + 0xe42c3a9dU, 0x0d507892U, 0x9b6a5fccU, 0x62547e46U, + 0xc2f68d13U, 0xe890d8b8U, 0x5e2e39f7U, 0xf582c3afU, + 0xbe9f5d80U, 0x7c69d093U, 0xa96fd52dU, 0xb3cf2512U, + 0x3bc8ac99U, 0xa710187dU, 0x6ee89c63U, 0x7bdb3bbbU, + 0x09cd2678U, 0xf46e5918U, 0x01ec9ab7U, 0xa8834f9aU, + 0x65e6956eU, 0x7eaaffe6U, 0x0821bccfU, 0xe6ef15e8U, + 0xd9bae79bU, 0xce4a6f36U, 0xd4ea9f09U, 0xd629b07cU, + 0xaf31a4b2U, 0x312a3f23U, 0x30c6a594U, 0xc035a266U, + 0x37744ebcU, 0xa6fc82caU, 0xb0e090d0U, 0x1533a7d8U, + 0x4af10498U, 0xf741ecdaU, 0x0e7fcd50U, 0x2f1791f6U, + 0x8d764dd6U, 0x4d43efb0U, 0x54ccaa4dU, 0xdfe49604U, + 0xe39ed1b5U, 0x1b4c6a88U, 0xb8c12c1fU, 0x7f466551U, + 0x049d5eeaU, 0x5d018c35U, 0x73fa8774U, 0x2efb0b41U, + 0x5ab3671dU, 0x5292dbd2U, 0x33e91056U, 0x136dd647U, + 0x8c9ad761U, 0x7a37a10cU, 0x8e59f814U, 0x89eb133cU, + 0xeecea927U, 0x35b761c9U, 0xede11ce5U, 0x3c7a47b1U, + 0x599cd2dfU, 0x3f55f273U, 0x791814ceU, 0xbf73c737U, + 0xea53f7cdU, 0x5b5ffdaaU, 0x14df3d6fU, 0x867844dbU, + 0x81caaff3U, 0x3eb968c4U, 0x2c382434U, 0x5fc2a340U, + 0x72161dc3U, 0x0cbce225U, 0x8b283c49U, 0x41ff0d95U, + 0x7139a801U, 0xde080cb3U, 0x9cd8b4e4U, 0x906456c1U, + 0x617bcb84U, 0x70d532b6U, 0x74486c5cU, 0x42d0b857U, +}; + +static const u32 Td2[256] = { + 0xa75051f4U, 0x65537e41U, 0xa4c31a17U, 0x5e963a27U, + 0x6bcb3babU, 0x45f11f9dU, 0x58abacfaU, 0x03934be3U, + 0xfa552030U, 0x6df6ad76U, 0x769188ccU, 0x4c25f502U, + 0xd7fc4fe5U, 0xcbd7c52aU, 0x44802635U, 0xa38fb562U, + 0x5a49deb1U, 0x1b6725baU, 0x0e9845eaU, 0xc0e15dfeU, + 0x7502c32fU, 0xf012814cU, 0x97a38d46U, 0xf9c66bd3U, + 0x5fe7038fU, 0x9c951592U, 0x7aebbf6dU, 0x59da9552U, + 0x832dd4beU, 0x21d35874U, 0x692949e0U, 0xc8448ec9U, + 0x896a75c2U, 0x7978f48eU, 0x3e6b9958U, 0x71dd27b9U, + 0x4fb6bee1U, 0xad17f088U, 0xac66c920U, 0x3ab47dceU, + 0x4a1863dfU, 0x3182e51aU, 0x33609751U, 0x7f456253U, + 0x77e0b164U, 0xae84bb6bU, 0xa01cfe81U, 0x2b94f908U, + 0x68587048U, 0xfd198f45U, 0x6c8794deU, 0xf8b7527bU, + 0xd323ab73U, 0x02e2724bU, 0x8f57e31fU, 0xab2a6655U, + 0x2807b2ebU, 0xc2032fb5U, 0x7b9a86c5U, 0x08a5d337U, + 0x87f23028U, 0xa5b223bfU, 0x6aba0203U, 0x825ced16U, + 0x1c2b8acfU, 0xb492a779U, 0xf2f0f307U, 0xe2a14e69U, + 0xf4cd65daU, 0xbed50605U, 0x621fd134U, 0xfe8ac4a6U, + 0x539d342eU, 0x55a0a2f3U, 0xe132058aU, 0xeb75a4f6U, + 0xec390b83U, 0xefaa4060U, 0x9f065e71U, 0x1051bd6eU, + + 0x8af93e21U, 0x063d96ddU, 0x05aedd3eU, 0xbd464de6U, + 0x8db59154U, 0x5d0571c4U, 0xd46f0406U, 0x15ff6050U, + 0xfb241998U, 0xe997d6bdU, 0x43cc8940U, 0x9e7767d9U, + 0x42bdb0e8U, 0x8b880789U, 0x5b38e719U, 0xeedb79c8U, + 0x0a47a17cU, 0x0fe97c42U, 0x1ec9f884U, 0x00000000U, + 0x86830980U, 0xed48322bU, 0x70ac1e11U, 0x724e6c5aU, + 0xfffbfd0eU, 0x38560f85U, 0xd51e3daeU, 0x3927362dU, + 0xd9640a0fU, 0xa621685cU, 0x54d19b5bU, 0x2e3a2436U, + 0x67b10c0aU, 0xe70f9357U, 0x96d2b4eeU, 0x919e1b9bU, + 0xc54f80c0U, 0x20a261dcU, 0x4b695a77U, 0x1a161c12U, + 0xba0ae293U, 0x2ae5c0a0U, 0xe0433c22U, 0x171d121bU, + 0x0d0b0e09U, 0xc7adf28bU, 0xa8b92db6U, 0xa9c8141eU, + 0x198557f1U, 0x074caf75U, 0xddbbee99U, 0x60fda37fU, + 0x269ff701U, 0xf5bc5c72U, 0x3bc54466U, 0x7e345bfbU, + 0x29768b43U, 0xc6dccb23U, 0xfc68b6edU, 0xf163b8e4U, + 0xdccad731U, 0x85104263U, 0x22401397U, 0x112084c6U, + 0x247d854aU, 0x3df8d2bbU, 0x3211aef9U, 0xa16dc729U, + 0x2f4b1d9eU, 0x30f3dcb2U, 0x52ec0d86U, 0xe3d077c1U, + 0x166c2bb3U, 0xb999a970U, 0x48fa1194U, 0x642247e9U, + 0x8cc4a8fcU, 0x3f1aa0f0U, 0x2cd8567dU, 0x90ef2233U, + 0x4ec78749U, 0xd1c1d938U, 0xa2fe8ccaU, 0x0b3698d4U, + 0x81cfa6f5U, 0xde28a57aU, 0x8e26dab7U, 0xbfa43fadU, + 0x9de42c3aU, 0x920d5078U, 0xcc9b6a5fU, 0x4662547eU, + 0x13c2f68dU, 0xb8e890d8U, 0xf75e2e39U, 0xaff582c3U, + 0x80be9f5dU, 0x937c69d0U, 0x2da96fd5U, 0x12b3cf25U, + 0x993bc8acU, 0x7da71018U, 0x636ee89cU, 0xbb7bdb3bU, + 0x7809cd26U, 0x18f46e59U, 0xb701ec9aU, 0x9aa8834fU, + 0x6e65e695U, 0xe67eaaffU, 0xcf0821bcU, 0xe8e6ef15U, + 0x9bd9bae7U, 0x36ce4a6fU, 0x09d4ea9fU, 0x7cd629b0U, + 0xb2af31a4U, 0x23312a3fU, 0x9430c6a5U, 0x66c035a2U, + 0xbc37744eU, 0xcaa6fc82U, 0xd0b0e090U, 0xd81533a7U, + 0x984af104U, 0xdaf741ecU, 0x500e7fcdU, 0xf62f1791U, + 0xd68d764dU, 0xb04d43efU, 0x4d54ccaaU, 0x04dfe496U, + 0xb5e39ed1U, 0x881b4c6aU, 0x1fb8c12cU, 0x517f4665U, + 0xea049d5eU, 0x355d018cU, 0x7473fa87U, 0x412efb0bU, + 0x1d5ab367U, 0xd25292dbU, 0x5633e910U, 0x47136dd6U, + 0x618c9ad7U, 0x0c7a37a1U, 0x148e59f8U, 0x3c89eb13U, + 0x27eecea9U, 0xc935b761U, 0xe5ede11cU, 0xb13c7a47U, + 0xdf599cd2U, 0x733f55f2U, 0xce791814U, 0x37bf73c7U, + 0xcdea53f7U, 0xaa5b5ffdU, 0x6f14df3dU, 0xdb867844U, + 0xf381caafU, 0xc43eb968U, 0x342c3824U, 0x405fc2a3U, + 0xc372161dU, 0x250cbce2U, 0x498b283cU, 0x9541ff0dU, + 0x017139a8U, 0xb3de080cU, 0xe49cd8b4U, 0xc1906456U, + 0x84617bcbU, 0xb670d532U, 0x5c74486cU, 0x5742d0b8U, +}; + +static const u32 Td3[256] = { + 0xf4a75051U, 0x4165537eU, 0x17a4c31aU, 0x275e963aU, + 0xab6bcb3bU, 0x9d45f11fU, 0xfa58abacU, 0xe303934bU, + 0x30fa5520U, 0x766df6adU, 0xcc769188U, 0x024c25f5U, + 0xe5d7fc4fU, 0x2acbd7c5U, 0x35448026U, 0x62a38fb5U, + 0xb15a49deU, 0xba1b6725U, 0xea0e9845U, 0xfec0e15dU, + 0x2f7502c3U, 0x4cf01281U, 0x4697a38dU, 0xd3f9c66bU, + 0x8f5fe703U, 0x929c9515U, 0x6d7aebbfU, 0x5259da95U, + 0xbe832dd4U, 0x7421d358U, 0xe0692949U, 0xc9c8448eU, + 0xc2896a75U, 0x8e7978f4U, 0x583e6b99U, 0xb971dd27U, + 0xe14fb6beU, 0x88ad17f0U, 0x20ac66c9U, 0xce3ab47dU, + 0xdf4a1863U, 0x1a3182e5U, 0x51336097U, 0x537f4562U, + 0x6477e0b1U, 0x6bae84bbU, 0x81a01cfeU, 0x082b94f9U, + 0x48685870U, 0x45fd198fU, 0xde6c8794U, 0x7bf8b752U, + 0x73d323abU, 0x4b02e272U, 0x1f8f57e3U, 0x55ab2a66U, + 0xeb2807b2U, 0xb5c2032fU, 0xc57b9a86U, 0x3708a5d3U, + 0x2887f230U, 0xbfa5b223U, 0x036aba02U, 0x16825cedU, + 0xcf1c2b8aU, 0x79b492a7U, 0x07f2f0f3U, 0x69e2a14eU, + 0xdaf4cd65U, 0x05bed506U, 0x34621fd1U, 0xa6fe8ac4U, + 0x2e539d34U, 0xf355a0a2U, 0x8ae13205U, 0xf6eb75a4U, + 0x83ec390bU, 0x60efaa40U, 0x719f065eU, 0x6e1051bdU, + 0x218af93eU, 0xdd063d96U, 0x3e05aeddU, 0xe6bd464dU, + 0x548db591U, 0xc45d0571U, 0x06d46f04U, 0x5015ff60U, + 0x98fb2419U, 0xbde997d6U, 0x4043cc89U, 0xd99e7767U, + 0xe842bdb0U, 0x898b8807U, 0x195b38e7U, 0xc8eedb79U, + 0x7c0a47a1U, 0x420fe97cU, 0x841ec9f8U, 0x00000000U, + 0x80868309U, 0x2bed4832U, 0x1170ac1eU, 0x5a724e6cU, + 0x0efffbfdU, 0x8538560fU, 0xaed51e3dU, 0x2d392736U, + 0x0fd9640aU, 0x5ca62168U, 0x5b54d19bU, 0x362e3a24U, + 0x0a67b10cU, 0x57e70f93U, 0xee96d2b4U, 0x9b919e1bU, + 0xc0c54f80U, 0xdc20a261U, 0x774b695aU, 0x121a161cU, + 0x93ba0ae2U, 0xa02ae5c0U, 0x22e0433cU, 0x1b171d12U, + 0x090d0b0eU, 0x8bc7adf2U, 0xb6a8b92dU, 0x1ea9c814U, + 0xf1198557U, 0x75074cafU, 0x99ddbbeeU, 0x7f60fda3U, + 0x01269ff7U, 0x72f5bc5cU, 0x663bc544U, 0xfb7e345bU, + 0x4329768bU, 0x23c6dccbU, 0xedfc68b6U, 0xe4f163b8U, + 0x31dccad7U, 0x63851042U, 0x97224013U, 0xc6112084U, + 0x4a247d85U, 0xbb3df8d2U, 0xf93211aeU, 0x29a16dc7U, + 0x9e2f4b1dU, 0xb230f3dcU, 0x8652ec0dU, 0xc1e3d077U, + 0xb3166c2bU, 0x70b999a9U, 0x9448fa11U, 0xe9642247U, + 0xfc8cc4a8U, 0xf03f1aa0U, 0x7d2cd856U, 0x3390ef22U, + 0x494ec787U, 0x38d1c1d9U, 0xcaa2fe8cU, 0xd40b3698U, + 0xf581cfa6U, 0x7ade28a5U, 0xb78e26daU, 0xadbfa43fU, + 0x3a9de42cU, 0x78920d50U, 0x5fcc9b6aU, 0x7e466254U, + 0x8d13c2f6U, 0xd8b8e890U, 0x39f75e2eU, 0xc3aff582U, + 0x5d80be9fU, 0xd0937c69U, 0xd52da96fU, 0x2512b3cfU, + 0xac993bc8U, 0x187da710U, 0x9c636ee8U, 0x3bbb7bdbU, + 0x267809cdU, 0x5918f46eU, 0x9ab701ecU, 0x4f9aa883U, + 0x956e65e6U, 0xffe67eaaU, 0xbccf0821U, 0x15e8e6efU, + 0xe79bd9baU, 0x6f36ce4aU, 0x9f09d4eaU, 0xb07cd629U, + 0xa4b2af31U, 0x3f23312aU, 0xa59430c6U, 0xa266c035U, + 0x4ebc3774U, 0x82caa6fcU, 0x90d0b0e0U, 0xa7d81533U, + 0x04984af1U, 0xecdaf741U, 0xcd500e7fU, 0x91f62f17U, + 0x4dd68d76U, 0xefb04d43U, 0xaa4d54ccU, 0x9604dfe4U, + 0xd1b5e39eU, 0x6a881b4cU, 0x2c1fb8c1U, 0x65517f46U, + 0x5eea049dU, 0x8c355d01U, 0x877473faU, 0x0b412efbU, + 0x671d5ab3U, 0xdbd25292U, 0x105633e9U, 0xd647136dU, + 0xd7618c9aU, 0xa10c7a37U, 0xf8148e59U, 0x133c89ebU, + 0xa927eeceU, 0x61c935b7U, 0x1ce5ede1U, 0x47b13c7aU, + 0xd2df599cU, 0xf2733f55U, 0x14ce7918U, 0xc737bf73U, + 0xf7cdea53U, 0xfdaa5b5fU, 0x3d6f14dfU, 0x44db8678U, + 0xaff381caU, 0x68c43eb9U, 0x24342c38U, 0xa3405fc2U, + 0x1dc37216U, 0xe2250cbcU, 0x3c498b28U, 0x0d9541ffU, + 0xa8017139U, 0x0cb3de08U, 0xb4e49cd8U, 0x56c19064U, + 0xcb84617bU, 0x32b670d5U, 0x6c5c7448U, 0xb85742d0U, +}; + +static const u8 Td4[256] = { + 0x52U, 0x09U, 0x6aU, 0xd5U, + 0x30U, 0x36U, 0xa5U, 0x38U, + 0xbfU, 0x40U, 0xa3U, 0x9eU, + 0x81U, 0xf3U, 0xd7U, 0xfbU, + 0x7cU, 0xe3U, 0x39U, 0x82U, + 0x9bU, 0x2fU, 0xffU, 0x87U, + 0x34U, 0x8eU, 0x43U, 0x44U, + 0xc4U, 0xdeU, 0xe9U, 0xcbU, + 0x54U, 0x7bU, 0x94U, 0x32U, + 0xa6U, 0xc2U, 0x23U, 0x3dU, + 0xeeU, 0x4cU, 0x95U, 0x0bU, + 0x42U, 0xfaU, 0xc3U, 0x4eU, + 0x08U, 0x2eU, 0xa1U, 0x66U, + 0x28U, 0xd9U, 0x24U, 0xb2U, + 0x76U, 0x5bU, 0xa2U, 0x49U, + 0x6dU, 0x8bU, 0xd1U, 0x25U, + 0x72U, 0xf8U, 0xf6U, 0x64U, + 0x86U, 0x68U, 0x98U, 0x16U, + 0xd4U, 0xa4U, 0x5cU, 0xccU, + 0x5dU, 0x65U, 0xb6U, 0x92U, + 0x6cU, 0x70U, 0x48U, 0x50U, + 0xfdU, 0xedU, 0xb9U, 0xdaU, + 0x5eU, 0x15U, 0x46U, 0x57U, + 0xa7U, 0x8dU, 0x9dU, 0x84U, + 0x90U, 0xd8U, 0xabU, 0x00U, + 0x8cU, 0xbcU, 0xd3U, 0x0aU, + 0xf7U, 0xe4U, 0x58U, 0x05U, + 0xb8U, 0xb3U, 0x45U, 0x06U, + 0xd0U, 0x2cU, 0x1eU, 0x8fU, + 0xcaU, 0x3fU, 0x0fU, 0x02U, + 0xc1U, 0xafU, 0xbdU, 0x03U, + 0x01U, 0x13U, 0x8aU, 0x6bU, + 0x3aU, 0x91U, 0x11U, 0x41U, + 0x4fU, 0x67U, 0xdcU, 0xeaU, + 0x97U, 0xf2U, 0xcfU, 0xceU, + 0xf0U, 0xb4U, 0xe6U, 0x73U, + 0x96U, 0xacU, 0x74U, 0x22U, + 0xe7U, 0xadU, 0x35U, 0x85U, + 0xe2U, 0xf9U, 0x37U, 0xe8U, + 0x1cU, 0x75U, 0xdfU, 0x6eU, + 0x47U, 0xf1U, 0x1aU, 0x71U, + 0x1dU, 0x29U, 0xc5U, 0x89U, + 0x6fU, 0xb7U, 0x62U, 0x0eU, + 0xaaU, 0x18U, 0xbeU, 0x1bU, + 0xfcU, 0x56U, 0x3eU, 0x4bU, + 0xc6U, 0xd2U, 0x79U, 0x20U, + 0x9aU, 0xdbU, 0xc0U, 0xfeU, + 0x78U, 0xcdU, 0x5aU, 0xf4U, + 0x1fU, 0xddU, 0xa8U, 0x33U, + 0x88U, 0x07U, 0xc7U, 0x31U, + 0xb1U, 0x12U, 0x10U, 0x59U, + 0x27U, 0x80U, 0xecU, 0x5fU, + 0x60U, 0x51U, 0x7fU, 0xa9U, + 0x19U, 0xb5U, 0x4aU, 0x0dU, + 0x2dU, 0xe5U, 0x7aU, 0x9fU, + 0x93U, 0xc9U, 0x9cU, 0xefU, + 0xa0U, 0xe0U, 0x3bU, 0x4dU, + 0xaeU, 0x2aU, 0xf5U, 0xb0U, + 0xc8U, 0xebU, 0xbbU, 0x3cU, + 0x83U, 0x53U, 0x99U, 0x61U, + 0x17U, 0x2bU, 0x04U, 0x7eU, + 0xbaU, 0x77U, 0xd6U, 0x26U, + 0xe1U, 0x69U, 0x14U, 0x63U, + 0x55U, 0x21U, 0x0cU, 0x7dU, +}; + +static const u32 rcon[] = { + 0x01000000, 0x02000000, 0x04000000, 0x08000000, + 0x10000000, 0x20000000, 0x40000000, 0x80000000, + 0x1B000000, 0x36000000, /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */ +}; + +#define GETU32(pt) (((u32)(pt)[0] << 24) ^ ((u32)(pt)[1] << 16) ^ ((u32)(pt)[2] << 8) ^ ((u32)(pt)[3])) +#define PUTU32(ct, st) { (ct)[0] = (u8)((st) >> 24); (ct)[1] = (u8)((st) >> 16); (ct)[2] = (u8)((st) >> 8); (ct)[3] = (u8)(st); } + +/** + * Expand the cipher key into the encryption key schedule. + * + * @return the number of rounds for the given cipher key size. + */ +static int +rijndaelKeySetupEnc(UINT rk[ /*4*(Nr + 1) */ ], const UINT8 cipherKey[], + int keyBits) +{ + int i = 0; + u32 temp; + + rk[0] = GETU32(cipherKey); + rk[1] = GETU32(cipherKey + 4); + rk[2] = GETU32(cipherKey + 8); + rk[3] = GETU32(cipherKey + 12); + if (keyBits == 128) { + for (;;) { + temp = rk[3]; + rk[4] = rk[0] ^ + (Te4[(temp >> 16) & 0xff] << 24) ^ + (Te4[(temp >> 8) & 0xff] << 16) ^ + (Te4[(temp) & 0xff] << 8) ^ + (Te4[(temp >> 24)]) ^ rcon[i]; + rk[5] = rk[1] ^ rk[4]; + rk[6] = rk[2] ^ rk[5]; + rk[7] = rk[3] ^ rk[6]; + if (++i == 10) { + return 10; + } + rk += 4; + } + } + + /** Handle 24 bytes key length */ + rk[4] = GETU32(cipherKey + 16); + rk[5] = GETU32(cipherKey + 20); + if (keyBits == 192) { + for (;;) { + temp = rk[5]; + rk[6] = rk[0] ^ + (Te4[(temp >> 16) & 0xff] << 24) ^ + (Te4[(temp >> 8) & 0xff] << 16) ^ + (Te4[(temp) & 0xff] << 8) ^ + (Te4[(temp >> 24)]) ^ rcon[i]; + rk[7] = rk[1] ^ rk[6]; + rk[8] = rk[2] ^ rk[7]; + rk[9] = rk[3] ^ rk[8]; + if (++i == 8) { + return 12; + } + rk[10] = rk[4] ^ rk[9]; + rk[11] = rk[5] ^ rk[10]; + rk += 6; + } + } + + /** Handle 32 bytes key length */ + rk[6] = GETU32(cipherKey + 24); + rk[7] = GETU32(cipherKey + 28); + if (keyBits == 256) { + for (;;) { + temp = rk[7]; + rk[8] = rk[0] ^ + (Te4[(temp >> 16) & 0xff] << 24) ^ + (Te4[(temp >> 8) & 0xff] << 16) ^ + (Te4[(temp) & 0xff] << 8) ^ + (Te4[(temp >> 24)]) ^ rcon[i]; + rk[9] = rk[1] ^ rk[8]; + rk[10] = rk[2] ^ rk[9]; + rk[11] = rk[3] ^ rk[10]; + if (++i == 7) { + return 14; + } + temp = rk[11]; + rk[12] = rk[4] ^ + (Te4[(temp >> 24)] << 24) ^ + (Te4[(temp >> 16) & 0xff] << 16) ^ + (Te4[(temp >> 8) & 0xff] << 8) ^ + (Te4[(temp) & 0xff]); + rk[13] = rk[5] ^ rk[12]; + rk[14] = rk[6] ^ rk[13]; + rk[15] = rk[7] ^ rk[14]; + rk += 8; + } + } + return 0; +} + +/** + * Expand the cipher key into the decryption key schedule. + * + * @return the number of rounds for the given cipher key size. + */ +static int +rijndaelKeySetupDec(u32 rk[ /*4*(Nr + 1) */ ], const u8 cipherKey[], + int keyBits, + int have_encrypt) +{ + int Nr, i, j; + u32 temp; + + if (have_encrypt) { + Nr = have_encrypt; + } else { + /* expand the cipher key: */ + Nr = rijndaelKeySetupEnc(rk, cipherKey, keyBits); + } + /* invert the order of the round keys: */ + for (i = 0, j = 4 * Nr; i < j; i += 4, j -= 4) { + temp = rk[i]; + rk[i] = rk[j]; + rk[j] = temp; + temp = rk[i + 1]; + rk[i + 1] = rk[j + 1]; + rk[j + 1] = temp; + temp = rk[i + 2]; + rk[i + 2] = rk[j + 2]; + rk[j + 2] = temp; + temp = rk[i + 3]; + rk[i + 3] = rk[j + 3]; + rk[j + 3] = temp; + } + /* apply the inverse MixColumn transform to all round keys but the first and the last: */ + for (i = 1; i < Nr; i++) { + rk += 4; + rk[0] = Td0[Te4[(rk[0] >> 24)]] ^ + Td1[Te4[(rk[0] >> 16) & 0xff]] ^ + Td2[Te4[(rk[0] >> 8) & 0xff]] ^ + Td3[Te4[(rk[0]) & 0xff]]; + rk[1] = Td0[Te4[(rk[1] >> 24)]] ^ + Td1[Te4[(rk[1] >> 16) & 0xff]] ^ + Td2[Te4[(rk[1] >> 8) & 0xff]] ^ + Td3[Te4[(rk[1]) & 0xff]]; + rk[2] = Td0[Te4[(rk[2] >> 24)]] ^ + Td1[Te4[(rk[2] >> 16) & 0xff]] ^ + Td2[Te4[(rk[2] >> 8) & 0xff]] ^ + Td3[Te4[(rk[2]) & 0xff]]; + rk[3] = Td0[Te4[(rk[3] >> 24)]] ^ + Td1[Te4[(rk[3] >> 16) & 0xff]] ^ + Td2[Te4[(rk[3] >> 8) & 0xff]] ^ + Td3[Te4[(rk[3]) & 0xff]]; + } + return Nr; +} + +//#ifdef AES_WRAP_FUNCTION +static void +rijndaelEncrypt(const u32 rk[ /*4*(Nr + 1) */ ], int Nr, const u8 pt[16], + u8 ct[16]) +{ + u32 s0, s1, s2, s3, t0, t1, t2, t3; +#ifndef FULL_UNROLL + int r; +#endif /* ?FULL_UNROLL */ + + /* + * map byte array block to cipher state + * and add initial round key: + */ + s0 = GETU32(pt) ^ rk[0]; + s1 = GETU32(pt + 4) ^ rk[1]; + s2 = GETU32(pt + 8) ^ rk[2]; + s3 = GETU32(pt + 12) ^ rk[3]; +#ifdef FULL_UNROLL + /* round 1: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ + Te3[s3 & 0xff] ^ rk[4]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ + Te3[s0 & 0xff] ^ rk[5]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ + Te3[s1 & 0xff] ^ rk[6]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ + Te3[s2 & 0xff] ^ rk[7]; + /* round 2: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ + Te3[t3 & 0xff] ^ rk[8]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ + Te3[t0 & 0xff] ^ rk[9]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ + Te3[t1 & 0xff] ^ rk[10]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ + Te3[t2 & 0xff] ^ rk[11]; + /* round 3: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ + Te3[s3 & 0xff] ^ rk[12]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ + Te3[s0 & 0xff] ^ rk[13]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ + Te3[s1 & 0xff] ^ rk[14]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ + Te3[s2 & 0xff] ^ rk[15]; + /* round 4: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ + Te3[t3 & 0xff] ^ rk[16]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ + Te3[t0 & 0xff] ^ rk[17]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ + Te3[t1 & 0xff] ^ rk[18]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ + Te3[t2 & 0xff] ^ rk[19]; + /* round 5: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ + Te3[s3 & 0xff] ^ rk[20]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ + Te3[s0 & 0xff] ^ rk[21]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ + Te3[s1 & 0xff] ^ rk[22]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ + Te3[s2 & 0xff] ^ rk[23]; + /* round 6: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ + Te3[t3 & 0xff] ^ rk[24]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ + Te3[t0 & 0xff] ^ rk[25]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ + Te3[t1 & 0xff] ^ rk[26]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ + Te3[t2 & 0xff] ^ rk[27]; + /* round 7: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ + Te3[s3 & 0xff] ^ rk[28]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ + Te3[s0 & 0xff] ^ rk[29]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ + Te3[s1 & 0xff] ^ rk[30]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ + Te3[s2 & 0xff] ^ rk[31]; + /* round 8: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ + Te3[t3 & 0xff] ^ rk[32]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ + Te3[t0 & 0xff] ^ rk[33]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ + Te3[t1 & 0xff] ^ rk[34]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ + Te3[t2 & 0xff] ^ rk[35]; + /* round 9: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ + Te3[s3 & 0xff] ^ rk[36]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ + Te3[s0 & 0xff] ^ rk[37]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ + Te3[s1 & 0xff] ^ rk[38]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ + Te3[s2 & 0xff] ^ rk[39]; + if (Nr > 10) { + /* round 10: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & + 0xff] ^ Te3[t3 + & + 0xff] + ^ rk[40]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & + 0xff] ^ Te3[t0 + & + 0xff] + ^ rk[41]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & + 0xff] ^ Te3[t1 + & + 0xff] + ^ rk[42]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & + 0xff] ^ Te3[t2 + & + 0xff] + ^ rk[43]; + /* round 11: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & + 0xff] ^ Te3[s3 + & + 0xff] + ^ rk[44]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & + 0xff] ^ Te3[s0 + & + 0xff] + ^ rk[45]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & + 0xff] ^ Te3[s1 + & + 0xff] + ^ rk[46]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & + 0xff] ^ Te3[s2 + & + 0xff] + ^ rk[47]; + if (Nr > 12) { + /* round 12: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ + Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[48]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ + Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[49]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ + Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[50]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ + Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[51]; + /* round 13: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ + Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[52]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ + Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[53]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ + Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[54]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ + Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[55]; + } + } + rk += Nr << 2; +#else /* !FULL_UNROLL */ + /* + * Nr - 1 full rounds: + */ + r = Nr >> 1; + for (;;) { + t0 = Te0[(s0 >> 24)] ^ + Te1[(s1 >> 16) & 0xff] ^ + Te2[(s2 >> 8) & 0xff] ^ Te3[(s3) & 0xff] ^ rk[4]; + t1 = Te0[(s1 >> 24)] ^ + Te1[(s2 >> 16) & 0xff] ^ + Te2[(s3 >> 8) & 0xff] ^ Te3[(s0) & 0xff] ^ rk[5]; + t2 = Te0[(s2 >> 24)] ^ + Te1[(s3 >> 16) & 0xff] ^ + Te2[(s0 >> 8) & 0xff] ^ Te3[(s1) & 0xff] ^ rk[6]; + t3 = Te0[(s3 >> 24)] ^ + Te1[(s0 >> 16) & 0xff] ^ + Te2[(s1 >> 8) & 0xff] ^ Te3[(s2) & 0xff] ^ rk[7]; + + rk += 8; + if (--r == 0) { + break; + } + + s0 = Te0[(t0 >> 24)] ^ + Te1[(t1 >> 16) & 0xff] ^ + Te2[(t2 >> 8) & 0xff] ^ Te3[(t3) & 0xff] ^ rk[0]; + s1 = Te0[(t1 >> 24)] ^ + Te1[(t2 >> 16) & 0xff] ^ + Te2[(t3 >> 8) & 0xff] ^ Te3[(t0) & 0xff] ^ rk[1]; + s2 = Te0[(t2 >> 24)] ^ + Te1[(t3 >> 16) & 0xff] ^ + Te2[(t0 >> 8) & 0xff] ^ Te3[(t1) & 0xff] ^ rk[2]; + s3 = Te0[(t3 >> 24)] ^ + Te1[(t0 >> 16) & 0xff] ^ + Te2[(t1 >> 8) & 0xff] ^ Te3[(t2) & 0xff] ^ rk[3]; + } +#endif /* ?FULL_UNROLL */ + /* + * apply last round and + * map cipher state to byte array block: + */ + s0 = (Te4[(t0 >> 24)] << 24) ^ + (Te4[(t1 >> 16) & 0xff] << 16) ^ + (Te4[(t2 >> 8) & 0xff] << 8) ^ (Te4[(t3) & 0xff]) ^ rk[0]; + PUTU32(ct, s0); + s1 = (Te4[(t1 >> 24)] << 24) ^ + (Te4[(t2 >> 16) & 0xff] << 16) ^ + (Te4[(t3 >> 8) & 0xff] << 8) ^ (Te4[(t0) & 0xff]) ^ rk[1]; + PUTU32(ct + 4, s1); + s2 = (Te4[(t2 >> 24)] << 24) ^ + (Te4[(t3 >> 16) & 0xff] << 16) ^ + (Te4[(t0 >> 8) & 0xff] << 8) ^ (Te4[(t1) & 0xff]) ^ rk[2]; + PUTU32(ct + 8, s2); + s3 = (Te4[(t3 >> 24)] << 24) ^ + (Te4[(t0 >> 16) & 0xff] << 16) ^ + (Te4[(t1 >> 8) & 0xff] << 8) ^ (Te4[(t2) & 0xff]) ^ rk[3]; + PUTU32(ct + 12, s3); +} + +//#endif + +static void +rijndaelDecrypt(const u32 rk[ /*4*(Nr + 1) */ ], int Nr, const UINT8 ct[16], + UINT8 pt[16]) +{ + u32 s0, s1, s2, s3, t0, t1, t2, t3; +#ifndef FULL_UNROLL + int r; +#endif /* ?FULL_UNROLL */ + + /* + * map byte array block to cipher state + * and add initial round key: + */ + s0 = GETU32(ct) ^ rk[0]; + s1 = GETU32(ct + 4) ^ rk[1]; + s2 = GETU32(ct + 8) ^ rk[2]; + s3 = GETU32(ct + 12) ^ rk[3]; +#ifdef FULL_UNROLL + /* round 1: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ + Td3[s1 & 0xff] ^ rk[4]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ + Td3[s2 & 0xff] ^ rk[5]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ + Td3[s3 & 0xff] ^ rk[6]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ + Td3[s0 & 0xff] ^ rk[7]; + /* round 2: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ + Td3[t1 & 0xff] ^ rk[8]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ + Td3[t2 & 0xff] ^ rk[9]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ + Td3[t3 & 0xff] ^ rk[10]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ + Td3[t0 & 0xff] ^ rk[11]; + /* round 3: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ + Td3[s1 & 0xff] ^ rk[12]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ + Td3[s2 & 0xff] ^ rk[13]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ + Td3[s3 & 0xff] ^ rk[14]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ + Td3[s0 & 0xff] ^ rk[15]; + /* round 4: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ + Td3[t1 & 0xff] ^ rk[16]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ + Td3[t2 & 0xff] ^ rk[17]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ + Td3[t3 & 0xff] ^ rk[18]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ + Td3[t0 & 0xff] ^ rk[19]; + /* round 5: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ + Td3[s1 & 0xff] ^ rk[20]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ + Td3[s2 & 0xff] ^ rk[21]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ + Td3[s3 & 0xff] ^ rk[22]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ + Td3[s0 & 0xff] ^ rk[23]; + /* round 6: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ + Td3[t1 & 0xff] ^ rk[24]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ + Td3[t2 & 0xff] ^ rk[25]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ + Td3[t3 & 0xff] ^ rk[26]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ + Td3[t0 & 0xff] ^ rk[27]; + /* round 7: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ + Td3[s1 & 0xff] ^ rk[28]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ + Td3[s2 & 0xff] ^ rk[29]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ + Td3[s3 & 0xff] ^ rk[30]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ + Td3[s0 & 0xff] ^ rk[31]; + /* round 8: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ + Td3[t1 & 0xff] ^ rk[32]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ + Td3[t2 & 0xff] ^ rk[33]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ + Td3[t3 & 0xff] ^ rk[34]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ + Td3[t0 & 0xff] ^ rk[35]; + /* round 9: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ + Td3[s1 & 0xff] ^ rk[36]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ + Td3[s2 & 0xff] ^ rk[37]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ + Td3[s3 & 0xff] ^ rk[38]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ + Td3[s0 & 0xff] ^ rk[39]; + if (Nr > 10) { + /* round 10: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & + 0xff] ^ Td3[t1 + & + 0xff] + ^ rk[40]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & + 0xff] ^ Td3[t2 + & + 0xff] + ^ rk[41]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & + 0xff] ^ Td3[t3 + & + 0xff] + ^ rk[42]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & + 0xff] ^ Td3[t0 + & + 0xff] + ^ rk[43]; + /* round 11: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & + 0xff] ^ Td3[s1 + & + 0xff] + ^ rk[44]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & + 0xff] ^ Td3[s2 + & + 0xff] + ^ rk[45]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & + 0xff] ^ Td3[s3 + & + 0xff] + ^ rk[46]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & + 0xff] ^ Td3[s0 + & + 0xff] + ^ rk[47]; + if (Nr > 12) { + /* round 12: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ + Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[48]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ + Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[49]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ + Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[50]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ + Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[51]; + /* round 13: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ + Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[52]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ + Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[53]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ + Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[54]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ + Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[55]; + } + } + rk += Nr << 2; +#else /* !FULL_UNROLL */ + /* + * Nr - 1 full rounds: + */ + r = Nr >> 1; + for (;;) { + t0 = Td0[(s0 >> 24)] ^ + Td1[(s3 >> 16) & 0xff] ^ + Td2[(s2 >> 8) & 0xff] ^ Td3[(s1) & 0xff] ^ rk[4]; + t1 = Td0[(s1 >> 24)] ^ + Td1[(s0 >> 16) & 0xff] ^ + Td2[(s3 >> 8) & 0xff] ^ Td3[(s2) & 0xff] ^ rk[5]; + t2 = Td0[(s2 >> 24)] ^ + Td1[(s1 >> 16) & 0xff] ^ + Td2[(s0 >> 8) & 0xff] ^ Td3[(s3) & 0xff] ^ rk[6]; + t3 = Td0[(s3 >> 24)] ^ + Td1[(s2 >> 16) & 0xff] ^ + Td2[(s1 >> 8) & 0xff] ^ Td3[(s0) & 0xff] ^ rk[7]; + + rk += 8; + if (--r == 0) { + break; + } + + s0 = Td0[(t0 >> 24)] ^ + Td1[(t3 >> 16) & 0xff] ^ + Td2[(t2 >> 8) & 0xff] ^ Td3[(t1) & 0xff] ^ rk[0]; + s1 = Td0[(t1 >> 24)] ^ + Td1[(t0 >> 16) & 0xff] ^ + Td2[(t3 >> 8) & 0xff] ^ Td3[(t2) & 0xff] ^ rk[1]; + s2 = Td0[(t2 >> 24)] ^ + Td1[(t1 >> 16) & 0xff] ^ + Td2[(t0 >> 8) & 0xff] ^ Td3[(t3) & 0xff] ^ rk[2]; + s3 = Td0[(t3 >> 24)] ^ + Td1[(t2 >> 16) & 0xff] ^ + Td2[(t1 >> 8) & 0xff] ^ Td3[(t0) & 0xff] ^ rk[3]; + } +#endif /* ?FULL_UNROLL */ + /* + * apply last round and + * map cipher state to byte array block: + */ + s0 = (Td4[(t0 >> 24)] << 24) ^ + (Td4[(t3 >> 16) & 0xff] << 16) ^ + (Td4[(t2 >> 8) & 0xff] << 8) ^ (Td4[(t1) & 0xff]) ^ rk[0]; + PUTU32(pt, s0); + s1 = (Td4[(t1 >> 24)] << 24) ^ + (Td4[(t0 >> 16) & 0xff] << 16) ^ + (Td4[(t3 >> 8) & 0xff] << 8) ^ (Td4[(t2) & 0xff]) ^ rk[1]; + PUTU32(pt + 4, s1); + s2 = (Td4[(t2 >> 24)] << 24) ^ + (Td4[(t1 >> 16) & 0xff] << 16) ^ + (Td4[(t0 >> 8) & 0xff] << 8) ^ (Td4[(t3) & 0xff]) ^ rk[2]; + PUTU32(pt + 8, s2); + s3 = (Td4[(t3 >> 24)] << 24) ^ + (Td4[(t2 >> 16) & 0xff] << 16) ^ + (Td4[(t1 >> 8) & 0xff] << 8) ^ (Td4[(t0) & 0xff]) ^ rk[3]; + PUTU32(pt + 12, s3); +} + +void +rijndael_set_key(rijndael_ctx *ctx, UINT8 *key, int bits, int encrypt) +{ + ctx->Nr = rijndaelKeySetupEnc(ctx->key, key, bits); + if (encrypt) { + ctx->decrypt = 0; + } else { + ctx->decrypt = 1; + rijndaelKeySetupDec(ctx->key, key, bits, ctx->Nr); + } +} + +void +rijndael_decrypt(rijndael_ctx *ctx, UINT8 *src, UINT8 *dst) +{ + rijndaelDecrypt(ctx->key, ctx->Nr, src, dst); +} + +//#ifdef AES_WRAP_FUNCTION +void +rijndael_encrypt(rijndael_ctx *ctx, UINT8 *src, UINT8 *dst) +{ + rijndaelEncrypt(ctx->key, ctx->Nr, src, dst); +} + +//#endif diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/rijndael.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/rijndael.h new file mode 100644 index 000000000000..150f7b11be3f --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/rijndael.h @@ -0,0 +1,49 @@ +/** @file rijndael.h + * + * @brief This file contains the function optimised ANSI C code for the Rijndael cipher (now AES) + * + * Copyright (C) 2014-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 03/07/2014: Initial version +******************************************************/ + +#ifndef __RIJNDAEL_H +#define __RIJNDAEL_H +#include "wltypes.h" + +#define MAXKC (256/32) +#define MAXKB (256/8) +#define MAXNR 14 +/* +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +*/ +/* The structure for key information */ +typedef struct { + int decrypt; + int Nr; /* key-length-dependent number of rounds */ + UINT key[4 * (MAXNR + 1)]; /* encrypt or decrypt key schedule */ +} rijndael_ctx; + +void rijndael_set_key(rijndael_ctx *, UINT8 *, int, int); +void rijndael_decrypt(rijndael_ctx *, UINT8 *, UINT8 *); +void rijndael_encrypt(rijndael_ctx *, UINT8 *, UINT8 *); + +#endif /* __RIJNDAEL_H */ diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/sha1.c b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/sha1.c new file mode 100644 index 000000000000..0d68704cc19e --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/sha1.c @@ -0,0 +1,401 @@ +/** @file sha1.c + * + * @brief This file defines the sha1 functions + * + * Copyright (C) 2014-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 03/07/2014: Initial version +******************************************************/ +#include "wltypes.h" +#include "sha1.h" +#include "hostsa_ext_def.h" +#include "authenticator.h" + +/* + * Define the SHA1 circular left shift macro + */ + +#define Mrvl_SHA1CircularShift(bits,word) \ + (((word) << (bits)) | ((word) >> (32-(bits)))) + +/* Local Function Prototyptes */ +static void Mrvl_SHA1PadMessage(Mrvl_SHA1_CTX *); +static void Mrvl_SHA1ProcessMessageBlock(Mrvl_SHA1_CTX *); +/* + * SHA1Init + * + * Description: + * This function will initialize the SHA1_CTX in preparation + * for computing a new SHA1 message digest. + * + * Parameters: + * context: [in/out] + * The context to reset. + * + * Returns: + * sha Error Code. + * + */ +/*int SHA1Init(SHA1_CTX *context) */ +int +Mrvl_SHA1Init(Mrvl_SHA1_CTX *context) +{ + if (!context) { + return shaNull; + } + + context->Length_Low = 0; + context->Length_High = 0; + context->Message_Block_Index = 0; + + context->Intermediate_Hash[0] = 0x67452301; + context->Intermediate_Hash[1] = 0xEFCDAB89; + context->Intermediate_Hash[2] = 0x98BADCFE; + context->Intermediate_Hash[3] = 0x10325476; + context->Intermediate_Hash[4] = 0xC3D2E1F0; + + context->Computed = 0; + context->Corrupted = 0; + + return shaSuccess; +} + +/* + * SHA1Final + * + * Description: + * This function will return the 160-bit message digest into the + * Message_Digest array provided by the caller. + * NOTE: The first octet of hash is stored in the 0th element, + * the last octet of hash in the 19th element. + * + * Parameters: + * context: [in/out] + * The context to use to calculate the SHA-1 hash. + * Message_Digest: [out] + * Where the digest is returned. + * + * Returns: + * sha Error Code. + * + */ +int +Mrvl_SHA1Final(void *priv, Mrvl_SHA1_CTX *context, + UINT8 Message_Digest[A_SHA_DIGEST_LEN]) +{ + phostsa_private psapriv = (phostsa_private)priv; + hostsa_util_fns *util_fns = &psapriv->util_fns; + int i; + + if (!context || !Message_Digest) { + return shaNull; + } + + if (context->Corrupted) { + return context->Corrupted; + } + + if (!context->Computed) { + Mrvl_SHA1PadMessage(context); + for (i = 0; i < 64; ++i) { + /* message may be sensitive, clear it out */ + context->Message_Block[i] = 0; + } + context->Length_Low = 0; /* and clear length */ + context->Length_High = 0; + context->Computed = 1; + + } + + for (i = 0; i < A_SHA_DIGEST_LEN; ++i) { + Message_Digest[i] = context->Intermediate_Hash[i >> 2] + >> 8 * (3 - (i & 0x03)); + } + + memset(util_fns, context, 0x00, sizeof(Mrvl_SHA1_CTX)); + + return shaSuccess; +} + +/* + * SHA1Update + * + * Description: + * This function accepts an array of octets as the next portion + * of the message. + * + * Parameters: + * context: [in/out] + * The SHA context to update + * message_array: [in] + * An array of characters representing the next portion of + * the message. + * length: [in] + * The length of the message in message_array + * + * Returns: + * sha Error Code. + * + */ +int +Mrvl_SHA1Update(Mrvl_SHA1_CTX *context, + const UINT8 *message_array, unsigned length) +{ + if (!length) { + return shaSuccess; + } + + if (!context || !message_array) { + return shaNull; + } + + if (context->Computed) { + context->Corrupted = shaStateError; + + return shaStateError; + } + + if (context->Corrupted) { + return context->Corrupted; + } + while (length-- && !context->Corrupted) { + context->Message_Block[context->Message_Block_Index++] = + (*message_array & 0xFF); + + context->Length_Low += 8; + if (context->Length_Low == 0) { + context->Length_High++; + if (context->Length_High == 0) { + /* Message is too long */ + context->Corrupted = 1; + } + } + + if (context->Message_Block_Index == 64) { + Mrvl_SHA1ProcessMessageBlock(context); + } + + message_array++; + } + + return shaSuccess; +} + +/* + * SHA1ProcessMessageBlock + * + * Description: + * This function will process the next 512 bits of the message + * stored in the Message_Block array. + * + * Parameters: + * None. + * + * Returns: + * Nothing. + * + * Comments: + + + +Eastlake & Jones Informational [Page 14] + +RFC 3174 US Secure Hash Algorithm 1 (SHA1) September 2001 + + + * Many of the variable names in this code, especially the + * single character names, were used because those were the + * names used in the publication. + * + * + */ + +#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) + +/* blk0() and blk() perform the initial expand. */ +/* I got the idea of expanding during the round function from SSLeay */ +#define blk(i) (W[i&15] = rol(W[(i+13)&15]^W[(i+8)&15]^W[(i+2)&15]^W[i&15],1)) +#define blk0(i) ((i & 0x30)? blk(i) : W[i]) + +/* +NOte- Some of the variables are made static in this file because +this function runs in the idle task. The idle task dosent have +enough stack space to accomodate these variables. In the future +if this function is run in a different task with large stack space +or if the stack space of the idle task is increased then we can +remove the static defination from these variables. +*/ +void +Mrvl_SHA1ProcessMessageBlock(Mrvl_SHA1_CTX *context) +{ + static const UINT32 K[] = { /* Constants defined in SHA-1 */ + 0x5A827999, + 0x6ED9EBA1, + 0x8F1BBCDC, + 0xCA62C1D6 + }; + int t; /* Loop counter */ + UINT32 temp; /* Temporary word value */ + UINT32 *W; + UINT32 A, B, C, D, E; /* Word buffers */ + + /* WLAN buffers are aligned, so the context starts at a UINT32 boundary */ + W = context->Scratch; + + for (t = 0; t < 16; t++) { + W[t] = context->Message_Block[t * 4] << 24; + W[t] |= context->Message_Block[t * 4 + 1] << 16; + W[t] |= context->Message_Block[t * 4 + 2] << 8; + W[t] |= context->Message_Block[t * 4 + 3]; + } + + A = context->Intermediate_Hash[0]; + B = context->Intermediate_Hash[1]; + C = context->Intermediate_Hash[2]; + D = context->Intermediate_Hash[3]; + E = context->Intermediate_Hash[4]; + + for (t = 0; t < 20; t++) { + temp = Mrvl_SHA1CircularShift(5, A) + + ((B & C) | ((~B) & D)) + E + blk0(t) + K[0]; + E = D; + D = C; + C = Mrvl_SHA1CircularShift(30, B); + + B = A; + A = temp; + } + + for (t = 20; t < 40; t++) { + temp = Mrvl_SHA1CircularShift(5, + A) + (B ^ C ^ D) + E + blk(t) + + K[1]; + E = D; + D = C; + C = Mrvl_SHA1CircularShift(30, B); + B = A; + A = temp; + } + + for (t = 40; t < 60; t++) { + temp = Mrvl_SHA1CircularShift(5, A) + + ((B & C) | (B & D) | (C & D)) + E + blk(t) + K[2]; + E = D; + D = C; + C = Mrvl_SHA1CircularShift(30, B); + B = A; + A = temp; + } + + for (t = 60; t < 80; t++) { + temp = Mrvl_SHA1CircularShift(5, + A) + (B ^ C ^ D) + E + blk(t) + + K[3]; + E = D; + D = C; + C = Mrvl_SHA1CircularShift(30, B); + B = A; + A = temp; + } + + context->Intermediate_Hash[0] += A; + context->Intermediate_Hash[1] += B; + context->Intermediate_Hash[2] += C; + context->Intermediate_Hash[3] += D; + context->Intermediate_Hash[4] += E; + + context->Message_Block_Index = 0; +} + +/* + * SHA1PadMessage + * + + + +Eastlake & Jones Informational [Page 16] + +RFC 3174 US Secure Hash Algorithm 1 (SHA1) September 2001 + + + * Description: + * According to the standard, the message must be padded to an even + * 512 bits. The first padding bit must be a '1'. The last 64 + * bits represent the length of the original message. All bits in + * between should be 0. This function will pad the message + * according to those rules by filling the Message_Block array + * accordingly. It will also call the ProcessMessageBlock function + * provided appropriately. When it returns, it can be assumed that + * the message digest has been computed. + * + * Parameters: + * context: [in/out] + * The context to pad + * ProcessMessageBlock: [in] + * The appropriate SHA*ProcessMessageBlock function + * Returns: + * Nothing. + * + */ + +void +Mrvl_SHA1PadMessage(Mrvl_SHA1_CTX *context) +{ + /* + * Check to see if the current message block is too small to hold + * the initial padding bits and length. If so, we will pad the + * block, process it, and then continue padding into a second + * block. + */ + if (context->Message_Block_Index > 55) { + context->Message_Block[context->Message_Block_Index++] = 0x80; + while (context->Message_Block_Index < 64) { + context->Message_Block[context->Message_Block_Index++] = + 0; + } + + Mrvl_SHA1ProcessMessageBlock(context); + + while (context->Message_Block_Index < 56) { + context->Message_Block[context->Message_Block_Index++] = + 0; + } + } else { + context->Message_Block[context->Message_Block_Index++] = 0x80; + while (context->Message_Block_Index < 56) { + + context->Message_Block[context->Message_Block_Index++] = + 0; + } + } + + /* + * Store the message length as the last 8 octets + */ + context->Message_Block[56] = context->Length_High >> 24; + context->Message_Block[57] = context->Length_High >> 16; + context->Message_Block[58] = context->Length_High >> 8; + context->Message_Block[59] = context->Length_High; + context->Message_Block[60] = context->Length_Low >> 24; + context->Message_Block[61] = context->Length_Low >> 16; + context->Message_Block[62] = context->Length_Low >> 8; + context->Message_Block[63] = context->Length_Low; + + Mrvl_SHA1ProcessMessageBlock(context); +} diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/sha1.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/sha1.h new file mode 100644 index 000000000000..ff9b56cf5260 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/sha1.h @@ -0,0 +1,82 @@ +/** @file sha1.h + * + * @brief This file contains the sha1 functions + * + * Copyright (C) 2014-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 03/07/2014: Initial version +******************************************************/ +#ifndef _SHA1_H_ +#define _SHA1_H_ + +#include "wltypes.h" + +enum { + shaSuccess = 0, + shaNull, /* Null pointer parameter */ + shaInputTooLong, /* input data too long */ + shaStateError /* called Input after Result */ +}; + +#define A_SHA_DIGEST_LEN 20 + +/* + * This structure will hold context information for the SHA-1 + * hashing operation + */ +typedef struct { + UINT32 Intermediate_Hash[A_SHA_DIGEST_LEN / 4]; /* Message Digest */ + + UINT32 Length_Low; /* Message length in bits */ + UINT32 Length_High; /* Message length in bits */ + + UINT32 Scratch[16]; /* This is used to reduce the memory + ** requirements of the transform + **function + */ + UINT8 Message_Block[64]; /* 512-bit message blocks */ + /* Index into message block array */ + SINT16 Message_Block_Index; + UINT8 Computed; /* Is the digest computed? */ + UINT8 Corrupted; /* Is the message digest corrupted? */ +} Mrvl_SHA1_CTX; + +/* + * Function Prototypes + */ + +extern int Mrvl_SHA1Init(Mrvl_SHA1_CTX *); +extern int Mrvl_SHA1Update(Mrvl_SHA1_CTX *, const UINT8 *, unsigned int); +extern int Mrvl_SHA1Final(void *priv, Mrvl_SHA1_CTX *, + UINT8 Message_Digest[A_SHA_DIGEST_LEN]); + +extern void Mrvl_PRF(void *priv, unsigned char *key, + int key_len, + unsigned char *prefix, + int prefix_len, + unsigned char *data, + int data_len, unsigned char *output, int len); + +extern void Mrvl_hmac_sha1(void *priv, unsigned char **ppText, + int *pTextLen, + int textNum, + unsigned char *key, + int key_len, unsigned char *output, int outputLen); + +#endif diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/sha256.c b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/sha256.c new file mode 100644 index 000000000000..c08d665ffdee --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/sha256.c @@ -0,0 +1,459 @@ +/** @file sha_256.c + * + * @brief This file defines the SHA256 hash implementation and interface functions + * + * Copyright (C) 2014-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 03/07/2014: Initial version +******************************************************/ +/* + * SHA-256 hash implementation and interface functions + * + * Copyright ?2003-2006, Jouni Malinen + * + * Copyright ?2006-2007, Marvell International Ltd. and its affiliates + * All rights reserved. + * + * 1. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 2. Neither the name of Jouni Malinen, Marvell nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "sha_256.h" +#include "hostsa_ext_def.h" +#include "authenticator.h" + +#define WPA_GET_BE32(a) ((((UINT32) (a)[0]) << 24) | \ + (((UINT32) (a)[1]) << 16) | \ + (((UINT32) (a)[2]) << 8) | \ + ((UINT32) (a)[3])) + +#define WPA_PUT_BE32(a, val) \ + do { \ + (a)[0] = (UINT8) (((UINT32) (val)) >> 24); \ + (a)[1] = (UINT8) (((UINT32) (val)) >> 16); \ + (a)[2] = (UINT8) (((UINT32) (val)) >> 8); \ + (a)[3] = (UINT8) (((UINT32) (val)) & 0xff); \ + } while (0) + +#define WPA_PUT_BE64(a, val) \ + do { \ + (a)[0] = (UINT8) (((UINT64) (val)) >> 56); \ + (a)[1] = (UINT8) (((UINT64) (val)) >> 48); \ + (a)[2] = (UINT8) (((UINT64) (val)) >> 40); \ + (a)[3] = (UINT8) (((UINT64) (val)) >> 32); \ + (a)[4] = (UINT8) (((UINT64) (val)) >> 24); \ + (a)[5] = (UINT8) (((UINT64) (val)) >> 16); \ + (a)[6] = (UINT8) (((UINT64) (val)) >> 8); \ + (a)[7] = (UINT8) (((UINT64) (val)) & 0xff); \ + } while (0) + +/** + * @brief hmac_sha256_vector - HMAC-SHA256 over data vector (RFC 2104) + * @param priv pointer to previous element + * @param key: Key for HMAC operations + * @param key_len: Length of the key in bytes + * @param num_elem: Number of elements in the data vector; including [0] spare + * @param addr: Pointers to the data areas, [0] element must be left as spare + * @param len: Lengths of the data blocks, [0] element must be left as spare + * @param mac: Buffer for the hash (32 bytes) + * @param pScratchMem: Scratch Memory; At least a 492 byte buffer. + */ +void +hmac_sha256_vector(void *priv, UINT8 *key, + size_t key_len, + size_t num_elem, + UINT8 *addr[], size_t * len, UINT8 *mac, UINT8 *pScratchMem) +{ + phostsa_private psapriv = (phostsa_private)priv; + hostsa_util_fns *util_fns = &psapriv->util_fns; + size_t i; + UINT8 *pKpad; /* was UINT8 k_pad[64], padding - key XORd with ipad/opad */ + UINT8 *pTk; /* was UINT8 tk[32] */ + UINT8 *pTmpBuf; + UINT32 *ptrU32; + + pKpad = pScratchMem; /* kpad = 64 bytes */ + pTk = pKpad + 64; /* tk = 32 bytes */ + pTmpBuf = pTk + 32; /* offset into the scratch buf = +96 bytes */ + + /* if key is longer than 64 bytes reset it to key = SHA256(key) */ + if (key_len > 64) { + /* pTmpBuf = At least 396 bytes */ + sha256_vector(priv, 1, &key, &key_len, pTk, pTmpBuf); + key = pTk; + key_len = 32; + } + + /* the HMAC_SHA256 transform looks like: + * + * SHA256(K XOR opad, SHA256(K XOR ipad, text)) + * + * where K is an n byte key + * ipad is the byte 0x36 repeated 64 times + * opad is the byte 0x5c repeated 64 times + * and text is the data being protected + */ + + /* start out by storing key in ipad */ + memset(util_fns, pKpad, 0x00, 64); + memcpy(util_fns, pKpad, key, key_len); + + /* XOR key with ipad values */ + ptrU32 = (UINT32 *)pKpad; + for (i = 16; i > 0; i--) { + *ptrU32++ ^= 0x36363636; + } + + /* perform inner SHA256 */ + addr[0] = pKpad; + len[0] = 64; + + /* pTmpBuf = At least 396 bytes */ + sha256_vector((void *)priv, num_elem, addr, len, mac, pTmpBuf); + memset(util_fns, pKpad, 0x00, 64); + memcpy(util_fns, pKpad, key, key_len); + + /* XOR key with opad values */ + ptrU32 = (UINT32 *)pKpad; + for (i = 16; i > 0; i--) { + *ptrU32++ ^= 0x5C5C5C5C; + } + + /* perform outer SHA256 */ + addr[0] = pKpad; + len[0] = 64; + addr[1] = mac; + len[1] = SHA256_MAC_LEN; + + /* pTmpBuf = At least 396 bytes */ + sha256_vector((void *)priv, 2, addr, len, mac, pTmpBuf); +} + +static int sha256_process(void *priv, struct sha256_state *md, + const UINT8 *in, + unsigned int inlen, UINT8 *pScratchMem); + +static int sha256_done(void *priv, struct sha256_state *md, + UINT8 *out, UINT8 *pScratchMem); + +/** + * @brief sha256_vector - SHA256 hash for data vector + * @param priv pointer to previous element + * @param num_elem Number of elements in the data vector + * @param addr Pointers to the data areas + * @param len Lengths of the data blocks + * @param mac Buffer for the hash + * @param pScratchMem Scratch memory; At least (108 + 288) = 396 bytes */ +void +sha256_vector(void *priv, size_t num_elem, + UINT8 *addr[], size_t * len, UINT8 *mac, UINT8 *pScratchMem) +{ + UINT8 *pTmpBuf; + size_t i; + struct sha256_state *pCtx; + + /* + ** sizeof(struct sha256_state) + ** + ** UINT64 length = 8 + ** UINT32 state[8], curlen; = (9 * 4) = 36 + ** UINT8 buf[64]; = 64 + ** ----- + ** 108 + */ + pCtx = (struct sha256_state *)pScratchMem; + pTmpBuf = pScratchMem + sizeof(struct sha256_state); + + sha256_init(pCtx); + + for (i = 0; i < num_elem; i++) { + /* pTmpBuf = At least 288 bytes of memory */ + sha256_process((void *)priv, pCtx, addr[i], len[i], pTmpBuf); + } + sha256_done((void *)priv, pCtx, mac, pTmpBuf); +} + +/* ===== start - public domain SHA256 implementation ===== */ + +/* This is based on SHA256 implementation in LibTomCrypt that was released into + * public domain by Tom St Denis. */ + +/* the K array */ +static const unsigned int K[64] = { + 0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, 0x3956c25bUL, + 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL, 0xd807aa98UL, 0x12835b01UL, + 0x243185beUL, 0x550c7dc3UL, 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, + 0xc19bf174UL, 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL, + 0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL, 0x983e5152UL, + 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL, 0xc6e00bf3UL, 0xd5a79147UL, + 0x06ca6351UL, 0x14292967UL, 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, + 0x53380d13UL, 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL, + 0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL, 0xd192e819UL, + 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL, 0x19a4c116UL, 0x1e376c08UL, + 0x2748774cUL, 0x34b0bcb5UL, 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, + 0x682e6ff3UL, 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL, + 0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL +}; + +/* Various logical functions */ +#define RORc(x, y) \ + (((((unsigned int)(x) & 0xFFFFFFFFUL) >> (unsigned int)((y) & 31)) | \ + ((unsigned int)(x) << (unsigned int)(32 - ((y) & 31)))) & 0xFFFFFFFFUL) + +#define Ch(x,y,z) (z ^ (x & (y ^ z))) +#define Maj(x,y,z) (((x | y) & z) | (x & y)) +#define S(x, n) RORc((x), (n)) +#define R(x, n) (((x)&0xFFFFFFFFUL)>>(n)) +#define Sigma0(x) (S(x, 2) ^ S(x, 13) ^ S(x, 22)) +#define Sigma1(x) (S(x, 6) ^ S(x, 11) ^ S(x, 25)) +#define Gamma0(x) (S(x, 7) ^ S(x, 18) ^ R(x, 3)) +#define Gamma1(x) (S(x, 17) ^ S(x, 19) ^ R(x, 10)) +#ifndef MIN +#define MIN(x, y) (((x) < (y)) ? (x) : (y)) +#endif + +/** + * sha256_compress - Compress 512-bits. + * @param md: Pointer to the element holding hash state. + * @param msgBuf: Pointer to the buffer containing the data to be hashed. + * @param pScratchMem: Scratch memory; At least 288 bytes of free memory * + * + */ +int +sha256_compress(void *priv, struct sha256_state *md, + UINT8 *msgBuf, UINT8 *pScratchMem) +{ + phostsa_private psapriv = (phostsa_private)priv; + hostsa_util_fns *util_fns = &psapriv->util_fns; + UINT32 *pW; /* was UINT32 W[64] */ + UINT32 *pS; /* was UINT32 S[8] */ + UINT32 t0; + UINT32 t1; + UINT32 t; + UINT32 i; + UINT32 *ptrU32; + + /* pW = (64 * 4) = 256 + ** pS = (8 * 4) = 32 + ** ----- + ** 288 + */ + ptrU32 = pW = (UINT32 *)pScratchMem; + pS = pW + 64; + + /* copy state into S */ + memcpy(util_fns, (UINT8 *)pS, (UINT8 *)md->state, 32); + + /* copy the a message block of 512-bits into pW[0..15] */ + for (i = 16; i > 0; i--) { + int a0, a1; + a0 = *msgBuf++; + a1 = *msgBuf++; + a0 <<= 8; + a0 |= a1; + a1 = *msgBuf++; + a0 <<= 8; + a0 |= a1; + a1 = *msgBuf++; + a0 <<= 8; + *ptrU32++ = a0 | a1; + } + + /* fill pW[16..63] */ + for (i = 48; i > 0; i--) { + *ptrU32 = + (Gamma1(ptrU32[-2]) + ptrU32[-7] + Gamma0(ptrU32[-15]) + + ptrU32[-16]); + ptrU32++; + } + + /* Compress */ +#define RND(a,b,c,d,e,f,g,h,i) \ + t0 = h + Sigma1(e) + Ch(e, f, g) + K[i] + pW[i]; \ + t1 = Sigma0(a) + Maj(a, b, c); \ + d += t0; \ + h = t0 + t1; + + for (i = 0; i < 64; ++i) { + RND(pS[0], pS[1], pS[2], pS[3], pS[4], pS[5], pS[6], pS[7], i); + t = pS[7]; + pS[7] = pS[6]; + pS[6] = pS[5]; + pS[5] = pS[4]; + pS[4] = pS[3]; + pS[3] = pS[2]; + pS[2] = pS[1]; + pS[1] = pS[0]; + pS[0] = t; + } + + /* feedback */ + for (i = 0; i < 8; i++) { + md->state[i] = md->state[i] + pS[i]; + } + return 0; +} + +/* Initialize the hash state */ +void +sha256_init(struct sha256_state *md) +{ + md->curlen = 0; + md->length = 0; + md->state[0] = 0x6A09E667UL; + md->state[1] = 0xBB67AE85UL; + md->state[2] = 0x3C6EF372UL; + md->state[3] = 0xA54FF53AUL; + md->state[4] = 0x510E527FUL; + md->state[5] = 0x9B05688CUL; + md->state[6] = 0x1F83D9ABUL; + md->state[7] = 0x5BE0CD19UL; +} + +/** + Process a block of memory though the hash + @param md The hash state + @param in The data to hash + @param inlen The length of the data (octets) + @param pScratchMem Temporary Memory Buf; At least 288 bytes. + @return CRYPT_OK if successful +*/ +static int +sha256_process(void *priv, struct sha256_state *md, + const unsigned char *in, unsigned int inlen, UINT8 *pScratchMem) +{ + phostsa_private psapriv = (phostsa_private)priv; + hostsa_util_fns *util_fns = &psapriv->util_fns; + unsigned int n; +#define block_size 64 + + if (md->curlen > sizeof(md->buf)) { + return -1; + } + + while (inlen > 0) { + if (md->curlen == 0 && inlen >= block_size) { + /* pScratchMem = At least 288 bytes of memory */ + if (sha256_compress + ((void *)priv, md, (UINT8 *)in, pScratchMem) < 0) { + return -1; + } + md->length += block_size * 8; + in += block_size; + inlen -= block_size; + } else { + n = MIN(inlen, (block_size - md->curlen)); + memcpy(util_fns, md->buf + md->curlen, in, n); + md->curlen += n; + in += n; + inlen -= n; + if (md->curlen == block_size) { + /* pScratchMem = At least 288 bytes of memory */ + if (sha256_compress + ((void *)priv, md, md->buf, + pScratchMem) < 0) { + return -1; + } + md->length += 8 * block_size; + md->curlen = 0; + } + } + } + + return 0; +} + +/** + Terminate the hash to get the digest + @param md The hash state + @param out [out] The destination of the hash (32 bytes) + @param pScratchMem [in] Scratch memory; At least 288 bytes + @return CRYPT_OK if successful +*/ +static int +sha256_done(void *priv, struct sha256_state *md, UINT8 *out, UINT8 *pScratchMem) +{ + int i; + UINT32 *ptrU32; + UINT32 tmpU32; + + if (md->curlen >= sizeof(md->buf)) { + return -1; + } + + /* increase the length of the message */ + md->length += md->curlen * 8; + + /* append the '1' bit */ + md->buf[md->curlen++] = (unsigned char)0x80; + + /* if the length is currently above 56 bytes we append zeros + * then compress. Then we can fall back to padding zeros and length + * encoding like normal. + */ + if (md->curlen > 56) { + while (md->curlen < 64) { + md->buf[md->curlen++] = (unsigned char)0; + } + /* pScratchMem = At least 288 bytes of memory */ + sha256_compress((void *)priv, md, md->buf, pScratchMem); + md->curlen = 0; + } + + /* pad upto 56 bytes of zeroes */ + while (md->curlen < 56) { + md->buf[md->curlen++] = (unsigned char)0; + } + + /* store length */ + ptrU32 = (UINT32 *)&md->length; + for (i = 0; i < 2; i++) { + tmpU32 = *ptrU32++; + WPA_PUT_BE32(md->buf + 60 - 4 * i, tmpU32); + } + + /* pScratchMem = At least 288 bytes of memory */ + sha256_compress((void *)priv, md, md->buf, pScratchMem); + + ptrU32 = md->state; + /* copy output */ + for (i = 8; i > 0; i--) { + tmpU32 = *ptrU32++; + WPA_PUT_BE32(out, tmpU32); + out += sizeof(UINT32); + } + return 0; +} + +/* ===== end - public domain SHA256 implementation ===== */ diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/sha_256.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/sha_256.h new file mode 100644 index 000000000000..b063831a80ca --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/sha_256.h @@ -0,0 +1,80 @@ +/** @file sha_256.h + * + * @brief This file contains the SHA256 hash implementation and interface functions + * + * Copyright (C) 2014-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 03/07/2014: Initial version +******************************************************/ +#ifndef SHA256_H +#define SHA256_H + +#include "wltypes.h" + +#define SHA256_MAC_LEN 32 +#define HMAC_SHA256_MIN_SCRATCH_BUF (500) +#define SHA256_MIN_SCRATCH_BUF (400) + +struct sha256_state { + UINT64 length; + UINT32 state[8], curlen; + UINT8 buf[64]; +}; + +void sha256_init(struct sha256_state *md); + +/** + * @brief sha256_compress - Compress 512-bits. + * @param priv pointer to previous element + * @param md: Pointer to the element holding hash state. + * @param msgBuf: Pointer to the buffer containing the data to be hashed. + * @param pScratchMem: Scratch memory; At least 288 bytes of free memory * + * + */ +int sha256_compress(void *priv, struct sha256_state *md, + UINT8 *msgBuf, UINT8 *pScratchMem); + +/** + * sha256_vector - SHA256 hash for data vector + * @param num_elem: Number of elements in the data vector + * @param addr: Pointers to the data areas + * @param len: Lengths of the data blocks + * @param mac: Buffer for the hash + * @param pScratchMem: Scratch memory; Buffer of SHA256_MIN_SCRATCH_BUF size + */ +void sha256_vector(void *priv, size_t num_elem, + UINT8 *addr[], size_t * len, UINT8 *mac, UINT8 *pScratchMem); + +/** + * hmac_sha256_vector - HMAC-SHA256 over data vector (RFC 2104) + * @param key: Key for HMAC operations + * @param key_len: Length of the key in bytes + * @param num_elem: Number of elements in the data vector; including [0] + * @param addr: Pointers to the data areas, [0] element must be left as spare + * @param len: Lengths of the data blocks, [0] element must be left as spare + * @param mac: Buffer for the hash (32 bytes) + * @param pScratchMem: Scratch Memory; Buffer of HMAC_SHA256_MIN_SCRATCH_BUF size + */ +void hmac_sha256_vector(void *priv, UINT8 *key, + size_t key_len, + size_t num_elem, + UINT8 *addr[], + size_t * len, UINT8 *mac, UINT8 *pScratchMem); + +#endif /* SHA256_H */ diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/tlv.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/tlv.h new file mode 100644 index 000000000000..92ca0c24d0b1 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/tlv.h @@ -0,0 +1,875 @@ +/** @file tlv.h + * + * @brief Definitions of the Marvell TLV and parsing functions. + * + * + * Copyright (C) 2014-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/***************************************************************************** + * + * File: tlv.h + * + * + * + * Author(s): Kapil Chhabra + * Date: 2005-01-27 + * Description: Definitions of the Marvell TLV and parsing functions. + * + *****************************************************************************/ +#ifndef TLV_H__ +#define TLV_H__ + +#include "IEEE_types.h" + +#define PROPRIETARY_TLV_BASE_ID 0x0100 + +/* Terminating TLV Type */ +#define MRVL_TERMINATE_TLV_ID 0xffff + +/* Defines for MRVL TLV IDs*/ + +/* IEEE TLVs*/ +#define MRVL_SSID_TLV_ID 0x0000 +#define MRVL_RATES_TLV_ID 0x0001 +#define MRVL_PHYPARAMFHSET_TLV_ID 0x0002 +#define MRVL_PHYPARAMDSSET_TLV_ID 0x0003 +#define MRVL_CFPARAMSET_TLV_ID 0x0004 +#define MRVL_IBSSPARAMSET_TLV_ID 0x0006 +#define MRVL_COUNTRY_TLV_ID 0x0007 +#define MRVL_PWR_CONSTRAINT_TLV_ID 0x0020 +#define MRVL_PWR_CAPABILITY_TLV_ID 0x0021 +#define MRVL_SUPPORTEDCHANNELS_TLV_ID 0x0024 +#define MRVL_QUIET_TLV_ID 0x0028 +#define MRVL_IBSSDFS_TLV_ID 0x0029 +#define MRVL_HT_CAPABILITY_TLV_ID 0x002d +#define MRVL_QOSCAPABILITY_TLV_ID 0x002e +#define MRVL_RSN_TLV_ID 0x0030 +#define MRVL_SUPPORTED_REGCLASS_TLV_ID 0x003b +#define MRVL_HT_INFORMATION_TLV_ID 0x003d +#define MRVL_SECONDARY_CHAN_OFFSET 0x003e +#define MRVL_2040_BSS_COEX_TLV_ID 0x0048 +#define MRVL_OVERLAP_BSS_SCAN_TLV_ID 0x004a +#define MRVL_EXTENDED_CAP_TLV_ID 0x007f +#define MRVL_VHT_CAPABILITIES_TLV_ID 0x00bf +#define MRVL_VHT_OPERATION_TLV_ID 0x00c0 +#define MRVL_AID_TLV_ID 0x00c5 +#define MRVL_VHT_OPMODENTF_TLV_ID 0x00c7 +#define MRVL_VENDORSPECIFIC_TLV_ID 0x00dd + +/* Some of these TLV ids are used in ROM and should not be updated. +** You can confirm if it is not being used in rom then it can be updated. +*/ +/* Proprietary TLVs */ +#define MRVL_KEYPARAMSET_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x00) +#define MRVL_CHANNELLIST_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x01) +#define MRVL_NUMPROBES_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x02) +#define MRVL_OMNI_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x03) +#define MRVL_RSSITHRESHOLD_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x04) +#define MRVL_SNRTHRESHOLD_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x05) +#define MRVL_BCNLOWRSSITHRESHOLD_TLV_ID MRVL_RSSITHRESHOLD_TLV_ID +#define MRVL_BCNLOWSNRTHRESHOLD_TLV_ID MRVL_SNRTHRESHOLD_TLV_ID +#define MRVL_FAILURECOUNT_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x06) +#define MRVL_BEACONMISSED_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x07) +#define MRVL_LEDGPIO_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x08) +#define MRVL_LEDBEHAVIOR_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x09) +#define MRVL_PASSTHROUGH_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x0a) +#define MRVL_REASSOCAP_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x0b) +#define MRVL_POWER_TBL_2_4GHZ_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x0c) +#define MRVL_POWER_TBL_5GHZ_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x0d) +/* sending Brocast SSID */ +#define MRVL_BCASTPROBE_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x0e) +/* number of SSIDs for which directed probes need to be generated */ +#define MRVL_NUMSSIDPROBE_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x0f) +#define MRVL_WMMQSTATUS_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x10) +#define MRVL_CRYPTO_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x11) +#define MRVL_WILDCARD_SSID_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x12) +#define MRVL_TSFTIMESTAMP_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x13) +#define MRVL_POWER_ADAPT_CFG_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x14) +#define MRVL_HOSTSLEEP_FILTER_TYPE1_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x15) +#define MRVL_BCNHIGHRSSITHRESHOLD_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x16) +#define MRVL_BCNHIGHSNRTHRESHOLD_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x17) +#define MRVL_AUTOTX_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x18) +#define MRVL_WSC_SELECTED_REGISTRAR_TLV (PROPRIETARY_TLV_BASE_ID + 0x19) +#define MRVL_WSC_ENROLLEE_TMO_TLV (PROPRIETARY_TLV_BASE_ID + 0x1a) +#define MRVL_WSC_ENROLLEE_PROBE_REQ_TLV (PROPRIETARY_TLV_BASE_ID + 0x1b) +#define MRVL_WSC_REGISTRAR_BEACON_TLV (PROPRIETARY_TLV_BASE_ID + 0x1c) +#define MRVL_WSC_REGISTRAR_PROBE_RESP_TLV (PROPRIETARY_TLV_BASE_ID + 0x1d) +#define MRVL_STARTBGSCANLATER_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x1e) +#define MRVL_AUTHTYPE_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x1f) +#define MRVL_STA_MAC_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x20) +#define MRVL_CUSTOM_ADHOC_PROBE_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x21) +#define MRVL_CUSTOM_ADHOC_PYXIS_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x22) +#define MRVL_CUSTOM_BSSID_LIST_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x23) +#define MRVL_CUSTOM_LINK_INDICATION_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x24) +#define MRVL_MESHIE_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x25) +#define MRVL_DATA_LOWRSSITHRESHOLD_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x26) +#define MRVL_DATA_LOWSNRTHRESHOLD_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x27) +#define MRVL_DATA_HIGHRSSITHRESHOLD_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x28) +#define MRVL_DATA_HIGHSNRTHRESHOLD_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x29) +#define MRVL_CHANNELBANDLIST_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x2a) +#define MRVL_AP_MAC_ADDRESS_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x2b) +#define MRVL_BEACON_PERIOD_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x2c) +#define MRVL_DTIM_PERIOD_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x2d) +#define MRVL_BASIC_RATES_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x2e) +#define MRVL_TX_POWER_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x2f) +#define MRVL_BCAST_SSID_CTL_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x30) +#define MRVL_PREAMBLE_CTL_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x31) +#define MRVL_ANTENNA_CTL_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x32) +#define MRVL_RTS_THRESHOLD_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x33) +#define MRVL_RADIO_CTL_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x34) +#define MRVL_TX_DATA_RATE_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x35) +#define MRVL_PKT_FWD_CTL_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x36) +#define MRVL_STA_INFO_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x37) +#define MRVL_STA_MAC_ADDR_FILTER_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x38) +#define MRVL_STA_AGEOUT_TIMER_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x39) +#define MRVL_SECURITY_CFG_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x3a) +#define MRVL_WEP_KEY_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x3b) +#define MRVL_WPA_PASSPHRASE_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x3c) +#define MRVL_SCAN_TIMING_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x3d) +#define MRVL_NEIGHBOR_ENTRY_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x3e) +#define MRVL_NEIGHBOR_SCAN_PERIOD_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x3f) +#define MRVL_ENCRYPTION_PROTOCOL_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x40) +#define MRVL_AKMP_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x41) +#define MRVL_CIPHER_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x42) +#define MRVL_OFFLOAD_ENABLE_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x43) +#define MRVL_SUPPLICANT_PMK_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x44) +#define MRVL_SUPPLICANT_PASSPHRASE_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x45) +#define MRVL_FRAG_THRESHOLD_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x46) +#define MRVL_GRP_REKEY_TIME_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x47) +#define MRVL_ICV_ERROR_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x48) +#define MRVL_PRE_BEACONMISSED_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x49) +#define MRVL_OLD_HT_CAPABILITY_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x4a) +#define MRVL_OLD_HT_INFORMATION_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x4b) +#define MRVL_OLD_SECONDARY_CHAN_OFFSET_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x4c) +#define MRVL_OLD_2040_BSS_COEX_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x4d) +#define MRVL_OLD_OVERLAP_BSS_SCAN_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x4e) +#define MRVL_OLD_EXTENDED_CAP_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x4f) +#define MRVL_HT_OPERATIONAL_MCSSET_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x50) +#define MRVL_RATEDROPPATTERN_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x51) +#define MRVL_RATEDROPCONTROL_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x52) +#define MRVL_RATESCOPE_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x53) +#define MRVL_TYPES_POWER_GROUP_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x54) +#define MRVL_MAX_STA_CNT_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x55) +#define MRVL_BSS_SCAN_RSP_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x56) +#define MRVL_BSS_SCAN_INFO_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x57) +#define MRVL_CHANRPT_BCN_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x58) +#define MRVL_CHANRPT_CHAN_LOAD_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x59) +#define MRVL_CHANRPT_NOISE_HIST_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x5a) +#define MRVL_CHANRPT_11H_BASIC_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x5b) +#define MRVL_CHANRPT_FRAME_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x5c) +#define MRVL_RETRY_LIMIT_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x5d) +#define MRVL_WAPI_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x5e) +#define MRVL_ASSOC_REASON_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x5f) +#define MRVL_ROBUST_COEX_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x60) +#define MRVL_ROBUST_COEX_PERIOD_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x61) +#define MRVL_MCBC_DATA_RATE_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x62) +#define MRVL_MEASUREMENT_TIMING_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x63) +#define MRVL_RSN_REPLAY_PROT_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x64) +#define MRVL_WAPI_INFO_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x67) +#define MRVL_MGMT_FRAME_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x68) +#define MRVL_MGMT_IE_LIST_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x69) +#define MRVL_AP_SLEEP_PARAM_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x6a) +#define MRVL_AP_INACT_SLEEP_PARAM_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x6b) +#define MRVL_AP_BT_COEX_COMMON_CFG_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x6c) +#define MRVL_AP_BT_COEX_SCO_CFG_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x6d) +#define MRVL_AP_BT_COEX_ACL_CFG_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x6e) +#define MRVL_AP_BT_COEX_STATS_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x6f) +#define MRVL_MGMT_PASSTHRU_MASK_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x70) +#define MRVL_AUTO_DEEP_SLEEP_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x71) +#define MRVL_ENHANCED_STA_POWER_SAVE_MODE_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x72) +#define MRVL_HOSTWAKE_STADB_CFG_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x73) +#define MRVL_HOSTWAKE_OUI_CFG_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x74) +#define MRVL_EAPOL_PWK_HSK_TIMEOUT_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x75) +#define MRVL_EAPOL_PWK_HSK_RETRIES_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x76) +#define MRVL_EAPOL_GWK_HSK_TIMEOUT_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x77) +#define MRVL_EAPOL_GWK_HSK_RETRIES_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x78) + +#define MRVL_OPCHAN_CONTROL_DESC_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x79) +#define MRVL_OPCHAN_CHANGRP_CTRL_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x7a) +#define MRVL_PS_STA_AGEOUT_TIMER_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x7b) + +#define MRVL_WFD_DISC_PERIOD_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x7c) +#define MRVL_WFD_SCAN_ENABLE_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x7d) +#define MRVL_WFD_SCAN_PEER_DEVICE_ID_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x7e) +#define MRVL_WFD_REQ_DEVICE_TYPE_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x7f) +#define MRVL_WFD_DEVICE_STATE_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x80) +#define MRVL_WFD_INTENT_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x81) +#define MRVL_WFD_CAPABILITY_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x82) +#define MRVL_WFD_NOA_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x83) +#define MRVL_WFD_OPP_PS_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x84) +#define MRVL_WFD_INVITATION_LIST_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x85) +#define MRVL_WFD_LISTEN_CHANNEL_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x86) +#define MRVL_WFD_OPERATING_CHANNEL_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x87) +#define MRVL_WFD_PERSISTENT_GROUP_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x88) +#define MRVL_CHANNEL_TRPC_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x89) + +#define MRVL_IEEE_ACTION_FRAME_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x8c) +#define MRVL_WIFI_DIRECT_PRESENCE_REQ_PARAMS_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x8d) +#define MRVL_WIFI_DIRECT_EXTENDED_LISTEN_TIME_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x8e) +#define MRVL_WIFI_DIRECT_PROVISIONING_PARAMS_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x8f) +#define MRVL_WIFI_DIRECT_WPS_PARAMS_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x90) +#define MRVL_WIFI_DIRECT_ACTION_FRAME_SEND_TIMEOUT_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0xb3) + +#define MRVL_CIPHER_PWK_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x91) +#define MRVL_CIPHER_GWK_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x92) +#define MRVL_AP_BSS_STATUS_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x93) +#define MRVL_TX_DATA_PAUSE_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x94) +#define MRVL_STICKY_TIM_CONFIG_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x96) +#define MRVL_STICKY_TIM_STA_MAC_ADDR_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x97) +#define MRVL_2040_BSS_COEX_CONTROL_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x98) +#define MRVL_KEYPARAMSET_V2_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x9c) +#define MRVL_RXBA_SYNC_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x99) +#define MRVL_PKT_COALESCE_RULE_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x9a) +#define MRVL_NETWORK_LIST_CFG_TLV (PROPRIETARY_TLV_BASE_ID + 0X9b) +#define MRVL_MEF_CFG_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x9d) + +#define MRVL_WFD_SCAN_CFG_TLV (PROPRIETARY_TLV_BASE_ID + 158) +#define MRVL_WFD_SENDTIMEOUT_TLV_ID (PROPRIETARY_TLV_BASE_ID + 159) +#define MRVL_WFD_GROUPID_TLV_ID (PROPRIETARY_TLV_BASE_ID + 160) +#define MRVL_WFD_DEVICE_ID_TLV_ID (PROPRIETARY_TLV_BASE_ID + 161) +#define MRVL_WFD_INTENDEDINTF_ADDR_TLV_ID (PROPRIETARY_TLV_BASE_ID + 162) +#define MRVL_WFD_STATUS_TLV_ID (PROPRIETARY_TLV_BASE_ID + 163) +#define MRVL_WFD_DEVICE_INFO_TLV_ID (PROPRIETARY_TLV_BASE_ID + 164) +#define MRVL_WFD_CFG_TIMEOUT_TLV_ID (PROPRIETARY_TLV_BASE_ID + 165) +#define MRVL_WFD_INVITATION_TLV_ID (PROPRIETARY_TLV_BASE_ID + 166) +#define MRVL_WFD_GROUP_BSSID_TLV_ID (PROPRIETARY_TLV_BASE_ID + 167) +#define MRVL_WFD_WPA_PSK_TLV_ID (PROPRIETARY_TLV_BASE_ID + 168) +#define MRVL_MAX_MGMT_IE_TLV_ID (PROPRIETARY_TLV_BASE_ID + 170) +#define MRVL_REGION_DOMAIN_CODE_TLV_ID (PROPRIETARY_TLV_BASE_ID + 171) +#define MRVL_AOIP_IBSS_MODE_TLV_ID (PROPRIETARY_TLV_BASE_ID + 172) +#define MRVL_AOIP_MANAGE_PEERS_TLV_ID (PROPRIETARY_TLV_BASE_ID + 173) +#define MRVL_AOIP_STA_INFO_TLV_ID (PROPRIETARY_TLV_BASE_ID + 174) +#define MRVL_AOIP_REMOTE_ADDR_MODE_TLV_ID (PROPRIETARY_TLV_BASE_ID + 175) +#define MRVL_BGSCAN_REPEAT_CNT_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0xb0) + +#define MRVL_TLV_USB_AGGR_PARAM (PROPRIETARY_TLV_BASE_ID + 177) + +#define MRVL_PS_PARAMS_IN_HS_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0xb5) +#define MRVL_HS_WAKE_HOLDOFF_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0xb6) + +#define MRVL_MULTI_CHAN_INFO_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0xb7) +#define MRVL_MULTI_CHAN_GROUP_INFO_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0xb8) +#define MRVL_RESTRICT_CLIENT_MODE_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0xc1) + +#define MRVL_WFD_SERVICE_HASH_TLV_ID (PROPRIETARY_TLV_BASE_ID + 195) +#define MRVL_WFD_SERVICES_LIST_TLV_ID (PROPRIETARY_TLV_BASE_ID + 196) +#define MRVL_API_VER_INFO_TLV_ID (PROPRIETARY_TLV_BASE_ID + 199) + +#define MRVL_FLOOR_RATE_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0xb9) +#define MRVL_SCAN_CHAN_GAP_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0xC5) +#define MRVL_CHAN_STATS_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0xC6) + +/* This struct is used in ROM and should not be changed at all */ +typedef MLAN_PACK_START struct { + UINT16 Type; + UINT16 Length; +} MLAN_PACK_END MrvlIEParamSet_t; + +typedef MLAN_PACK_START struct { + MrvlIEParamSet_t IEParam; + UINT8 Value[1]; +} MLAN_PACK_END MrvlIEGeneric_t; + +/* MultiChannel TLV*/ +typedef MLAN_PACK_START struct { + MrvlIEParamSet_t IEParam; + UINT16 Status; // 1 = Active, 0 = Inactive + UINT8 TlvBuffer[1]; +} MLAN_PACK_END MrvlIEMultiChanInfo_t; + +typedef MLAN_PACK_START struct { + MrvlIEParamSet_t IEParam; + UINT8 ChanGroupId; + UINT8 ChanBufWt; + ChanBandInfo_t ChanBandInfo; + UINT32 ChanTime; + UINT32 Reserved; + UINT8 HidPortNum; + UINT8 NumIntf; + UINT8 BssTypeNumList[1]; +} MLAN_PACK_END MrvlIEMultiChanGroupInfo_t; + +/* Key Material TLV */ +typedef MLAN_PACK_START struct MrvlIEKeyParamSet_t { + MrvlIEParamSet_t hdr; + UINT16 keyMgtId; +} MLAN_PACK_END MrvlIEKeyParamSet_t; + +#ifdef KEY_MATERIAL_V2 + +typedef MLAN_PACK_START struct wep_key_t { + UINT16 len; + UINT8 key[1]; +} MLAN_PACK_END wep_key_t; + +typedef MLAN_PACK_START struct wpax_key_t { + UINT8 pn[8]; + UINT16 len; + UINT8 key[1]; +} MLAN_PACK_END wpax_key_t; + +typedef MLAN_PACK_START struct wapi_key_t { + UINT8 pn[16]; + UINT16 len; + UINT8 key[16]; + UINT8 micKey[16]; +} MLAN_PACK_END wapi_key_t; + +typedef MLAN_PACK_START struct MrvlIEKeyParamSet_v2_t { + MrvlIEParamSet_t hdr; + IEEEtypes_MacAddr_t macAddr; + UINT8 keyIdx; + UINT8 keyType; + UINT16 keyInfo; + + MLAN_PACK_START union { + wep_key_t wep; + wpax_key_t wpax; + wapi_key_t wapi; + } MLAN_PACK_END keySet; + +} MLAN_PACK_END MrvlIEKeyParamSet_v2_t; +#endif + +/* Marvell Power Constraint TLV */ +typedef MLAN_PACK_START struct MrvlIEPowerConstraint_t { + MrvlIEParamSet_t IEParam; + UINT8 channel; + UINT8 dBm; +} MLAN_PACK_END MrvlIEPowerConstraint_t; + +/* Marvell WSC Selected Registar TLV */ +typedef MLAN_PACK_START struct MrvlIEWSCSelectedRegistrar_t { + MrvlIEParamSet_t IEParam; + UINT16 devPwdID; +} MLAN_PACK_END MrvlIEWSCSelectedRegistrar_t; + +/* Marvell WSC Enrollee TMO TLV */ +typedef MLAN_PACK_START struct MrvlIEWSCEnrolleeTmo_t { + MrvlIEParamSet_t IEParam; + UINT16 tmo; +} MLAN_PACK_END MrvlIEWSCEnrolleeTmo_t; + +/**************** + * AES CRYPTION FEATURE + * + * DEFINE STARTS -------------- + */ +typedef MLAN_PACK_START struct MrvlIEAesCrypt_t { + MrvlIEParamSet_t hdr; + UINT8 payload[40]; +} MLAN_PACK_END MrvlIEAesCrypt_t; + +/* DEFINE ENDS ---------------- + */ + +/* Marvell Power Capability TLV */ +typedef MLAN_PACK_START struct MrvlIEPowerCapability_t { + MrvlIEParamSet_t IEParam; + UINT8 minPwr; + UINT8 maxPwr; +} MLAN_PACK_END MrvlIEPowerCapability_t; + +/* Marvell TLV for OMNI Serial Number and Hw Revision Information. */ +typedef MLAN_PACK_START struct MrvlIE_OMNI_t { + MrvlIEParamSet_t IEParam; + UINT8 SerialNumber[16]; + UINT8 HWRev; + UINT8 Reserved[3]; +} MLAN_PACK_END MrvlIE_OMNI_t; + +/* Marvell LED Behavior TLV */ +typedef MLAN_PACK_START struct MrvlIELedBehavior_t { + MrvlIEParamSet_t IEParam; + UINT8 FirmwareState; + UINT8 LedNumber; + UINT8 LedState; + UINT8 LedArgs; +} MLAN_PACK_END MrvlIELedBehavior_t; + +/* Marvell LED GPIO Mapping TLV */ +typedef MLAN_PACK_START struct MrvlIELedGpio_t { + MrvlIEParamSet_t IEParam; + UINT8 LEDNumber; + UINT8 GPIONumber; +} MLAN_PACK_END MrvlIELedGpio_t; + +typedef MLAN_PACK_START struct { + MrvlIEParamSet_t IEParam; + /* + ** Set a place holder for the TSF values. Sized to max BSS for message + ** allocation. The TLV will return a variable number of TSF values. + */ + UINT64 TSFValue[IEEEtypes_MAX_BSS_DESCRIPTS]; +} MLAN_PACK_END MrvlIETsfArray_t; + +typedef MLAN_PACK_START struct { + MrvlIEParamSet_t IEParam; + UINT8 maxLen; + IEEEtypes_SsId_t ssid; +} MLAN_PACK_END MrvlIEWildcardSsid_t; + +typedef MLAN_PACK_START struct { + MrvlIEParamSet_t IEParam; + UINT8 snrThreshold; + UINT8 reportFrequency; +} MLAN_PACK_END MrvlIELowSnrThreshold_t; + +typedef MLAN_PACK_START struct { + MrvlIEParamSet_t IEParam; + UINT8 rssiThreshold; + UINT8 reportFrequency; +} MLAN_PACK_END MrvlIELowRssiThreshold_t; + +/* Marvell AutoTx TLV */ +#define MAX_KEEPALIVE_PKT_LEN (0x60) +typedef MLAN_PACK_START struct { + MrvlIEParamSet_t IEParam; + UINT16 Interval; /* in seconds */ + UINT8 Priority; + UINT8 Reserved; + UINT16 EtherFrmLen; + UINT8 DestAddr[6]; + UINT8 SrcAddr[6]; + UINT8 EtherFrmBody[MAX_KEEPALIVE_PKT_LEN]; //Last 4 bytes are 32bit FCS +} MLAN_PACK_END MrvlIEAutoTx_t; + +typedef MLAN_PACK_START struct { + MrvlIEParamSet_t IEParam; + IEEEtypes_DFS_Map_t map; +} MLAN_PACK_END MrvlIEChanRpt11hBasic_t; + +typedef MLAN_PACK_START struct { + MrvlIEParamSet_t IEParam; + UINT8 scanReqId; +} MLAN_PACK_END MrvlIEChanRptBeacon_t; + +typedef MLAN_PACK_START struct { + MrvlIEParamSet_t IEParam; + UINT8 ccaBusyFraction; +} MLAN_PACK_END MrvlIEChanRptChanLoad_t; + +typedef MLAN_PACK_START struct { + MrvlIEParamSet_t IEParam; + SINT16 anpi; + UINT8 rpiDensities[11]; +} MLAN_PACK_END MrvlIEChanRptNoiseHist_t; + +typedef MLAN_PACK_START struct { + MrvlIEParamSet_t IEParam; + IEEEtypes_MacAddr_t sourceAddr; + IEEEtypes_MacAddr_t bssid; + SINT16 rssi; + UINT16 frameCnt; +} MLAN_PACK_END MrvlIEChanRptFrame_t; + +typedef MLAN_PACK_START struct { + MrvlIEParamSet_t IEParam; + SINT16 rssi; + SINT16 anpi; + UINT8 ccaBusyFraction; +#ifdef SCAN_REPORT_THROUGH_EVENT + BandConfig_t band; + UINT8 channel; + UINT8 reserved; + UINT64 tsf; +#endif +} MLAN_PACK_END MrvlIEBssScanStats_t; + +typedef MLAN_PACK_START struct { + /** Header */ + MrvlIEParamSet_t IEParam; + UINT32 mode; + UINT32 maxOff; + UINT32 maxOn; +} MLAN_PACK_END MrvlIETypes_MeasurementTiming_t; + +typedef MLAN_PACK_START struct { + /** Header */ + MrvlIEParamSet_t IEParam; + UINT32 mode; + UINT32 dwell; + UINT32 maxOff; + UINT32 minLink; + UINT32 rspTimeout; +} MLAN_PACK_END MrvlIETypes_ConfigScanTiming_t; + +typedef MLAN_PACK_START struct { + MrvlIEParamSet_t IEParam; + UINT8 KeyIndex; + UINT8 IsDefaultIndex; + UINT8 Value[1]; +} MLAN_PACK_END MrvlIETypes_WepKey_t; + +typedef MLAN_PACK_START struct { + MrvlIEParamSet_t IEParam; + UINT8 PMK[32]; +} MLAN_PACK_END MrvlIETypes_PMK_t; + +typedef MLAN_PACK_START struct { + MrvlIEParamSet_t IEParam; + UINT8 ssid[1]; +} MLAN_PACK_END MrvlIETypes_Ssid_Param_t; + +typedef MLAN_PACK_START struct { + MrvlIEParamSet_t IEParam; + UINT8 Passphrase[64]; +} MLAN_PACK_END MrvlIETypes_Passphrase_t; + +typedef MLAN_PACK_START struct { + MrvlIEParamSet_t IEParam; + UINT8 BSSID[6]; +} MLAN_PACK_END MrvlIETypes_BSSID_t; + +typedef MLAN_PACK_START struct { + UINT16 Type; + UINT16 Length; + IEEEtypes_MacAddr_t Bssid; + UINT16 Rsvd; + SINT16 Rssi; //Signal strength + UINT16 Age; + UINT32 QualifiedNeighborBitmap; + UINT32 BlackListDuration; +} MLAN_PACK_END MrvlIETypes_NeighbourEntry_t; + +typedef MLAN_PACK_START struct { + MrvlIEParamSet_t IEParam; + UINT16 SearchMode; + UINT16 State; + UINT32 ScanPeriod; +} MLAN_PACK_END MrvlIETypes_NeighbourScanPeriod_t; + +typedef MLAN_PACK_START struct { + MrvlIEParamSet_t IEParam; + UINT8 Rssi; + UINT8 Frequency; +} MLAN_PACK_END MrvlIETypes_BeaconHighRssiThreshold_t; + +typedef MLAN_PACK_START struct { + MrvlIEParamSet_t IEParam; + UINT8 Rssi; + UINT8 Frequency; +} MLAN_PACK_END MrvlIETypes_BeaconLowRssiThreshold_t; + +typedef MLAN_PACK_START struct { + MrvlIEParamSet_t IEParam; + UINT8 Value; + UINT8 Frequency; +} MLAN_PACK_END MrvlIETypes_RoamingAgent_Threshold_t; + +typedef MLAN_PACK_START struct { + MrvlIEParamSet_t IEParam; + UINT8 AssocReason; +} MLAN_PACK_END MrvlIETypes_AssocReason_t; + +typedef MLAN_PACK_START struct { + MrvlIEParamSet_t IEParam; + IEEEtypes_MacAddr_t macAddr; + UINT8 txPauseState; + UINT8 totalQueued; +} MLAN_PACK_END MrvlIETypes_TxDataPause_t; + +typedef MLAN_PACK_START struct { + UINT16 startFreq; + UINT8 chanWidth; + UINT8 chanNum; +} MLAN_PACK_END MrvlIEChannelDesc_t; + +typedef MLAN_PACK_START struct { + MrvlIEParamSet_t IEParam; + MrvlIEChannelDesc_t chanDesc; + UINT16 controlFlags; + UINT16 reserved; + UINT8 activePower; + UINT8 mdMinPower; + UINT8 mdMaxPower; + UINT8 mdPower; +} MLAN_PACK_END MrvlIETypes_OpChanControlDesc_t; + +typedef MLAN_PACK_START struct { + MrvlIEParamSet_t IEParam; + UINT32 chanGroupBitmap; + ChanScanMode_t scanMode; + UINT8 numChan; + MrvlIEChannelDesc_t chanDesc[50]; +} MLAN_PACK_END MrvlIETypes_ChanGroupControl_t; + +typedef MLAN_PACK_START struct { + MrvlIEParamSet_t IEParam; + ChanBandInfo_t ChanBandInfo[IEEEtypes_MAX_BSS_DESCRIPTS]; +} MLAN_PACK_END MrvlIEChanBandList_t; +#if 0 +typedef MLAN_PACK_START struct { + MrvlIEParamSet_t IEParam; + IEEEtypes_MacAddr_t srcAddr; + IEEEtypes_MacAddr_t dstAddr; + IEEEtypes_ActionFrame_t actionFrame; +} MLAN_PACK_END MrvlIEActionFrame_t; +#endif +typedef MLAN_PACK_START struct { + MrvlIEParamSet_t IEParam; + HtEntry_t htEntry[IEEEtypes_MAX_BSS_DESCRIPTS]; +} MLAN_PACK_END MrvlIEHtList_t; + +/* This struct is used in ROM code and all the fields of +** this should be kept intact +*/ +typedef MLAN_PACK_START struct { + MrvlIEParamSet_t IEParam; + UINT16 bmpRateOfHRDSSS; + UINT16 bmpRateOfOFDM; + UINT32 bmpRateOfHT_DW0; + UINT32 bmpRateOfHT_DW1; + UINT32 bmpRateOfHT_DW2; + UINT32 bmpRateOfHT_DW3; +#ifdef DOT11AC + UINT16 bmpRateOfVHT[8]; //per SS +#endif +} MLAN_PACK_END MrvlIE_TxRateScope_t; + +typedef MLAN_PACK_START struct { + UINT8 mod_class; + UINT8 rate; + UINT8 attemptLimit; + UINT8 reserved; +} MLAN_PACK_END MrvlIE_RateInfoEntry_t; + +typedef MLAN_PACK_START struct { + MrvlIEParamSet_t IEParam; + MrvlIE_RateInfoEntry_t rate_info[8]; +} MLAN_PACK_END MrvlIE_RateDropTable_t; + +typedef MLAN_PACK_START struct { + MrvlIEParamSet_t IEParam; + UINT32 mode; + // for 1x1 11n, 9 HT rate, 8 OFDM rate, 4 DSSS rate + MrvlIE_RateDropTable_t rateDropTbls[9 + 8 + 4]; +} MLAN_PACK_END MrvlIE_RateDropPattern_t; + +#ifdef USB_FRAME_AGGR + +#define USB_TX_AGGR_ENABLE ( 1 << 1 ) +#define USB_RX_AGGR_ENABLE ( 1 << 0 ) + +#define USB_RX_AGGR_MODE_MASK ( 1 << 0 ) +#define USB_RX_AGGR_MODE_SIZE (1) +#define USB_RX_AGGR_MODE_NUM (0) + +typedef MLAN_PACK_START struct { + MrvlIEParamSet_t IEParam; + UINT16 enable; + UINT16 rx_mode; + UINT16 rx_align; + UINT16 rx_max; + UINT16 rx_timeout; + UINT16 tx_mode; + UINT16 tx_align; +} MLAN_PACK_END MrvlIE_USBAggrTLV_t; + +extern MrvlIE_USBAggrTLV_t g_Aggr_Conf; + +#endif + +typedef MLAN_PACK_START struct { + UINT8 mod_class; + UINT8 firstRateCode; + UINT8 lastRateCode; + SINT8 power_step; + SINT8 min_power; + SINT8 max_power; + UINT8 ht_bandwidth; + UINT8 reserved[1]; +} MLAN_PACK_END MrvlIE_PowerGroupEntry_t; + +#define MRVL_MAX_PWR_GROUP 15 +typedef MLAN_PACK_START struct { + MrvlIEParamSet_t IEParam; + MrvlIE_PowerGroupEntry_t PowerGroup[MRVL_MAX_PWR_GROUP]; +} MLAN_PACK_END MrvlIE_PowerGroup_t; + +#ifdef AP_STA_PS +typedef MLAN_PACK_START struct { + MrvlIEParamSet_t IEParam; + UINT16 NullPktInterval; + UINT16 numDtims; + UINT16 BCNMissTimeOut; + UINT16 LocalListenInterval; + UINT16 AdhocAwakePeriod; + UINT16 PS_mode; + UINT16 DelayToPS; +} MrvlIETypes_StaSleepParams_t; + +typedef MLAN_PACK_START struct { + MrvlIEParamSet_t IEParam; + UINT16 idleTime; +} MrvlIETypes_AutoDeepSleepParams_t; +#endif + +#ifdef MESH +typedef MLAN_PACK_START struct _MrvlMeshIE_Tlv_t { + MrvlIEParamSet_t hdr; + IEEEtypes_VendorSpecific_MeshIE_t meshIE; + +} MLAN_PACK_END MrvlMeshIE_Tlv_t; +#endif + +typedef MLAN_PACK_START struct { + MrvlIEParamSet_t IEParam; + UINT8 RBCMode; + UINT8 Reserved[3]; +} MLAN_PACK_END MrvlIERobustCoex_t; + +typedef MLAN_PACK_START struct { + MrvlIEParamSet_t IEParam; + UINT16 Mode; + UINT16 Reserved; + UINT32 BTTime; + UINT32 Period; +} MLAN_PACK_END MrvlIERobustCoexPeriod_t; + +typedef MLAN_PACK_START struct { + MrvlIEParamSet_t IEParam; + UINT8 staMacAddr[IEEEtypes_ADDRESS_SIZE]; + IEEEtypes_IE_Param_t IeBuf; +} MLAN_PACK_END MrvlIEHostWakeStaDBCfg; + +typedef MLAN_PACK_START struct { + MrvlIEParamSet_t IEParam; + UINT16 ouiCmpLen; + UINT8 ouiBuf[6]; +} MLAN_PACK_END MrvlIEHostWakeOuiCfg; + +#ifdef MICRO_AP_MODE +/* This struct is used in ROM and should not be changed at all */ +typedef MLAN_PACK_START struct { + MrvlIEParamSet_t hdr; + IEEEtypes_MacAddr_t macAddr; + UINT8 pwrMode; + SINT8 rssi; +} MLAN_PACK_END MrvlIEStaInfo_t; +#endif + +typedef struct { + MrvlIEParamSet_t Hdr; + uint16 protocol; + uint8 cipher; + uint8 reserved; +} MrvlIETypes_PwkCipher_t; + +typedef struct { + MrvlIEParamSet_t Hdr; + uint8 cipher; + uint8 reserved; +} MrvlIETypes_GwkCipher_t; + +typedef MLAN_PACK_START struct { + uint8 modGroup; + uint8 txPower; +} MLAN_PACK_END MrvlIE_ChanTrpcEntry_t; + +typedef MLAN_PACK_START struct { + MrvlIEParamSet_t Hdr; + MrvlIEChannelDesc_t chanDesc; + MrvlIE_ChanTrpcEntry_t data[1]; +} MLAN_PACK_END MrvlIETypes_ChanTrpcCfg_t; + +typedef MLAN_PACK_START struct { + MrvlIEParamSet_t IEParam; + IEEEtypes_MacAddr_t mac[IEEEtypes_MAX_BSS_DESCRIPTS]; +} MLAN_PACK_END MrvlIETypes_MacAddr_t; + +#ifdef AP_BTCOEX +typedef enum _tagScoCoexBtTraffic { + ONLY_SCO, + ACL_BEFORE_SCO, + ACL_AFTER_SCO, + BT_TRAFFIC_RESERVED, + BT_TRAFFIC_MAX +} ScoCoexBtTraffic_e; + +typedef MLAN_PACK_START struct { + MrvlIEParamSet_t IEParam; + UINT32 configBitmap; /* Bit 0 : overrideCts2SelfProtection + ** Bit 1-31 : Reserved + */ + UINT32 apStaBtCoexEnabled; + UINT32 reserved[3]; /* For future use. */ +} MLAN_PACK_END MrvlIETypes_ApBTCoexCommonConfig_t; + +typedef MLAN_PACK_START struct { + MrvlIEParamSet_t IEParam; + UINT16 protectionFrmQTime[BT_TRAFFIC_MAX]; /* Index 0 for ONLY_SCO + ** 1 for ACL_BEFORE_SCO + ** 2 for ACL_AFTER_SCO + ** 3 is Reserved + */ + UINT16 protectionFrmRate; + UINT16 aclFrequency; + UINT32 reserved[4]; /* For future use. */ +} MLAN_PACK_END MrvlIETypes_ApBTCoexScoConfig_t; + +typedef MLAN_PACK_START struct { + MrvlIEParamSet_t IEParam; + UINT16 enabled; + UINT16 btTime; + UINT16 wlanTime; + UINT16 protectionFrmRate; + UINT32 reserved[4]; /* For future use. */ +} MLAN_PACK_END MrvlIETypes_ApBTCoexAclConfig_t; + +typedef MLAN_PACK_START struct { + MrvlIEParamSet_t IEParam; + UINT32 nullNotSent; + UINT32 numOfNullQueued; + UINT32 nullNotQueued; + UINT32 numOfCfEndQueued; + UINT32 cfEndNotQueued; + UINT32 nullAllocationFail; + UINT32 cfEndAllocationFail; + UINT32 reserved[8]; /* For future use. */ +} MLAN_PACK_END MrvlIETypes_ApBTCoexStats_t; +#endif //AP_BTCOEX + +typedef MLAN_PACK_START struct { + MrvlIEParamSet_t Hdr; + + IEEEtypes_MacAddr_t macAddr; + UINT8 tid; + UINT8 reserved; + UINT16 startSeqNum; + + UINT16 bitMapLen; + UINT8 bitMap[1]; +} MLAN_PACK_END MrvlIETypes_RxBaSync_t; + +#ifdef SCAN_CHAN_STATISTICS +typedef MLAN_PACK_START struct MrvlIEChannelStats { + MrvlIEParamSet_t IEParam; + UINT8 chanStat[1]; +} MLAN_PACK_END MrvlIEChannelStats_t; +#endif + +/* API Version Info Entry for MRVL_API_VER_INFO_TLV_ID */ +typedef MLAN_PACK_START struct MrvlIE_ApiVersionEntry_t { + UINT16 apiId; + UINT8 major; + UINT8 minor; +} MLAN_PACK_END MrvlIE_ApiVersionEntry_t; + +/** API Version Ids */ +#define KEY_API_VER_ID 0x1 +#endif //_TLV_H_ diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/wl_macros.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/wl_macros.h new file mode 100644 index 000000000000..526e1013e790 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/wl_macros.h @@ -0,0 +1,136 @@ +/** @file wl_macros.h + * + * @brief Common macros are defined here. Must include "wltypes.h" before this file + * + * Copyright (C) 2014-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 03/07/2014: Initial version +******************************************************/ +#if !defined(WL_MACROS_H__) +#define WL_MACROS_H__ + +#define MACRO_START do { +#define MACRO_END } while (0) + +#define WL_REGS8(x) (*(volatile unsigned char *)(x)) +#define WL_REGS16(x) (*(volatile unsigned short *)(x)) +#define WL_REGS32(x) (*(volatile unsigned int *)(x)) + +#define WL_READ_REGS8(reg,val) ((val) = WL_REGS8(reg)) +#define WL_READ_REGS16(reg,val) ((val) = WL_REGS16(reg)) +#define WL_READ_REGS32(reg,val) ((val) = WL_REGS32(reg)) +#define WL_READ_BYTE(reg,val) ((val) = WL_REGS8(reg)) +#define WL_READ_HWORD(reg,val) ((val) = WL_REGS16(reg)) /*half word; */ +/*16bits */ +#define WL_READ_WORD(reg,val) ((val) = WL_REGS32(reg)) /*32 bits */ +#define WL_WRITE_REGS8(reg,val) (WL_REGS8(reg) = (val)) +#define WL_WRITE_REGS16(reg,val) (WL_REGS16(reg) = (val)) +#define WL_WRITE_REGS32(reg,val) (WL_REGS32(reg) = (val)) +#define WL_WRITE_BYTE(reg,val) (WL_REGS8(reg) = (val)) +#define WL_WRITE_HWORD(reg,val) (WL_REGS16(reg) = (val)) /*half word; */ +/*16bits */ +#define WL_WRITE_WORD(reg,val) (WL_REGS32(reg) = (val)) /*32 bits */ +#define WL_REGS8_SETBITS(reg, val) (WL_REGS8(reg) |= (UINT8)(val)) +#define WL_REGS16_SETBITS(reg, val) (WL_REGS16(reg) |= (UINT16)(val)) +#define WL_REGS32_SETBITS(reg, val) (WL_REGS32(reg) |= (val)) + +#define WL_REGS8_CLRBITS(reg, val) (WL_REGS8(reg) = \ + (UINT8)(WL_REGS8(reg)&~(val))) + +#define WL_REGS16_CLRBITS(reg, val) (WL_REGS16(reg) = \ + (UINT16)(WL_REGS16(reg)&~(val))) + +#define WL_REGS32_CLRBITS(reg, val) (WL_REGS32(reg) = \ + (WL_REGS32(reg)&~(val))) + +#define WL_WRITE_CHUNK(dst, src, length) (memcpy((void*) (dst), \ + (void*) (src), (length))) +/*! + * Bitmask macros + */ +#define WL_BITMASK(nbits) ((0x1 << nbits) - 1) + +/*! + * Macro to put the WLAN SoC into sleep mode + */ +#define WL_GO_TO_SLEEP asm volatile ("MCR p15, 0, r3, c7, c0, 4;") + +/*! + * BE vs. LE macros + */ +#ifdef BE /* Big Endian */ +#define SHORT_SWAP(X) (X) +#define WORD_SWAP(X) (X) +#define LONG_SWAP(X) ((l64)(X)) +#else /* Little Endian */ + +#define SHORT_SWAP(X) ((X <<8 ) | (X >> 8)) //!< swap bytes in a 16 bit short + +#define WORD_SWAP(X) (((X)&0xff)<<24)+ \ + (((X)&0xff00)<<8)+ \ + (((X)&0xff0000)>>8)+ \ + (((X)&0xff000000)>>24) //!< swap bytes in a 32 bit word + +#define LONG_SWAP(X) ( (l64) (((X)&0xffULL)<<56)+ \ + (((X)&0xff00ULL)<<40)+ \ + (((X)&0xff0000ULL)<<24)+ \ + (((X)&0xff000000ULL)<<8)+ \ + (((X)&0xff00000000ULL)>>8)+ \ + (((X)&0xff0000000000ULL)>>24)+ \ + (((X)&0xff000000000000ULL)>>40)+ \ + (((X)&0xff00000000000000ULL)>>56)) //!< swap bytes in a 64 bit long +#endif + +/*! + * Alignment macros + */ +#define ALIGN4(x) (((x) + 3) & ~3) +#define ALIGN4BYTE(x) (x=ALIGN4(x)) +#define ROUNDUP4US(x) (ALIGN4(x)) + +#ifndef min +#define min(a,b) (((a) < (b)) ? (a) : (b)) +#endif +#ifndef max +#define max(a,b) (((a) > (b)) ? (a) : (b)) +#endif + +#ifndef MIN +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) +#endif +#ifndef MAX +#define MAX(a,b) (((a) > (b)) ? (a) : (b)) +#endif + +#ifndef NELEMENTS +#define NELEMENTS(x) (sizeof(x)/sizeof(x[0])) +#endif + +#define HWORD_LOW_BYTE(x) ((x) & 0xFF) +#define HWORD_HIGH_BYTE(x) (((x) >> 8) & 0xFF) + +#define htons(x) (UINT16)SHORT_SWAP(x) +#define htonl(x) (UINT32)WORD_SWAP(x) + +#define ntohs(x) (UINT16)SHORT_SWAP(x) +#define ntohl(x) (UINT32)WORD_SWAP(x) + +#define CEIL_aByb(a, b) ((a + b - 1) / b) + +#endif /* _WL_MACROS_H_ */ diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/wl_mib.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/wl_mib.h new file mode 100644 index 000000000000..d084dc8b8e36 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/wl_mib.h @@ -0,0 +1,586 @@ +/** @file wl_mib.h + * + * @brief This file contains the MIB structure definitions based on IEEE 802.11 specification. + * + * Copyright (C) 2014-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 03/07/2014: Initial version +******************************************************/ +#if !defined(WL_MIB_H__) + +#define WL_MIB_H__ +#include "wl_mib_rom.h" + +/*============================================================================= */ +/* Management Information Base STRUCTURES (IEEE 802.11) */ +/*============================================================================= */ + +/*-----------------------------*/ +/* Station Configuration Table */ +/*-----------------------------*/ + +typedef struct MIB_StaCfg_s +{ + UINT8 CfPeriod; /* 0 to 255 */ + UINT16 CfpMax; /* 0 to 65535 */ + UINT8 PwrMgtMode; /* PwrMgmtMode_e values */ + UINT8 OpRateSet[16]; /* 16 byte array is sufficient for + 14 rate */ +#ifdef DOT11H + Boolean dot11SpectrumManagementRequired; +#endif + Boolean dot11WWSenabled; +} MIB_STA_CFG; + +/*------------------------*/ +/* WEP Key Mappings Table */ +/*------------------------*/ + +/* This struct is used in ROM and it should not be changed at all */ +typedef struct MIB_WepKeyMappings_s { + UINT32 WepKeyMappingIdx; + IEEEtypes_MacAddr_t WepKeyMappingAddr; + UINT8 WepKeyMappingWepOn; /* SNMP_Boolean_e values */ + UINT8 WepKeyMappingVal[WEP_KEY_USER_INPUT]; /* 5 byte string */ + UINT8 WepKeyMappingStatus; /* SNMP_Rowstatus_e values */ +} MIB_WEP_KEY_MAPPINGS; + +/*---------------*/ +/* Privacy Table */ +/*---------------*/ + +typedef struct MIB_PrivacyTable_s { + UINT8 PrivInvoked; /* SNMP_Boolean_e values */ + UINT8 WepDefaultKeyId; /* 0 to 3 */ + UINT32 WepKeyMappingLen; /* 10 to 4294967295 */ + UINT8 ExcludeUnencrypt; /* SNMP_Boolean_e values */ + UINT32 WepIcvErrCnt; + UINT32 WepExcludedCnt; + UINT8 RSNEnabled; /* SNMP_Boolean_e values */ +} MIB_PRIVACY_TABLE; + +/*============================================================================= */ +/* MAC ATTRIBUTES */ +/*============================================================================= */ + +/*---------------------*/ +/* MAC Operation Table */ +/*---------------------*/ + +typedef struct MIB_OpData_s +{ + IEEEtypes_MacAddr_t StaMacAddr; + UINT16 RtsThresh; /* 0 to 2347 */ + UINT8 ShortRetryLim; /* 1 to 255 */ + UINT8 LongRetryLim; /* 1 to 255 */ + UINT16 FragThresh; /* 256 to 2346 */ + UINT32 MaxTxMsduLife; /* 1 to 4294967295 */ + UINT32 MaxRxLife; /* 1 to 4294967295 */ +#ifdef IN_USE + UINT8 ManufId[128]; /* 128 byte string */ + UINT8 ProdId[128]; /* 128 byte string */ +#endif +} MIB_OP_DATA; + +/*----------------*/ +/* Counters Table */ +/*----------------*/ + +typedef struct MIB_Counters_s { + UINT32 RxFrmCnt; + UINT32 MulticastTxFrmCnt; + UINT32 FailedCnt; + UINT32 RetryCnt; + UINT32 MultRetryCnt; + UINT32 FrmDupCnt; + UINT32 RtsSuccessCnt; + UINT32 RtsFailCnt; + UINT32 AckFailCnt; + UINT32 RxFragCnt; + UINT32 MulticastRxFrmCnt; + UINT32 FcsErrCnt; + UINT32 TxFrmCnt; + UINT32 WepUndecryptCnt; +} MIB_COUNTERS; + +/*-----------------------*/ +/* Group Addresses Table */ +/*-----------------------*/ + +typedef struct MIB_GroupAddr_s { + UINT32 GroupAddrIdx; + IEEEtypes_MacAddr_t Addr; + UINT8 GroupAddrStatus; /* SNMP_Rowstatus_e values */ +} MIB_GROUP_ADDR; + +/*----------------------------*/ +/* Resource Information Table */ +/*----------------------------*/ + +typedef struct MIB_RsrcInfo_s { + UINT8 ManufOui[3]; /* 3 byte string */ + UINT8 ManufName[128]; /* 128 byte string */ + UINT8 ManufProdName[128]; /* 128 byte string */ + UINT8 ManufProdVer[128]; /* 128 byte string */ +} MIB_RESOURCE_INFO; + +/*============================================================================= */ +/* PHY ATTRIBUTES */ +/*============================================================================= */ + +/*---------------------*/ +/* PHY Operation Table */ +/*---------------------*/ +typedef struct MIB_PhyOpTable_s { + UINT8 PhyType; /* SNMP_PhyType_e values */ + UINT32 CurrRegDomain; + UINT8 TempType; /* SNMP_TempType_e values */ +} MIB_PHY_OP_TABLE; + +/*-------------------*/ + +/* PHY Antenna Table */ + +/*-------------------*/ + +typedef struct MIB_PhyAntTable_s { + UINT8 CurrTxAnt; /* 1 to 255 */ + UINT8 DivSupport; /* SNMP_DivSupp_e values */ + UINT8 CurrRxAnt; /* 1 to 255 */ +} MIB_PHY_ANT_TABLE; + +typedef struct MIB_PhyAntSelect_s { + UINT8 SelectRxAnt; /* 0 to 1 */ + UINT8 SelectTxAnt; /* 0 to 1 */ + UINT8 DiversityRxAnt; /* Boolean */ + UINT8 DiversityTxAnt; /* Boolean */ +} MIB_PHY_ANT_SELECT; + +/*--------------------------*/ +/* PHY Transmit Power Table */ +/*--------------------------*/ + +typedef struct MIB_PhyTxPwrTable_s { + UINT8 NumSuppPwrLevels; /* 1 to 8 */ + UINT16 TxPwrLevel1; /* 0 to 10000 */ + UINT16 TxPwrLevel2; /* 0 to 10000 */ + UINT16 TxPwrLevel3; /* 0 to 10000 */ + UINT16 TxPwrLevel4; /* 0 to 10000 */ + UINT16 TxPwrLevel5; /* 0 to 10000 */ + UINT16 TxPwrLevel6; /* 0 to 10000 */ + UINT16 TxPwrLevel7; /* 0 to 10000 */ + UINT16 TxPwrLevel8; /* 0 to 10000 */ + UINT8 CurrTxPwrLevel; /* 1 to 8 */ +} MIB_PHY_TX_POWER_TABLE; + +/*---------------------------------------------*/ + +/* PHY Frequency Hopping Spread Spectrum Table */ + +/*---------------------------------------------*/ + +typedef struct MIB_PhyFHSSTable_s { + + UINT8 HopTime; /* 224? */ + UINT8 CurrChanNum; /* 0 to 99 */ + UINT16 MaxDwellTime; /* 0 to 65535 */ + UINT16 CurrDwellTime; /* 0 to 65535 */ + UINT16 CurrSet; /* 0 to 255 */ + UINT16 CurrPattern; /* 0 to 255 */ + UINT16 CurrIdx; /* 0 to 255 */ + +} MIB_PHY_FHSS_TABLE; + +/*-------------------------------------------*/ + +/* PHY Direct Sequence Spread Spectrum Table */ + +/*-------------------------------------------*/ + +typedef enum MIB_CCAMode_s { + ENERGY_DETECT_ONLY = 1, + CARRIER_SENSE_ONLY = 2, + CARRIER_SENSE_AND_ENERGY_DETECT = 4 +} MIB_CCA_MODE; + +typedef struct MIB_PhyDSSSTable_s { + UINT8 CurrChan; /* 0 to 14 */ + UINT8 CcaModeSupp; /* 1 to 7 */ + UINT16 CurrCcaMode; /* MIB_CCA_MODE values only */ + UINT32 EdThresh; +} MIB_PHY_DSSS_TABLE; + +/*--------------*/ + +/* PHY IR Table */ + +/*--------------*/ + +typedef struct MIB_PhyIRTable_s { + UINT32 CcaWatchDogTmrMax; + UINT32 CcaWatchDogCntMax; + UINT32 CcaWatchDogTmrMin; + UINT32 CcaWatchDogCntMin; +} MIB_PHY_IR_TABLE; + +/*----------------------------------------*/ + +/* PHY Regulatory Domains Supported Table */ + +/*----------------------------------------*/ + +typedef struct MIB_PhyRegDomainsSupp_s { + UINT32 RegDomainsSuppIdx; + UINT8 RegDomainsSuppVal; /*SNMP_RegDomainsSuppVal_e values */ +} MIB_PHY_REG_DOMAINS_SUPPPORTED; + +/*-------------------------*/ + +/* PHY Antennas List Table */ + +/*-------------------------*/ + +typedef struct MIB_PhyAntList_s { + UINT8 AntListIdx; + UINT8 SuppTxAnt; /*SNMP_Boolean_e values */ + UINT8 SuppRxAnt; /*SNMP_Boolean_e values */ + UINT8 RxDiv; /*SNMP_Boolean_e values */ +} MIB_PHY_ANT_LIST; + +/*----------------------------------------*/ + +/* PHY Supported Receive Data Rates Table */ + +/*----------------------------------------*/ + +typedef struct MIB_PhySuppDataRatesRx_s { + UINT8 SuppDataRatesRxIdx; /*1 to 8 */ + UINT8 SuppDataRatesRxVal; /*2 to 127 */ +} MIB_PHY_SUPP_DATA_RATES_RX; + +typedef struct MIB_DHCP_s { + UINT32 IPAddr; + UINT32 SubnetMask; + UINT32 GwyAddr; + +#ifdef GATEWAY + UINT32 PrimaryDNS; + UINT32 SecondaryDNS; +#endif + +} MIB_DHCP; + +#if defined(GATEWAY) + +typedef struct MIB_IP_LAN_s { + UINT32 IPAddr; + UINT32 SubnetMask; +} MIB_IP_LAN; + +#endif + +/* Added for WB31 */ + +typedef struct _MIB_WB { + UINT8 devName[16]; // Must be a string: + // 15 Max characters + UINT8 cloneMacAddr[6]; // cloned MAC Address + UINT8 opMode; // 0 for infrastructure, + // 1 for ad-hoc + UINT8 macCloneEnable; // boolean +} MIB_WB; + +/* Added for WB31 end */ + +/*---------------------*/ + +/* RSN Config Table */ + +/*---------------------*/ + +typedef struct MIB_RSNConfig_s { + UINT32 Index; + UINT32 Version; + UINT32 PairwiseKeysSupported; + UINT8 MulticastCipher[4]; + UINT8 GroupRekeyMethod; + UINT32 GroupRekeyTime; + UINT32 GroupRekeyPackets; + UINT8 GroupRekeyStrict; + UINT8 PSKValue[40]; + UINT8 PSKPassPhrase[64]; + UINT8 TSNEnabled; + UINT32 GroupMasterRekeyTime; + UINT32 GroupUpdateTimeOut; + UINT32 GroupUpdateCount; + UINT32 PairwiseUpdateTimeOut; + UINT32 PairwiseUpdateCount; +} MIB_RSNCONFIG; + +/*---------------------*/ + +/* RSN Unicast Cipher Suites Config Table */ + +/*---------------------*/ + +typedef struct MIB_RSNConfigUnicastCiphers_s { + UINT32 Index; + UINT8 UnicastCipher[4]; + UINT8 Enabled; +} MIB_RSNCONFIG_UNICAST_CIPHERS; + +/*---------------------*/ + +/* RSN Authentication Suites Config Table */ + +/*---------------------*/ + +typedef struct MIB_RSNConfigAuthSuites_s { + UINT32 Index; + UINT8 AuthSuites[4]; + UINT8 Enabled; +} MIB_RSNCONFIG_AUTH_SUITES; + +typedef struct Mrvl_MIB_RSN_GrpKey_s { + UINT8 GrpMasterKey[32]; + UINT8 EncryptKey[16]; + UINT32 TxMICKey[2]; + UINT32 RxMICKey[2]; + UINT32 g_IV32; + UINT16 g_IV16; + UINT16 g_Phase1Key[5]; + UINT8 g_KeyIndex; +} MRVL_MIB_RSN_GRP_KEY; + +#ifdef MIB_STATS + +typedef struct Mrvl_MIB_StatsDetails { + /* WARNING: Do not change the order of variables in this structure */ + UINT32 TKIPLocalMICFailures; /* OID: 0x0b -> 0 */ + UINT32 CCMPDecryptErrors; /* OID: 0x0c -> 1 */ + UINT32 WEPUndecryptableCount; /* OID: 0x0d -> 2 */ + UINT32 WEPICVErrorCount; /* OID: 0x0e -> 3 */ + UINT32 DecryptFailureCount; /* OID: 0x0f -> 4 */ + UINT32 failed; /* OID: 0x12 -> 5 */ + UINT32 retry; /* OID: 0x13 -> 6 */ + UINT32 multiretry; /* OID: 0x14 -> 7 */ + UINT32 framedup; /* OID: 0x15 -> 8 */ + UINT32 rtssuccess; /* OID: 0x16 -> 9 */ + UINT32 rtsfailure; /* OID: 0x17 -> 10 */ + UINT32 ackfailure; /* OID: 0x18 -> 11 */ + UINT32 rxfrag; /* OID: 0x19 -> 12 */ + UINT32 mcastrxframe; /* OID: 0x1a -> 13 */ + UINT32 fcserror; /* OID: 0x1b -> 14 */ + UINT32 txframe; /* OID: 0x1c -> 15 */ + UINT32 rsntkipcminvoked; /* OID: 0x1d -> 16 */ + UINT32 rsn4wayhandshakefailure; /* OID: 0x1e -> 17 */ + UINT32 mcasttxframe; /* OID: 0x1f -> 18 */ + UINT32 TKIPICVErrors; /* Not in the OID list */ + UINT32 TKIPReplays; /* Not in the OID list */ + UINT32 CCMPReplays; /* Not in the OID list */ + UINT32 CMACICVErrors; /* Not in the OID list */ + UINT32 CMACReplays; /* Not in the OID list */ + UINT32 WEPFragError; /* Not in the OID list */ + UINT32 DecryptSuccessCount; /* Not in the OID list */ + UINT32 wepicverrCnt[4]; /* Not in the OID list */ + + /* EAPoL Tx Stats */ + UINT16 eapolSentTotalCnt; + UINT16 eapolSentFrmFwCnt; + UINT16 eapolSentSuccessCnt; + UINT16 eapolSentFailCnt; + + /* EAPoL Rx Stats */ + UINT16 eapolRxTotalCnt; + UINT16 eapolRxForESUPPCnt; + + /* Key Stats */ + UINT16 PTKRecvdTotalCnt; + UINT16 PTKSentFrmESUPPCnt; + + UINT16 GTKRecvdTotalCnt; + UINT16 GTKSentFrmESUPPCnt; +} MRVL_MIB_STATSDETAILS; + +#define NUM_OF_STATS_OIDS (19) + +#define INC_MIB_STAT(x, a) if (x && x->pMibStats) { x->pMibStats->data.mib.a++; } +#define INC_MIB_STAT2(x, a, b) if (x && x->pMibStats) { x->pMibStats->data.mib.a++; x->pMibStats->data.mib.b++;} +#define INC_MIB_STAT3(x, a, b, c) if (x && x->pMibStats) { x->pMibStats->data.mib.a++; x->pMibStats->data.mib.b++; x->pMibStats->data.mib.c++;} +#define CLR_MIB_STAT(x, a) if (x && x->pMibStats) { x->pMibStats->data.mib.a = 0; } + +typedef struct Mrvl_MIB_Stats { + union { + MRVL_MIB_STATSDETAILS mib; + UINT32 mib_stats[NUM_OF_STATS_OIDS]; + } data; +} MRVL_MIB_STATS; +#endif + +typedef struct MIB_BURST_MODE { + UINT8 mib_burstmode; + UINT32 mib_burstrate; +} MIB_BURST_MODE; + +#ifdef BRIDGE_STP + +typedef struct mib_dot1dPortEntry_s { + UINT8 mib_dot1dStpPortPriority; /* (0..255) */ + UINT8 mib_dot1dStpPortEnable; /*1: enable; 2: disable */ + UINT16 mib_dot1dStpPortPathCost; /* (1..65535) */ +} mib_dot1dPortEntry_t; + +typedef struct mib_dot1dStp_s { + UINT8 mib_dot1dStpPortPriority; /* (0..255) */ + UINT8 mib_dot1dStpPortEnable; /*1: enable; 2: disable */ + UINT16 mib_dot1dStpPortPathCost; /* (1..65535) */ + UINT32 mib_dot1dTpAgingTime; /* (10..1000000) */ + UINT16 mib_dot1dStpPriority; + UINT16 mib_dot1dStpBridgeMaxAge; + UINT16 mib_dot1dStpBridgeHelloTime; + UINT16 mib_dot1dStpBridgeForwardDelay; +} mib_dot1dStp_t; + +typedef struct mib_priv_dot1dStp_s { + UINT8 mib_priv_dot1dStpEnable; /* 1 or 0 */ +} mib_priv_dot1dStp_t; + +#endif + +typedef struct MIB_802DOT11_s { + + /*-----------------------------------------*/ + + /* Station Management Attributes */ + + /*-----------------------------------------*/ + + MIB_STA_CFG StationConfig; /* station configuration table */ + + MIB_WEP_DEFAULT_KEYS WepDefaultKeys[4]; /* wep default keys table */ + + MIB_WEP_KEY_MAPPINGS WepKeyMappings; /* wep key mappings table */ + + MIB_PRIVACY_TABLE Privacy; /* privacy table */ + + /* SMT Notification Objects */ + +#ifdef AP_SW + + MIB_DISASSOC_NOT NoteDisassoc; /* disassociate notification */ + + MIB_DEAUTH_NOT NoteDeauth; /* deauthentication notification */ + + MIB_AUTH_FAIL_NOT NoteAuthFail; /* authentication fail notification */ + +#endif + + /*-----------------------------------------*/ + + /* MAC Attributes */ + + /*-----------------------------------------*/ + + MIB_OP_DATA OperationTable; + +#ifdef WMM_IMPLEMENTED + MIB_EDCA_CONFIG EdcaConfigTable[WMM_MAX_TIDS]; +#endif + +#ifdef AP_SW + + MIB_COUNTERS CountersTable; + + MIB_GROUP_ADDR GroupAddrTable; + + /*-----------------------------------------*/ + + /* Resource Type */ + + /*-----------------------------------------*/ + + MIB_RESOURCE_INFO ResourceInfo; + + /*-----------------------------------------*/ + + /* PHY Attributes */ + + /*-----------------------------------------*/ + + MIB_PHY_OP_TABLE PhyOpTable; + + MIB_PHY_TX_POWER_TABLE PhyPowerTable; + + MIB_PHY_FHSS_TABLE PhyFHSSTable; + + MIB_PHY_IR_TABLE PhyIRTable; + + MIB_PHY_REG_DOMAINS_SUPPPORTED PhyRegDomainsSupp; + + MIB_PHY_ANT_LIST AntennasListTable; + +#endif + + MIB_PHY_ANT_TABLE PhyAntTable; + + MIB_PHY_DSSS_TABLE PhyDSSSTable; + + MIB_PHY_SUPP_DATA_RATES_TX SuppDataRatesTx[IEEEtypes_MAX_DATA_RATES_G]; + + MIB_PHY_SUPP_DATA_RATES_RX SuppDataRatesRx; + +#if defined(AP_SW) + MIB_RSNCONFIG RSNConfig; +#endif + + //MIB_RSNCONFIG_UNICAST_CIPHERS UnicastCiphers; + + //MIB_RSNCONFIG_AUTH_SUITES RSNConfigAuthSuites; + +#ifdef AP_WPA2 + + MIB_RSNCONFIGWPA2 RSNConfigWPA2; + + MIB_RSNCONFIGWPA2_UNICAST_CIPHERS WPA2UnicastCiphers; + + MIB_RSNCONFIGWPA2_UNICAST_CIPHERS WPA2UnicastCiphers2; + + MIB_RSNCONFIGWPA2_AUTH_SUITES WPA2AuthSuites; + +#endif + +#ifdef BURST_MODE + + MIB_BURST_MODE BurstMode; + +#endif + + MIB_PHY_ANT_SELECT PhyAntSelect; + +#ifdef WEP_RSN_STATS_MIB + MIB_RSNSTATS RSNStats; +#endif + +} MIB_802DOT11; + +extern BOOLEAN mib_InitSta(MIB_802DOT11 *mib); + +extern BOOLEAN mib_InitAp(MIB_802DOT11 *mib); + +#endif /* _WL_MIB_H_ */ diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/wl_mib_rom.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/wl_mib_rom.h new file mode 100644 index 000000000000..75b31c13d2e9 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/wl_mib_rom.h @@ -0,0 +1,60 @@ +/** @file wl_mib_rom.h + * + * @brieThis file contains the MIB structure definitions + * based on IEEE 802.11 specification. + * + * Copyright (C) 2014-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 03/07/2014: Initial version +******************************************************/ +#if !defined(WL_MIB_ROM_H__) + +#define WL_MIB_ROM_H__ +#include "IEEE_types.h" + +#define MIB_EDCA_MSDU_LIFETIME_DEFAULT 512 +#define WEP_KEY_USER_INPUT 13 /** Also defined in keyApiStaTypes.h */ + +/*-----------------------------------------*/ + +/* PHY Supported Transmit Data Rates Table */ + +/*-----------------------------------------*/ + +typedef struct MIB_PhySuppDataRatesTx_s { + UINT8 SuppDataRatesTxIdx; /*1 to IEEEtypes_MAX_DATA_RATES_G */ + UINT8 SuppDataRatesTxVal; /*2 to 127 */ +} MIB_PHY_SUPP_DATA_RATES_TX; + +/*------------------------*/ +/* WEP Default Keys Table */ +/*------------------------*/ +/* This struct is used in ROM and it should not be changed at all */ +typedef struct MIB_WepDefaultKeys_s { + UINT8 WepDefaultKeyIdx; /* 1 to 4 */ + UINT8 WepDefaultKeyType; /* */ + UINT8 WepDefaultKeyValue[WEP_KEY_USER_INPUT]; /* 5 byte string */ +} MIB_WEP_DEFAULT_KEYS; + +typedef struct { + /* Maximum lifetime of an MSDU from when it enters the MAC, 802.11e */ + UINT16 MSDULifetime; /* 0 to 500, 500 default */ +} MIB_EDCA_CONFIG; + +#endif /* _WL_MIB_ROM_H_ */ diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/wlpd.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/wlpd.h new file mode 100644 index 000000000000..2580817a8e61 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/wlpd.h @@ -0,0 +1,161 @@ +/** @file wlpd.h + * + * @brief RX and TX packet descriptor + * + * Copyright (C) 2014-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 03/07/2014: Initial version +******************************************************/ +#ifndef WLPD_H__ +#define WLPD_H__ + +/** include files **/ +#include "packetType.h" +#include "wltypes.h" + + /** @defgroup PacketTypes Tx Rx Data Packet Types + * Functions exported by wlpd.h + * @{ + */ + +/*****************************************************************************/ +typedef MLAN_PACK_START struct { + UINT8 tdlsPkt:1; + UINT8 rsvd:7; +} MLAN_PACK_END rxFlags_t; +/** +*** @brief Enumeration of action to be take for returned Rx Packets. +**/ + +/** +*** @brief Receive Packet Descriptor +**/ +typedef MLAN_PACK_START struct RxPD_t { + /* TODO: Port EMBEDDED_TCPIP and VISTA_802_11_DRIVER_INTERFACE + members to W8786 */ + UINT8 RxBSSType; + UINT8 RxBSSNum; + UINT16 RxPacketLength; //!< Rx Packet Length + SINT16 RxPacketOffset; //!< Offset to the Rx Data + UINT16 RxPacketType; + UINT16 SeqNum; + UINT8 userPriority; + UINT8 RxRate; // LG 0-3 (11b), 5-12(11g), HT :MCS# (11n) + SINT8 SNR; + SINT8 RxSQ2; // defined to RxNF + UINT8 RxHTInfo; // [Bit o] RxRate format : Legacy = 0 , HT =1 + //[Bit 1] HT Bandwidth :BW20 =0 , BW40 = 1 + //[Bit 2] HT Guard Interval : LGI = 0, SGI = 1 +#if defined(VISTA_802_11_DRIVER_INTERFACE) || defined(SNIFFER_MODE_ENABLE) + UINT8 PacketType; + UINT8 NumFragments; + UINT8 EncryptionStatus; +#else + UINT8 Reserved2[3]; +#endif + rxFlags_t flags; + UINT8 Reserved3; +} MLAN_PACK_END RxPD_t; + +#define PACKET_TYPE_802_3 0 +#define PACKET_TYPE_802_11 1 +#define PACKET_TYPE_802_11_QOS 2 +#define PACKET_TYPE_802_11_MRVL_MESH 3 +#define PACKET_TYPE_TDLS 4 + +#define MESH_FWD_PACKET_MASK (1 << 0) +#define MESH_OLPC_PKT_MASK (1 << 1) + +#define PACKET_DECRYPTED 0 +#define PACKET_NOT_DECRYPTED 1 + +#if defined(VISTA_802_11_DRIVER_INTERFACE) + +#define PACKET_NO_DECRYPT_NEEDED 2 +#define MAGIC_PACKET_MARKER_BITMASK (1<<3) +#define COALESCED_PACKET_MARKER_BITMASK (1<<2) + +#endif + +#define RxNF RxSQ2 + +// Since Small Debug print and Myung's Debug Facility both use +// PKT_TYPE_DEBUG rx pkt, the following debug header is required to distinguish +// Small Debug from Myung's. +// +typedef MLAN_PACK_START struct { + UINT8 dbg_type; + UINT8 reserve[3]; +} MLAN_PACK_END to_host_dbg_hdr_t; +#define DBG_TYPE_SMALL 2 + +/* The following fields have been added for Null frame handling + in Power Save Mode. + */ +typedef MLAN_PACK_START struct { + UINT8 nullPkt:1; + UINT8 overRideFwPM:1; + UINT8 pmVal:1; + UINT8 lastTxPkt:1; + UINT8 tdlsPkt:1; + UINT8 rsvd:3; +} MLAN_PACK_END wcb_flags_t; + +/** +*** @brief Transmit Packet Descriptor +**/ +typedef MLAN_PACK_START struct { + /* TODO: Port EMBEDDED_TCPIP and VISTA_802_11_DRIVER_INTERFACE + members to W8786 */ + UINT8 TxBSSType; + UINT8 TxBSSNum; + UINT16 TxPacketLength; //!< Tx Packet Length + UINT16 TxPacketOffset; //!< Offset to Tx Data + UINT16 TxPacketType; //!< Tx Packet Type + UINT32 TxControl; //b3-0: RateID; b4:HostRateCtrl; + //b11-8: RetryLimit; b12:HostRetryCtrl; + //b14-13: Ack Policy, 10 ACK_IMMD + // 11 NO_ACK 0x ACK_PER_FRM + UINT8 userPriority; + wcb_flags_t flags; // These BitFields are for Null Frame Handling and + // other Power Save requirements. + UINT8 PktDelay_2ms; /* Driver queue delay used in stats and MSDU + ** lifetime expiry calcs; value is represented + ** by 2ms units (ms bit shifted by 1) + */ +#ifdef VISTA_802_11_DRIVER_INTERFACE + /* Include Packet type for NWF */ + UINT8 PacketType; + UINT8 EncrOpt; +#else + UINT8 Reserved[2]; +#endif + UINT8 TxTokenId; + +} MLAN_PACK_END wcb_t; + +// Encryption Option is an 8 bit field +#define ENCR_OPT_NORMAL 0x00 // Normal packet. Follows Enc rules in FW +#define ENCR_OPT_FORCE_PTEXT 0x01 // Force plain text. No encryption. +#define ENCR_OPT_FW_KEY_MAP 0x02 // Encrypt using key mapping table in FW +#define ENCR_OPT_PTEXT_80211_PKT 0x03 // No encryption for 802.11 pkt + +/*@}*/ + +#endif /* _WLPD_H_ */ diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/wltypes.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/wltypes.h new file mode 100644 index 000000000000..0126bccf8538 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/common/wltypes.h @@ -0,0 +1,574 @@ +/** @file wltypes.h + * + * @brief Basic types common to all the modules must be defined in this file. + * + * Copyright (C) 2014-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 03/07/2014: Initial version +******************************************************/ +#if !defined(WLTYPES_H__) +#define WLTYPES_H__ +/* +* Copyright 2003, Marvell Semiconductor, Inc. +* This code contains confidential information of Marvell Semiconductor, Inc. +* No rights are granted herein under any patent, mask work right or copyright +* of Marvell or any third party. +* Marvell reserves the right at its sole discretion to request that this code +* be immediately returned to Marvell. This code is provided "as is". +* Marvell makes no warranties, express, implied or otherwise, regarding its +* accuracy, completeness or performance. +*/ + +/*! + * \file wltypes.h + * \brief Basic types common to all the modules must be defined in this file. + * +*/ + /** @defgroup DataTypes Data Types used in SDK + * Functions exported by wltypes.h + * @{ + */ +/*! Create type names for native C types for portability reasons */ +typedef unsigned long long UINT64; //!< 64 bit unsigned +typedef signed long long SINT64; //!< 64 bit signed +typedef unsigned int UINT32; //!< 32 bit unsigned +typedef signed int SINT32; //!< 32 bit signed +typedef unsigned short UINT16; //!< 16 bit unsigned +typedef signed short SINT16; //!< 16 bit signed +typedef unsigned char UINT8; //!< 8 bit unsigned +typedef signed char SINT8; //!< 8 bit signed + +typedef unsigned long long uint64; //!< 64 bit unsigned +typedef signed long long sint64; //!< 64 bit signed +typedef unsigned int uint32; //!< 32 bit unsigned + +typedef signed int sint32; //!< 32 bit signed +typedef unsigned short uint16; //!< 16 bit unsigned +typedef signed short sint16; //!< 16 bit signed +typedef unsigned char uint8; //!< 8 bit unsigned +typedef signed char sint8; //!< 8 bit signed +typedef signed char int8; //!< 8 bit signed +typedef int BOOLEAN; //!< boolean +typedef int Boolean; //!< boolean +typedef signed int BIT_FIELD; //!< bit field + +typedef unsigned int UINT; //!< unsigned integer + +//From dwc_os.h +#define size_t uint32 + +extern SINT8 SINT8_minus_SINT8(SINT8 x, SINT8 y); +extern SINT8 SINT8_plus_SINT8(SINT8 x, SINT8 y); + +#ifdef __GNUC__ +/** Structure packing begins */ +#define MLAN_PACK_START +/** Structure packeing end */ +#define MLAN_PACK_END __attribute__((packed)) +#else /* !__GNUC__ */ +#ifdef PRAGMA_PACK +/** Structure packing begins */ +#define MLAN_PACK_START +/** Structure packeing end */ +#define MLAN_PACK_END +#else /* !PRAGMA_PACK */ +/** Structure packing begins */ +#define MLAN_PACK_START __packed +/** Structure packing end */ +#define MLAN_PACK_END +#endif /* PRAGMA_PACK */ +#endif /* __GNUC__ */ + +#ifndef INLINE +#ifdef __GNUC__ +/** inline directive */ +#define INLINE inline +#else +/** inline directive */ +#define INLINE __inline +#endif +#endif + +typedef struct _REFCLK_VAL8_REG7F { + UINT8 refClk; + UINT8 val; +} REFCLK_VAL8_REG7F; + +#define REG8_TERMINATOR (0xFF) +typedef struct _ADDR8_VAL8_REG { + UINT8 reg; + UINT8 val; +} ADDR8_VAL8_REG; + +#define REG16_TERMINATOR (0xFFFF) +typedef struct _ADDR16_VAL8_REG { + UINT16 reg; + UINT8 val; +} ADDR16_VAL8_REG; + +#define REG32_TERMINATOR (0xFFFFFFFF) +typedef struct _ADDR32_VAL32_REG { + UINT32 reg; + UINT32 val; +} ADDR32_VAL32_REG; + +#if defined(BBP_9BIT_ADDR) +#define BBP_TERMINATOR REG16_TERMINATOR +#else +#define BBP_TERMINATOR REG8_TERMINATOR +#endif + +/*! Generic status code */ +#define WL_STATUS_OK 0 //!< ok +#define WL_STATUS_ERR -1 //!< error +#define WL_STATUS_BAD_PARAM -2 //!< bad parameter + +/* Some BT files require access to wltypes.h file which result in duplicate definition of FALSE & TRUE + * undef TRUE and FALSE to avoid these warnings */ +#undef FALSE +#undef TRUE +#undef INLINE + +/*! BOOLEAN values */ +#define FALSE 0 //!< False +#define TRUE 1 //!< True + +/** +*** @brief Enumeration of Status type +**/ +typedef enum { + SUCCESS, //!< 0 + FAIL //!< 1 +} Status_e; + +typedef Status_e WL_STATUS; + +/*! Value for NULL pointer */ +#undef NULL +#define NULL ((void *)0) //!< null +#if 0 +#ifndef LINT +#define MLAN_PACK_START __packed //!< packed structure +#define MLAN_PACK_END //!< end of packed structure + +#define ALIGNED_START(x) __align(x) //!< allignment macro +#define ALIGNED_END(x) //!< allignment end +#else +#define MLAN_PACK_START +#define MLAN_PACK_END + +#define ALIGNED_START(x) +#define ALIGNED_END(x) +#endif +#endif +#define INLINE + +#define BIT0 (0x00000001 << 0) +#define BIT1 (0x00000001 << 1) +#define BIT2 (0x00000001 << 2) +#define BIT3 (0x00000001 << 3) +#define BIT4 (0x00000001 << 4) +#define BIT5 (0x00000001 << 5) +#define BIT6 (0x00000001 << 6) +#define BIT7 (0x00000001 << 7) +#define BIT8 (0x00000001 << 8) +#define BIT9 (0x00000001 << 9) +#define BIT10 (0x00000001 << 10) +#define BIT11 (0x00000001 << 11) +#define BIT12 (0x00000001 << 12) +#define BIT13 (0x00000001 << 13) +#define BIT14 (0x00000001 << 14) +#define BIT15 (0x00000001 << 15) +#define BIT16 (0x00000001 << 16) +#define BIT17 (0x00000001 << 17) +#define BIT18 (0x00000001 << 18) +#define BIT19 (0x00000001 << 19) +#define BIT20 (0x00000001 << 20) +#define BIT21 (0x00000001 << 21) +#define BIT22 (0x00000001 << 22) +#define BIT23 (0x00000001 << 23) +#define BIT24 (0x00000001 << 24) +#define BIT25 (0x00000001 << 25) +#define BIT26 (0x00000001 << 26) +#define BIT27 (0x00000001 << 27) +#define BIT28 (0x00000001 << 28) +#define BIT29 (0x00000001 << 29) +#define BIT30 (0x00000001 << 30) +#define BIT31 (0x00000001UL << 31) + +// VOLT_RESOLUTION_50mV +#define VOLT_0_5 10 // 10x50mV = 5x100mV +#define VOLT_0_6 12 // 12x50mV = 6x100mV +#define VOLT_0_7 14 // 14x50mV = 7x100mV +#define VOLT_0_7_5 15 // 15x50mV = 7.5x100mV +#define VOLT_0_8 16 // 16x50mV = 8x100mV +#define VOLT_0_8_5 17 // 17x50mV = 8.5x100mV +#define VOLT_0_9 18 // 18x50mV = 9x100mV +#define VOLT_1_0 20 // 20x50mV = 10x100mV +#define VOLT_1_0_5 21 // 21x50mV +#define VOLT_1_1 22 // 22x50mV = 11x100mV +#define VOLT_1_1_5 23 // 23x50mV +#define VOLT_1_2 24 // 24x50mV = 12x100mV +#define VOLT_1_2_5 25 // 25x50mV +#define VOLT_1_3 26 // 26x50mV = 13x100mV +#define VOLT_1_4 28 // 28x50mV = 14x100mV +#define VOLT_1_5 30 // 30x50mV = 15x100mV +#define VOLT_1_5_5 31 // 31x50mV = 15.5x100mV +#define VOLT_1_6 32 // 32x50mV = 16x100mV +#define VOLT_1_6_5 33 // 33x50mV = 16.5x100mV +#define VOLT_1_7 34 // 34x50mV = 17x100mV +#define VOLT_1_7_5 35 // 35x50mV = 17.5x100mV +#define VOLT_1_8 36 // 36x50mV = 18x100mV +#define VOLT_1_8_5 37 // 37x50mV = 18.5x100mV +#define VOLT_1_9 38 // 38x50mV = 19x100mV +#define VOLT_1_9_5 39 // 39x50mV = 19.5x100mV +#define VOLT_2_0 40 // 40x50mV = 20x100mV +#define VOLT_2_1 42 // 42x50mV = 21x100mV +#define VOLT_2_2 44 // 44x50mV = 22x100mV +#define VOLT_2_3 46 // 46x50mV = 23x100mV +#define VOLT_2_4 48 // 48x50mV = 24x100mV +#define VOLT_2_5 50 // 50x50mV = 25x100mV +#define VOLT_2_6 52 // 52x50mV = 26x100mV +#define VOLT_2_7 54 // 54x50mV = 27x100mV +#define VOLT_2_8 56 // 56x50mV = 28x100mV +#define VOLT_2_9 58 // 58x50mV = 29x100mV +#define VOLT_3_0 60 // 60x50mV = 30x100mV +#define VOLT_3_1 62 // 62x50mV = 31x100mV +#define VOLT_3_2 64 // 64x50mV = 32x100mV +#define VOLT_3_3 66 // 66x50mV = 33x100mV +#define VOLT_3_4 68 // 68x50mV = 34x100mV +#define VOLT_3_5 70 // 70x50mV = 35x100mV +#define VOLT_3_6 72 // 72x50mV = 36x100mV + +typedef enum { + OFF = 0, + ON = 1, + UNKOWN = 0xFF +} ON_OFF_UNKOWN_e; + +typedef unsigned char MRVL_RATEID; + +#if defined(DOT11AC) && defined(STREAM_2x2) +typedef unsigned long long MRVL_RATEID_BITMAP_UNIT; +typedef struct { + unsigned long long bitmap[2]; +} MRVL_RATEID_BITMAP; +#define MRVL_RATEID_BIT(rateId) (1ULL << (rateId)) +#else +#if defined(STREAM_2x2) || defined(DOT11AC) +typedef unsigned long long MRVL_RATEID_BITMAP_UNIT; +typedef unsigned long long MRVL_RATEID_BITMAP; +#define MRVL_RATEID_BIT(rateId) (1ULL << (rateId)) +#else +typedef unsigned int MRVL_RATEID_BITMAP_UNIT; +typedef unsigned int MRVL_RATEID_BITMAP; +#define MRVL_RATEID_BIT(rateId) (1UL << (rateId)) +#endif +#endif + +#define NUM_RATE_BITMAP_UNIT (sizeof(MRVL_RATEID_BITMAP_UNIT)*8) + +enum { + RATEID_DBPSK1Mbps, //(0) + RATEID_DQPSK2Mbps, //(1) + RATEID_CCK5_5Mbps, //(2) + RATEID_CCK11Mbps, //(3) + RATEID_CCK22Mbps, //(4) + RATEID_OFDM6Mbps, //(5) + RATEID_OFDM9Mbps, //(6) + RATEID_OFDM12Mbps, //(7) + RATEID_OFDM18Mbps, //(8) + RATEID_OFDM24Mbps, //(9) + RATEID_OFDM36Mbps, //(10) + RATEID_OFDM48Mbps, //(11) + RATEID_OFDM54Mbps, //(12) + RATEID_OFDM72Mbps, //(13) + + RATEID_MCS0_6d5Mbps, //(14) //RATEID_OFDM72Mbps + 1 + RATEID_MCS1_13Mbps, //(15) + RATEID_MCS2_19d5Mbps, //(16) + RATEID_MCS3_26Mbps, //(17) + RATEID_MCS4_39Mbps, //(18) + RATEID_MCS5_52Mbps, //(19) + RATEID_MCS6_58d5Mbps, //(20) + RATEID_MCS7_65Mbps, //(21) + +#ifdef STREAM_2x2 + RATEID_MCS8_13Mbps, //(22) + RATEID_MCS9_26Mbps, //(23) + RATEID_MCS10_39Mbps, //(24) + RATEID_MCS11_52Mbps, //(25) + RATEID_MCS12_78Mbps, //(26) + RATEID_MCS13_104Mbps, //(27) + RATEID_MCS14_117Mbps, //(28) + RATEID_MCS15_130Mbps, //(29) +#endif + + RATEID_MCS32BW40_6Mbps, //(30), (22) + RATEID_MCS0BW40_13d5Mbps, //(31), (23) + RATEID_MCS1BW40_27Mbps, //(32), (24) + RATEID_MCS2BW40_40d5Mbps, //(33), (25) + RATEID_MCS3BW40_54Mbps, //(34), (26) + RATEID_MCS4BW40_81Mbps, //(35), (27) + RATEID_MCS5BW40_108Mbps, //(36), (28) + RATEID_MCS6BW40_121d5Mbps, //(37), (29) + RATEID_MCS7BW40_135Mbps, //(38), (30) + +#ifdef STREAM_2x2 + RATEID_MCS8BW40_27Mbps, //(39) + RATEID_MCS9BW40_54Mbps, //(40) + RATEID_MCS10BW40_81Mbps, //(41) + RATEID_MCS11BW40_108Mbps, //(42) + RATEID_MCS12BW40_162Mbps, //(43) + RATEID_MCS13BW40_216Mbps, //(44) + RATEID_MCS14BW40_243Mbps, //(45) + RATEID_MCS15BW40_270Mbps, //(46) +#endif + +#if defined(DOT11AC) + RATEID_VHT_MCS0_1SS_BW20, //(47), (31) //6.5 Mbps + RATEID_VHT_MCS1_1SS_BW20, //(48), (32) //13 Mbps + RATEID_VHT_MCS2_1SS_BW20, //(49), (33) //19.5 Mbps + RATEID_VHT_MCS3_1SS_BW20, //(50), (34) //26 Mbps + RATEID_VHT_MCS4_1SS_BW20, //(51), (35) //39 Mbps + RATEID_VHT_MCS5_1SS_BW20, //(52), (36) //52 Mbps + RATEID_VHT_MCS6_1SS_BW20, //(53), (37) //58.5 Mbps + RATEID_VHT_MCS7_1SS_BW20, //(54), (38) //65 Mbps + RATEID_VHT_MCS8_1SS_BW20, //(55), (39) //78 Mbps + RATEID_VHT_MCS9_1SS_BW20, //(56), (40) //86.7 Mbps(INVALID) + +#ifdef STREAM_2x2 + RATEID_VHT_MCS0_2SS_BW20, //(57) //13 Mbps + RATEID_VHT_MCS1_2SS_BW20, //(58) //26 Mbps + RATEID_VHT_MCS2_2SS_BW20, //(59) //39 Mbps + RATEID_VHT_MCS3_2SS_BW20, //(60) //52 Mbps + RATEID_VHT_MCS4_2SS_BW20, //(61) //78 Mbps + RATEID_VHT_MCS5_2SS_BW20, //(62) //104 Mbps + RATEID_VHT_MCS6_2SS_BW20, //(63) //117 Mbps + RATEID_VHT_MCS7_2SS_BW20, //(64) //130 Mbps + RATEID_VHT_MCS8_2SS_BW20, //(65) //156 Mbps + RATEID_VHT_MCS9_2SS_BW20, //(66) //173.3 Mbps(INVALID) +#endif + + RATEID_VHT_MCS0_1SS_BW40, //(67), (41) //13.5 Mbps + RATEID_VHT_MCS1_1SS_BW40, //(68), (42) //27 Mbps + RATEID_VHT_MCS2_1SS_BW40, //(69), (43) //40.5 Mbps + RATEID_VHT_MCS3_1SS_BW40, //(70), (44) //54 Mbps + RATEID_VHT_MCS4_1SS_BW40, //(71), (45) //81 Mbps + RATEID_VHT_MCS5_1SS_BW40, //(72), (46) //108 Mbps + RATEID_VHT_MCS6_1SS_BW40, //(73), (47) //121.5 Mbps + RATEID_VHT_MCS7_1SS_BW40, //(74), (48) //135 Mbps + RATEID_VHT_MCS8_1SS_BW40, //(75), (49) //162 Mbps + RATEID_VHT_MCS9_1SS_BW40, //(76), (50) //180 Mbps + +#ifdef STREAM_2x2 + RATEID_VHT_MCS0_2SS_BW40, //(77) //27 Mbps + RATEID_VHT_MCS1_2SS_BW40, //(78) //54 Mbps + RATEID_VHT_MCS2_2SS_BW40, //(79) //81 Mbps + RATEID_VHT_MCS3_2SS_BW40, //(80) //108 Mbps + RATEID_VHT_MCS4_2SS_BW40, //(81) //162 Mbps + RATEID_VHT_MCS5_2SS_BW40, //(82) //216 Mbps + RATEID_VHT_MCS6_2SS_BW40, //(83) //243 Mbps + RATEID_VHT_MCS7_2SS_BW40, //(84) //270 Mbps + RATEID_VHT_MCS8_2SS_BW40, //(85) //324 Mbps + RATEID_VHT_MCS9_2SS_BW40, //(86) //360 Mbps +#endif + + RATEID_VHT_MCS0_1SS_BW80, //(87), (51) //29.3 Mbps + RATEID_VHT_MCS1_1SS_BW80, //(88), (52) //58.5 Mbps + RATEID_VHT_MCS2_1SS_BW80, //(89), (53) //87.8 Mbps + RATEID_VHT_MCS3_1SS_BW80, //(90), (54) //117 Mbps + RATEID_VHT_MCS4_1SS_BW80, //(91), (55) //175.5 Mbps + RATEID_VHT_MCS5_1SS_BW80, //(92), (56) //234 Mbps + RATEID_VHT_MCS6_1SS_BW80, //(93), (57) //263.3 Mbps + RATEID_VHT_MCS7_1SS_BW80, //(94), (58) //292.5 Mbps + RATEID_VHT_MCS8_1SS_BW80, //(95), (59) //351 Mbps + RATEID_VHT_MCS9_1SS_BW80, //(96), (60) //390 Mbps + +#ifdef STREAM_2x2 + RATEID_VHT_MCS0_2SS_BW80, //(97) //58.5 Mbps + RATEID_VHT_MCS1_2SS_BW80, //(98) //117 Mbps + RATEID_VHT_MCS2_2SS_BW80, //(99) //175 Mbps + RATEID_VHT_MCS3_2SS_BW80, //(100) //234 Mbps + RATEID_VHT_MCS4_2SS_BW80, //(101) //351 Mbps + RATEID_VHT_MCS5_2SS_BW80, //(102) //468 Mbps + RATEID_VHT_MCS6_2SS_BW80, //(103) //526.5 Mbps + RATEID_VHT_MCS7_2SS_BW80, //(104) //585 Mbps + RATEID_VHT_MCS8_2SS_BW80, //(105) //702 Mbps + RATEID_VHT_MCS9_2SS_BW80, //(106) //780 Mbps +#endif +#endif //DOT11AC + + RATEID_AUTO = 0xFE, + RATEID_UNKNOWN = 0xFF +}; + +//MAX RATE DEFINITION +#define RATEID_DSSSMAX RATEID_DQPSK2Mbps + +#define RATEID_CCKMAX RATEID_CCK11Mbps + +#define RATEID_OFDMMAX RATEID_OFDM54Mbps +#define RATEID_OFDMMIN RATEID_OFDM6Mbps + +#define RATEID_HTBW20MIN RATEID_MCS0_6d5Mbps +#ifdef STREAM_2x2 +#define RATEID_HTBW20MAX RATEID_MCS15_130Mbps +#else +#define RATEID_HTBW20MAX RATEID_MCS7_65Mbps +#endif + +#define RATEID_HTBW40MIN RATEID_MCS32BW40_6Mbps +#ifdef STREAM_2x2 +#define RATEID_HTBW40MAX RATEID_MCS15BW40_270Mbps +#else +#define RATEID_HTBW40MAX RATEID_MCS7BW40_135Mbps +#endif + +#define RATEID_HTMIN RATEID_HTBW20MIN +#define RATEID_HTMAX RATEID_HTBW40MAX + +#if defined(DOT11AC) +#define RATEID_VHTBW20MIN RATEID_VHT_MCS0_1SS_BW20 +#define RATEID_VHTBW40MIN RATEID_VHT_MCS0_1SS_BW40 +#define RATEID_VHTBW80MIN RATEID_VHT_MCS0_1SS_BW80 + +#define RATEID_VHTMIN RATEID_VHT_MCS0_1SS_BW20 +#ifdef STREAM_2x2 +#define RATEID_VHTBW20MAX RATEID_VHT_MCS9_2SS_BW20 +#define RATEID_VHTBW40MAX RATEID_VHT_MCS9_2SS_BW40 +#define RATEID_VHTMAX RATEID_VHT_MCS9_2SS_BW80 +#else +#define RATEID_VHTBW20MAX RATEID_VHT_MCS9_1SS_BW20 +#define RATEID_VHTBW40MAX RATEID_VHT_MCS9_1SS_BW40 +#define RATEID_VHTMAX RATEID_VHT_MCS9_1SS_BW80 +#endif +#define RATEID_VHTBW80MAX RATEID_VHTMAX + +/* 160 is not supported yet; define them correctly when supported */ +#define RATEID_VHTBW160MIN 0xFF +#define RATEID_VHTBW160MAX 0xFF + +#endif //DOT11AC + +#define MLME_SUCCESS 0 +#define MLME_INPROCESS 1 +#define MLME_FAILURE -1 + +#define RATEID_9_6_MASK (((1<<(1+RATEID_OFDM9Mbps-RATEID_OFDM6Mbps))-1) \ + << RATEID_OFDM6Mbps) + +#define RATEID_12_6_MASK (((1<<(1+RATEID_OFDM12Mbps-RATEID_OFDM6Mbps))-1) \ + << RATEID_OFDM6Mbps) + +#define RATEID_18_6_MASK (((1<<(1+RATEID_OFDM18Mbps-RATEID_OFDM6Mbps))-1) \ + << RATEID_OFDM6Mbps) + +#define RATEID_24_6_MASK (((1<<(1+RATEID_OFDM24Mbps-RATEID_OFDM6Mbps))-1) \ + << RATEID_OFDM6Mbps) + +#define RATEID_36_6_MASK (((1<<(1+RATEID_OFDM36Mbps-RATEID_OFDM6Mbps))-1) \ + << RATEID_OFDM6Mbps) + +#define RATEID_48_6_MASK (((1<<(1+RATEID_OFDM48Mbps-RATEID_OFDM6Mbps))-1) \ + << RATEID_OFDM6Mbps) + +#define RATEID_54_6_MASK (((1<<(1+RATEID_OFDM54Mbps-RATEID_OFDM6Mbps))-1) \ + << RATEID_OFDM6Mbps) + +#define RATEID_CCK_MASK ( (1 << RATEID_DBPSK1Mbps) \ + | (1 << RATEID_DQPSK2Mbps) \ + | (1 << RATEID_CCK5_5Mbps) \ + | (1 << RATEID_CCK11Mbps) ) + +#define HT_RATE_MASK_MCS0_7 ( (1ULL << RATEID_MCS0_6d5Mbps) \ + | (1ULL << RATEID_MCS1_13Mbps) \ + | (1ULL << RATEID_MCS2_19d5Mbps) \ + | (1ULL << RATEID_MCS3_26Mbps) \ + | (1ULL << RATEID_MCS4_39Mbps) \ + | (1ULL << RATEID_MCS5_52Mbps) \ + | (1ULL << RATEID_MCS6_58d5Mbps) \ + | (1ULL << RATEID_MCS7_65Mbps) ) +#ifdef STREAM_2x2 +#define HT_RATE_MASK_MCS8_15 ( (1ULL << RATEID_MCS8_13Mbps) \ + | (1ULL << RATEID_MCS9_26Mbps) \ + | (1ULL << RATEID_MCS10_39Mbps) \ + | (1ULL << RATEID_MCS11_52Mbps) \ + | (1ULL << RATEID_MCS12_78Mbps) \ + | (1ULL << RATEID_MCS13_104Mbps) \ + | (1ULL << RATEID_MCS14_117Mbps) \ + | (1ULL << RATEID_MCS15_130Mbps) ) + +#else //1x1 +#define HT_RATE_MASK_MCS8_15 (0) +#endif + +#define HT_RATE_MASK_MCS0_7_32_40MHZ ( (1ULL << RATEID_MCS32BW40_6Mbps) \ + | (1ULL << RATEID_MCS0BW40_13d5Mbps) \ + | (1ULL << RATEID_MCS1BW40_27Mbps) \ + | (1ULL << RATEID_MCS2BW40_40d5Mbps) \ + | (1ULL << RATEID_MCS3BW40_54Mbps) \ + | (1ULL << RATEID_MCS4BW40_81Mbps) \ + | (1ULL << RATEID_MCS5BW40_108Mbps) \ + | (1ULL << RATEID_MCS6BW40_121d5Mbps) \ + | (1ULL << RATEID_MCS7BW40_135Mbps) ) + +#ifdef STREAM_2x2 +#define HT_RATE_MASK_MCS8_15_40MHZ ( (1ULL << RATEID_MCS8BW40_27Mbps) \ + | (1ULL << RATEID_MCS9BW40_54Mbps) \ + | (1ULL << RATEID_MCS10BW40_81Mbps) \ + | (1ULL << RATEID_MCS11BW40_108Mbps) \ + | (1ULL << RATEID_MCS12BW40_162Mbps) \ + | (1ULL << RATEID_MCS13BW40_216Mbps) \ + | (1ULL << RATEID_MCS14BW40_243Mbps) \ + | (1ULL << RATEID_MCS15BW40_270Mbps) ) + +#else //1x1 +#define HT_RATE_MASK_MCS8_15_40MHZ (0) +#endif + +#define RATEID_MCS_MASK ( HT_RATE_MASK_MCS0_7 \ + | HT_RATE_MASK_MCS8_15 \ + | HT_RATE_MASK_MCS0_7_32_40MHZ \ + | HT_RATE_MASK_MCS8_15_40MHZ ) + +#define RATEID_MCSBW40_MASK ( HT_RATE_MASK_MCS0_7_32_40MHZ \ + | HT_RATE_MASK_MCS8_15_40MHZ ) + +#define RATEID_MCS1x1_MASK ( HT_RATE_MASK_MCS0_7 \ + | HT_RATE_MASK_MCS0_7_32_40MHZ) + +#define RATEID_MCS2x2_MASK ( HT_RATE_MASK_MCS8_15 \ + | HT_RATE_MASK_MCS8_15_40MHZ ) + +#define IS_DSSS_FRAME(rateid) (rateid < RATEID_OFDM6Mbps) +#define IS_OFDM_FRAME(rateid) (rateid >= RATEID_OFDM6Mbps) + +typedef unsigned char MRVL_TRPCID; +typedef signed long PWR_in_dBm; + +#if 0 +#define ASSERT_RATEID(rateid) {while(rateid > RATEID_MAX);} +#define ASSERT_NonZero(x) {while(x == 0);} +#else +#define ASSERT_RATEID(rateid) +#define ASSERT_NonZero(x) +#endif + +#endif /* _WLTYPES_H_ */ diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/hostsa_def.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/hostsa_def.h new file mode 100644 index 000000000000..8d32f11f88c8 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/hostsa_def.h @@ -0,0 +1,137 @@ +/** @file hostsa_def.h + * + * @brief This file contains data structrue for authenticator/supplicant. + * + * Copyright (C) 2014-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 03/07/2014: Initial version +******************************************************/ +#ifndef _HOSTSA_DEF_H +#define _HOSTSA_DEF_H +/* subset of mlan_callbacks data structure */ +/** hostsa_util_fns data structure */ +typedef struct _hostsa_util_fns { + /** pmoal_handle */ + t_void *pmoal_handle; + /** moal_malloc */ + mlan_status (*moal_malloc) (IN t_void *pmoal_handle, + IN t_u32 size, + IN t_u32 flag, OUT t_u8 **ppbuf); + /** moal_mfree */ + mlan_status (*moal_mfree) (IN t_void *pmoal_handle, IN t_u8 *pbuf); + /** moal_memset */ + t_void *(*moal_memset) (IN t_void *pmoal_handle, + IN t_void *pmem, IN t_u8 byte, IN t_u32 num); + /** moal_memcpy */ + t_void *(*moal_memcpy) (IN t_void *pmoal_handle, + IN t_void *pdest, + IN const t_void *psrc, IN t_u32 num); + /** moal_memmove */ + t_void *(*moal_memmove) (IN t_void *pmoal_handle, + IN t_void *pdest, + IN const t_void *psrc, IN t_u32 num); + /** moal_memcmp */ + t_s32 (*moal_memcmp) (IN t_void *pmoal_handle, + IN const t_void *pmem1, + IN const t_void *pmem2, IN t_u32 num); + /** moal_udelay */ + t_void (*moal_udelay) (IN t_void *pmoal_handle, IN t_u32 udelay); + /** moal_get_system_time */ + mlan_status (*moal_get_system_time) (IN t_void *pmoal_handle, + OUT t_u32 *psec, OUT t_u32 *pusec); + /** moal_init_timer*/ + mlan_status (*moal_init_timer) (IN t_void *pmoal_handle, + OUT t_void **pptimer, + IN t_void (*callback) (t_void + *pcontext), + IN t_void *pcontext); + /** moal_free_timer */ + mlan_status (*moal_free_timer) (IN t_void *pmoal_handle, + IN t_void *ptimer); + /** moal_start_timer*/ + mlan_status (*moal_start_timer) (IN t_void *pmoal_handle, + IN t_void *ptimer, + IN t_u8 periodic, IN t_u32 msec); + /** moal_stop_timer*/ + mlan_status (*moal_stop_timer) (IN t_void *pmoal_handle, + IN t_void *ptimer); + /** moal_init_lock */ + mlan_status (*moal_init_lock) (IN t_void *pmoal_handle, + OUT t_void **pplock); + /** moal_free_lock */ + mlan_status (*moal_free_lock) (IN t_void *pmoal_handle, + IN t_void *plock); + /** moal_spin_lock */ + mlan_status (*moal_spin_lock) (IN t_void *pmoal_handle, + IN t_void *plock); + /** moal_spin_unlock */ + mlan_status (*moal_spin_unlock) (IN t_void *pmoal_handle, + IN t_void *plock); + /** moal_print */ + t_void (*moal_print) (IN t_void *pmoal_handle, + IN t_u32 level, IN char *pformat, IN ... + ); + /** moal_print_netintf */ + t_void (*moal_print_netintf) (IN t_void *pmoal_handle, + IN t_u32 bss_index, IN t_u32 level); +} hostsa_util_fns, *phostsa_util_fns; +/* Required functions from mlan */ +/** hostsa_mlan_fns data structure */ +typedef struct _hostsa_mlan_fns { + /** pmlan_private */ + t_void *pmlan_private; + /** pmlan_adapter */ + t_void *pmlan_adapter; + /** BSS index */ + t_u8 bss_index; + /** BSS type */ + t_u8 bss_type; + + pmlan_buffer (*hostsa_alloc_mlan_buffer) (t_void *pmlan_adapter, + t_u32 data_len, + t_u32 head_room, + t_u32 malloc_flag); + void (*hostsa_tx_packet) (t_void *pmlan_private, + pmlan_buffer pmbuf, t_u16 frameLen); + void (*hostsa_set_encrypt_key) (t_void *pmlan_private, + mlan_ds_encrypt_key *encrypt_key); + void (*hostsa_clr_encrypt_key) (t_void *pmlan_private); + void (*hostsa_SendDeauth) (t_void *pmlan_private, + t_u8 *addr, t_u16 reason); + void (*Hostsa_DisAssocAllSta) (void *pmlan_private, t_u16 reason); + void (*hostsa_free_mlan_buffer) (t_void *pmlan_adapter, + mlan_buffer *pmbuf); + void (*Hostsa_get_station_entry) (t_void *pmlan_private, + t_u8 *mac, t_void **ppconPtr); + void (*Hostsa_set_mgmt_ie) (t_void *pmlan_private, + t_u8 *pbuf, t_u16 len, t_u8 clearIE); + void (*Hostsa_find_connection) (t_void *pmlan_private, + t_void **ppconPtr, t_void **ppsta_node); + void (*Hostsa_find_next_connection) (t_void *pmlan_private, + t_void **ppconPtr, + t_void **ppsta_node); + t_void (*Hostsa_StaControlledPortOpen) (t_void *pmlan_private); + void (*hostsa_StaSendDeauth) (t_void *pmlan_private, + t_u8 *addr, t_u16 reason); + t_u8 (*Hostsa_get_bss_role) (t_void *pmlan_private); + t_u8 (*Hostsa_get_intf_hr_len) (t_void *pmlan_private); + t_void (*Hostsa_sendEventRsnConnect) (t_void *pmlan_private, + t_u8 *addr); +} hostsa_mlan_fns, *phostsa_mlan_fns; +#endif diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/hostsa_ext_def.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/hostsa_ext_def.h new file mode 100644 index 000000000000..ee800e23e13c --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/hostsa_ext_def.h @@ -0,0 +1,1217 @@ +/** @file hostsa_ext_def.h + * + * @brief This file declares the generic data structures and APIs. + * + * Copyright (C) 2014-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 03/07/2014: initial version +******************************************************/ + +#ifndef _HOSTSA_EXT_DEF_H_ +#define _HOSTSA_EXT_DEF_H_ + +#include "common/IEEE_types.h" +/* #################### + From mlan_decl.h + #################### */ + +/** Re-define generic data types for MLAN/MOAL */ +/** Signed char (1-byte) */ +typedef signed char t_s8; +/** Unsigned char (1-byte) */ +typedef unsigned char t_u8; +/** Signed short (2-bytes) */ +typedef short t_s16; +/** Unsigned short (2-bytes) */ +typedef unsigned short t_u16; +/** Signed long (4-bytes) */ +typedef int t_s32; +/** Unsigned long (4-bytes) */ +typedef unsigned int t_u32; +/** Signed long long 8-bytes) */ +typedef long long t_s64; +/** Unsigned long long 8-bytes) */ +typedef unsigned long long t_u64; +/** Void pointer (4-bytes) */ +typedef void t_void; +/** Size type */ +typedef t_u32 t_size; +/** Boolean type */ +typedef t_u8 t_bool; + +#ifdef MLAN_64BIT +/** Pointer type (64-bit) */ +typedef t_u64 t_ptr; +/** Signed value (64-bit) */ +typedef t_s64 t_sval; +#else +/** Pointer type (32-bit) */ +typedef t_u32 t_ptr; +/** Signed value (32-bit) */ +typedef t_s32 t_sval; +#endif + +/** MLAN MNULL pointer */ +#define MNULL (0) + +/** MLAN TRUE */ +#define MTRUE (1) +/** MLAN FALSE */ +#define MFALSE (0) + +/** MLAN MAC Address Length */ +#define MLAN_MAC_ADDR_LENGTH (6) + +/** MLAN_MEM_DEF */ +#define MLAN_MEM_DEF (0) + +/** MLAN BSS type */ +typedef enum _mlan_bss_type { + MLAN_BSS_TYPE_STA = 0, + MLAN_BSS_TYPE_UAP = 1, +#ifdef WIFI_DIRECT_SUPPORT + MLAN_BSS_TYPE_WIFIDIRECT = 2, +#endif + MLAN_BSS_TYPE_NAN = 4, + MLAN_BSS_TYPE_ANY = 0xff, +} mlan_bss_type; + +/** MLAN BSS role */ +typedef enum _mlan_bss_role { + MLAN_BSS_ROLE_STA = 0, + MLAN_BSS_ROLE_UAP = 1, + MLAN_BSS_ROLE_ANY = 0xff, +} mlan_bss_role; + +/** mlan_status */ +typedef enum _mlan_status { + MLAN_STATUS_FAILURE = 0xffffffff, + MLAN_STATUS_SUCCESS = 0, + MLAN_STATUS_PENDING, + MLAN_STATUS_RESOURCE, + MLAN_STATUS_COMPLETE, +} mlan_status; + +/** mlan_buf_type */ +typedef enum _mlan_buf_type { + MLAN_BUF_TYPE_CMD = 1, + MLAN_BUF_TYPE_DATA, + MLAN_BUF_TYPE_EVENT, + MLAN_BUF_TYPE_RAW_DATA, + MLAN_BUF_TYPE_SPA_DATA, +} mlan_buf_type; + +typedef MLAN_PACK_START struct { +#ifdef BIG_ENDIAN_SUPPORT + /** Host tx power ctrl: + 0x0: use fw setting for TX power + 0x1: value specified in bit[6] and bit[5:0] are valid */ + t_u8 hostctl:1; + /** Sign of the power specified in bit[5:0] */ + t_u8 sign:1; + /** Power to be used for transmission(in dBm) */ + t_u8 abs_val:6; +#else + /** Power to be used for transmission(in dBm) */ + t_u8 abs_val:6; + /** Sign of the power specified in bit[5:0] */ + t_u8 sign:1; + /** Host tx power ctrl: + 0x0: use fw setting for TX power + 0x1: value specified in bit[6] and bit[5:0] are valid */ + t_u8 hostctl:1; +#endif +} MLAN_PACK_END tx_power_t; + +/* pkt_txctrl */ +typedef MLAN_PACK_START struct _pkt_txctrl { + /**Data rate in unit of 0.5Mbps */ + t_u16 data_rate; + /*Channel number to transmit the frame */ + t_u8 channel; + /** Bandwidth to transmit the frame*/ + t_u8 bw; + /** Power to be used for transmission*/ + union { + tx_power_t tp; + t_u8 val; + } tx_power; + /** Retry time of tx transmission*/ + t_u8 retry_limit; +} MLAN_PACK_END pkt_txctrl, *ppkt_txctrl; + +/** pkt_rxinfo */ +typedef MLAN_PACK_START struct _pkt_rxinfo { + /** Data rate of received paccket*/ + t_u16 data_rate; + /** Channel on which packet was received*/ + t_u8 channel; + /** Rx antenna*/ + t_u8 antenna; + /** Rx Rssi*/ + t_u8 rssi; +} MLAN_PACK_END pkt_rxinfo, *ppkt_rxinfo; + +/** mlan_buffer data structure */ +typedef struct _mlan_buffer { + /** Pointer to previous mlan_buffer */ + struct _mlan_buffer *pprev; + /** Pointer to next mlan_buffer */ + struct _mlan_buffer *pnext; + /** Status code from firmware/driver */ + t_u32 status_code; + /** Flags for this buffer */ + t_u32 flags; + /** BSS index number for multiple BSS support */ + t_u32 bss_index; + /** Buffer descriptor, e.g. skb in Linux */ + t_void *pdesc; + /** Pointer to buffer */ + t_u8 *pbuf; + /** Offset to data */ + t_u32 data_offset; + /** Data length */ + t_u32 data_len; + /** Buffer type: data, cmd, event etc. */ + mlan_buf_type buf_type; + + /** Fields below are valid for data packet only */ + /** QoS priority */ + t_u32 priority; + /** Time stamp when packet is received (seconds) */ + t_u32 in_ts_sec; + /** Time stamp when packet is received (micro seconds) */ + t_u32 in_ts_usec; + /** Time stamp when packet is processed (seconds) */ + t_u32 out_ts_sec; + /** Time stamp when packet is processed (micro seconds) */ + t_u32 out_ts_usec; + /** tx_seq_num */ + t_u32 tx_seq_num; + + /** Fields below are valid for MLAN module only */ + /** Pointer to parent mlan_buffer */ + struct _mlan_buffer *pparent; + /** Use count for this buffer */ + t_u32 use_count; + union { + pkt_txctrl tx_info; + pkt_rxinfo rx_info; + } u; +} mlan_buffer, *pmlan_buffer; + +/** Maximum data rates */ +#define MAX_DATA_RATES 14 +/** Maximum key length */ +#define MLAN_MAX_KEY_LENGTH 32 +/** Maximum data rates */ +#define MAX_DATA_RATES 14 +/** Maximum number of AC QOS queues available in the driver/firmware */ +#define MAX_AC_QUEUES 4 + +/** MLAN Maximum SSID Length */ +#define MLAN_MAX_SSID_LENGTH (32) + +/** Max Ie length */ +#define MAX_IE_SIZE 256 + +/** Max channel */ +#define MLAN_MAX_CHANNEL 165 + +#ifdef UAP_SUPPORT +/** Maximum packet forward control value */ +#define MAX_PKT_FWD_CTRL 15 +/** Maximum BEACON period */ +#define MAX_BEACON_PERIOD 4000 +/** Minimum BEACON period */ +#define MIN_BEACON_PERIOD 50 +/** Maximum DTIM period */ +#define MAX_DTIM_PERIOD 100 +/** Minimum DTIM period */ +#define MIN_DTIM_PERIOD 1 +/** Maximum TX Power Limit */ +#define MAX_TX_POWER 20 +/** Minimum TX Power Limit */ +#define MIN_TX_POWER 0 +/** MAX station count */ +#define MAX_STA_COUNT 10 +/** Maximum RTS threshold */ +#define MAX_RTS_THRESHOLD 2347 +/** Maximum fragmentation threshold */ +#define MAX_FRAG_THRESHOLD 2346 +/** Minimum fragmentation threshold */ +#define MIN_FRAG_THRESHOLD 256 +/** data rate 54 M */ +#define DATA_RATE_54M 108 +/** antenna A */ +#define ANTENNA_MODE_A 0 +/** antenna B */ +#define ANTENNA_MODE_B 1 +/** transmit antenna */ +#define TX_ANTENNA 1 +/** receive antenna */ +#define RX_ANTENNA 0 +/** Maximum stage out time */ +#define MAX_STAGE_OUT_TIME 864000 +/** Minimum stage out time */ +#define MIN_STAGE_OUT_TIME 300 +/** Maximum Retry Limit */ +#define MAX_RETRY_LIMIT 14 + +/** Maximum group key timer in seconds */ +#define MAX_GRP_TIMER 86400 +/**Default ssid for micro AP*/ +#define AP_DEFAULT_SSID "Marvell Micro AP" +/**Default pairwise key handshake retry times*/ +#define PWS_HSK_RETRIES 3 +/**Default group key handshake retry times*/ +#define GRP_HSK_RETRIES 3 +/**Default pairwise key handshake timeout*/ +#define PWS_HSK_TIMEOUT 100 //100 ms +/**Default group key handshake timeout*/ +#define GRP_HSK_TIMEOUT 100 //100 ms +/**Default Group key rekey time*/ +#define GRP_REKEY_TIME 86400 //86400 sec + +/** Maximum value of 4 byte configuration */ +#define MAX_VALID_DWORD 0x7FFFFFFF /* (1 << 31) - 1 */ + +/** Band config ACS mode */ +#define BAND_CONFIG_ACS_MODE 0x40 +/** Band config manual */ +#define BAND_CONFIG_MANUAL 0x00 + +/** Maximum data rates */ +#define MAX_DATA_RATES 14 + +/** auto data rate */ +#define DATA_RATE_AUTO 0 + +/**filter mode: disable */ +#define MAC_FILTER_MODE_DISABLE 0 +/**filter mode: block mac address */ +#define MAC_FILTER_MODE_ALLOW_MAC 1 +/**filter mode: block mac address */ +#define MAC_FILTER_MODE_BLOCK_MAC 2 +/** Maximum mac filter num */ +#define MAX_MAC_FILTER_NUM 16 + +/* Bitmap for protocol to use */ +/** No security */ +#define PROTOCOL_NO_SECURITY 0x01 +/** Static WEP */ +#define PROTOCOL_STATIC_WEP 0x02 +/** WPA */ +#define PROTOCOL_WPA 0x08 +/** WPA2 */ +#define PROTOCOL_WPA2 0x20 +/** WP2 Mixed */ +#define PROTOCOL_WPA2_MIXED 0x28 +/** EAP */ +#define PROTOCOL_EAP 0x40 +/** WAPI */ +#define PROTOCOL_WAPI 0x80 + +/** Key_mgmt_psk */ +#define KEY_MGMT_NONE 0x04 +/** Key_mgmt_none */ +#define KEY_MGMT_PSK 0x02 +/** Key_mgmt_eap */ +#define KEY_MGMT_EAP 0x01 +/** Key_mgmt_psk_sha256 */ +#define KEY_MGMT_PSK_SHA256 0x100 + +/** TKIP */ +#define CIPHER_TKIP 0x04 +/** AES CCMP */ +#define CIPHER_AES_CCMP 0x08 + +/** Valid cipher bitmap */ +#define VALID_CIPHER_BITMAP 0x0c +/** 60 seconds */ +#define MRVDRV_TIMER_60S 60000 +/** 10 seconds */ +#define MRVDRV_TIMER_10S 10000 +/** 5 seconds */ +#define MRVDRV_TIMER_5S 5000 +/** 1 second */ +#define MRVDRV_TIMER_1S 1000 +/** DMA alignment */ +#define DMA_ALIGNMENT 32 +/** max size of TxPD */ +#define MAX_TXPD_SIZE 32 + +typedef t_u8 mlan_802_11_mac_addr[MLAN_MAC_ADDR_LENGTH]; + +/** mlan_802_11_ssid data structure */ +typedef struct _mlan_802_11_ssid { + /** SSID Length */ + t_u32 ssid_len; + /** SSID information field */ + t_u8 ssid[MLAN_MAX_SSID_LENGTH]; +} mlan_802_11_ssid, *pmlan_802_11_ssid; + +/** mac_filter data structure */ +typedef struct _mac_filter { + /** mac filter mode */ + t_u16 filter_mode; + /** mac adress count */ + t_u16 mac_count; + /** mac address list */ + mlan_802_11_mac_addr mac_list[MAX_MAC_FILTER_NUM]; +} mac_filter; + +/** wpa parameter */ +typedef struct _wpa_param { + /** Pairwise cipher WPA */ + t_u8 pairwise_cipher_wpa; + /** Pairwise cipher WPA2 */ + t_u8 pairwise_cipher_wpa2; + /** group cipher */ + t_u8 group_cipher; + /** RSN replay protection */ + t_u8 rsn_protection; + /** passphrase length */ + t_u32 length; + /** passphrase */ + t_u8 passphrase[64]; + /**group key rekey time in seconds */ + t_u32 gk_rekey_time; +} wpa_param; + +/** wep key */ +typedef struct _wep_key { + /** key index 0-3 */ + t_u8 key_index; + /** is default */ + t_u8 is_default; + /** length */ + t_u16 length; + /** key data */ + t_u8 key[26]; +} wep_key; + +/** wep param */ +typedef struct _wep_param { + /** key 0 */ + wep_key key0; + /** key 1 */ + wep_key key1; + /** key 2 */ + wep_key key2; + /** key 3 */ + wep_key key3; +} wep_param; + +/** Data structure of WMM QoS information */ +typedef struct _wmm_qos_info_t { +#ifdef BIG_ENDIAN_SUPPORT + /** QoS UAPSD */ + t_u8 qos_uapsd:1; + /** Reserved */ + t_u8 reserved:3; + /** Parameter set count */ + t_u8 para_set_count:4; +#else + /** Parameter set count */ + t_u8 para_set_count:4; + /** Reserved */ + t_u8 reserved:3; + /** QoS UAPSD */ + t_u8 qos_uapsd:1; +#endif /* BIG_ENDIAN_SUPPORT */ +} wmm_qos_info_t, *pwmm_qos_info_t; + +/** Data structure of WMM ECW */ +typedef struct _wmm_ecw_t { +#ifdef BIG_ENDIAN_SUPPORT + /** Maximum Ecw */ + t_u8 ecw_max:4; + /** Minimum Ecw */ + t_u8 ecw_min:4; +#else + /** Minimum Ecw */ + t_u8 ecw_min:4; + /** Maximum Ecw */ + t_u8 ecw_max:4; +#endif /* BIG_ENDIAN_SUPPORT */ +} wmm_ecw_t, *pwmm_ecw_t; + +/** 5G band */ +#define BAND_CONFIG_5G 0x01 +/** 2.4 G band */ +#define BAND_CONFIG_2G 0x00 +/** MAX BG channel */ +#define MAX_BG_CHANNEL 14 +/** mlan_bss_param + * Note: For each entry you must enter an invalid value + * in the MOAL function woal_set_sys_config_invalid_data(). + * Otherwise for a valid data an unwanted TLV will be + * added to that command. + */ + +/**ethernet II header len*/ +#define ETHII_HEADER_LEN (14) + +/** The bit to indicate the key is for unicast */ +#define MLAN_KEY_INDEX_UNICAST 0x40000000 +/** The key index to indicate default key */ +#define MLAN_KEY_INDEX_DEFAULT 0x000000ff +/** Maximum key length */ +/* #define MLAN_MAX_KEY_LENGTH 32 */ +/** Minimum passphrase length */ +#define MLAN_MIN_PASSPHRASE_LENGTH 8 +/** Maximum passphrase length */ +#define MLAN_MAX_PASSPHRASE_LENGTH 63 +/** PMK length */ +#define MLAN_PMK_HEXSTR_LENGTH 64 +/* A few details needed for WEP (Wireless Equivalent Privacy) */ +/** 104 bits */ +#define MAX_WEP_KEY_SIZE 13 +/** 40 bits RC4 - WEP */ +#define MIN_WEP_KEY_SIZE 5 +/** packet number size */ +#define PN_SIZE 16 +/** max seq size of wpa/wpa2 key */ +#define SEQ_MAX_SIZE 8 + +/** key flag for tx_seq */ +#define KEY_FLAG_TX_SEQ_VALID 0x00000001 +/** key flag for rx_seq */ +#define KEY_FLAG_RX_SEQ_VALID 0x00000002 +/** key flag for group key */ +#define KEY_FLAG_GROUP_KEY 0x00000004 +/** key flag for tx */ +#define KEY_FLAG_SET_TX_KEY 0x00000008 +/** key flag for mcast IGTK */ +#define KEY_FLAG_AES_MCAST_IGTK 0x00000010 +/** key flag for remove key */ +#define KEY_FLAG_REMOVE_KEY 0x80000000 +/** Type definition of mlan_ds_encrypt_key for MLAN_OID_SEC_CFG_ENCRYPT_KEY */ +typedef struct _mlan_ds_encrypt_key { + /** Key disabled, all other fields will be + * ignore when this flag set to MTRUE + */ + t_u32 key_disable; + /** key removed flag, when this flag is set + * to MTRUE, only key_index will be check + */ + t_u32 key_remove; + /** Key index, used as current tx key index + * when is_current_wep_key is set to MTRUE + */ + t_u32 key_index; + /** Current Tx key flag */ + t_u32 is_current_wep_key; + /** Key length */ + t_u32 key_len; + /** Key */ + t_u8 key_material[MLAN_MAX_KEY_LENGTH]; + /** mac address */ + t_u8 mac_addr[MLAN_MAC_ADDR_LENGTH]; + /** wapi key flag */ + t_u32 is_wapi_key; + /** Initial packet number */ + t_u8 pn[PN_SIZE]; + /** key flags */ + t_u32 key_flags; +} mlan_ds_encrypt_key, *pmlan_ds_encrypt_key; + +/** mlan_deauth_param */ +typedef struct _mlan_deauth_param { + /** STA mac addr */ + t_u8 mac_addr[MLAN_MAC_ADDR_LENGTH]; + /** deauth reason */ + t_u16 reason_code; +} mlan_deauth_param; + +/** Enumeration for PSK */ +enum _mlan_psk_type { + MLAN_PSK_PASSPHRASE = 1, + MLAN_PSK_PMK, + MLAN_PSK_CLEAR, + MLAN_PSK_QUERY, +}; +/** Type definition of mlan_passphrase_t */ +typedef struct _mlan_passphrase_t { + /** Length of passphrase */ + t_u32 passphrase_len; + /** Passphrase */ + t_u8 passphrase[MLAN_MAX_PASSPHRASE_LENGTH]; +} mlan_passphrase_t; + +/** Maximum PMK R0 NAME key length */ +#define MLAN_MAX_PMKR0_NAME_LENGTH 16 + +/** Type defnition of mlan_pmk_t */ +typedef struct _mlan_pmk_t { + /** PMK */ + t_u8 pmk[MLAN_MAX_KEY_LENGTH]; + t_u8 pmk_r0[MLAN_MAX_KEY_LENGTH]; + t_u8 pmk_r0_name[MLAN_MAX_PMKR0_NAME_LENGTH]; +} mlan_pmk_t; + +/** Type definition of mlan_ds_passphrase for MLAN_OID_SEC_CFG_PASSPHRASE */ +typedef struct _mlan_ds_passphrase { + /** SSID may be used */ + mlan_802_11_ssid ssid; + /** BSSID may be used */ + mlan_802_11_mac_addr bssid; + /** Flag for passphrase or pmk used */ + t_u16 psk_type; + /** Passphrase or PMK */ + union { + /** Passphrase */ + mlan_passphrase_t passphrase; + /** PMK */ + mlan_pmk_t pmk; + } psk; +} mlan_ds_passphrase, *pmlan_ds_passphrase; + +/** mlan_ssid_bssid data structure for + * MLAN_OID_BSS_START and MLAN_OID_BSS_FIND_BSS + */ +typedef struct _mlan_ssid_bssid { + /** SSID */ + mlan_802_11_ssid ssid; + /** BSSID */ + mlan_802_11_mac_addr bssid; + /** index in BSSID list, start from 1 */ + t_u32 idx; + /** Receive signal strength in dBm */ + t_s32 rssi; + /**channel*/ + t_u16 channel; + /**mobility domain value*/ + t_u16 ft_md; + /**ft capability*/ + t_u8 ft_cap; + /**band*/ + t_u16 bss_band; + t_u32 channel_flags; +} mlan_ssid_bssid; + +/** Channel List Entry */ +typedef struct _channel_list { + /** Channel Number */ + t_u8 chan_number; + /** Band Config */ + BandConfig_t bandcfg; +} scan_chan_list; + +/** Data structure of WMM Aci/Aifsn */ +typedef struct _wmm_aci_aifsn_t { +#ifdef BIG_ENDIAN_SUPPORT + /** Reserved */ + t_u8 reserved:1; + /** Aci */ + t_u8 aci:2; + /** Acm */ + t_u8 acm:1; + /** Aifsn */ + t_u8 aifsn:4; +#else + /** Aifsn */ + t_u8 aifsn:4; + /** Acm */ + t_u8 acm:1; + /** Aci */ + t_u8 aci:2; + /** Reserved */ + t_u8 reserved:1; +#endif /* BIG_ENDIAN_SUPPORT */ +} wmm_aci_aifsn_t, *pwmm_aci_aifsn_t; + +/** Data structure of WMM AC parameters */ +typedef struct _wmm_ac_parameters_t { + wmm_aci_aifsn_t aci_aifsn; /**< AciAifSn */ + wmm_ecw_t ecw; /**< Ecw */ + t_u16 tx_op_limit; /**< Tx op limit */ +} wmm_ac_parameters_t, *pwmm_ac_parameters_t; + +/** Data structure of WMM parameter IE */ +typedef struct _wmm_parameter_t { + /** OuiType: 00:50:f2:02 */ + t_u8 ouitype[4]; + /** Oui subtype: 01 */ + t_u8 ouisubtype; + /** version: 01 */ + t_u8 version; + /** QoS information */ + t_u8 qos_info; + /** Reserved */ + t_u8 reserved; + /** AC Parameters Record WMM_AC_BE, WMM_AC_BK, WMM_AC_VI, WMM_AC_VO */ + wmm_ac_parameters_t ac_params[MAX_AC_QUEUES]; +} wmm_parameter_t, *pwmm_parameter_t; + +typedef struct _mlan_uap_bss_param { + /** AP mac addr */ + mlan_802_11_mac_addr mac_addr; + /** SSID */ + mlan_802_11_ssid ssid; + /** Broadcast ssid control */ + t_u8 bcast_ssid_ctl; + /** Radio control: on/off */ + t_u8 radio_ctl; + /** dtim period */ + t_u8 dtim_period; + /** beacon period */ + t_u16 beacon_period; + /** rates */ + t_u8 rates[MAX_DATA_RATES]; + /** Tx data rate */ + t_u16 tx_data_rate; + /** Tx beacon rate */ + t_u16 tx_beacon_rate; + /** multicast/broadcast data rate */ + t_u16 mcbc_data_rate; + /** Tx power level in dBm */ + t_u8 tx_power_level; + /** Tx antenna */ + t_u8 tx_antenna; + /** Rx antenna */ + t_u8 rx_antenna; + /** packet forward control */ + t_u8 pkt_forward_ctl; + /** max station count */ + t_u16 max_sta_count; + /** mac filter */ + mac_filter filter; + /** station ageout timer in unit of 100ms */ + t_u32 sta_ageout_timer; + /** PS station ageout timer in unit of 100ms */ + t_u32 ps_sta_ageout_timer; + /** RTS threshold */ + t_u16 rts_threshold; + /** fragmentation threshold */ + t_u16 frag_threshold; + /** retry_limit */ + t_u16 retry_limit; + /** pairwise update timeout in milliseconds */ + t_u32 pairwise_update_timeout; + /** pairwise handshake retries */ + t_u32 pwk_retries; + /** groupwise update timeout in milliseconds */ + t_u32 groupwise_update_timeout; + /** groupwise handshake retries */ + t_u32 gwk_retries; + /** preamble type */ + t_u8 preamble_type; + /** band cfg */ + BandConfig_t band_cfg; + /** channel */ + t_u8 channel; + /** auth mode */ + t_u16 auth_mode; + /** encryption protocol */ + t_u16 protocol; + /** key managment type */ + t_u16 key_mgmt; + /** wep param */ + wep_param wep_cfg; + /** wpa param */ + wpa_param wpa_cfg; + /** Mgmt IE passthru mask */ + t_u32 mgmt_ie_passthru_mask; + /* + * 11n HT Cap HTCap_t ht_cap + */ + /** HT Capabilities Info field */ + t_u16 ht_cap_info; + /** A-MPDU Parameters field */ + t_u8 ampdu_param; + /** Supported MCS Set field */ + t_u8 supported_mcs_set[16]; + /** HT Extended Capabilities field */ + t_u16 ht_ext_cap; + /** Transmit Beamforming Capabilities field */ + t_u32 tx_bf_cap; + /** Antenna Selection Capability field */ + t_u8 asel; + /** Enable 2040 Coex */ + t_u8 enable_2040coex; + /** key management operation */ + t_u16 key_mgmt_operation; + /** BSS status */ + t_u16 bss_status; +#ifdef WIFI_DIRECT_SUPPORT + /* pre shared key */ + t_u8 psk[MLAN_MAX_KEY_LENGTH]; +#endif /* WIFI_DIRECT_SUPPORT */ + /** Number of channels in scan_channel_list */ + t_u32 num_of_chan; + /** scan channel list in ACS mode */ + scan_chan_list chan_list[MLAN_MAX_CHANNEL]; + /** Wmm parameters */ + wmm_parameter_t wmm_para; + +} mlan_uap_bss_param; +#endif + +/** Enumeration for authentication mode */ +enum _mlan_auth_mode { + MLAN_AUTH_MODE_OPEN = 0x00, + MLAN_AUTH_MODE_SHARED = 0x01, + MLAN_AUTH_MODE_FT = 0x02, + MLAN_AUTH_MODE_NETWORKEAP = 0x80, + MLAN_AUTH_MODE_AUTO = 0xFF, +}; + +#ifdef UAP_SUPPORT +/** TxPD descriptor */ +typedef MLAN_PACK_START struct _UapTxPD { + /** BSS type */ + t_u8 bss_type; + /** BSS number */ + t_u8 bss_num; + /** Tx packet length */ + t_u16 tx_pkt_length; + /** Tx packet offset */ + t_u16 tx_pkt_offset; + /** Tx packet type */ + t_u16 tx_pkt_type; + /** Tx Control */ + t_u32 tx_control; + /** Pkt Priority */ + t_u8 priority; + /** Transmit Pkt Flags*/ + t_u8 flags; + /** Amount of time the packet has been queued + * in the driver (units = 2ms)*/ + t_u8 pkt_delay_2ms; + /** reserved */ + t_u8 reserved; + /** Tx Control */ + t_u32 tx_control_1; +} MLAN_PACK_END UapTxPD, *PUapTxPD; + +/** RxPD Descriptor */ +typedef MLAN_PACK_START struct _UapRxPD { + /** BSS Type */ + t_u8 bss_type; + /** BSS number*/ + t_u8 bss_num; + /** Rx packet length */ + t_u16 rx_pkt_length; + /** Rx packet offset */ + t_u16 rx_pkt_offset; + /** Rx packet type */ + t_u16 rx_pkt_type; + /** Sequence nunmber */ + t_u16 seq_num; + /** Packet Priority */ + t_u8 priority; + /** Rx Packet Rate */ + t_u8 rx_rate; + /** SNR */ + t_s8 snr; + /** Noise Floor */ + t_s8 nf; + /** [Bit 1] [Bit 0] RxRate format: legacy rate = 00 HT = 01 VHT = 10 + * [Bit 3] [Bit 2] HT/VHT Bandwidth BW20 = 00 BW40 = 01 BW80 = 10 BW160 = 11 + * [Bit 4] HT/VHT Guard interval LGI = 0 SGI = 1 + * [Bit 5] STBC support Enabled = 1 + * [Bit 6] LDPC support Enabled = 1 + * [Bit 7] Reserved */ + t_u8 rate_info; + /** Reserved */ + t_u8 reserved1[3]; + /** TDLS flags, bit 0: 0=InfraLink, 1=DirectLink */ + t_u8 flags; + /** For SD8887 ntenna info: 0 = 2.4G antenna a; 1 = 2.4G antenna b; 3 = 5G antenna; 0xff = invalid value */ + t_u8 antenna; + /* [31:0] ToA of the rx packet, [63:32] ToD of the ack for the rx packet Both ToA and ToD are in nanoseconds */ + t_u64 toa_tod_tstamps; + /** rx info */ + t_u32 rx_info; +} MLAN_PACK_END UapRxPD, *PUapRxPD; +#endif /* UAP_SUPPORT */ + +/* #################### + From mlan_main.h + #################### */ + +/** 16 bits byte swap */ +#define swap_byte_16(x) \ +((t_u16)((((t_u16)(x) & 0x00ffU) << 8) | \ + (((t_u16)(x) & 0xff00U) >> 8))) + +/** 32 bits byte swap */ +#define swap_byte_32(x) \ +((t_u32)((((t_u32)(x) & 0x000000ffUL) << 24) | \ + (((t_u32)(x) & 0x0000ff00UL) << 8) | \ + (((t_u32)(x) & 0x00ff0000UL) >> 8) | \ + (((t_u32)(x) & 0xff000000UL) >> 24))) + +/** 64 bits byte swap */ +#define swap_byte_64(x) \ +((t_u64)((t_u64)(((t_u64)(x) & 0x00000000000000ffULL) << 56) | \ + (t_u64)(((t_u64)(x) & 0x000000000000ff00ULL) << 40) | \ + (t_u64)(((t_u64)(x) & 0x0000000000ff0000ULL) << 24) | \ + (t_u64)(((t_u64)(x) & 0x00000000ff000000ULL) << 8) | \ + (t_u64)(((t_u64)(x) & 0x000000ff00000000ULL) >> 8) | \ + (t_u64)(((t_u64)(x) & 0x0000ff0000000000ULL) >> 24) | \ + (t_u64)(((t_u64)(x) & 0x00ff000000000000ULL) >> 40) | \ + (t_u64)(((t_u64)(x) & 0xff00000000000000ULL) >> 56))) + +#ifdef BIG_ENDIAN_SUPPORT +/** Convert ulong n/w to host */ +#define mlan_ntohl(x) x +/** Convert host ulong to n/w */ +#define mlan_htonl(x) x +/** Convert n/w to host */ +#define mlan_ntohs(x) x +/** Convert host to n/w */ +#define mlan_htons(x) x +/** Convert from 16 bit little endian format to CPU format */ +#define wlan_le16_to_cpu(x) swap_byte_16(x) +/** Convert from 32 bit little endian format to CPU format */ +#define wlan_le32_to_cpu(x) swap_byte_32(x) +/** Convert from 64 bit little endian format to CPU format */ +#define wlan_le64_to_cpu(x) swap_byte_64(x) +/** Convert to 16 bit little endian format from CPU format */ +#define wlan_cpu_to_le16(x) swap_byte_16(x) +/** Convert to 32 bit little endian format from CPU format */ +#define wlan_cpu_to_le32(x) swap_byte_32(x) +/** Convert to 64 bit little endian format from CPU format */ +#define wlan_cpu_to_le64(x) swap_byte_64(x) + +/** Convert TxPD to little endian format from CPU format */ +#define endian_convert_TxPD(x) \ + { \ + (x)->tx_pkt_length = wlan_cpu_to_le16((x)->tx_pkt_length); \ + (x)->tx_pkt_offset = wlan_cpu_to_le16((x)->tx_pkt_offset); \ + (x)->tx_pkt_type = wlan_cpu_to_le16((x)->tx_pkt_type); \ + (x)->tx_control = wlan_cpu_to_le32((x)->tx_control); \ + (x)->tx_control_1 = wlan_cpu_to_le32((x)->tx_control_1); \ + } +/** Convert RxPD from little endian format to CPU format */ +#define endian_convert_RxPD(x) \ + { \ + (x)->rx_pkt_length = wlan_le16_to_cpu((x)->rx_pkt_length); \ + (x)->rx_pkt_offset = wlan_le16_to_cpu((x)->rx_pkt_offset); \ + (x)->rx_pkt_type = wlan_le16_to_cpu((x)->rx_pkt_type); \ + (x)->seq_num = wlan_le16_to_cpu((x)->seq_num); \ + (x)->rx_info = wlan_le32_to_cpu((x)->rx_info); \ + } +/** Convert RxPD extra header from little endian format to CPU format */ +#define endian_convert_RxPD_extra_header(x) \ + { \ + (x)->channel_flags = wlan_le16_to_cpu((x)->channel_flags); \ + } +#else +/** Convert ulong n/w to host */ +#define mlan_ntohl(x) swap_byte_32(x) +/** Convert host ulong to n/w */ +#define mlan_htonl(x) swap_byte_32(x) +/** Convert n/w to host */ +#define mlan_ntohs(x) swap_byte_16(x) +/** Convert host to n/w */ +#define mlan_htons(x) swap_byte_16(x) +/** Do nothing */ +#define wlan_le16_to_cpu(x) x +/** Do nothing */ +#define wlan_le32_to_cpu(x) x +/** Do nothing */ +#define wlan_le64_to_cpu(x) x +/** Do nothing */ +#define wlan_cpu_to_le16(x) x +/** Do nothing */ +#define wlan_cpu_to_le32(x) x +/** Do nothing */ +#define wlan_cpu_to_le64(x) x + +/** Convert TxPD to little endian format from CPU format */ +#define endian_convert_TxPD(x) do {} while (0) +/** Convert RxPD from little endian format to CPU format */ +#define endian_convert_RxPD(x) do {} while (0) +/** Convert RxPD extra header from little endian format to CPU format */ +#define endian_convert_RxPD_extra_header(x) do {} while (0) +#endif /* BIG_ENDIAN_SUPPORT */ + +/** Find minimum */ +#ifndef MIN +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#endif + +/** Find maximum */ +#ifndef MAX +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#endif + +#ifdef memset +#undef memset +#endif +/** Memset routine */ +#define memset(mpl_utils, s, c, len) \ + (mpl_utils->moal_memset(mpl_utils->pmoal_handle, s, c, len)) + +#ifdef memmove +#undef memmove +#endif +/** Memmove routine */ +#define memmove(mpl_utils, dest, src, len) \ + (mpl_utils->moal_memmove(mpl_utils->pmoal_handle, dest, src, len)) + +#ifdef memcpy +#undef memcpy +#endif +/** Memcpy routine */ +#define memcpy(mpl_utils, to, from, len) \ + (mpl_utils->moal_memcpy(mpl_utils->pmoal_handle, to, from, len)) + +#ifdef memcmp +#undef memcmp +#endif +/** Memcmp routine */ +#define memcmp(mpl_utils, s1, s2, len) \ + (mpl_utils->moal_memcmp(mpl_utils->pmoal_handle, s1, s2, len)) + +/** Find number of elements */ +#ifndef NELEMENTS +#define NELEMENTS(x) (sizeof(x)/sizeof(x[0])) +#endif + +#define MOAL_ALLOC_MLAN_BUFFER (0) +#define MOAL_MALLOC_BUFFER (1) + +/* ################## + From mlan_fw.h + ################## */ +/** TxPD descriptor */ +typedef MLAN_PACK_START struct _TxPD { + /** BSS type */ + t_u8 bss_type; + /** BSS number */ + t_u8 bss_num; + /** Tx packet length */ + t_u16 tx_pkt_length; + /** Tx packet offset */ + t_u16 tx_pkt_offset; + /** Tx packet type */ + t_u16 tx_pkt_type; + /** Tx Control */ + t_u32 tx_control; + /** Pkt Priority */ + t_u8 priority; + /** Transmit Pkt Flags*/ + t_u8 flags; + /** Amount of time the packet has been queued + * in the driver (units = 2ms)*/ + t_u8 pkt_delay_2ms; + /** reserved */ + t_u8 reserved; + /** Tx Control */ + t_u32 tx_control_1; +} MLAN_PACK_END TxPD, *PTxPD; + +/** 2K buf size */ +#define MLAN_TX_DATA_BUF_SIZE_2K 2048 + +/* #################### + From mlan_decl.h + #################### */ + +/** IN parameter */ +#define IN +/** OUT parameter */ +#define OUT + +/** BIT value */ +#define MBIT(x) (((t_u32)1) << (x)) + +#ifdef DEBUG_LEVEL1 +/** Debug level bit definition */ +#define MMSG MBIT(0) +#define MFATAL MBIT(1) +#define MERROR MBIT(2) +#define MDATA MBIT(3) +#define MCMND MBIT(4) +#define MEVENT MBIT(5) +#define MINTR MBIT(6) +#define MIOCTL MBIT(7) + +#define MMPA_D MBIT(15) +#define MDAT_D MBIT(16) +#define MCMD_D MBIT(17) +#define MEVT_D MBIT(18) +#define MFW_D MBIT(19) +#define MIF_D MBIT(20) + +#define MENTRY MBIT(28) +#define MWARN MBIT(29) +#define MINFO MBIT(30) +#define MHEX_DUMP MBIT(31) +#endif /* DEBUG_LEVEL1 */ + +/** Wait until a condition becomes true */ +#define MASSERT(cond) \ +do { \ + if (!(cond)) { \ + PRINTM(MFATAL, "ASSERT: %s: %i\n", __FUNCTION__, __LINE__); \ + } \ +} while (0) + +/** Log entry point for debugging */ +#define ENTER() PRINTM(MENTRY, "Enter: %s\n", __FUNCTION__) +/** Log exit point for debugging */ +#define LEAVE() PRINTM(MENTRY, "Leave: %s\n", __FUNCTION__) + +/* #################### + From mlan_main.h + #################### */ + +#ifdef DEBUG_LEVEL1 +extern t_void (*print_callback) (IN t_void *pmoal_handle, + IN t_u32 level, IN char *pformat, IN ... + ); + +extern mlan_status (*get_sys_time_callback) (IN t_void *pmoal_handle, + OUT t_u32 *psec, OUT t_u32 *pusec); + +extern t_u32 mlan_drvdbg; + +#ifdef DEBUG_LEVEL2 +#define PRINTM_MINFO(msg...) do {if ((mlan_drvdbg & MINFO) && (print_callback)) \ + print_callback(MNULL, MINFO, msg); } while (0) +#define PRINTM_MWARN(msg...) do {if ((mlan_drvdbg & MWARN) && (print_callback)) \ + print_callback(MNULL, MWARN, msg); } while (0) +#define PRINTM_MENTRY(msg...) do {if ((mlan_drvdbg & MENTRY) && (print_callback)) \ + print_callback(MNULL, MENTRY, msg); } while (0) +#define PRINTM_GET_SYS_TIME(level, psec, pusec) \ +do { \ + if ((level & mlan_drvdbg) && (get_sys_time_callback)) \ + get_sys_time_callback(MNULL, psec, pusec); \ +} while (0) + +/** Hexdump for level-2 debugging */ +#define HEXDUMP(x, y, z) \ +do { \ + if ((mlan_drvdbg & (MHEX_DUMP | MINFO)) && (print_callback)) \ + print_callback(MNULL, MHEX_DUMP | MINFO, x, y, z); \ +} while (0) + +#else + +#define PRINTM_MINFO(msg...) do {} while (0) +#define PRINTM_MWARN(msg...) do {} while (0) +#define PRINTM_MENTRY(msg...) do {} while (0) + +#define PRINTM_GET_SYS_TIME(level, psec, pusec) \ +do { \ + if ((level & mlan_drvdbg) && (get_sys_time_callback) \ + && (level != MINFO) && (level != MWARN)) \ + get_sys_time_callback(MNULL, psec, pusec); \ +} while (0) + +/** Hexdump for debugging */ +#define HEXDUMP(x, y, z) do {} while (0) + +#endif /* DEBUG_LEVEL2 */ + +#define PRINTM_MFW_D(msg...) do {if ((mlan_drvdbg & MFW_D) && (print_callback)) \ + print_callback(MNULL, MFW_D, msg); } while (0) +#define PRINTM_MCMD_D(msg...) do {if ((mlan_drvdbg & MCMD_D) && (print_callback)) \ + print_callback(MNULL, MCMD_D, msg); } while (0) +#define PRINTM_MDAT_D(msg...) do {if ((mlan_drvdbg & MDAT_D) && (print_callback)) \ + print_callback(MNULL, MDAT_D, msg); } while (0) +#define PRINTM_MIF_D(msg...) do {if ((mlan_drvdbg & MIF_D) && (print_callback)) \ + print_callback(MNULL, MIF_D, msg); } while (0) + +#define PRINTM_MIOCTL(msg...) do {if ((mlan_drvdbg & MIOCTL) && (print_callback)) \ + print_callback(MNULL, MIOCTL, msg); } while (0) +#define PRINTM_MINTR(msg...) do {if ((mlan_drvdbg & MINTR) && (print_callback)) \ + print_callback(MNULL, MINTR, msg); } while (0) +#define PRINTM_MEVENT(msg...) do {if ((mlan_drvdbg & MEVENT) && (print_callback)) \ + print_callback(MNULL, MEVENT, msg); } while (0) +#define PRINTM_MCMND(msg...) do {if ((mlan_drvdbg & MCMND) && (print_callback)) \ + print_callback(MNULL, MCMND, msg); } while (0) +#define PRINTM_MDATA(msg...) do {if ((mlan_drvdbg & MDATA) && (print_callback)) \ + print_callback(MNULL, MDATA, msg); } while (0) +#define PRINTM_MERROR(msg...) do {if ((mlan_drvdbg & MERROR) && (print_callback)) \ + print_callback(MNULL, MERROR, msg); } while (0) +#define PRINTM_MFATAL(msg...) do {if ((mlan_drvdbg & MFATAL) && (print_callback)) \ + print_callback(MNULL, MFATAL, msg); } while (0) +#define PRINTM_MMSG(msg...) do {if ((mlan_drvdbg & MMSG) && (print_callback)) \ + print_callback(MNULL, MMSG, msg); } while (0) + +#define PRINTM(level, msg...) PRINTM_##level((char *)msg) + +/** Log debug message */ +#ifdef __GNUC__ +#define PRINTM_NETINTF(level, pmu, pml) \ +do { \ + if ((mlan_drvdbg & level) && pmu && pml \ + && pmu->moal_print_netintf) \ + pmu->moal_print_netintf( \ + pmu->pmoal_handle, \ + pml->bss_index, level); \ +} while (0) +#endif /* __GNUC__ */ + +/** Max hex dump data length */ +#define MAX_DATA_DUMP_LEN 64 + +/** Debug hexdump for level-1 debugging */ +#define DBG_HEXDUMP(level, x, y, z) \ +do { \ + if ((mlan_drvdbg & level) && print_callback) \ + print_callback(MNULL, MHEX_DUMP | level, x, y, z); \ +} while (0) + +#else /* DEBUG_LEVEL1 */ + +#define PRINTM(level, msg...) do {} while (0) + +#define PRINTM_NETINTF(level, pmpriv) do {} while (0) + +/** Debug hexdump for level-1 debugging */ +#define DBG_HEXDUMP(level, x, y, z) do {} while (0) + +/** Hexdump for debugging */ +#define HEXDUMP(x, y, z) do {} while (0) + +#define PRINTM_GET_SYS_TIME(level, psec, pusec) do { } while (0) + +#endif /* DEBUG_LEVEL1 */ + +/* ####################################################### + embedded authenticator and supplicant specific + ################ ########################################*/ + +/** Get_system_time routine */ +#define get_system_time(mpl_utils, psec, pusec) \ + (mpl_utils->moal_get_system_time(mpl_utils->pmoal_handle, psec, pusec)) + +/** malloc routine */ +#ifdef malloc +#undef malloc +#endif +#define malloc(mpl_utils, len, pptr) \ + (mpl_utils->moal_malloc(mpl_utils->pmoal_handle, len, MLAN_MEM_DEF, pptr)) + +/** free routine */ +#ifdef free +#undef free +#endif +#define free(mpl_utils, ptr) \ + (mpl_utils->moal_mfree(mpl_utils->pmoal_handle, ptr)) + +#endif /* _HOSTSA_EXT_DEF_H_ */ diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/hostsa_init.c b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/hostsa_init.c new file mode 100644 index 000000000000..4c7ff0eec646 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/hostsa_init.c @@ -0,0 +1,649 @@ +/** @file hostsa_init.c + * + * @brief This file defines the initialize /free APIs for authenticator and supplicant. + * + * Copyright (C) 2014-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 03/07/2014: Initial version +******************************************************/ +#include "mlan.h" +#ifdef STA_SUPPORT +#include "mlan_join.h" +#endif +#include "mlan_ioctl.h" +#include "mlan_util.h" +#include "mlan_fw.h" +#include "mlan_main.h" +#include "mlan_init.h" +#include "mlan_wmm.h" +#include "hostsa_def.h" + +#include "authenticator_api.h" + +/********************* + Local Variables + *********************/ + +/********************* + Global Variables + *********************/ + +/********************* + Local Functions + *********************/ + +/********************* + Global Functions + *********************/ + +/********************* + Utility Handler + *********************/ + +/** + * @brief alloc mlan buffer + * + * @param pmlan_adapter A void pointer + * @param data_len request buffer len + * @param head_room head room len + * @param malloc_flag flag for mlan buffer + + * @return pointer to mlan buffer + */ +pmlan_buffer +hostsa_alloc_mlan_buffer(t_void *pmlan_adapter, + t_u32 data_len, t_u32 head_room, t_u32 malloc_flag) +{ + mlan_adapter *pmadapter = (mlan_adapter *)pmlan_adapter; + pmlan_buffer newbuf = MNULL; + + newbuf = wlan_alloc_mlan_buffer(pmadapter, data_len, head_room, + malloc_flag); + + return newbuf; +} + +/** + * @brief free mlan buffer + * + * @param pmlan_adapter A void pointer + * @param pmbuf a pointer to mlan buffer + * + * @return + */ +void +hostsa_free_mlan_buffer(t_void *pmlan_adapter, mlan_buffer *pmbuf) +{ + mlan_adapter *pmadapter = (mlan_adapter *)pmlan_adapter; + + ENTER(); + + wlan_free_mlan_buffer(pmadapter, pmbuf); + + LEAVE(); +} + +/** + * @brief send packet + * + * @param pmlan_private A void pointer + * @param pmbuf a pointer to mlan buffer + * @param frameLen paket len + * + * @return + */ +void +hostsa_tx_packet(t_void *pmlan_private, pmlan_buffer pmbuf, t_u16 frameLen) +{ + mlan_private *pmpriv = (mlan_private *)pmlan_private; + mlan_adapter *pmadapter = pmpriv->adapter; + + ENTER(); + + pmbuf->bss_index = pmpriv->bss_index; + pmbuf->data_len = frameLen; + + wlan_add_buf_bypass_txqueue(pmadapter, pmbuf); + wlan_recv_event(pmpriv, MLAN_EVENT_ID_DRV_DEFER_HANDLING, MNULL); + + LEAVE(); +} + +/** + * @brief set key to fw + * + * @param pmlan_private A void pointer + * @param encrypt_key key structure + * + * @return + */ +void +wlan_set_encrypt_key(t_void *pmlan_private, mlan_ds_encrypt_key *encrypt_key) +{ + mlan_private *priv = (mlan_private *)pmlan_private; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (!encrypt_key->key_len) { + PRINTM(MCMND, "Skip set key with key_len = 0\n"); + LEAVE(); + return; + } + + ret = wlan_prepare_cmd(priv, + HostCmd_CMD_802_11_KEY_MATERIAL, + HostCmd_ACT_GEN_SET, + KEY_INFO_ENABLED, MNULL, encrypt_key); + + if (ret == MLAN_STATUS_SUCCESS) + wlan_recv_event(priv, MLAN_EVENT_ID_DRV_DEFER_HANDLING, MNULL); + + LEAVE(); + +} + +/** + * @brief clear key to fw + * + * @param pmlan_private A void pointer + * + * @return + */ +void +wlan_clr_encrypt_key(t_void *pmlan_private) +{ + mlan_private *priv = (mlan_private *)pmlan_private; + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_encrypt_key encrypt_key; + + ENTER(); + + memset(priv->adapter, &encrypt_key, 0, sizeof(mlan_ds_encrypt_key)); + encrypt_key.key_disable = MTRUE; + encrypt_key.key_flags = KEY_FLAG_REMOVE_KEY; + + ret = wlan_prepare_cmd(priv, + HostCmd_CMD_802_11_KEY_MATERIAL, + HostCmd_ACT_GEN_SET, + KEY_INFO_ENABLED, MNULL, &encrypt_key); + + if (ret == MLAN_STATUS_SUCCESS) + wlan_recv_event(priv, MLAN_EVENT_ID_DRV_DEFER_HANDLING, MNULL); + + LEAVE(); + +} + +/** + * @brief send deauth frame + * + * @param pmlan_private A void pointer + * @param addr destination mac address + * @param reason deauth reason + * + * @return + */ +void +hostsa_SendDeauth(t_void *pmlan_private, t_u8 *addr, t_u16 reason) +{ + mlan_private *pmpriv = (mlan_private *)pmlan_private; + mlan_adapter *pmadapter = pmpriv->adapter; + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_deauth_param deauth; + + ENTER(); + + memcpy(pmadapter, deauth.mac_addr, addr, MLAN_MAC_ADDR_LENGTH); + deauth.reason_code = reason; + + ret = wlan_prepare_cmd(pmpriv, + HOST_CMD_APCMD_STA_DEAUTH, + HostCmd_ACT_GEN_SET, + 0, MNULL, (t_void *)&deauth); + if (ret == MLAN_STATUS_SUCCESS) + wlan_recv_event(pmpriv, MLAN_EVENT_ID_DRV_DEFER_HANDLING, + MNULL); + + LEAVE(); +} + +/** + * @brief deauth all connected stations + * + * @param pmlan_private A void pointer + * @param reason deauth reason + * + * @return + */ +void +ApDisAssocAllSta(void *pmlan_private, t_u16 reason) +{ + mlan_private *pmpriv = (mlan_private *)pmlan_private; + mlan_adapter *pmadapter = pmpriv->adapter; + sta_node *sta_ptr; + + ENTER(); + + sta_ptr = (sta_node *)util_peek_list(pmadapter->pmoal_handle, + &pmpriv->sta_list, + pmadapter->callbacks. + moal_spin_lock, + pmadapter->callbacks. + moal_spin_unlock); + + if (!sta_ptr) { + LEAVE(); + return; + } + + while (sta_ptr != (sta_node *)&pmpriv->sta_list) { + hostsa_SendDeauth((t_void *)pmpriv, sta_ptr->mac_addr, reason); + sta_ptr = sta_ptr->pnext; + } + + LEAVE(); +} + +/** + * @brief get station entry + * + * @param pmlan_private A void pointer + * @param mac pointer to station mac address + * @param ppconPtr pointer to pointer to connection + * + * @return + */ +void +Hostsa_get_station_entry(t_void *pmlan_private, t_u8 *mac, t_void **ppconPtr) +{ + mlan_private *priv = (mlan_private *)pmlan_private; + sta_node *sta_ptr = MNULL; + + ENTER(); + + sta_ptr = wlan_get_station_entry(priv, mac); + if (sta_ptr) + *ppconPtr = sta_ptr->cm_connectioninfo; + else + *ppconPtr = MNULL; + + LEAVE(); +} + +/** + * @brief find a connection + * + * @param pmlan_private A void pointer + * @param ppconPtr a pointer to pointer to connection + * @param ppsta_node a pointer to pointer to sta node + * + * @return + */ +void +Hostsa_find_connection(t_void *pmlan_private, t_void **ppconPtr, + t_void **ppsta_node) +{ + mlan_private *priv = (mlan_private *)pmlan_private; + sta_node *sta_ptr = MNULL; + + ENTER(); + + sta_ptr = (sta_node *)util_peek_list(priv->adapter->pmoal_handle, + &priv->sta_list, + priv->adapter->callbacks. + moal_spin_lock, + priv->adapter->callbacks. + moal_spin_unlock); + + if (!sta_ptr) { + LEAVE(); + return; + } + + *ppsta_node = (t_void *)sta_ptr; + *ppconPtr = sta_ptr->cm_connectioninfo; + + LEAVE(); +} + +/** + * @brief find next connection + * + * @param pmlan_private A void pointer + * @param ppconPtr a pointer to pointer to connection + * @param ppsta_node a pointer to pointer to sta node + * + * @return + */ +void +Hostsa_find_next_connection(t_void *pmlan_private, t_void **ppconPtr, + t_void **ppsta_node) +{ + mlan_private *priv = (mlan_private *)pmlan_private; + sta_node *sta_ptr = (sta_node *)*ppsta_node; + + ENTER(); + if (sta_ptr != (sta_node *)&priv->sta_list) + sta_ptr = sta_ptr->pnext; + + *ppsta_node = MNULL; + *ppconPtr = MNULL; + if ((sta_ptr != MNULL) && (sta_ptr != (sta_node *)&priv->sta_list)) { + *ppsta_node = (t_void *)sta_ptr; + *ppconPtr = sta_ptr->cm_connectioninfo; + } + LEAVE(); +} + +/** + * @brief set management ie for beacon or probe response + * + * @param pmlan_private A void pointer + * @param pbuf ie buf + * @param len ie len + * @param clearIE clear ie + * + * @return + */ +void +Hostsa_set_mgmt_ie(t_void *pmlan_private, t_u8 *pbuf, t_u16 len, t_u8 clearIE) +{ + mlan_private *priv = (mlan_private *)pmlan_private; + mlan_adapter *pmadapter = priv->adapter; + mlan_ioctl_req *pioctl_req = MNULL; + mlan_ds_misc_cfg *pds_misc_cfg = MNULL; + custom_ie *pmgmt_ie = MNULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (pmadapter == MNULL) { + LEAVE(); + return; + } + + /* allocate buffer for mlan_ioctl_req and mlan_ds_misc_cfg */ + /* FYI - will be freed as part of cmd_response handler */ + ret = pmadapter->callbacks.moal_malloc(pmadapter->pmoal_handle, + sizeof(mlan_ioctl_req) + + sizeof(mlan_ds_misc_cfg), + MLAN_MEM_DEF, + (t_u8 **)&pioctl_req); + if ((ret != MLAN_STATUS_SUCCESS) || !pioctl_req) { + PRINTM(MERROR, "%s(): Could not allocate ioctl req\n", + __func__); + LEAVE(); + return; + } + pds_misc_cfg = (mlan_ds_misc_cfg *)((t_u8 *)pioctl_req + + sizeof(mlan_ioctl_req)); + + /* prepare mlan_ioctl_req */ + memset(pmadapter, pioctl_req, 0x00, sizeof(mlan_ioctl_req)); + pioctl_req->req_id = MLAN_IOCTL_MISC_CFG; + pioctl_req->action = MLAN_ACT_SET; + pioctl_req->bss_index = priv->bss_index; + pioctl_req->pbuf = (t_u8 *)pds_misc_cfg; + pioctl_req->buf_len = sizeof(mlan_ds_misc_cfg); + + /* prepare mlan_ds_misc_cfg */ + memset(pmadapter, pds_misc_cfg, 0x00, sizeof(mlan_ds_misc_cfg)); + pds_misc_cfg->sub_command = MLAN_OID_MISC_CUSTOM_IE; + pds_misc_cfg->param.cust_ie.type = TLV_TYPE_MGMT_IE; + pds_misc_cfg->param.cust_ie.len = (sizeof(custom_ie) - MAX_IE_SIZE); + + /* configure custom_ie api settings */ + pmgmt_ie = (custom_ie *)&pds_misc_cfg->param.cust_ie.ie_data_list[0]; + pmgmt_ie->ie_index = 0xffff; /* Auto index */ + pmgmt_ie->ie_length = len; + pmgmt_ie->mgmt_subtype_mask = MBIT(8) | MBIT(5); /* add IE for BEACON | PROBE_RSP */ + if (clearIE) + pmgmt_ie->mgmt_subtype_mask = 0; + memcpy(pmadapter, pmgmt_ie->ie_buffer, pbuf, len); + + pds_misc_cfg->param.cust_ie.len += pmgmt_ie->ie_length; + + DBG_HEXDUMP(MCMD_D, "authenticator: RSN or WPA IE", + (t_u8 *)pmgmt_ie, pds_misc_cfg->param.cust_ie.len); + + ret = wlan_misc_ioctl_custom_ie_list(pmadapter, pioctl_req, MFALSE); + + if (ret != MLAN_STATUS_SUCCESS && ret != MLAN_STATUS_PENDING) { + PRINTM(MERROR, + "%s(): Could not set IE for priv=%p [priv_bss_idx=%d]!\n", + __func__, priv, priv->bss_index); + /* TODO: how to handle this error case?? ignore & continue? */ + } + /* free ioctl buffer memory before we leave */ + pmadapter->callbacks.moal_mfree(pmadapter->pmoal_handle, + (t_u8 *)pioctl_req); + +} + +t_void +StaControlledPortOpen(t_void *pmlan_private) +{ + mlan_private *priv = (mlan_private *)pmlan_private; + + PRINTM(MERROR, "StaControlledPortOpen\n"); + if (priv->port_ctrl_mode == MTRUE) { + PRINTM(MINFO, "PORT_REL: port_status = OPEN\n"); + priv->port_open = MTRUE; + } + priv->adapter->scan_block = MFALSE; + wlan_recv_event(priv, MLAN_EVENT_ID_FW_PORT_RELEASE, MNULL); +} + +void +hostsa_StaSendDeauth(t_void *pmlan_private, t_u8 *addr, t_u16 reason) +{ + mlan_private *pmpriv = (mlan_private *)pmlan_private; + mlan_adapter *pmadapter = pmpriv->adapter; + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_deauth_param deauth; + + ENTER(); + + memcpy(pmadapter, deauth.mac_addr, addr, MLAN_MAC_ADDR_LENGTH); + deauth.reason_code = reason; + + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_802_11_DEAUTHENTICATE, + HostCmd_ACT_GEN_SET, + 0, MNULL, (t_void *)&deauth); + if (ret == MLAN_STATUS_SUCCESS) + wlan_recv_event(pmpriv, MLAN_EVENT_ID_DRV_DEFER_HANDLING, + MNULL); + + LEAVE(); +} + +t_u8 +Hostsa_get_bss_role(t_void *pmlan_private) +{ + mlan_private *pmpriv = (mlan_private *)pmlan_private; + + return GET_BSS_ROLE(pmpriv); +} + +t_u8 +Hostsa_get_intf_hr_len(t_void *pmlan_private) +{ + mlan_private *pmpriv = (mlan_private *)pmlan_private; + + return pmpriv->intf_hr_len; +} + +/** + * @brief send event to moal to notice that 4 way handshake complete + * + * @param pmlan_private A void pointer + * @param addr pointer to station mac address + * + * @return + */ +t_void +Hostsa_sendEventRsnConnect(t_void *pmlan_private, t_u8 *addr) +{ + mlan_private *pmpriv = (mlan_private *)pmlan_private; + mlan_adapter *pmadapter = pmpriv->adapter; + t_u8 *event_buf = MNULL, *pos = MNULL; + t_u32 event_cause = 0; + mlan_event *pevent = MNULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + ret = pmadapter->callbacks.moal_malloc(pmadapter->pmoal_handle, + MAX_EVENT_SIZE, MLAN_MEM_DEF, + &event_buf); + if ((ret != MLAN_STATUS_SUCCESS) || !event_buf) { + PRINTM(MERROR, "Could not allocate buffer for event buf\n"); + goto done; + } + pevent = (pmlan_event)event_buf; + memset(pmadapter, event_buf, 0, MAX_EVENT_SIZE); + + pevent->event_id = MLAN_EVENT_ID_DRV_PASSTHRU; + pevent->bss_index = pmpriv->bss_index; + event_cause = wlan_cpu_to_le32(0x51); /*MICRO_AP_EV_ID_RSN_CONNECT */ + memcpy(pmadapter, (t_u8 *)pevent->event_buf, + (t_u8 *)&event_cause, sizeof(event_cause)); + pos = pevent->event_buf + sizeof(event_cause) + 2; /*reserved 2 byte */ + memcpy(pmadapter, (t_u8 *)pos, addr, MLAN_MAC_ADDR_LENGTH); + + pevent->event_len = MLAN_MAC_ADDR_LENGTH + sizeof(event_cause) + 2; + wlan_recv_event(pmpriv, MLAN_EVENT_ID_DRV_PASSTHRU, event_buf); + +done: + if (event_buf) + pmadapter->callbacks.moal_mfree(pmadapter->pmoal_handle, + event_buf); + + LEAVE(); +} + +/** + * @brief This function initializes callbacks that hostsa interface uses. + * + * @param pmpriv A pointer to mlan_private structure + * @param putil_fns A pointer to hostsa_util_fns structure + * @param pmlan_fns A pointer to hostsa_mlan_fns structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static t_void +hostsa_mlan_callbacks(IN pmlan_private pmpriv, + IN hostsa_util_fns *putil_fns, + IN hostsa_mlan_fns *pmlan_fns) +{ + pmlan_adapter pmadapter = pmpriv->adapter; + pmlan_callbacks pcb = &pmadapter->callbacks; + + putil_fns->pmoal_handle = pmadapter->pmoal_handle; + putil_fns->moal_malloc = pcb->moal_malloc; + putil_fns->moal_mfree = pcb->moal_mfree; + putil_fns->moal_memset = pcb->moal_memset; + putil_fns->moal_memcpy = pcb->moal_memcpy; + putil_fns->moal_memmove = pcb->moal_memmove; + putil_fns->moal_memcmp = pcb->moal_memcmp; + putil_fns->moal_udelay = pcb->moal_udelay; + putil_fns->moal_get_system_time = pcb->moal_get_system_time; + putil_fns->moal_init_timer = pcb->moal_init_timer; + putil_fns->moal_free_timer = pcb->moal_free_timer; + putil_fns->moal_start_timer = pcb->moal_start_timer; + putil_fns->moal_stop_timer = pcb->moal_stop_timer; + putil_fns->moal_init_lock = pcb->moal_init_lock; + putil_fns->moal_free_lock = pcb->moal_free_lock; + putil_fns->moal_spin_lock = pcb->moal_spin_lock; + putil_fns->moal_spin_unlock = pcb->moal_spin_unlock; + putil_fns->moal_print = pcb->moal_print; + putil_fns->moal_print_netintf = pcb->moal_print_netintf; + + pmlan_fns->pmlan_private = pmpriv; + pmlan_fns->pmlan_adapter = pmpriv->adapter; + pmlan_fns->bss_index = pmpriv->bss_index; + pmlan_fns->bss_type = pmpriv->bss_type; +#if 0 + pmlan_fns->mlan_add_buf_txqueue = mlan_add_buf_txqueue; + pmlan_fns->mlan_select_wmm_queue = mlan_select_wmm_queue; + pmlan_fns->mlan_write_data_complete = mlan_write_data_complete; +#endif + pmlan_fns->hostsa_alloc_mlan_buffer = hostsa_alloc_mlan_buffer; + pmlan_fns->hostsa_tx_packet = hostsa_tx_packet; + pmlan_fns->hostsa_set_encrypt_key = wlan_set_encrypt_key; + pmlan_fns->hostsa_clr_encrypt_key = wlan_clr_encrypt_key; + pmlan_fns->hostsa_SendDeauth = hostsa_SendDeauth; + pmlan_fns->Hostsa_DisAssocAllSta = ApDisAssocAllSta; + pmlan_fns->hostsa_free_mlan_buffer = hostsa_free_mlan_buffer; + pmlan_fns->Hostsa_get_station_entry = Hostsa_get_station_entry; + pmlan_fns->Hostsa_set_mgmt_ie = Hostsa_set_mgmt_ie; + pmlan_fns->Hostsa_find_connection = Hostsa_find_connection; + pmlan_fns->Hostsa_find_next_connection = Hostsa_find_next_connection; + pmlan_fns->Hostsa_StaControlledPortOpen = StaControlledPortOpen; + pmlan_fns->hostsa_StaSendDeauth = hostsa_StaSendDeauth; + pmlan_fns->Hostsa_get_bss_role = Hostsa_get_bss_role; + pmlan_fns->Hostsa_get_intf_hr_len = Hostsa_get_intf_hr_len; + pmlan_fns->Hostsa_sendEventRsnConnect = Hostsa_sendEventRsnConnect; +}; + +/** + * @brief Init hostsa data + * + * @param pmpriv A pointer to mlan_private structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +hostsa_init(pmlan_private pmpriv) +{ + hostsa_util_fns util_fns; + hostsa_mlan_fns mlan_fns; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + hostsa_mlan_callbacks(pmpriv, &util_fns, &mlan_fns); + ret = supplicant_authenticator_init(&pmpriv->psapriv, &util_fns, + &mlan_fns, pmpriv->curr_addr); + + LEAVE(); + return ret; +} + +/** + * @brief Cleanup hostsa data + * + * @param pmpriv A pointer to mlan_private structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +hostsa_cleanup(pmlan_private pmpriv) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + supplicant_authenticator_free(pmpriv->psapriv); + + LEAVE(); + return ret; +} diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/hostsa_init.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/hostsa_init.h new file mode 100644 index 000000000000..9305f433e6a8 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/hostsa_init.h @@ -0,0 +1,42 @@ +/** @file hostsa_init.h + * + * @brief This file contains definitions the APIs. + * + * Copyright (C) 2014-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 03/07/2014: Initial version +******************************************************/ + +#ifndef _HOSTSA_INIT_H_ +#define _HOSTSA_INIT_H_ + +#ifndef MACSTR +/** MAC address security format */ +#define MACSTR "%02x:XX:XX:XX:%02x:%02x" +#endif + +#ifndef MAC2STR +/** MAC address security print arguments */ +#define MAC2STR(a) (a)[0], (a)[4], (a)[5] +#endif + +extern mlan_status hostsa_init(pmlan_private pmpriv); +extern mlan_status hostsa_cleanup(pmlan_private pmpriv); + +#endif /* _MPL_MAIN_H_ */ diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/keyApiStaTypes.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/keyApiStaTypes.h new file mode 100644 index 000000000000..535d7b58105d --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/keyApiStaTypes.h @@ -0,0 +1,165 @@ +/** @file KeyApiStaTypes.h + * + * @brief This file defines some of the macros and types + * Please do not change those macros and types. + * + * Copyright (C) 2014-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 03/07/2014: Initial version +******************************************************/ +#ifndef KEY_API_STA_TYPES_H__ +#define KEY_API_STA_TYPES_H__ + +#include "KeyApiStaDefs.h" +#include "wl_mib.h" +#include "keyCommonDef.h" + +/******************************** Pool ID 11 ********************************/ +#define POOL_ID11_BLOCK_POOL_CNT 1 +#define POOL_ID11_BUF0_SZ CIPHER_KEYS_BUF_SIZE +#define POOL_ID11_BUF0_CNT CIPHER_KEYS_BUF_NUM +#define POOL_ID11_BUF0_REAL_SZ (POOL_ID11_BUF0_SZ) +#define POOL_ID11_BUF0_TOT_SZ (POOL_ID11_BUF0_REAL_SZ * POOL_ID11_BUF0_CNT) +#define POOL_ID11_SZ (POOL_ID11_BUF0_TOT_SZ) +#define POOL_ID11_OVERHEAD ((POOL_ID11_BUF0_CNT) * sizeof(uint32)) + +#define KEY_STATE_ENABLE 0x01 +#define KEY_STATE_FORCE_EAPOL_UNENCRYPTED 0x02 + +#define KEY_ACTION_ADD 0x01 +#define KEY_ACTION_DELETE 0x02 +#define KEY_ACTION_FLUSHALL 0xFF + +#define KEY_DIRECTION_UNSPEC (0x0) +#define KEY_DIRECTION_RX (0x1) +#define KEY_DIRECTION_TX (0x2) +#define KEY_DIRECTION_RXTX (KEY_DIRECTION_RX \ + | KEY_DIRECTION_TX) + +#define KEY_INFO_PWK (0x1) +#define KEY_INFO_GWK (0x2) +#define KEY_INFO_IGWK (0x40) +#define KEY_INFO_KEY_MAPPING_KEY (0x4) +#define KEY_INFO_WEP_SUBTYPE (0x8) +#define KEY_INFO_GLOBAL (0x10) + +/* + we use BIT6 and BIT7 of + keyInfo field of cipher_key_hdr_t to pass keyID. +*/ + +#define CIPHERKEY_KEY_ID_MASK (BIT6 | BIT7) +#define CIPHERKEY_KEY_ID_OFFSET 6 + +#define KEY_INFO_DEFAULT_KEY (~KEY_INFO_KEY_MAPPING_KEY) + +#define VERSION_0 0 +#define VERSION_2 2 + +#define IS_KEY_INFO_WEP_SUBTYPE_104BIT(x) \ + ( (x) ? \ + ((x)->hdr.keyInfo & KEY_INFO_WEP_SUBTYPE) : 0 ) + +#define IS_KEY_INFO_WEP_SUBTYPE_40BIT(x) \ + ( (x) ? \ + (!((x)->hdr.keyInfo & KEY_INFO_WEP_SUBTYPE)) : 0 ) + +#define SET_KEY_INFO_WEP_SUBTYPE_104BIT(x) \ + ( (x) ? ((x)->hdr.keyInfo |= KEY_INFO_WEP_SUBTYPE) : 0 ) + +#define SET_KEY_INFO_WEP_SUBTYPE_40BIT(x) \ + ( (x) ? ((x)->hdr.keyInfo &= ~KEY_INFO_WEP_SUBTYPE) : 0 ) + +#define SET_KEY_INFO_PWKGWK(x) \ + ( (x) ? \ + ((x)->hdr.keyInfo |= KEY_INFO_PWK|KEY_INFO_GWK) : 0) + +#define SET_KEY_INFO_PWK(x) \ + ( (x) ? ((x)->hdr.keyInfo |= KEY_INFO_PWK, \ + ((x)->hdr.keyInfo &= ~KEY_INFO_GWK)) : 0 ) + +#define SET_KEY_INFO_GWK(x) \ + ( (x) ? ((x)->hdr.keyInfo |= KEY_INFO_GWK, \ + ((x)->hdr.keyInfo &= ~KEY_INFO_PWK)): 0 ) + +#define SET_KEY_INFO_GLOBAL(x) \ + ( (x) ? ((x)->hdr.keyInfo |= KEY_INFO_GLOBAL) : 0 ) + +#define IS_KEY_INFO_GLOBAL(x) \ + ( (x) ? ((x)->hdr.keyInfo & KEY_INFO_GLOBAL) : 0 ) + +#define SET_KEY_STATE_ENABLED(x, state) (((state) == TRUE) ? \ + ((x)->hdr.keyState |= KEY_STATE_ENABLE) \ + : ((x)->hdr.keyState &= ~KEY_STATE_ENABLE) ) + +#define IS_KEY_STATE_ENABLED(x) \ + ( (x) ? ((x)->hdr.keyState & KEY_STATE_ENABLE) : 0 ) + +#define IS_KEY_STATE_FORCE_EAPOL_UNENCRYPTED(x) \ + ( (x) ? \ + ((x)->hdr.keyState & KEY_STATE_FORCE_EAPOL_UNENCRYPTED) : 0) + +#define SET_KEY_STATE_FORCE_EAPOL_UNENCRYPTED(x,state) \ + (((state) == TRUE) ? \ + ((x)->hdr.keyState |= KEY_STATE_FORCE_EAPOL_UNENCRYPTED) \ + : ((x)->hdr.keyState &= ~KEY_STATE_FORCE_EAPOL_UNENCRYPTED) ) + +typedef MLAN_PACK_START struct { + IEEEtypes_MacAddr_t macAddr; + UINT8 keyDirection; + UINT8 keyType; + UINT16 keyLen; + UINT8 keyAction; + UINT8 keyInfo; +} MLAN_PACK_END nwf_key_mgthdr_t; + +/* host command for GET/SET Key Material */ +typedef MLAN_PACK_START struct { + UINT16 Action; + nwf_key_mgthdr_t key_material; +} MLAN_PACK_END host_802_11_NWF_Key_Material_t; + +/* host command for set wep parameters in Vista uses different struct + from the one we use in FW */ + +typedef MLAN_PACK_START struct host_802_11_wep_key { + UINT8 WepDefaultKeyIdx; /* 1 to 4 */ + UINT8 WepDefaultKeyValue[WEP_KEY_USER_INPUT]; /* 5 byte string */ +} MLAN_PACK_END host_802_11_wep_key_t; + +typedef struct host_802_11_wep_key_data_t { + host_802_11_wep_key_t WepDefaultKeys[MAX_WEP_KEYS]; + UINT8 default_key_idx; + UINT8 Reserved1[3]; +} host_802_11_wep_key_data_t; + +typedef UINT32 buffer_t; + +typedef struct cipher_key_buf { + struct cipher_key_buf_t *prev; + struct cipher_key_buf_t *next; + + cipher_key_t cipher_key; +} cipher_key_buf_t; + +#define CIPHER_KEYS_BUF_SIZE (sizeof(cipher_key_buf_t)) + +#define CIPHER_KEYS_BUF_NUM (CM_MAX_CONNECTIONS) + +#endif /* KEY_API_STA_H__ end */ diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/keyCommonDef.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/keyCommonDef.h new file mode 100644 index 000000000000..3aeca4c5e2d9 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/keyCommonDef.h @@ -0,0 +1,231 @@ +/** @file keyCommonDef.h + * + * @brief This file contains normal data type for key management + * + * Copyright (C) 2014-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 03/07/2014: Initial version +******************************************************/ +#ifndef _KEYMGMT_COMMON_H_ +#define _KEYMGMT_COMMON_H_ + +#include "wltypes.h" +#include "IEEE_types.h" +#include "wl_mib_rom.h" +#include "KeyApiStaDefs.h" + +#define NONCE_SIZE 32 +#define EAPOL_MIC_KEY_SIZE 16 +#define EAPOL_MIC_SIZE 16 +#define EAPOL_ENCR_KEY_SIZE 16 +#define MAC_ADDR_SIZE 6 +#define TK_SIZE 16 +#define HDR_8021x_LEN 4 +#define KEYMGMTTIMEOUTVAL 10 +#define TDLS_MIC_KEY_SIZE 16 + +#define EAPOL_PROTOCOL_V1 1 +#define EAPOL_PROTOCOL_V2 2 + +#define UAP_HOSTCMD_KEYMGMT_EAP BIT0 +#define UAP_HOSTCMD_KEYMGMT_PSK BIT1 +#define UAP_HOSTCMD_KEYMGMT_NONE BIT2 +#define UAP_HOSTCMD_KEYMGMT_PSK_SHA256 BIT8 + +#define UAP_HOSTCMD_CIPHER_WEP40 0x01 +#define UAP_HOSTCMD_CIPHER_WEP104 0x02 +#define UAP_HOSTCMD_CIPHER_TKIP 0x04 +#define UAP_HOSTCMD_CIPHER_CCMP 0x08 +#define UAP_HOSTCMD_CIPHER_MASK 0x0F + +typedef struct { + UINT8 Key[TK_SIZE]; + UINT8 RxMICKey[8]; + UINT8 TxMICKey[8]; + UINT32 TxIV32; + UINT16 TxIV16; + UINT16 KeyIndex; +} KeyData_t; + +#define MAX_WEP_KEYS 4 +/* This structure is used in rom and existing fields should not be changed */ +/* This structure is already aligned and hence packing is not needed */ + +typedef struct cipher_key_hdr_t { + IEEEtypes_MacAddr_t macAddr; + UINT8 keyDirection; + UINT8 keyType:4; + UINT8 version:4; + UINT16 keyLen; + UINT8 keyState; + UINT8 keyInfo; +} cipher_key_hdr_t; + +/* This structure is used in rom and existing fields should not be changed */ +typedef struct tkip_aes_key_data_t { + // key material information (TKIP/AES/WEP) + UINT8 key[CRYPTO_KEY_LEN_MAX]; + UINT8 txMICKey[MIC_KEY_LEN_MAX]; + UINT8 rxMICKey[MIC_KEY_LEN_MAX]; + UINT32 hiReplayCounter32; //!< initialized by host + UINT16 loReplayCounter16; //!< initialized by host + UINT32 txIV32; //!< controlled by FW + UINT16 txIV16; //!< controlled by FW + UINT32 TKIPMicLeftValue; + UINT32 TKIPMicRightValue; + + /* HW new design for 8682 only to support interleaving + * FW need to save these value and + * restore for next fragment + */ + UINT32 TKIPMicData0Value; + UINT32 TKIPMicData1Value; + UINT32 TKIPMicData2Value; + UINT8 keyIdx; + UINT8 reserved[3]; + +} tkip_aes_key_data_t; + +/* This structure is used in rom and existing fields should not be changed */ +typedef struct wep_key_data_t { + MIB_WEP_DEFAULT_KEYS WepDefaultKeys[MAX_WEP_KEYS]; + UINT8 default_key_idx; + UINT8 keyCfg; + UINT8 Reserved; +} wep_key_data_t; + +/* This structure is used in rom and existing fields should not be changed */ +typedef struct { + UINT8 key_idx; + UINT8 mickey[WAPI_MIC_LEN]; + UINT8 rawkey[WAPI_KEY_LEN]; +} wapi_key_detail_t; + +/* cipher_key_t -> tkip_aes is much bigger than wapi_key_data_t and + * since wapi_key_data_t is not used by ROM it is ok to change this size. */ + +typedef struct { + wapi_key_detail_t key; + UINT8 pn_inc; + UINT8 TxPN[WAPI_PN_LEN]; + UINT8 RxPN[WAPI_PN_LEN]; + UINT8 *pLastKey; //keep the orig cipher_key_t pointer +} wapi_key_data_t; + +typedef struct { + UINT8 ANonce[NONCE_SIZE]; + KeyData_t pwsKeyData; +} eapolHskData_t; + +/* This structure is used in rom and existing fields should not be changed */ +typedef struct cipher_key_t { + cipher_key_hdr_t hdr; + + union ckd { + tkip_aes_key_data_t tkip_aes; + wep_key_data_t wep; + wapi_key_data_t wapi; + eapolHskData_t hskData; + } ckd; +} cipher_key_t; + +typedef MLAN_PACK_START struct { + UINT8 protocol_ver; + IEEEtypes_8021x_PacketType_e pckt_type; + UINT16 pckt_body_len; +} MLAN_PACK_END Hdr_8021x_t; + +typedef MLAN_PACK_START struct { + /* don't change this order. It is set to match the + ** endianness of the message + */ + + /* Byte 1 */ + UINT16 KeyMIC:1; /* Bit 8 */ + UINT16 Secure:1; /* Bit 9 */ + UINT16 Error:1; /* Bit 10 */ + UINT16 Request:1; /* Bit 11 */ + UINT16 EncryptedKeyData:1; /* Bit 12 */ + UINT16 Reserved:3; /* Bits 13-15 */ + + /* Byte 0 */ + UINT16 KeyDescriptorVersion:3; /* Bits 0-2 */ + UINT16 KeyType:1; /* Bit 3 */ + UINT16 KeyIndex:2; /* Bits 4-5 */ + UINT16 Install:1; /* Bit 6 */ + UINT16 KeyAck:1; /* Bit 7 */ + +} MLAN_PACK_END key_info_t; + +#define KEY_DESCRIPTOR_HMAC_MD5_RC4 (1U << 0) +#define KEY_DESCRIPTOR_HMAC_SHA1_AES (1U << 1) +#define EAPOL_KeyMsg_Len (100) +/* WPA2 GTK IE */ +typedef MLAN_PACK_START struct { + UINT8 KeyID:2; + UINT8 Tx:1; + UINT8 rsvd:5; + UINT8 rsvd1; + UINT8 GTK[1]; +} MLAN_PACK_END GTK_KDE_t; + +/* WPA2 Key Data */ +typedef MLAN_PACK_START struct { + UINT8 type; + UINT8 length; + UINT8 OUI[3]; + UINT8 dataType; + UINT8 data[1]; +} MLAN_PACK_END KDE_t; + +typedef MLAN_PACK_START struct { + uint8 llc[3]; + uint8 snap_oui[3]; + uint16 snap_type; +} MLAN_PACK_END llc_snap_t; + +typedef MLAN_PACK_START struct { + Hdr_8021x_t hdr_8021x; + UINT8 desc_type; + key_info_t key_info; + UINT16 key_length; + UINT32 replay_cnt[2]; + UINT8 key_nonce[NONCE_SIZE]; /*32 bytes */ + UINT8 EAPOL_key_IV[16]; + UINT8 key_RSC[8]; + UINT8 key_ID[8]; + UINT8 key_MIC[EAPOL_MIC_KEY_SIZE]; + UINT16 key_material_len; + UINT8 key_data[1]; +} MLAN_PACK_END EAPOL_KeyMsg_t; + +typedef MLAN_PACK_START struct { + Hdr_8021x_t hdr_8021x; + IEEEtypes_8021x_CodeType_e code; + UINT8 identifier; + UINT16 length; + UINT8 data[1]; +} MLAN_PACK_END EAP_PacketMsg_t; + +typedef MLAN_PACK_START struct { + ether_hdr_t ethHdr; + EAPOL_KeyMsg_t keyMsg; +} MLAN_PACK_END EAPOL_KeyMsg_Tx_t; + +#endif diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/keyMgmtAp.c b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/keyMgmtAp.c new file mode 100644 index 000000000000..489a599df03a --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/keyMgmtAp.c @@ -0,0 +1,967 @@ +/** @file keyMgntAP.c + * + * @brief This file defined the eapol paket process and key management for authenticator + * + * Copyright (C) 2014-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 03/07/2014: Initial version +******************************************************/ +//Authenticator related function definitions +#include "wltypes.h" +#include "IEEE_types.h" + +#include "hostsa_ext_def.h" +#include "authenticator.h" + +#include "wl_macros.h" +#include "wlpd.h" +#include "pass_phrase.h" +#include "sha1.h" +#include "crypt_new.h" +#include "parser.h" +#include "keyCommonDef.h" +#include "keyMgmtStaTypes.h" +#include "AssocAp_srv_rom.h" +#include "pmkCache.h" +#include "keyMgmtApTypes.h" +#include "keyMgmtAp.h" +#include "rc4.h" +#include "authenticator_api.h" + +////////////////////// +// STATIC FUNCTIONS +////////////////////// + +//#ifdef AUTHENTICATOR +//////////////////////// +// FORWARD DECLARATIONS +//////////////////////// +Status_e GeneratePWKMsg3(hostsa_private *priv, cm_Connection *connPtr); +Status_e GenerateGrpMsg1(hostsa_private *priv, cm_Connection *connPtr); +Status_e GenerateApEapolMsg(hostsa_private *priv, + t_void *pconnPtr, keyMgmtState_e msgState); + +static void +handleFailedHSK(t_void *priv, t_void *pconnPtr, IEEEtypes_ReasonCode_t reason) +{ + phostsa_private psapriv = (phostsa_private)priv; + hostsa_mlan_fns *pm_fns = &psapriv->mlan_fns; + cm_Connection *connPtr = (cm_Connection *)pconnPtr; + + KeyMgmtStopHskTimer(connPtr); + pm_fns->hostsa_SendDeauth(pm_fns->pmlan_private, connPtr->mac_addr, + (t_u16)reason); +} + +static void +incrementReplayCounter(apKeyMgmtInfoSta_t *pKeyMgmtInfo) +{ + if (++pKeyMgmtInfo->counterLo == 0) { + pKeyMgmtInfo->counterHi++; + } +} + +int +isValidReplayCount(t_void *priv, apKeyMgmtInfoSta_t *pKeyMgmtInfo, + UINT8 *pRxReplayCount) +{ + phostsa_private psapriv = (phostsa_private)priv; + hostsa_util_fns *util_fns = &psapriv->util_fns; + UINT32 rxCounterHi; + UINT32 rxCounterLo; + + memcpy(util_fns, &rxCounterHi, pRxReplayCount, 4); + memcpy(util_fns, &rxCounterLo, pRxReplayCount + 4, 4); + + if ((pKeyMgmtInfo->counterHi == WORD_SWAP(rxCounterHi)) && + (pKeyMgmtInfo->counterLo == WORD_SWAP(rxCounterLo))) { + return 1; + } + return 0; +} + +void +KeyMgmtSendGrpKeyMsgToAllSta(hostsa_private *priv) +{ + hostsa_mlan_fns *pm_fns = &priv->mlan_fns; + apKeyMgmtInfoSta_t *pKeyMgmtInfo; + cm_Connection *connPtr = MNULL; + t_void *sta_node = MNULL; + + ENTER(); + + pm_fns->Hostsa_find_connection(priv->pmlan_private, (t_void *)&connPtr, + &sta_node); + + while (connPtr != MNULL) { + pKeyMgmtInfo = &connPtr->staData.keyMgmtInfo; + + if (pKeyMgmtInfo->rom.keyMgmtState == HSK_END) { + GenerateApEapolMsg(priv, connPtr, + GRP_REKEY_MSG1_PENDING); + } else if ((pKeyMgmtInfo->rom.keyMgmtState == WAITING_4_GRPMSG2) + || (pKeyMgmtInfo->rom.staSecType.wpa2 && + (pKeyMgmtInfo->rom.keyMgmtState == + WAITING_4_MSG4)) || + (pKeyMgmtInfo->rom.keyMgmtState == + WAITING_4_GRP_REKEY_MSG2)) { + // TODO:How to handle group rekey if either Groupwise handshake + // Group rekey is already in progress for this STA? + } + + pm_fns->Hostsa_find_next_connection(priv->pmlan_private, + (t_void *)&connPtr, + &sta_node); + } + + LEAVE(); +} + +UINT32 +keyApi_ApUpdateKeyMaterial(void *priv, cm_Connection *connPtr, + BOOLEAN updateGrpKey) +{ + phostsa_private psapriv = (phostsa_private)priv; + hostsa_util_fns *util_fns = &psapriv->util_fns; + hostsa_mlan_fns *pm_fns = &psapriv->mlan_fns; + BssConfig_t *pBssConfig = MNULL; + KeyData_t *pKeyData = MNULL; + //cipher_key_buf_t *pCipherKeyBuf; + Cipher_t *pCipher = MNULL; + //KeyData_t pwsKeyData; + mlan_ds_encrypt_key encrypt_key; + t_u8 bcast_addr[MAC_ADDR_SIZE] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + apInfo_t *pApInfo = &psapriv->apinfo; + + pBssConfig = &pApInfo->bssConfig; + + if (pBssConfig->SecType.wpa || pBssConfig->SecType.wpa2) { + memset(util_fns, &encrypt_key, 0, sizeof(mlan_ds_encrypt_key)); + + //connPtr->cmFlags.RSNEnabled = TRUE; + if (updateGrpKey == TRUE) { + pKeyData = &pApInfo->bssData.grpKeyData; + pCipher = &pBssConfig->RsnConfig.mcstCipher; + } else if (connPtr) { + /* pCipherKeyBuf = connPtr->pwTxRxCipherKeyBuf; + memcpy((void*)&pwsKeyData, + (void*)&pCipherKeyBuf->cipher_key.ckd.hskData.pwsKeyData, + sizeof(KeyData_t)); */ + pKeyData = &connPtr->hskData.pwsKeyData; + pCipher = + &connPtr->staData.keyMgmtInfo.rom.staUcstCipher; + } + + if (updateGrpKey == TRUE) { + memcpy(util_fns, encrypt_key.mac_addr, bcast_addr, + MAC_ADDR_SIZE); + encrypt_key.key_flags |= KEY_FLAG_GROUP_KEY; + } else if (connPtr) { + memcpy(util_fns, encrypt_key.mac_addr, + connPtr->mac_addr, MAC_ADDR_SIZE); + encrypt_key.key_flags |= KEY_FLAG_SET_TX_KEY; + } + + if (!pKeyData || !pCipher) { + PRINTM(MERROR, "Invalid KeyData or Cipher pointer!\n"); + return 1; + } + + if (pCipher->ccmp) { + /**AES*/ + encrypt_key.key_len = TK_SIZE; + memcpy(util_fns, encrypt_key.key_material, + pKeyData->Key, TK_SIZE); + } else if (pCipher->tkip) { + /**TKIP*/ + encrypt_key.key_len = + TK_SIZE + MIC_KEY_SIZE + MIC_KEY_SIZE; + memcpy(util_fns, encrypt_key.key_material, + pKeyData->Key, TK_SIZE); + memcpy(util_fns, &encrypt_key.key_material[TK_SIZE], + pKeyData->TxMICKey, MIC_KEY_SIZE); + memcpy(util_fns, + &encrypt_key.key_material[TK_SIZE + + MIC_KEY_SIZE], + pKeyData->RxMICKey, MIC_KEY_SIZE); + } + /**set pn 0*/ + memset(util_fns, encrypt_key.pn, 0, PN_SIZE); + + /**key flag*/ + encrypt_key.key_flags |= KEY_FLAG_RX_SEQ_VALID; + /**key index*/ + encrypt_key.key_index = pKeyData->KeyIndex; + /**set command to fw update key*/ + pm_fns->hostsa_set_encrypt_key(psapriv->pmlan_private, + &encrypt_key); + + } + + return 0; +} + +void +GenerateGTK(void *priv) +{ + phostsa_private psapriv = (phostsa_private)priv; + apInfo_t *pApInfo = &psapriv->apinfo; + BssData_t *pBssData = MNULL; + + pBssData = &pApInfo->bssData; + + GenerateGTK_internal(psapriv, &pBssData->grpKeyData, + pBssData->GNonce, psapriv->curr_addr); +} + +void +ReInitGTK(t_void *priv) +{ + phostsa_private psapriv = (phostsa_private)priv; + apInfo_t *pApInfo = &psapriv->apinfo; + BssData_t *pBssData; + + pBssData = &pApInfo->bssData; + + /* + Disabled for interop + Not all clients like this + pBssData->grpKeyData.KeyIndex = (pBssData->grpKeyData.KeyIndex & 3) + 1; + */ + + ROM_InitGTK(psapriv, &pBssData->grpKeyData, + pBssData->GNonce, psapriv->curr_addr); + + keyApi_ApUpdateKeyMaterial(priv, MNULL, MTRUE); +} + +void +KeyMgmtGrpRekeyCountUpdate(t_void *context) +{ + phostsa_private psapriv = (phostsa_private)context; + apInfo_t *pApInfo = &psapriv->apinfo; + hostsa_util_fns *putil_fns = &psapriv->util_fns; + + ENTER(); + + if (psapriv->GrpRekeyTimerIsSet && + pApInfo->bssData.grpRekeyCntRemaining) { + //Periodic group rekey is configured. + pApInfo->bssData.grpRekeyCntRemaining--; + if (!pApInfo->bssData.grpRekeyCntRemaining) { + //Group rekey timeout hit. + pApInfo->bssData.grpRekeyCntRemaining + = pApInfo->bssData.grpRekeyCntConfigured; + + ReInitGTK((t_void *)psapriv); + + KeyMgmtSendGrpKeyMsgToAllSta(psapriv); + } + + /**start Group rekey timer*/ + putil_fns->moal_start_timer(putil_fns->pmoal_handle, + psapriv->GrpRekeytimer, + MFALSE, MRVDRV_TIMER_60S); + } + + LEAVE(); +} + +void +KeyMgmtInit(void *priv) +{ + phostsa_private psapriv = (phostsa_private)priv; + apInfo_t *pApInfo = &psapriv->apinfo; + UINT8 *pPassPhrase; + UINT8 *pPskValue; + UINT8 passPhraseLen; + + pPassPhrase = pApInfo->bssConfig.RsnConfig.PSKPassPhrase; + pPskValue = pApInfo->bssConfig.RsnConfig.PSKValue; + passPhraseLen = pApInfo->bssConfig.RsnConfig.PSKPassPhraseLen; + + ROM_InitGTK(psapriv, &pApInfo->bssData.grpKeyData, + pApInfo->bssData.GNonce, psapriv->curr_addr); + + if (pApInfo->bssData.updatePassPhrase == MTRUE) { + pmkCacheGeneratePSK(priv, pApInfo->bssConfig.SsId, + pApInfo->bssConfig.SsIdLen, + (char *)pPassPhrase, pPskValue); + + pApInfo->bssData.updatePassPhrase = MFALSE; + } +} + +void +KeyMgmtHskTimeout(t_void *context) +{ + cm_Connection *connPtr = (cm_Connection *)context; + phostsa_private priv = (phostsa_private)connPtr->priv; + hostsa_mlan_fns *pm_fns = &priv->mlan_fns; + apKeyMgmtInfoSta_t *pKeyMgmtInfo; + apRsnConfig_t *pRsnConfig; + IEEEtypes_ReasonCode_t reason; + BOOLEAN maxRetriesDone = MFALSE; + apInfo_t *pApInfo = &priv->apinfo; + + PRINTM(MMSG, "ENTER: %s\n", __FUNCTION__); + + pKeyMgmtInfo = &connPtr->staData.keyMgmtInfo; + pRsnConfig = &pApInfo->bssConfig.RsnConfig; + + connPtr->timer_is_set = 0; + /* Assume when this function gets called pKeyMgmtInfo->keyMgmtState + ** will not be in HSK_NOT_STARTED or HSK_END + */ + if (pKeyMgmtInfo->rom.keyMgmtState <= WAITING_4_MSG4) { + if (pKeyMgmtInfo->numHskTries >= pRsnConfig->MaxPwsHskRetries) { + maxRetriesDone = MTRUE; + reason = IEEEtypes_REASON_4WAY_HANDSHK_TIMEOUT; + } + } else { + if (pKeyMgmtInfo->numHskTries >= pRsnConfig->MaxGrpHskRetries) { + maxRetriesDone = MTRUE; + reason = IEEEtypes_REASON_GRP_KEY_UPD_TIMEOUT; + } + } + + if (maxRetriesDone) { + // Some STAs do not respond to PWK Msg1 if the EAPOL Proto Version is 1 + // in 802.1X header, hence switch to v2 after all attempts with v1 fail + // for PWK Msg1. Set the HskTimeoutCtn to 1 to get the same "retries" + // as with v1. + if (((WAITING_4_MSG2 == pKeyMgmtInfo->rom.keyMgmtState) + || (MSG1_PENDING == pKeyMgmtInfo->rom.keyMgmtState)) + && (EAPOL_PROTOCOL_V1 == pKeyMgmtInfo->EAPOLProtoVersion)) { + pKeyMgmtInfo->numHskTries = 1; + pKeyMgmtInfo->EAPOLProtoVersion = EAPOL_PROTOCOL_V2; + GenerateApEapolMsg(priv, connPtr, + pKeyMgmtInfo->rom.keyMgmtState); + } else { + pm_fns->hostsa_SendDeauth(priv->pmlan_private, + connPtr->mac_addr, + (t_u16)reason); + pKeyMgmtInfo->rom.keyMgmtState = HSK_END; + } + } else { + GenerateApEapolMsg(priv, connPtr, + pKeyMgmtInfo->rom.keyMgmtState); + } + + return; +} + +void +KeyMgmtStartHskTimer(void *context) +{ + cm_Connection *connPtr = (cm_Connection *)context; + phostsa_private psapriv = (phostsa_private)(connPtr->priv); + hostsa_util_fns *util_fns = &psapriv->util_fns; + apInfo_t *pApInfo = MNULL; + UINT32 timeoutInms; + apRsnConfig_t *pRsnConfig; + + PRINTM(MMSG, "ENTER: %s\n", __FUNCTION__); + + pApInfo = &psapriv->apinfo; + pRsnConfig = &pApInfo->bssConfig.RsnConfig; + if ((connPtr->staData.keyMgmtInfo.rom.keyMgmtState >= MSG1_PENDING) + && (connPtr->staData.keyMgmtInfo.rom.keyMgmtState <= + WAITING_4_MSG4)) { + timeoutInms = pRsnConfig->PwsHskTimeOut; + } else if ((connPtr->staData.keyMgmtInfo.rom.keyMgmtState >= + GRPMSG1_PENDING) + && (connPtr->staData.keyMgmtInfo.rom.keyMgmtState <= + WAITING_4_GRP_REKEY_MSG2)) { + timeoutInms = pRsnConfig->GrpHskTimeOut; + } else { + //EAPOL HSK is not in progress. No need to start HSK timer + return; + } + + // Retry happen, increase timeout to at least 1 sec + if (connPtr->staData.keyMgmtInfo.numHskTries > 0) { + timeoutInms = MAX(AP_RETRY_EAPOL_HSK_TIMEOUT, timeoutInms); + } + + /* if STA is in PS1 then we are using max(STA_PS_EAPOL_HSK_TIMEOUT, + * HSKtimeout)for timeout instead of configured timeout value + */ + /* if(PWR_MODE_PWR_SAVE == connPtr->staData.pwrSaveInfo.mode) + { + timeoutInms = MAX(STA_PS_EAPOL_HSK_TIMEOUT, timeoutInms); + } + */ + util_fns->moal_start_timer(util_fns->pmoal_handle, + connPtr->HskTimer, MFALSE, timeoutInms); + connPtr->timer_is_set = 1; +} + +void +KeyMgmtStopHskTimer(t_void *pconnPtr) +{ + cm_Connection *connPtr = (cm_Connection *)pconnPtr; + phostsa_private priv = (phostsa_private)connPtr->priv; + hostsa_util_fns *util_fns = &priv->util_fns; + + util_fns->moal_stop_timer(util_fns->pmoal_handle, connPtr->HskTimer); + + connPtr->timer_is_set = 0; +} + +void +PrepDefaultEapolMsg(phostsa_private priv, cm_Connection *connPtr, + EAPOL_KeyMsg_Tx_t **pTxEapolPtr, pmlan_buffer pmbuf) +{ + hostsa_util_fns *util_fns = &priv->util_fns; + apInfo_t *pApInfo = &priv->apinfo; + EAPOL_KeyMsg_Tx_t *tx_eapol_ptr; + apKeyMgmtInfoSta_t *pKeyMgmtInfo; + hostsa_mlan_fns *pm_fns = &priv->mlan_fns; + UINT8 intf_hr_len = + pm_fns->Hostsa_get_intf_hr_len(pm_fns->pmlan_private); +#define UAP_EAPOL_PRIORITY 7 /* Voice */ + + ENTER(); + + pmbuf->priority = UAP_EAPOL_PRIORITY; + pmbuf->buf_type = MLAN_BUF_TYPE_DATA; + pmbuf->data_offset = (sizeof(UapTxPD) + intf_hr_len + DMA_ALIGNMENT); + tx_eapol_ptr = + (EAPOL_KeyMsg_Tx_t *)((UINT8 *)pmbuf->pbuf + + pmbuf->data_offset); + memset(util_fns, (UINT8 *)tx_eapol_ptr, 0x00, + sizeof(EAPOL_KeyMsg_Tx_t)); + + formEAPOLEthHdr(priv, tx_eapol_ptr, connPtr->mac_addr, priv->curr_addr); + + pKeyMgmtInfo = &connPtr->staData.keyMgmtInfo; + + SetEAPOLKeyDescTypeVersion(tx_eapol_ptr, + pKeyMgmtInfo->rom.staSecType.wpa2, + MFALSE, + (pKeyMgmtInfo->rom.staUcstCipher.ccmp || + pApInfo->bssConfig.RsnConfig.mcstCipher. + ccmp)); + + *pTxEapolPtr = tx_eapol_ptr; + + LEAVE(); +} + +Status_e +GeneratePWKMsg1(hostsa_private *priv, cm_Connection *connPtr) +{ + hostsa_mlan_fns *pm_fns = &priv->mlan_fns; + EAPOL_KeyMsg_Tx_t *tx_eapol_ptr; + UINT16 frameLen = 0, packet_len = 0; + apKeyMgmtInfoSta_t *pKeyMgmtInfo; + UINT32 replay_cnt[2]; + eapolHskData_t *pHskData; + pmlan_buffer pmbuf = MNULL; + + PRINTM(MMSG, "ENTER: %s\n", __FUNCTION__); + + pmbuf = pm_fns->hostsa_alloc_mlan_buffer(priv->pmlan_adapter, + MLAN_TX_DATA_BUF_SIZE_2K, 0, + MOAL_MALLOC_BUFFER); + if (pmbuf == NULL) { + PRINTM(MERROR, "allocate buffer fail for eapol \n"); + return FAIL; + } + + PrepDefaultEapolMsg(priv, connPtr, &tx_eapol_ptr, pmbuf); + + pKeyMgmtInfo = &connPtr->staData.keyMgmtInfo; + pHskData = &connPtr->hskData; + + incrementReplayCounter(pKeyMgmtInfo); + replay_cnt[0] = pKeyMgmtInfo->counterHi; + replay_cnt[1] = pKeyMgmtInfo->counterLo; + + supplicantGenerateRand(priv, pHskData->ANonce, NONCE_SIZE); + + PopulateKeyMsg(priv, tx_eapol_ptr, + &pKeyMgmtInfo->rom.staUcstCipher, + PAIRWISE_KEY_MSG, replay_cnt, pHskData->ANonce); + + frameLen = EAPOL_KeyMsg_Len - sizeof(Hdr_8021x_t) + - sizeof(tx_eapol_ptr->keyMsg.key_data) + + tx_eapol_ptr->keyMsg.key_material_len; //key_mtr_len is 0 here + + packet_len = frameLen + sizeof(Hdr_8021x_t) + sizeof(ether_hdr_t); + + tx_eapol_ptr->keyMsg.hdr_8021x.protocol_ver + = pKeyMgmtInfo->EAPOLProtoVersion; + tx_eapol_ptr->keyMsg.hdr_8021x.pckt_type + = IEEE_8021X_PACKET_TYPE_EAPOL_KEY; + tx_eapol_ptr->keyMsg.hdr_8021x.pckt_body_len = htons(frameLen); + + UpdateEAPOLWcbLenAndTransmit(priv, pmbuf, packet_len); + return SUCCESS; +} + +// returns 0 on success, or error code +Status_e +ProcessPWKMsg2(hostsa_private *priv, + cm_Connection *connPtr, t_u8 *pbuf, t_u32 len) +{ + EAPOL_KeyMsg_t *rx_eapol_ptr; + UINT8 *PMK; + apKeyMgmtInfoSta_t *pKeyMgmtInfo; + BssConfig_t *pBssConfig = NULL; + IEPointers_t iePointers; + UINT32 ieLen; + UINT8 *pIe = NULL; + apInfo_t *pApInfo = &priv->apinfo; + Cipher_t wpaUcastCipher; + Cipher_t wpa2UcastCipher; + eapolHskData_t *pHskData = &connPtr->hskData; + + pKeyMgmtInfo = &connPtr->staData.keyMgmtInfo; + rx_eapol_ptr = (EAPOL_KeyMsg_t *)(pbuf + ETHII_HEADER_LEN); + + pBssConfig = &pApInfo->bssConfig; + // compare the RSN IE from assoc req to current + pIe = (UINT8 *)rx_eapol_ptr->key_data; + ieLen = pIe[1] + sizeof(IEEEtypes_InfoElementHdr_t); + GetIEPointers((void *)priv, pIe, ieLen, &iePointers); + wpaUcastCipher = pBssConfig->RsnConfig.wpaUcstCipher; + wpa2UcastCipher = pBssConfig->RsnConfig.wpa2UcstCipher; + PRINTM(MMSG, "ENTER: %s\n", __FUNCTION__); + + if (assocSrvAp_checkRsnWpa(connPtr, &pKeyMgmtInfo->rom, + wpaUcastCipher, + wpa2UcastCipher, + pBssConfig->RsnConfig.mcstCipher, + pBssConfig->RsnConfig.AuthKey, + &pKeyMgmtInfo->rom.staSecType, + iePointers.pRsn, + iePointers.pWpa, TRUE) == FAIL) { + handleFailedHSK(priv, connPtr, IEEEtypes_REASON_IE_4WAY_DIFF); + return FAIL; + } + + PMK = pApInfo->bssConfig.RsnConfig.PSKValue; + + KeyMgmtAp_DerivePTK(priv, PMK, + connPtr->mac_addr, + priv->curr_addr, + pHskData->ANonce, + rx_eapol_ptr->key_nonce, + pKeyMgmtInfo->EAPOL_MIC_Key, + pKeyMgmtInfo->EAPOL_Encr_Key, + &pHskData->pwsKeyData, MFALSE); + + if (!IsEAPOL_MICValid(priv, rx_eapol_ptr, pKeyMgmtInfo->EAPOL_MIC_Key)) { + return FAIL; + } + + KeyMgmtStopHskTimer(connPtr); + connPtr->staData.keyMgmtInfo.numHskTries = 0; + + return GenerateApEapolMsg(priv, connPtr, MSG3_PENDING); +} + +Status_e +GeneratePWKMsg3(hostsa_private *priv, cm_Connection *connPtr) +{ + hostsa_mlan_fns *pm_fns = &priv->mlan_fns; + EAPOL_KeyMsg_Tx_t *tx_eapol_ptr; + UINT16 frameLen = 0, packet_len = 0; + apKeyMgmtInfoSta_t *pKeyMgmtInfo; + apInfo_t *pApInfo = &priv->apinfo; + BssConfig_t *pBssConfig = MNULL; + UINT32 replay_cnt[2]; + eapolHskData_t *pHskData; + pmlan_buffer pmbuf = MNULL; + + PRINTM(MMSG, "ENTER: %s\n", __FUNCTION__); + + pmbuf = pm_fns->hostsa_alloc_mlan_buffer(priv->pmlan_adapter, + MLAN_TX_DATA_BUF_SIZE_2K, 0, + MOAL_MALLOC_BUFFER); + if (pmbuf == NULL) { + PRINTM(MERROR, "allocate buffer fail for eapol \n"); + return FAIL; + } + + PrepDefaultEapolMsg(priv, connPtr, &tx_eapol_ptr, pmbuf); + + pBssConfig = &pApInfo->bssConfig; + + pKeyMgmtInfo = &connPtr->staData.keyMgmtInfo; + pHskData = &connPtr->hskData; + + incrementReplayCounter(pKeyMgmtInfo); + + replay_cnt[0] = pKeyMgmtInfo->counterHi; + replay_cnt[1] = pKeyMgmtInfo->counterLo; + + PopulateKeyMsg(priv, tx_eapol_ptr, + &pKeyMgmtInfo->rom.staUcstCipher, + ((PAIRWISE_KEY_MSG | SECURE_HANDSHAKE_FLAG) | + ((pKeyMgmtInfo->rom.staSecType. + wpa2) ? WPA2_HANDSHAKE : 0)), replay_cnt, + pHskData->ANonce); + + /*if (pKeyMgmtInfo->staSecType.wpa2) + { + // Netgear WAG511 and USB55 cards don't like this field set to + // anything other than zero. Hence hard code this value to zero + // in all outbound EAPOL frames... + // The client is now vulnerable to replay attacks from the point + // it receives EAP-message3 till reception of first management + // frame from uAP. + + tx_eapol_ptr->keyMsg.key_RSC[0] = + pApInfo->bssConfig.grpKeyData.TxIV16 & 0x00FF; + tx_eapol_ptr->keyMsg.key_RSC[1] = + (pApInfo->bssConfig.grpKeyData.TxIV16 >> 8) & 0x00FF; + memcpy((void*)(tx_eapol_ptr->keyMsg.key_RSC + 2), + &pApInfo->bssData.grpKeyData.TxIV32, 4); + } */ +/* + pBcnFrame = (dot11MgtFrame_t *)BML_DATA_PTR(connPtr->pBcnBufferDesc); + if (pKeyMgmtInfo->rom.staSecType.wpa) + { + pWpaIE = syncSrv_ParseAttrib(pBcnFrame, + ELEM_ID_VENDOR_SPECIFIC, + IEEE_MSG_BEACON, + (UINT8 *)wpa_oui01, + sizeof(wpa_oui01)); + } + else if (pKeyMgmtInfo->rom.staSecType.wpa2) + { + pWpa2IE = syncSrv_ParseAttrib(pBcnFrame, + ELEM_ID_RSN, + IEEE_MSG_BEACON, + NULL, + 0); + }*/ + if (!KeyData_UpdateKeyMaterial(priv, tx_eapol_ptr, + &pKeyMgmtInfo->rom.staSecType, + pBssConfig->wpa_ie, + pBssConfig->rsn_ie)) { + /* We have WPA/WPA2 enabled but no corresponding IE */ + pm_fns->hostsa_free_mlan_buffer(pm_fns->pmlan_adapter, pmbuf); + return FAIL; + } + + if (pKeyMgmtInfo->rom.staSecType.wpa2) { // WPA2 + prepareKDE(priv, tx_eapol_ptr, + &pApInfo->bssData.grpKeyData, + &pApInfo->bssConfig.RsnConfig.mcstCipher); + + if (!Encrypt_keyData((t_void *)priv, tx_eapol_ptr, + pKeyMgmtInfo->EAPOL_Encr_Key, + &pKeyMgmtInfo->rom.staUcstCipher)) + { + pm_fns->hostsa_free_mlan_buffer(pm_fns->pmlan_adapter, + pmbuf); + return FAIL; + } + } + + frameLen = KeyMgmtSta_PopulateEAPOLLengthMic(priv, + tx_eapol_ptr, + pKeyMgmtInfo-> + EAPOL_MIC_Key, + pKeyMgmtInfo-> + EAPOLProtoVersion, + tx_eapol_ptr->keyMsg. + key_info. + KeyDescriptorVersion); + + packet_len = frameLen + sizeof(Hdr_8021x_t) + sizeof(ether_hdr_t); + UpdateEAPOLWcbLenAndTransmit(priv, pmbuf, packet_len); + return SUCCESS; +} + +Status_e +ProcessPWKMsg4(hostsa_private *priv, + cm_Connection *connPtr, t_u8 *pbuf, t_u32 len) +{ + hostsa_mlan_fns *pm_fns = &priv->mlan_fns; + EAPOL_KeyMsg_t *rx_eapol_ptr; + apKeyMgmtInfoSta_t *pKeyMgmtInfo; + eapolHskData_t *pHskData = &connPtr->hskData; + + pKeyMgmtInfo = &connPtr->staData.keyMgmtInfo; + rx_eapol_ptr = (EAPOL_KeyMsg_t *)(pbuf + ETHII_HEADER_LEN); + PRINTM(MMSG, "ENTER: %s\n", __FUNCTION__); + + if (!IsEAPOL_MICValid(priv, rx_eapol_ptr, pKeyMgmtInfo->EAPOL_MIC_Key)) { + return FAIL; + } + pHskData->pwsKeyData.TxIV16 = 0x0001; + pHskData->pwsKeyData.TxIV32 = 0; + + if (keyApi_ApUpdateKeyMaterial(priv, connPtr, FALSE)) { + handleFailedHSK(priv, connPtr, IEEEtypes_REASON_UNSPEC); + return FAIL; + } + + KeyMgmtStopHskTimer(connPtr); + connPtr->staData.keyMgmtInfo.numHskTries = 0; + if (pKeyMgmtInfo->rom.staSecType.wpa2) { + pm_fns->Hostsa_sendEventRsnConnect(priv->pmlan_private, + connPtr->mac_addr); + pKeyMgmtInfo->rom.keyMgmtState = HSK_END; + } else { + return GenerateApEapolMsg(priv, connPtr, GRPMSG1_PENDING); + } + + return SUCCESS; +} + +Status_e +GenerateGrpMsg1(hostsa_private *priv, cm_Connection *connPtr) +{ + hostsa_mlan_fns *pm_fns = &priv->mlan_fns; + EAPOL_KeyMsg_Tx_t *tx_eapol_ptr; + UINT16 frameLen = 0, packet_len = 0; + apKeyMgmtInfoSta_t *pKeyMgmtInfo; + apInfo_t *pApInfo = &priv->apinfo; + UINT32 replay_cnt[2]; + pmlan_buffer pmbuf = MNULL; + + PRINTM(MMSG, "ENTER: %s\n", __FUNCTION__); + + pmbuf = pm_fns->hostsa_alloc_mlan_buffer(priv->pmlan_adapter, + MLAN_TX_DATA_BUF_SIZE_2K, 0, + MOAL_MALLOC_BUFFER); + if (pmbuf == NULL) { + PRINTM(MERROR, "allocate buffer fail for eapol \n"); + return FAIL; + } + + PrepDefaultEapolMsg(priv, connPtr, &tx_eapol_ptr, pmbuf); + + pKeyMgmtInfo = &connPtr->staData.keyMgmtInfo; + + incrementReplayCounter(pKeyMgmtInfo); + + replay_cnt[0] = pKeyMgmtInfo->counterHi; + replay_cnt[1] = pKeyMgmtInfo->counterLo; + + PopulateKeyMsg(priv, tx_eapol_ptr, + &pApInfo->bssConfig.RsnConfig.mcstCipher, + (SECURE_HANDSHAKE_FLAG | + ((pKeyMgmtInfo->rom.staSecType. + wpa2) ? WPA2_HANDSHAKE : 0)), replay_cnt, + pApInfo->bssData.GNonce); + + KeyData_AddKey(priv, + tx_eapol_ptr, + &pKeyMgmtInfo->rom.staSecType, + &pApInfo->bssData.grpKeyData, + &pApInfo->bssConfig.RsnConfig.mcstCipher); + if (!Encrypt_keyData(priv, tx_eapol_ptr, + pKeyMgmtInfo->EAPOL_Encr_Key, + &pKeyMgmtInfo->rom.staUcstCipher)) + { + // nothing here. + } + + frameLen = KeyMgmtSta_PopulateEAPOLLengthMic(priv, + tx_eapol_ptr, + pKeyMgmtInfo-> + EAPOL_MIC_Key, + pKeyMgmtInfo-> + EAPOLProtoVersion, + tx_eapol_ptr->keyMsg. + key_info. + KeyDescriptorVersion); + + packet_len = frameLen + sizeof(Hdr_8021x_t) + sizeof(ether_hdr_t); + + UpdateEAPOLWcbLenAndTransmit(priv, pmbuf, packet_len); + return SUCCESS; +} + +Status_e +ProcessGrpMsg2(hostsa_private *priv, + cm_Connection *connPtr, t_u8 *pbuf, t_u32 len) +{ + hostsa_mlan_fns *pm_fns = &priv->mlan_fns; + EAPOL_KeyMsg_t *rx_eapol_ptr; + apKeyMgmtInfoSta_t *pKeyMgmtInfo; + + PRINTM(MMSG, "ENTER: %s\n", __FUNCTION__); + + rx_eapol_ptr = (EAPOL_KeyMsg_t *)(pbuf + ETHII_HEADER_LEN); + + pKeyMgmtInfo = &connPtr->staData.keyMgmtInfo; + + if (!IsEAPOL_MICValid(priv, rx_eapol_ptr, pKeyMgmtInfo->EAPOL_MIC_Key)) { + handleFailedHSK(priv, connPtr, IEEEtypes_REASON_IE_4WAY_DIFF); + return FAIL; + } + KeyMgmtStopHskTimer(connPtr); + connPtr->staData.keyMgmtInfo.numHskTries = 0; + + if (WAITING_4_GRPMSG2 == pKeyMgmtInfo->rom.keyMgmtState) { + /* sendEventRsnConnect(connPtr, pKeyMgmtInfo); */ + pm_fns->Hostsa_sendEventRsnConnect(priv->pmlan_private, + connPtr->mac_addr); + } + + pKeyMgmtInfo->rom.keyMgmtState = HSK_END; + + return SUCCESS; +} + +Status_e +GenerateApEapolMsg(hostsa_private *priv, + t_void *pconnPtr, keyMgmtState_e msgState) +{ + cm_Connection *connPtr = (cm_Connection *)pconnPtr; + Status_e status; + // OSASysContext prevContext; + + if (connPtr->timer_is_set) { + KeyMgmtStopHskTimer((t_void *)connPtr); + } + /* If msgState is any waiting_** state, + ** it will decrease to corresponding **_pending state. + */ + /* Note: it will reduce the if checks + */ + if ((msgState & 0x1) == 0) { + msgState--; + } + connPtr->staData.keyMgmtInfo.rom.keyMgmtState = msgState; + + if (msgState == MSG1_PENDING) { + status = GeneratePWKMsg1(priv, connPtr); + } else if (msgState == MSG3_PENDING) { + status = GeneratePWKMsg3(priv, connPtr); + } else if ((msgState == GRPMSG1_PENDING) + || (msgState == GRP_REKEY_MSG1_PENDING)) { + status = GenerateGrpMsg1(priv, connPtr); + } else { + //This should not happen + return FAIL; + } + if (SUCCESS == status) { + connPtr->staData.keyMgmtInfo.rom.keyMgmtState++; + } + + if (SUCCESS == status) { + connPtr->staData.keyMgmtInfo.numHskTries++; + /* we are starting the timer irrespective of whether the msg generation is + sucessful or not. This is because, if the msg generation fails because + of buffer unavailabilty then we can re-try the msg after the timeout + period. */ + if (!connPtr->timer_is_set) + KeyMgmtStartHskTimer(connPtr); + } + + return status; +} + +//#endif // AUTHENTICATOR + +void +ApMicErrTimerExpCb(t_void *context) +{ + cm_Connection *connPtr = (cm_Connection *)context; + phostsa_private priv; + // UINT32 int_save; + apInfo_t *pApInfo; + + if (connPtr == NULL) { + //no AP connection. Do nothing, just return + return; + } + priv = (phostsa_private)connPtr->priv; + if (!priv) + return; + pApInfo = &priv->apinfo; + + switch (connPtr->staData.apMicError.status) { + case FIRST_MIC_FAIL_IN_60_SEC: + connPtr->staData.apMicError.status = NO_MIC_FAILURE; + break; + case SECOND_MIC_FAIL_IN_60_SEC: + if ((pApInfo->bssConfig.RsnConfig.mcstCipher.tkip) && + IsAuthenticatorEnabled(priv)) { + // re-enable the group re-key timer + pApInfo->bssData.grpRekeyCntRemaining + = pApInfo->bssData.grpRekeyCntConfigured; + } + connPtr->staData.apMicError.status = NO_MIC_FAILURE; + connPtr->staData.apMicError.disableStaAsso = 0; + + break; + default: + break; + } +} + +void +ApMicCounterMeasureInvoke(t_void *pconnPtr) +{ + cm_Connection *connPtr = (cm_Connection *)pconnPtr; + phostsa_private priv = (phostsa_private)connPtr->priv; + hostsa_mlan_fns *pm_fns = &priv->mlan_fns; + hostsa_util_fns *util_fns = &priv->util_fns; + apInfo_t *pApInfo = &priv->apinfo; + + if (connPtr->staData.apMicError.MICCounterMeasureEnabled) { + switch (connPtr->staData.apMicError.status) { + case NO_MIC_FAILURE: + util_fns->moal_start_timer(util_fns->pmoal_handle, + connPtr->staData.apMicTimer, + MFALSE, MRVDRV_TIMER_60S); + connPtr->staData.apMicError.status = + FIRST_MIC_FAIL_IN_60_SEC; + break; + + case FIRST_MIC_FAIL_IN_60_SEC: + connPtr->staData.apMicError.disableStaAsso = 1; + connPtr->staData.apMicError.status = + SECOND_MIC_FAIL_IN_60_SEC; + //start timer for 60 seconds + util_fns->moal_stop_timer(util_fns->pmoal_handle, + connPtr->staData.apMicTimer); + util_fns->moal_start_timer(util_fns->pmoal_handle, + connPtr->staData.apMicTimer, + MFALSE, MRVDRV_TIMER_60S); + + /* smeAPStateMgr_sendSmeMsg(connPtr, MlmeApBcastDisassoc); */ + pm_fns->Hostsa_DisAssocAllSta(priv->pmlan_private, + IEEEtypes_REASON_MIC_FAILURE); + // if current GTK is tkip + if ((pApInfo->bssConfig.RsnConfig.mcstCipher.tkip) && + IsAuthenticatorEnabled(priv)) { + //Disable periodic group rekey and re-init GTK. + priv->GrpRekeyTimerIsSet = MFALSE; + pApInfo->bssData.grpRekeyCntRemaining = 0; + ReInitGTK(priv); + } + break; + + default: + break; + } + } + return; +} diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/keyMgmtAp.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/keyMgmtAp.h new file mode 100644 index 000000000000..14e31d6d800a --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/keyMgmtAp.h @@ -0,0 +1,64 @@ +/** @file keyMgntAP.h + * + * @brief This file contains the eapol paket process and key management for authenticator + * + * Copyright (C) 2014-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 03/07/2014: Initial version +******************************************************/ +#ifndef _KEYMGMTAP_H_ +#define _KEYMGMTAP_H_ +//Authenticator related data structures, function prototypes + +#include "wltypes.h" +#include "keyMgmtAp_rom.h" + +#define STA_PS_EAPOL_HSK_TIMEOUT 3000 //ms +#define AP_RETRY_EAPOL_HSK_TIMEOUT 1000 //ms + +extern void KeyMgmtInit(void *priv); +extern void ReInitGTK(void *priv); +int isValidReplayCount(t_void *priv, apKeyMgmtInfoSta_t *pKeyMgmtInfo, + UINT8 *pRxReplayCount); + +extern void KeyMgmtGrpRekeyCountUpdate(t_void *context); +extern void KeyMgmtStartHskTimer(void *context); +extern void KeyMgmtStopHskTimer(void *context); +extern void KeyMgmtHskTimeout(t_void *context); +extern UINT32 keyApi_ApUpdateKeyMaterial(void *priv, cm_Connection *connPtr, + BOOLEAN updateGrpKey); + +extern Status_e ProcessPWKMsg2(hostsa_private *priv, + cm_Connection *connPtr, t_u8 *pbuf, t_u32 len); +extern Status_e ProcessPWKMsg4(hostsa_private *priv, + cm_Connection *connPtr, t_u8 *pbuf, t_u32 len); +extern Status_e ProcessGrpMsg2(hostsa_private *priv, + cm_Connection *connPtr, t_u8 *pbuf, t_u32 len); +extern Status_e GenerateApEapolMsg(hostsa_private *priv, + t_void *pconnPtr, keyMgmtState_e msgState); +extern void ApMicErrTimerExpCb(t_void *context); +extern void ApMicCounterMeasureInvoke(t_void *pconnPtr); +extern t_u16 keyMgmtAp_FormatWPARSN_IE(void *priv, + IEEEtypes_InfoElementHdr_t *pIe, + UINT8 isRsn, + Cipher_t *pCipher, + UINT8 cipherCount, + Cipher_t *pMcastCipher, + UINT16 authKey, UINT16 authKeyCount); +#endif //_KEYMGMTAP_H_ diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/keyMgmtApStaCommon.c b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/keyMgmtApStaCommon.c new file mode 100644 index 000000000000..b52f44a349fd --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/keyMgmtApStaCommon.c @@ -0,0 +1,679 @@ +/** @file keyMgmtApStaCommon.c + * + * @brief This file defines common api for authenticator and supplicant. + * + * Copyright (C) 2014-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 03/07/2014: Initial version +******************************************************/ +//Authenticator related function definitions +#include "wltypes.h" +#include "IEEE_types.h" + +#include "hostsa_ext_def.h" +#include "authenticator.h" + +#include "keyMgmtAp_rom.h" +#include "crypt_new_rom.h" +#include "keyCommonDef.h" +#include "pmkCache_rom.h" +#include "crypt_new_rom.h" +#include "rc4_rom.h" +#include "aes_cmac_rom.h" +#include "sha1.h" +#include "md5.h" +#include "mrvl_sha256_crypto.h" + +const UINT8 wpa_oui_none[4] = { 0x00, 0x50, 0xf2, 0x00 }; +const UINT8 wpa_oui01[4] = { 0x00, 0x50, 0xf2, 0x01 }; +const UINT8 wpa_oui02[4] = { 0x00, 0x50, 0xf2, 0x02 }; +const UINT8 wpa_oui03[4] = { 0x00, 0x50, 0xf2, 0x03 }; +const UINT8 wpa_oui04[4] = { 0x00, 0x50, 0xf2, 0x04 }; +const UINT8 wpa_oui05[4] = { 0x00, 0x50, 0xf2, 0x05 }; +const UINT8 wpa_oui06[4] = { 0x00, 0x50, 0xf2, 0x06 }; + +const UINT8 wpa2_oui01[4] = { 0x00, 0x0f, 0xac, 0x01 }; +const UINT8 wpa2_oui02[4] = { 0x00, 0x0f, 0xac, 0x02 }; +const UINT8 wpa2_oui03[4] = { 0x00, 0x0f, 0xac, 0x03 }; +const UINT8 wpa2_oui04[4] = { 0x00, 0x0f, 0xac, 0x04 }; +const UINT8 wpa2_oui05[4] = { 0x00, 0x0f, 0xac, 0x05 }; +const UINT8 wpa2_oui06[4] = { 0x00, 0x0f, 0xac, 0x06 }; + +const UINT8 wpa_oui[3] = { 0x00, 0x50, 0xf2 }; +const UINT8 kde_oui[3] = { 0x00, 0x0f, 0xac }; + +/** + * @brief strlen + * + * @param str A pointer to string + * + * @return Length of string + */ +t_u32 +wlan_strlen(const char *str) +{ + t_u32 i; + + for (i = 0; str[i] != 0; i++) { + } + return i; +} + +static t_u32 +srand_new(void *priv) +{ + phostsa_private psapriv = (phostsa_private)priv; + hostsa_util_fns *util_fns = &psapriv->util_fns; + t_u32 sec, usec; + + ENTER(); + get_system_time(util_fns, &sec, &usec); + sec = (sec & 0xFFFF) + (sec >> 16); + usec = (usec & 0xFFFF) + (usec >> 16); + + LEAVE(); + return (usec << 16) | sec; +} + +static unsigned int +rand_new(unsigned int seed, UINT32 randvaule) +{ + unsigned int next = seed; + unsigned int result; + + next *= (3515245 * randvaule + randvaule * next); + next += 12345 + randvaule * 7; + result = (unsigned int)(next / 65536) % 2048; + + next *= (39018768 * randvaule + randvaule * next); + next += 56789 + randvaule * 4; + result <<= 10; + result ^= (unsigned int)(next / 65536) % 1024; + + next *= (89042053 * randvaule + randvaule * next); + next += 43728 + randvaule * 9; + result <<= 10; + result ^= (unsigned int)(next / 65536) % 1024; + + return result; +} + +void +supplicantGenerateRand(hostsa_private *priv, UINT8 *dataOut, UINT32 length) +{ + UINT32 i; + //UINT32 valueHi, valueLo; + + /* Read mac 0 timer. + ** Doesn't matter which one we read. We just need a good seed. + */ + //msi_wl_GetMCUCoreTimerTxTSF(&valueHi, &valueLo); + //srand(valueLo); + for (i = 0; i < length; i++) { + //dataOut[i] = rand(); + dataOut[i] = rand_new(srand_new(priv), i + 1); + } +} + +void +SetEAPOLKeyDescTypeVersion(EAPOL_KeyMsg_Tx_t *pTxEapol, + BOOLEAN isWPA2, BOOLEAN isKDF, BOOLEAN nonTKIP) +{ + if (isWPA2) { + /* WPA2 */ + pTxEapol->keyMsg.desc_type = 2; + } else { + /* WPA */ + pTxEapol->keyMsg.desc_type = 254; + } + + if (isKDF) { + /* 802.11r and 802.11w use SHA256-KDF and a different KeyDescVer */ + pTxEapol->keyMsg.key_info.KeyDescriptorVersion = 3; + } else if (nonTKIP) { + /* CCMP */ + pTxEapol->keyMsg.key_info.KeyDescriptorVersion = 2; + } else { + /* TKIP OR WEP */ + pTxEapol->keyMsg.key_info.KeyDescriptorVersion = 1; + } +} + +void +ComputeEAPOL_MIC(phostsa_private priv, EAPOL_KeyMsg_t *pKeyMsg, + UINT16 data_length, + UINT8 *MIC_Key, UINT8 MIC_Key_length, UINT8 micKeyDescVersion) +{ + int len = data_length; + UINT8 *pMicData; + + pMicData = (UINT8 *)pKeyMsg; + + /* Allow the caller to override the algorithm used to get by some Cisco + ** CCX bugs where the wrong MIC algorithm is used + */ + if (micKeyDescVersion == 0) { + /* Algorithm not specified, use proper one from key_info */ + micKeyDescVersion = pKeyMsg->key_info.KeyDescriptorVersion; + } + + switch (micKeyDescVersion) { + case 3: + /* AES-128-CMAC */ + mrvl_aes_cmac(priv, MIC_Key, pMicData, len, + (UINT8 *)pKeyMsg->key_MIC); + break; + + case 2: + /* CCMP */ + Mrvl_hmac_sha1((t_void *)priv, &pMicData, + &len, + 1, + MIC_Key, + (int)MIC_Key_length, + (UINT8 *)pKeyMsg->key_MIC, EAPOL_MIC_SIZE); + break; + + default: + case 1: + /* TKIP or WEP */ + Mrvl_hmac_md5((t_void *)priv, pMicData, + data_length, + MIC_Key, + (int)MIC_Key_length, (UINT8 *)pKeyMsg->key_MIC); + break; + } +} + +/* Returns TRUE if EAPOL MIC check passes, FALSE otherwise. */ +BOOLEAN +IsEAPOL_MICValid(phostsa_private priv, EAPOL_KeyMsg_t *pKeyMsg, UINT8 *pMICKey) +{ + hostsa_util_fns *util_fns = &priv->util_fns; + UINT8 msgMIC[EAPOL_MIC_SIZE]; + + /* pull the MIC */ + memcpy(util_fns, (UINT8 *)msgMIC, (UINT8 *)pKeyMsg->key_MIC, + EAPOL_MIC_SIZE); + + /* zero the MIC key field before calculating the data */ + memset(util_fns, (UINT8 *)pKeyMsg->key_MIC, 0x00, EAPOL_MIC_SIZE); + + ComputeEAPOL_MIC(priv, pKeyMsg, (ntohs(pKeyMsg->hdr_8021x.pckt_body_len) + + sizeof(pKeyMsg->hdr_8021x)), + pMICKey, EAPOL_MIC_KEY_SIZE, 0); + + if (memcmp(util_fns, (UINT8 *)pKeyMsg->key_MIC, msgMIC, EAPOL_MIC_SIZE)) { +#ifdef KEYMSG_DEBUG + hostEventPrintf(assocAgent_getConnPtr(), + "EAPOL MIC Failure: cmac(%d), sha1(%d), md5(%d)", + (pKeyMsg->key_info.KeyDescriptorVersion == 3), + (pKeyMsg->key_info.KeyDescriptorVersion == 2), + (pKeyMsg->key_info.KeyDescriptorVersion == 1)); +#endif + /* MIC Failure */ + return FALSE; + } + + return TRUE; +} + +void +supplicantConstructContext(phostsa_private priv, UINT8 *pAddr1, + UINT8 *pAddr2, + UINT8 *pNonce1, UINT8 *pNonce2, UINT8 *pContext) +{ + hostsa_util_fns *util_fns = &priv->util_fns; + if (memcmp(util_fns, pAddr1, pAddr2, 6) < 0) { + memcpy(util_fns, pContext, pAddr1, sizeof(IEEEtypes_MacAddr_t)); + memcpy(util_fns, (pContext + 6), pAddr2, + sizeof(IEEEtypes_MacAddr_t)); + } else { + memcpy(util_fns, pContext, pAddr2, sizeof(IEEEtypes_MacAddr_t)); + memcpy(util_fns, (pContext + 6), pAddr1, + sizeof(IEEEtypes_MacAddr_t)); + } + + if (memcmp(util_fns, pNonce1, pNonce2, NONCE_SIZE) < 0) { + memcpy(util_fns, pContext + 6 + 6, pNonce1, NONCE_SIZE); + memcpy(util_fns, pContext + 6 + 6 + NONCE_SIZE, pNonce2, + NONCE_SIZE); + } else { + memcpy(util_fns, pContext + 6 + 6, pNonce2, NONCE_SIZE); + memcpy(util_fns, pContext + 6 + 6 + NONCE_SIZE, pNonce1, + NONCE_SIZE); + } +} + +UINT16 +KeyMgmtSta_PopulateEAPOLLengthMic(phostsa_private priv, + EAPOL_KeyMsg_Tx_t *pTxEapol, + UINT8 *pEAPOLMICKey, + UINT8 eapolProtocolVersion, + UINT8 forceKeyDescVersion) +{ + UINT16 frameLen; + +#if 0 //!defined(REMOVE_PATCH_HOOKS) + if (KeyMgmtSta_PopulateEAPOLLengthMic_hook(pTxEapol, + pEAPOLMICKey, + eapolProtocolVersion, + forceKeyDescVersion, + &frameLen)) { + return frameLen; + } +#endif + + if (!pTxEapol) { + return 0; + } + + frameLen = sizeof(pTxEapol->keyMsg); + frameLen -= sizeof(pTxEapol->keyMsg.hdr_8021x); + frameLen -= sizeof(pTxEapol->keyMsg.key_data); + frameLen += pTxEapol->keyMsg.key_material_len; + + pTxEapol->keyMsg.hdr_8021x.protocol_ver = eapolProtocolVersion; + pTxEapol->keyMsg.hdr_8021x.pckt_type = IEEE_8021X_PACKET_TYPE_EAPOL_KEY; + pTxEapol->keyMsg.hdr_8021x.pckt_body_len = htons(frameLen); + + pTxEapol->keyMsg.key_material_len + = htons(pTxEapol->keyMsg.key_material_len); + + ComputeEAPOL_MIC(priv, &pTxEapol->keyMsg, + frameLen + sizeof(pTxEapol->keyMsg.hdr_8021x), + pEAPOLMICKey, EAPOL_MIC_KEY_SIZE, forceKeyDescVersion); + + return frameLen; +} + +/* +** This function generates the Pairwise transient key +*/ +void +KeyMgmt_DerivePTK(phostsa_private priv, UINT8 *pAddr1, + UINT8 *pAddr2, + UINT8 *pNonce1, + UINT8 *pNonce2, UINT8 *pPTK, UINT8 *pPMK, BOOLEAN use_kdf) +{ + UINT8 *pContext; + char *prefix; + + /* pPTK is expected to be an encryption pool buffer (at least 500 bytes). + ** + ** Use the first portion for the ptk output. Use memory in the end of + ** the buffer for the context construction (76 bytes). + ** + ** The sha256 routine assumes available memory after the context for its + ** own sha256 output. Space after the context (76 bytes) is required + ** for 2 digests (2 * 32). pContext must have at least 76 + 64 bytes + ** available. + */ + pContext = pPTK + 200; + + supplicantConstructContext(priv, pAddr1, pAddr2, pNonce1, pNonce2, + pContext); + + prefix = "Pairwise key expansion"; + + if (use_kdf) { + mrvl_sha256_crypto_kdf((t_void *)priv, pPMK, PMK_LEN_MAX, prefix, 22, /* strlen(prefix) */ + pContext, 76, /* sizeof constructed context */ + pPTK, 384); + } else { + Mrvl_PRF((void *)priv, pPMK, PMK_LEN_MAX, (UINT8 *)prefix, 22, /* strlen(prefix) */ + pContext, 76, /* sizeof constructed context */ + pPTK, 64); + } +} + +void +KeyMgmtSta_DeriveKeys(hostsa_private *priv, UINT8 *pPMK, + UINT8 *da, + UINT8 *sa, + UINT8 *ANonce, + UINT8 *SNonce, + UINT8 *EAPOL_MIC_Key, + UINT8 *EAPOL_Encr_Key, + KeyData_t *newPWKey, BOOLEAN use_kdf) +{ + hostsa_util_fns *util_fns = &priv->util_fns; +// phostsa_private psapriv = (phostsa_private) priv; + // hostsa_util_fns *util_fns = &psapriv->util_fns; + //BufferDesc_t* pBufDesc = NULL; + UINT8 buf[500] = { 0 }; + TkipPtk_t *pPtk; + +#if 0 +#if !defined(REMOVE_PATCH_HOOKS) + if (KeyMgmtSta_DeriveKeys_hook(pPMK, + da, + sa, + ANonce, + SNonce, + EAPOL_MIC_Key, + EAPOL_Encr_Key, newPWKey, use_kdf)) { + return; + } +#endif +#endif + if (!pPMK || !EAPOL_MIC_Key || !newPWKey) { + return; + } +#if 0 + /* Wait forever ensures a buffer */ + pBufDesc = (BufferDesc_t *) bml_AllocBuffer(ramHook_encrPoolConfig, + 500, BML_WAIT_FOREVER); + pPtk = (TkipPtk_t *)BML_DATA_PTR(pBufDesc); +#endif + pPtk = (TkipPtk_t *)buf; + + KeyMgmt_DerivePTK(priv, sa, da, ANonce, SNonce, (UINT8 *)pPtk, pPMK, + use_kdf); + + memcpy(util_fns, EAPOL_MIC_Key, pPtk->kck, sizeof(pPtk->kck)); + memcpy(util_fns, EAPOL_Encr_Key, pPtk->kek, sizeof(pPtk->kek)); + memcpy(util_fns, newPWKey->Key, pPtk->tk, sizeof(pPtk->tk)); + + memcpy(util_fns, newPWKey->RxMICKey, + pPtk->rxMicKey, sizeof(pPtk->rxMicKey)); + + memcpy(util_fns, newPWKey->TxMICKey, + pPtk->txMicKey, sizeof(pPtk->txMicKey)); + +// bml_FreeBuffer((UINT32)pBufDesc); +} + +void +UpdateEAPOLWcbLenAndTransmit(hostsa_private *priv, pmlan_buffer pmbuf, + UINT16 frameLen) +{ + hostsa_mlan_fns *pm_fns = &priv->mlan_fns; + + pm_fns->hostsa_tx_packet(priv->pmlan_private, pmbuf, frameLen); +} + +void +formEAPOLEthHdr(phostsa_private priv, EAPOL_KeyMsg_Tx_t *pTxEapol, + t_u8 *da, t_u8 *sa) +{ + hostsa_util_fns *util_fns = &priv->util_fns; + memcpy(util_fns, (void *)pTxEapol->ethHdr.da, da, + IEEEtypes_ADDRESS_SIZE); + memcpy(util_fns, (void *)pTxEapol->ethHdr.sa, sa, + IEEEtypes_ADDRESS_SIZE); + pTxEapol->ethHdr.type = 0x8E88; +} + +void +supplicantParseWpaIe(phostsa_private priv, IEEEtypes_WPAElement_t *pIe, + SecurityMode_t *pWpaType, + Cipher_t *pMcstCipher, + Cipher_t *pUcstCipher, + AkmSuite_t *pAkmList, UINT8 akmOutMax) +{ + hostsa_util_fns *util_fns = &priv->util_fns; + IEEEtypes_WPAElement_t *pTemp = pIe; + int count; + int akmCount = akmOutMax; + AkmSuite_t *pAkm = pAkmList; + + memset(util_fns, pMcstCipher, 0x00, sizeof(Cipher_t)); + memset(util_fns, pUcstCipher, 0x00, sizeof(Cipher_t)); + memset(util_fns, pAkmList, 0x00, akmOutMax * sizeof(AkmSuite_t)); + memset(util_fns, pWpaType, 0x00, sizeof(SecurityMode_t)); + + pWpaType->wpa = 1; + + /* record the AP's multicast cipher */ + if (!memcmp + (util_fns, (char *)pTemp->GrpKeyCipher, wpa_oui02, + sizeof(wpa_oui02))) { + /* WPA TKIP */ + pMcstCipher->tkip = 1; + } else if (!memcmp + (util_fns, (char *)pTemp->GrpKeyCipher, wpa_oui04, + sizeof(wpa_oui04))) { + /* WPA AES */ + pMcstCipher->ccmp = 1; + } else if (!memcmp + (util_fns, (char *)pTemp->GrpKeyCipher, wpa_oui01, + sizeof(wpa_oui01))) { + /* WPA WEP 40 */ + pMcstCipher->wep40 = 1; + } else if (!memcmp + (util_fns, (char *)pTemp->GrpKeyCipher, wpa_oui05, + sizeof(wpa_oui05))) { + /* WPA WEP 104 */ + pMcstCipher->wep104 = 1; + } + + count = wlan_le16_to_cpu(pTemp->PwsKeyCnt); + + while (count) { + /* record the AP's unicast cipher */ + if (!memcmp(util_fns, (char *)pTemp->PwsKeyCipherList, + wpa_oui02, sizeof(wpa_oui02))) { + /* WPA TKIP */ + pUcstCipher->tkip = 1; + } else if (!memcmp(util_fns, (char *)pTemp->PwsKeyCipherList, + wpa_oui04, sizeof(wpa_oui04))) { + /* WPA AES */ + pUcstCipher->ccmp = 1; + } + count--; + + if (count) { + pTemp = (IEEEtypes_WPAElement_t *)((UINT8 *)pTemp + + sizeof(pTemp-> + PwsKeyCipherList)); + } + } + + count = wlan_le16_to_cpu(pTemp->AuthKeyCnt); + + while (count) { + if (akmCount) { + /* Store the AKM */ + memcpy(util_fns, pAkm, + (char *)pTemp->AuthKeyList, + sizeof(pTemp->AuthKeyList)); + pAkm++; + akmCount--; + } + + count--; + + if (count) { + pTemp = (IEEEtypes_WPAElement_t *)((UINT8 *)pTemp + + + sizeof(pTemp-> + AuthKeyList)); + } + } + + if (!memcmp(util_fns, pAkmList, wpa_oui_none, sizeof(wpa_oui_none))) { + pWpaType->wpaNone = 1; + } +} + +void +supplicantParseMcstCipher(phostsa_private priv, Cipher_t *pMcstCipherOut, + UINT8 *pGrpKeyCipher) +{ + hostsa_util_fns *util_fns = &priv->util_fns; + memset(util_fns, pMcstCipherOut, 0x00, sizeof(Cipher_t)); + + /* record the AP's multicast cipher */ + if (!memcmp(util_fns, pGrpKeyCipher, wpa2_oui02, sizeof(wpa2_oui02))) { + /* WPA2 TKIP */ + pMcstCipherOut->tkip = 1; + } else if (!memcmp + (util_fns, pGrpKeyCipher, wpa2_oui04, sizeof(wpa2_oui04))) { + /* WPA2 AES */ + pMcstCipherOut->ccmp = 1; + } else if (!memcmp + (util_fns, pGrpKeyCipher, wpa2_oui01, sizeof(wpa2_oui01))) { + /* WPA2 WEP 40 */ + pMcstCipherOut->wep40 = 1; + } else if (!memcmp + (util_fns, pGrpKeyCipher, wpa2_oui05, sizeof(wpa2_oui05))) { + /* WPA2 WEP 104 */ + pMcstCipherOut->wep104 = 1; + } +} + +void +supplicantParseUcstCipher(phostsa_private priv, Cipher_t *pUcstCipherOut, + UINT8 pwsKeyCnt, UINT8 *pPwsKeyCipherList) +{ + hostsa_util_fns *util_fns = &priv->util_fns; + UINT8 count; + + memset(util_fns, pUcstCipherOut, 0x00, sizeof(Cipher_t)); + + /* Cycle through the PwsKeyCipherList and record each unicast cipher */ + for (count = 0; count < pwsKeyCnt; count++) { + /* record the AP's unicast cipher */ + if (!memcmp(util_fns, pPwsKeyCipherList + (count * 4), + wpa2_oui02, sizeof(wpa2_oui02))) { + /* WPA2 TKIP */ + pUcstCipherOut->tkip = 1; + } else if (!memcmp(util_fns, pPwsKeyCipherList + (count * 4), + wpa2_oui04, sizeof(wpa2_oui04))) { + /* WPA2 AES */ + pUcstCipherOut->ccmp = 1; + } + } +} + +void +supplicantParseRsnIe(phostsa_private priv, IEEEtypes_RSNElement_t *pRsnIe, + SecurityMode_t *pWpaTypeOut, + Cipher_t *pMcstCipherOut, + Cipher_t *pUcstCipherOut, + AkmSuite_t *pAkmListOut, + UINT8 akmOutMax, + IEEEtypes_RSNCapability_t *pRsnCapOut, + Cipher_t *pGrpMgmtCipherOut) +{ + hostsa_util_fns *util_fns = &priv->util_fns; + UINT8 *pIeData; + UINT8 *pIeEnd; + UINT8 *pGrpKeyCipher; + UINT16 pwsKeyCnt; + UINT8 *pPwsKeyCipherList; + UINT16 authKeyCnt; + UINT8 *pAuthKeyList; + + IEEEtypes_RSNCapability_t *pRsnCap; + + UINT16 *pPMKIDCnt; + UINT16 PMKIDCnt; + + UINT8 *pGrpMgmtCipher; +#if 0 +#if !defined(REMOVE_PATCH_HOOKS) + if (supplicantParseRsnIe_hook(pRsnIe, + pWpaTypeOut, + pMcstCipherOut, + pUcstCipherOut, + pAkmListOut, + akmOutMax, + pRsnCapOut, pGrpMgmtCipherOut)) { + return; + } +#endif +#endif + memset(util_fns, pWpaTypeOut, 0x00, sizeof(SecurityMode_t)); + + pWpaTypeOut->wpa2 = 1; + + /* Set the start and end of the IE data */ + pIeData = (UINT8 *)&pRsnIe->Ver; + pIeEnd = pIeData + pRsnIe->Len; + + /* Skip past the version field */ + pIeData += sizeof(pRsnIe->Ver); + + /* Parse the group key cipher list */ + pGrpKeyCipher = pIeData; + pIeData += sizeof(pRsnIe->GrpKeyCipher); + supplicantParseMcstCipher(priv, pMcstCipherOut, pGrpKeyCipher); + + /* Parse the pairwise key cipher list */ + pwsKeyCnt = wlan_le16_to_cpu(*(UINT16 *)pIeData); + pIeData += sizeof(pRsnIe->PwsKeyCnt); + + pPwsKeyCipherList = pIeData; + pIeData += pwsKeyCnt * sizeof(pRsnIe->PwsKeyCipherList); + supplicantParseUcstCipher(priv, pUcstCipherOut, pwsKeyCnt, + pPwsKeyCipherList); + + /* Parse and return the AKM list */ + authKeyCnt = wlan_le16_to_cpu(*(UINT16 *)pIeData); + pIeData += sizeof(pRsnIe->AuthKeyCnt); + + pAuthKeyList = pIeData; + pIeData += authKeyCnt * sizeof(pRsnIe->AuthKeyList); + memset(util_fns, pAkmListOut, 0x00, akmOutMax * sizeof(AkmSuite_t)); + memcpy(util_fns, pAkmListOut, + pAuthKeyList, + MIN(authKeyCnt, akmOutMax) * sizeof(pRsnIe->AuthKeyList)); + + DBG_HEXDUMP(MCMD_D, " pAuthKeyList", + (t_u8 *)pAuthKeyList, MIN(authKeyCnt, + akmOutMax) * + sizeof(pRsnIe->AuthKeyList)); + DBG_HEXDUMP(MCMD_D, " pAuthKeyList", (t_u8 *)pAkmListOut, + MIN(authKeyCnt, akmOutMax) * sizeof(pRsnIe->AuthKeyList)); + /* Check if the RSN Capability is included */ + if (pIeData < pIeEnd) { + pRsnCap = (IEEEtypes_RSNCapability_t *)pIeData; + pIeData += sizeof(pRsnIe->RsnCap); + + if (pRsnCapOut) { + memcpy(util_fns, pRsnCapOut, pRsnCap, + sizeof(IEEEtypes_RSNCapability_t)); + } + } + + /* Check if the PMKID count is included */ + if (pIeData < pIeEnd) { + pPMKIDCnt = (UINT16 *)pIeData; + PMKIDCnt = wlan_le16_to_cpu(*pPMKIDCnt); + pIeData += sizeof(pRsnIe->PMKIDCnt); + + /* Check if the PMKID List is included */ + if (pIeData < pIeEnd) { + /* pPMKIDList = pIeData; <-- Currently not used in parsing */ + pIeData += PMKIDCnt * sizeof(pRsnIe->PMKIDList); + } + } + + /* Check if the Group Mgmt Cipher is included */ + if (pIeData < pIeEnd) { + pGrpMgmtCipher = pIeData; + + if (pGrpMgmtCipherOut) { + memcpy(util_fns, pGrpMgmtCipherOut, + pGrpMgmtCipher, sizeof(pRsnIe->GrpMgmtCipher)); + } + } +} diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/keyMgmtApStaCommon.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/keyMgmtApStaCommon.h new file mode 100644 index 000000000000..edaa29549049 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/keyMgmtApStaCommon.h @@ -0,0 +1,83 @@ +/** @file keyMgmtApStaCommon.h + * + * @brief This file contains common api for authenticator and supplicant. + * + * Copyright (C) 2014-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 03/07/2014: Initial version +******************************************************/ +#ifndef KEYMGMTAPSTACOMMON_H__ +#define KEYMGMTAPSTACOMMON_H__ +//Authenticator related data structures, function prototypes + +#include "wltypes.h" +#include "IEEE_types.h" +#include "sha1.h" +#include "keyMgmtStaTypes.h" +#include "wl_macros.h" +#include "keyMgmtApTypes.h" +#include "rc4_rom.h" + +#include "keyCommonDef.h" +#include "authenticator.h" + +extern t_u32 wlan_strlen(const char *str); +extern void supplicantGenerateRand(hostsa_private *priv, UINT8 *dataOut, + UINT32 length); +extern void SetEAPOLKeyDescTypeVersion(EAPOL_KeyMsg_Tx_t *pTxEapol, + BOOLEAN isWPA2, BOOLEAN isKDF, + BOOLEAN nonTKIP); +extern void ComputeEAPOL_MIC(phostsa_private priv, EAPOL_KeyMsg_t *pKeyMsg, + UINT16 data_length, UINT8 *MIC_Key, + UINT8 MIC_Key_length, UINT8 micKeyDescVersion); +extern BOOLEAN IsEAPOL_MICValid(phostsa_private priv, EAPOL_KeyMsg_t *pKeyMsg, + UINT8 *pMICKey); +extern void supplicantConstructContext(phostsa_private priv, UINT8 *pAddr1, + UINT8 *pAddr2, UINT8 *pNonce1, + UINT8 *pNonce2, UINT8 *pContext); +extern UINT16 KeyMgmtSta_PopulateEAPOLLengthMic(phostsa_private priv, + EAPOL_KeyMsg_Tx_t *pTxEapol, + UINT8 *pEAPOLMICKey, + UINT8 eapolProtocolVersion, + UINT8 forceKeyDescVersion); +extern void KeyMgmt_DerivePTK(phostsa_private priv, UINT8 *pAddr1, + UINT8 *pAddr2, UINT8 *pNonce1, UINT8 *pNonce2, + UINT8 *pPTK, UINT8 *pPMK, BOOLEAN use_kdf); +extern void KeyMgmtSta_DeriveKeys(hostsa_private *priv, UINT8 *pPMK, UINT8 *da, + UINT8 *sa, UINT8 *ANonce, UINT8 *SNonce, + UINT8 *EAPOL_MIC_Key, UINT8 *EAPOL_Encr_Key, + KeyData_t *newPWKey, BOOLEAN use_kdf); +extern void UpdateEAPOLWcbLenAndTransmit(hostsa_private *priv, + pmlan_buffer pmbuf, UINT16 frameLen); +extern void formEAPOLEthHdr(phostsa_private priv, EAPOL_KeyMsg_Tx_t *pTxEapol, + t_u8 *da, t_u8 *sa); +extern void supplicantParseWpaIe(phostsa_private priv, + IEEEtypes_WPAElement_t *pIe, + SecurityMode_t *pWpaType, + Cipher_t *pMcstCipher, Cipher_t *pUcstCipher, + AkmSuite_t *pAkmList, UINT8 akmOutMax); +extern void supplicantParseRsnIe(phostsa_private priv, + IEEEtypes_RSNElement_t *pRsnIe, + SecurityMode_t *pWpaTypeOut, + Cipher_t *pMcstCipherOut, + Cipher_t *pUcstCipherOut, + AkmSuite_t *pAkmListOut, UINT8 akmOutMax, + IEEEtypes_RSNCapability_t *pRsnCapOut, + Cipher_t *pGrpMgmtCipherOut); +#endif //KEYMGMTAPSTACOMMON_H__ diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/keyMgmtApTypes.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/keyMgmtApTypes.h new file mode 100644 index 000000000000..4cd5fc32928d --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/keyMgmtApTypes.h @@ -0,0 +1,68 @@ +/** @file KeyMgmtApTypes.h + * + * @brief This file contains the key management type for ap + * + * Copyright (C) 2014-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 03/07/2014: Initial version +******************************************************/ +#ifndef _KEYMGMTAPTYPES_H_ +#define _KEYMGMTAPTYPES_H_ + +#include "wltypes.h" +#include "IEEE_types.h" +#include "keyMgmtStaTypes.h" +#include "keyMgmtApTypes_rom.h" +#include "keyCommonDef.h" + +typedef enum { + STA_ASSO_EVT, + MSGRECVD_EVT, + KEYMGMTTIMEOUT_EVT, + GRPKEYTIMEOUT_EVT, + UPDATEKEYS_EVT +} keyMgmt_HskEvent_e; + +/* Fields till keyMgmtState are being accessed in rom code and + * should be kept intact. Fields after keyMgmtState can be changed + * safely. + */ +typedef struct { + apKeyMgmtInfoStaRom_t rom; + UINT8 numHskTries; + UINT32 counterLo; + UINT32 counterHi; + UINT8 EAPOL_MIC_Key[EAPOL_MIC_KEY_SIZE]; + UINT8 EAPOL_Encr_Key[EAPOL_ENCR_KEY_SIZE]; + UINT8 EAPOLProtoVersion; + UINT8 rsvd[3]; +} apKeyMgmtInfoSta_t; + +/* Convert an Ascii character to a hex nibble + e.g. Input is 'b' : Output will be 0xb + Input is 'E' : Output will be 0xE + Input is '8' : Output will be 0x8 + Assumption is that input is a-f or A-F or 0-9 +*/ +#define ASCII2HEX(Asc) (((Asc) >= 'a') ? (Asc - 'a' + 0xA)\ + : ( (Asc) >= 'A' ? ( (Asc) - 'A' + 0xA ) : ((Asc) - '0') )) + +#define ETH_P_EAPOL 0x8E88 + +#endif //_KEYMGMTAPTYPES_H_ diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/keyMgmtApTypes_rom.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/keyMgmtApTypes_rom.h new file mode 100644 index 000000000000..02b79dcd2d0a --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/keyMgmtApTypes_rom.h @@ -0,0 +1,63 @@ +/** @file KeyMgmtApTypes_rom.h + * + * @brief This file contains the key management type for ap + * + * Copyright (C) 2014-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 03/07/2014: Initial version +******************************************************/ +#ifndef _KEYMGMTAPTYPES_ROM_H_ +#define _KEYMGMTAPTYPES_ROM_H_ + +#include "wltypes.h" +#include "keyMgmtStaTypes.h" + +#define KDE_IE_SIZE (2) // type+length of KDE_t +#define KDE_SIZE (KDE_IE_SIZE + 4 ) // OUI+datatype of KDE_t +#define GTK_IE_SIZE (2) +#define KEYDATA_SIZE (4 + GTK_IE_SIZE + TK_SIZE) //OUI+datatype+ GTK_IE+ GTK + +typedef enum { + HSK_NOT_STARTED, + MSG1_PENDING, + WAITING_4_MSG2, + MSG3_PENDING, + WAITING_4_MSG4, + GRPMSG1_PENDING, + WAITING_4_GRPMSG2, + GRP_REKEY_MSG1_PENDING, + WAITING_4_GRP_REKEY_MSG2, + /* the relative positions of the different enum elements + ** should not be changed since FW code checks for even/odd + ** values at certain places. + */ + HSK_DUMMY_STATE, + HSK_END +} keyMgmtState_e; + +/* This sturcture is being accessed in rom code and should be kept intact. */ +typedef struct { + UINT16 staRsnCap; + SecurityMode_t staSecType; + Cipher_t staUcstCipher; + UINT8 staAkmType; + keyMgmtState_e keyMgmtState; +} apKeyMgmtInfoStaRom_t; + +#endif diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/keyMgmtAp_rom.c b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/keyMgmtAp_rom.c new file mode 100644 index 000000000000..0ccc074c0f38 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/keyMgmtAp_rom.c @@ -0,0 +1,574 @@ +/** @file keyMgmtAp_rom.c + * + * @brief This file defines api for key managment + * + * Copyright (C) 2014-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 03/07/2014: Initial version +******************************************************/ +//Authenticator related function definitions +#include "wltypes.h" +#include "IEEE_types.h" + +#include "hostsa_ext_def.h" +#include "authenticator.h" + +#include "keyMgmtAp_rom.h" +#include "crypt_new_rom.h" +#include "keyCommonDef.h" +#include "pmkCache_rom.h" +#include "crypt_new_rom.h" +#include "rc4_rom.h" +#include "aes_cmac_rom.h" +#include "sha1.h" +#include "md5.h" +#include "mrvl_sha256_crypto.h" + +UINT32 +util_FindLowestBitSet(UINT32 val) +{ + UINT32 bitmap = 1; + + while (bitmap && (!(bitmap & val))) { + bitmap <<= 1; + } + + return bitmap; +} + +UINT8 +convertMrvlAuthToIEEEAuth(UINT32 mrvlauth) +{ + UINT8 auth; + + switch (mrvlauth) { + case UAP_HOSTCMD_KEYMGMT_EAP: + auth = IEEEtypes_RSN_AUTH_KEY_SUITE_8021X; + break; + case UAP_HOSTCMD_KEYMGMT_PSK: + auth = IEEEtypes_RSN_AUTH_KEY_SUITE_PSK; + break; + case UAP_HOSTCMD_KEYMGMT_NONE: + default: + auth = IEEEtypes_RSN_AUTH_KEY_SUITE_RSVD; + break; + } + return auth; +} + +UINT32 +convertIEEEAuthToMrvlAuth(UINT8 auth) +{ + UINT32 MrvlAuth = 0; + switch (auth) { + case IEEEtypes_RSN_AUTH_KEY_SUITE_8021X: + MrvlAuth |= UAP_HOSTCMD_KEYMGMT_EAP; + break; + case IEEEtypes_RSN_AUTH_KEY_SUITE_PSK: + MrvlAuth |= UAP_HOSTCMD_KEYMGMT_PSK; + break; + case IEEEtypes_RSN_AUTH_KEY_SUITE_RSVD: + default: + MrvlAuth = 0; + break; + } + return MrvlAuth; +} + +UINT8 +convertMrvlCipherToIEEECipher(UINT8 mrvlcipher) +{ + UINT8 Cipher; + switch (mrvlcipher) { + case UAP_HOSTCMD_CIPHER_WEP40: + Cipher = IEEEtypes_RSN_CIPHER_SUITE_WEP40; + break; + case UAP_HOSTCMD_CIPHER_WEP104: + Cipher = IEEEtypes_RSN_CIPHER_SUITE_WEP104; + break; + case UAP_HOSTCMD_CIPHER_TKIP: + Cipher = IEEEtypes_RSN_CIPHER_SUITE_TKIP; + break; + case UAP_HOSTCMD_CIPHER_CCMP: + Cipher = IEEEtypes_RSN_CIPHER_SUITE_CCMP; + break; + default: + Cipher = IEEEtypes_RSN_CIPHER_SUITE_NONE; + break; + } + return Cipher; +} + +UINT32 +convertIEEECipherToMrvlCipher(UINT8 cipher) +{ + UINT32 MrvlCipher = 0; + switch (cipher) { + case IEEEtypes_RSN_CIPHER_SUITE_WEP40: + MrvlCipher |= UAP_HOSTCMD_CIPHER_WEP40; + break; + case IEEEtypes_RSN_CIPHER_SUITE_TKIP: + MrvlCipher |= UAP_HOSTCMD_CIPHER_TKIP; + break; + case IEEEtypes_RSN_CIPHER_SUITE_CCMP: + MrvlCipher |= UAP_HOSTCMD_CIPHER_CCMP; + break; + case IEEEtypes_RSN_CIPHER_SUITE_WEP104: + MrvlCipher |= UAP_HOSTCMD_CIPHER_WEP104; + break; + case IEEEtypes_RSN_CIPHER_SUITE_NONE: + case IEEEtypes_RSN_CIPHER_SUITE_WRAP: + default: + MrvlCipher = 0; + break; + } + return MrvlCipher; +} + +void +GenerateGTK_internal(hostsa_private *priv, KeyData_t *grpKeyData, + UINT8 *nonce, UINT8 *StaMacAddr) +{ + hostsa_util_fns *util_fns = &priv->util_fns; + UINT8 inp_data[NONCE_SIZE + sizeof(IEEEtypes_MacAddr_t)]; + UINT8 prefix[] = "Group key expansion"; + UINT8 GTK[32]; //group transient key + UINT8 grpMasterKey[32]; + + if (!grpKeyData || !nonce) { + return; + } + + memcpy(util_fns, inp_data, StaMacAddr, sizeof(IEEEtypes_MacAddr_t)); + supplicantGenerateRand(priv, nonce, NONCE_SIZE); + memcpy(util_fns, inp_data + sizeof(IEEEtypes_MacAddr_t), nonce, + NONCE_SIZE); + supplicantGenerateRand(priv, grpMasterKey, sizeof(grpMasterKey)); + Mrvl_PRF((void *)priv, grpMasterKey, + sizeof(grpMasterKey), + prefix, + wlan_strlen((char *)prefix), + inp_data, sizeof(inp_data), GTK, sizeof(GTK)); + memcpy(util_fns, grpKeyData->Key, GTK, TK_SIZE); + memcpy(util_fns, grpKeyData->TxMICKey, GTK + TK_SIZE, MIC_KEY_SIZE); + memcpy(util_fns, grpKeyData->RxMICKey, GTK + TK_SIZE + MIC_KEY_SIZE, + MIC_KEY_SIZE); + +} + +/* + Populates EAPOL frame based on Cipher, given Nonce, replay counters, + and type, which encodes whether this is secure, part of WPA2 or WPA + handshake. + This is currently used for EAPOL sent from AP, msg1, msg3 and group + key msg. +*/ +void +PopulateKeyMsg(hostsa_private *priv, EAPOL_KeyMsg_Tx_t *tx_eapol_ptr, + Cipher_t *Cipher, + UINT16 Type, UINT32 replay_cnt[2], UINT8 *Nonce) +{ + hostsa_util_fns *util_fns = &priv->util_fns; + key_info_t *pKeyInfo; + + if (!tx_eapol_ptr || !Cipher) { + return; + } + + pKeyInfo = &tx_eapol_ptr->keyMsg.key_info; + + if (Cipher->tkip) { + //TKIP unicast + tx_eapol_ptr->keyMsg.key_length = + SHORT_SWAP((TK_SIZE + TK_SIZE)); + } else if (Cipher->ccmp) { + //CCMP unicast + tx_eapol_ptr->keyMsg.key_length = SHORT_SWAP(TK_SIZE); + } + + pKeyInfo->KeyAck = 1; + + if (Type & PAIRWISE_KEY_MSG) { + pKeyInfo->KeyType = 1; + if (Type & SECURE_HANDSHAKE_FLAG) { + pKeyInfo->KeyMIC = 1; + pKeyInfo->Install = 1; + pKeyInfo->EncryptedKeyData = pKeyInfo->Secure = + (Type & WPA2_HANDSHAKE) ? 1 : 0; + } + } else { + pKeyInfo->Secure = 1; + pKeyInfo->KeyMIC = 1; + pKeyInfo->EncryptedKeyData = (Type & WPA2_HANDSHAKE) ? 1 : 0; + } + + tx_eapol_ptr->keyMsg.replay_cnt[0] = WORD_SWAP(replay_cnt[0]); + tx_eapol_ptr->keyMsg.replay_cnt[1] = WORD_SWAP(replay_cnt[1]); + memcpy(util_fns, (void *)tx_eapol_ptr->keyMsg.key_nonce, Nonce, + NONCE_SIZE); + + DBG_HEXDUMP(MCMD_D, " nonce ", + (t_u8 *)tx_eapol_ptr->keyMsg.key_nonce, NONCE_SIZE); +} + +/* + Function to prepare KDE in EAPOL frame . + Used by the AP to encapsulate GTK +*/ + +void +prepareKDE(hostsa_private *priv, EAPOL_KeyMsg_Tx_t *tx_eapol_ptr, + KeyData_t *grKey, Cipher_t *cipher) +{ + hostsa_util_fns *util_fns = &priv->util_fns; + KDE_t *pKeyDataWPA2; + GTK_KDE_t *pGTK_IE; + UINT8 RsnIE_len = 0, PadLen = 0; + UINT8 *buf_p; + + if (!tx_eapol_ptr || !grKey || !cipher) { + return; + } + + RsnIE_len = tx_eapol_ptr->keyMsg.key_material_len; + buf_p = (UINT8 *)(tx_eapol_ptr->keyMsg.key_data + RsnIE_len); + + pKeyDataWPA2 = (KDE_t *)buf_p; + pKeyDataWPA2->type = 0xdd; + pKeyDataWPA2->length = KEYDATA_SIZE; + pKeyDataWPA2->OUI[0] = kde_oui[0]; + pKeyDataWPA2->OUI[1] = kde_oui[1]; + pKeyDataWPA2->OUI[2] = kde_oui[2]; + pKeyDataWPA2->dataType = 1; + buf_p = buf_p + KDE_SIZE; + + pGTK_IE = (GTK_KDE_t *)buf_p; + pGTK_IE->KeyID = 1; + buf_p = buf_p + GTK_IE_SIZE; + + // copy over GTK + memcpy(util_fns, (void *)buf_p, (void *)grKey->Key, TK_SIZE); + buf_p = buf_p + TK_SIZE; + + if (cipher->tkip) { + pKeyDataWPA2->length += (MIC_KEY_SIZE + MIC_KEY_SIZE); + memcpy(util_fns, (void *)buf_p, (void *)grKey->TxMICKey, + MIC_KEY_SIZE); + buf_p = buf_p + MIC_KEY_SIZE; + memcpy(util_fns, (void *)buf_p, (void *)grKey->RxMICKey, + MIC_KEY_SIZE); + buf_p = buf_p + MIC_KEY_SIZE; + } + + tx_eapol_ptr->keyMsg.key_material_len += (pKeyDataWPA2->length + + KDE_IE_SIZE); + + PadLen = ((8 - ((tx_eapol_ptr->keyMsg.key_material_len) % 8)) % 8); + if (PadLen) { + *(buf_p) = 0xdd; + memset(util_fns, (void *)(buf_p + 1), 0, PadLen - 1); + tx_eapol_ptr->keyMsg.key_material_len += PadLen; + } + +} + +BOOLEAN +Encrypt_keyData(hostsa_private *priv, EAPOL_KeyMsg_Tx_t *tx_eapol_ptr, + UINT8 *EAPOL_Encr_Key, Cipher_t *cipher) +{ + hostsa_util_fns *util_fns = &priv->util_fns; + UINT8 key[16]; + UINT8 cipherText[400] = { 0 }; + + if (!tx_eapol_ptr || !EAPOL_Encr_Key || !cipher) { + return FALSE; + } + + if (cipher->ccmp) { + // Pairwise is CCMP + memcpy(util_fns, (void *)key, EAPOL_Encr_Key, 16); + + // use AES-only mode from AEU HW to perform AES wrap + MRVL_AesWrap(key, 2, tx_eapol_ptr->keyMsg.key_material_len / 8, + tx_eapol_ptr->keyMsg.key_data, NULL, cipherText); + + tx_eapol_ptr->keyMsg.key_material_len += 8; + memcpy(util_fns, (void *)tx_eapol_ptr->keyMsg.key_data, + cipherText, tx_eapol_ptr->keyMsg.key_material_len); + } else if (cipher->tkip) { + // Pairwise is TKIP + supplicantGenerateRand(priv, + (UINT8 *)tx_eapol_ptr->keyMsg. + EAPOL_key_IV, 16); + RC4_Encrypt((t_void *)priv, (unsigned char *)EAPOL_Encr_Key, + (unsigned char *)&tx_eapol_ptr->keyMsg.EAPOL_key_IV, + 16, (unsigned char *)&tx_eapol_ptr->keyMsg.key_data, + (unsigned short)tx_eapol_ptr->keyMsg. + key_material_len, 256); + } else { + return FALSE; + } + + return TRUE; +} + +void +KeyMgmtAp_DerivePTK(hostsa_private *priv, + UINT8 *pPMK, + t_u8 *da, + t_u8 *sa, + UINT8 *ANonce, + UINT8 *SNonce, + UINT8 *EAPOL_MIC_Key, + UINT8 *EAPOL_Encr_Key, KeyData_t *newPWKey, BOOLEAN use_kdf) +{ + hostsa_util_fns *util_fns = &priv->util_fns; + UINT8 tmp[MIC_KEY_SIZE]; + + // call STA PTK generation funciton first + KeyMgmtSta_DeriveKeys(priv, pPMK, + da, + sa, + ANonce, + SNonce, + EAPOL_MIC_Key, EAPOL_Encr_Key, newPWKey, use_kdf); + + // We need to swap Rx/Tx Keys for AP + + memcpy(util_fns, tmp, newPWKey->RxMICKey, MIC_KEY_SIZE); + memcpy(util_fns, newPWKey->RxMICKey, newPWKey->TxMICKey, MIC_KEY_SIZE); + memcpy(util_fns, newPWKey->TxMICKey, tmp, MIC_KEY_SIZE); + +} + +BOOLEAN +KeyData_CopyWPAWP2(hostsa_private *priv, EAPOL_KeyMsg_Tx_t *pTxEAPOL, void *pIe) +{ + hostsa_util_fns *util_fns = &priv->util_fns; + IEEEtypes_InfoElementHdr_t *pElement = + (IEEEtypes_InfoElementHdr_t *)pIe; + + if (!pIe) { + return FALSE; + } + + pTxEAPOL->keyMsg.key_material_len = + pElement->Len + sizeof(IEEEtypes_InfoElementHdr_t); + + memcpy(util_fns, (void *)pTxEAPOL->keyMsg.key_data, + pIe, pTxEAPOL->keyMsg.key_material_len); + + return TRUE; +} + +BOOLEAN +KeyData_UpdateKeyMaterial(hostsa_private *priv, EAPOL_KeyMsg_Tx_t *pTxEAPOL, + SecurityMode_t *pSecType, void *pWPA, void *pWPA2) +{ + + if (pSecType->wpa || pSecType->wpaNone) { + if (KeyData_CopyWPAWP2(priv, pTxEAPOL, pWPA) == FALSE) { + return FALSE; + } + } else if (pSecType->wpa2) { + if (KeyData_CopyWPAWP2(priv, pTxEAPOL, pWPA2) == FALSE) { + return FALSE; + } + } + + return TRUE; +} + +void +KeyData_AddGTK(hostsa_private *priv, EAPOL_KeyMsg_Tx_t *pTxEAPOL, + KeyData_t *grKey, Cipher_t *cipher) +{ + hostsa_util_fns *util_fns = &priv->util_fns; + UINT8 *buf_p; + buf_p = (UINT8 *)pTxEAPOL->keyMsg.key_data; + memcpy(util_fns, (void *)buf_p, (void *)grKey, TK_SIZE); + + buf_p = buf_p + TK_SIZE; + + pTxEAPOL->keyMsg.key_material_len += TK_SIZE; + + if (cipher->tkip) { + memcpy(util_fns, (void *)buf_p, (void *)grKey->TxMICKey, + MIC_KEY_SIZE); + buf_p = buf_p + MIC_KEY_SIZE; + memcpy(util_fns, (void *)buf_p, (void *)grKey->RxMICKey, + MIC_KEY_SIZE); + pTxEAPOL->keyMsg.key_material_len += (MIC_KEY_SIZE + + MIC_KEY_SIZE); + } +} + +/* Returns FALSE if security type is other than WPA* */ +BOOLEAN +KeyData_AddKey(hostsa_private *priv, EAPOL_KeyMsg_Tx_t *pTxEAPOL, + SecurityMode_t *pSecType, KeyData_t *grKey, Cipher_t *cipher) +{ + hostsa_util_fns *util_fns = &priv->util_fns; + BOOLEAN status = FALSE; + + pTxEAPOL->keyMsg.key_info.KeyIndex = grKey->KeyIndex; + + pTxEAPOL->keyMsg.key_RSC[0] = grKey->TxIV16 & 0x00FF; + pTxEAPOL->keyMsg.key_RSC[1] = (grKey->TxIV16 >> 8) & 0x00FF; + memcpy(util_fns, (void *)(pTxEAPOL->keyMsg.key_RSC + 2), + &grKey->TxIV32, 4); + + if (pSecType->wpa || pSecType->wpaNone) { + KeyData_AddGTK(priv, pTxEAPOL, grKey, cipher); + status = TRUE; + } else if (pSecType->wpa2) { + prepareKDE(priv, pTxEAPOL, grKey, cipher); + status = TRUE; + } + return status; +} + +void +ROM_InitGTK(hostsa_private *priv, KeyData_t *grpKeyData, UINT8 *nonce, + UINT8 *StaMacAddr) +{ + grpKeyData->KeyIndex = 1; + grpKeyData->TxIV16 = 1; + grpKeyData->TxIV32 = 0; + + GenerateGTK_internal(priv, grpKeyData, nonce, StaMacAddr); +} + +t_u16 +keyMgmtAp_FormatWPARSN_IE_internal(phostsa_private priv, + IEEEtypes_InfoElementHdr_t *pIe, + UINT8 isRsn, + Cipher_t *pCipher, + UINT8 cipherCnt, + Cipher_t *pMcastCipher, + UINT16 authKey, UINT16 authKeyCnt) +{ + phostsa_util_fns util_fns = &priv->util_fns; + int i; + UINT8 *pBuf = NULL; + IEEEtypes_RSNElement_t *pRsn = (IEEEtypes_RSNElement_t *)pIe; + IEEEtypes_WPAElement_t *pWpa = (IEEEtypes_WPAElement_t *)pIe; + + UINT16 bitPos = 0; + UINT16 authKeyBitmap = authKey; + UINT8 ucastBitmap = *((UINT8 *)pCipher); + UINT8 mcastBitmap = *((UINT8 *)pMcastCipher); + UINT8 oui[3]; + UINT16 ieLength = 0; + + pIe->ElementId = (isRsn == 1) ? ELEM_ID_RSN : ELEM_ID_VENDOR_SPECIFIC; + + if (isRsn) { + memcpy(util_fns, (void *)&oui, (void *)&kde_oui, sizeof(oui)); + pBuf = (UINT8 *)&pRsn->Ver; + } else { + memcpy(util_fns, (void *)&oui, (void *)&wpa_oui01, sizeof(oui)); + memcpy(util_fns, (void *)&pWpa->OuiType, (void *)&wpa_oui01, + sizeof(wpa_oui01)); + pBuf = (UINT8 *)&pWpa->Ver; + } + + pBuf[0] = 0x1; + pBuf[1] = 0x0; + pBuf += 2; + + //filling group cipher + memcpy(util_fns, (void *)pBuf, (void *)&oui, sizeof(oui)); + + if (mcastBitmap) { + bitPos = util_FindLowestBitSet(mcastBitmap); + } + + pBuf[3] = convertMrvlCipherToIEEECipher(bitPos); + pBuf += 4; + + pBuf[0] = (cipherCnt >> 0) & 0xFF; + pBuf[1] = (cipherCnt >> 16) & 0xFF; + pBuf += 2; + + for (i = 0; i < cipherCnt; i++) { + pBuf[0] = oui[0]; + pBuf[1] = oui[1]; + pBuf[2] = oui[2]; + + bitPos = util_FindLowestBitSet(ucastBitmap); + + pBuf[3] = convertMrvlCipherToIEEECipher(bitPos); + + ucastBitmap &= ~bitPos; + + pBuf += 4; + } + + pBuf[0] = (authKeyCnt >> 0) & 0xFF; + pBuf[1] = (authKeyCnt >> 16) & 0xFF; + pBuf += 2; + + for (i = 0; i < authKeyCnt; i++) { + pBuf[0] = oui[0]; + pBuf[1] = oui[1]; + pBuf[2] = oui[2]; + + bitPos = util_FindLowestBitSet(authKeyBitmap); + + pBuf[3] = convertMrvlAuthToIEEEAuth(bitPos); + + authKeyBitmap &= ~bitPos; + pBuf += 4; + } + + if (isRsn) { + pBuf[0] = 0x0; + pBuf[1] = 0x0; + pBuf += 2; + } + + ieLength = (unsigned long)pBuf - (unsigned long)pIe; + pIe->Len = ieLength - sizeof(IEEEtypes_InfoElementHdr_t); + DBG_HEXDUMP(MCMD_D, "RSN or WPA IE", (t_u8 *)pIe, ieLength); + return ieLength; +} + +/* Ideally one day this function should re-use client code */ +t_u16 +keyMgmtAp_FormatWPARSN_IE(hostsa_private *priv, + IEEEtypes_InfoElementHdr_t *pIe, + UINT8 isRsn, + Cipher_t *pCipher, + UINT8 cipherCount, + Cipher_t *pMcastCipher, + UINT16 authKey, UINT16 authKeyCount) +{ + + UINT16 ieLength; + + ieLength = keyMgmtAp_FormatWPARSN_IE_internal(priv, pIe, + isRsn, + pCipher, + cipherCount, + pMcastCipher, + authKey, authKeyCount); + + return ieLength; +} diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/keyMgmtAp_rom.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/keyMgmtAp_rom.h new file mode 100644 index 000000000000..76ec8ec71883 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/keyMgmtAp_rom.h @@ -0,0 +1,84 @@ +/** @file keyMgmtAp_rom.h + * + * @brief This file contains define for key management + * + * Copyright (C) 2014-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 03/07/2014: Initial version +******************************************************/ +#ifndef KEYMGMTAP_ROM_H__ +#define KEYMGMTAP_ROM_H__ +//Authenticator related data structures, function prototypes + +#include "wltypes.h" +#include "IEEE_types.h" +#include "sha1.h" +#include "keyMgmtStaTypes.h" +#include "wl_macros.h" +#include "keyMgmtApTypes.h" +#include "rc4_rom.h" + +#include "keyCommonDef.h" +#include "authenticator.h" +#include "keyMgmtApStaCommon.h" + +/* This flags if the Secure flag in EAPOL key frame must be set */ +#define SECURE_HANDSHAKE_FLAG 0x0080 +/* Whether the EAPOL frame is for pairwise or group Key */ +#define PAIRWISE_KEY_MSG 0x0800 +/* Flags when WPA2 is enabled, not used for WPA */ +#define WPA2_HANDSHAKE 0x8000 + +extern void GenerateGTK_internal(hostsa_private *priv, KeyData_t *grpKeyData, + UINT8 *nonce, UINT8 *StaMacAddr); + +extern void PopulateKeyMsg(hostsa_private *priv, + EAPOL_KeyMsg_Tx_t *tx_eapol_ptr, Cipher_t *Cipher, + UINT16 Type, UINT32 replay_cnt[2], UINT8 *Nonce); + +extern void prepareKDE(hostsa_private *priv, EAPOL_KeyMsg_Tx_t *tx_eapol_ptr, + KeyData_t *grKey, Cipher_t *cipher); + +extern BOOLEAN Encrypt_keyData(hostsa_private *priv, + EAPOL_KeyMsg_Tx_t *tx_eapol_ptr, + UINT8 *EAPOL_Encr_Key, Cipher_t *cipher); + +extern void ROM_InitGTK(hostsa_private *priv, KeyData_t *grpKeyData, + UINT8 *nonce, UINT8 *StaMacAddr); + +extern void KeyData_AddGTK(hostsa_private *priv, EAPOL_KeyMsg_Tx_t *pTxEAPOL, + KeyData_t *grKey, Cipher_t *cipher); + +extern BOOLEAN KeyData_AddKey(hostsa_private *priv, EAPOL_KeyMsg_Tx_t *pTxEAPOL, + SecurityMode_t *pSecType, + KeyData_t *grKey, Cipher_t *cipher); + +extern BOOLEAN KeyData_CopyWPAWP2(hostsa_private *priv, + EAPOL_KeyMsg_Tx_t *pTxEAPOL, void *pIe); + +extern BOOLEAN KeyData_UpdateKeyMaterial(hostsa_private *priv, + EAPOL_KeyMsg_Tx_t *pTxEAPOL, + SecurityMode_t *pSecType, void *pWPA, + void *pWPA2); +extern void KeyMgmtAp_DerivePTK(hostsa_private *priv, UINT8 *pPMK, t_u8 *da, + t_u8 *sa, UINT8 *ANonce, UINT8 *SNonce, + UINT8 *EAPOL_MIC_Key, UINT8 *EAPOL_Encr_Key, + KeyData_t *newPWKey, BOOLEAN use_kdf); + +#endif //_KEYMGMTAP_ROM_H_ diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/keyMgmtSta.c b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/keyMgmtSta.c new file mode 100644 index 000000000000..f82efdfc3b9c --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/keyMgmtSta.c @@ -0,0 +1,2365 @@ +/** @file keyMgmtSta.c + * + * @brief This file defines key management API for sta + * Copyright (C) 2014-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 03/07/2014: Initial version +******************************************************/ + +#include "wltypes.h" +#include "IEEE_types.h" +#include "hostsa_ext_def.h" +#include "authenticator.h" +#include "wl_macros.h" +#include "keyMgmtSta.h" +#include "pass_phrase.h" +#include "wlpd.h" +#include "KeyApiStaDefs.h" +#include "crypt_new.h" +#include "pmkCache.h" +#include "sha1.h" +#include "md5.h" +#include "rc4.h" +#include "aes_cmac.h" +#include "mrvl_sha256_crypto.h" + +#include "keyMgmtSta_rom.h" +#include "tlv.h" + +#define DEAUTH_DELAY_TIME_INTERVAL 40000 /* 40 ms */ + +#define PWK_MSG1_RETRIES 7 + +/* 10 seconds timeout for completing RSN key handshake */ +//#define RSNSECUREDTIMEOUT 10000000 +#define RSNSECUREDTIMEOUT MRVDRV_TIMER_10S + +//static void SendMICFailReport_sta(cm_ConnectionInfo_t* connPtr, +// keyMgmtInfoSta_t* pKeyMgmtInfoSta, +// BOOLEAN isUnicast); +#if 0 +static BufferReturnNotify_t keyMgmtKeyGroupTxDone(phostsa_private priv); +static BufferReturnNotify_t keyMgmtKeyPairwiseTxDone(phostsa_private priv); +static BufferReturnNotify_t keyMgmtKeyPairAndGroupTxDone(phostsa_private priv); +#endif +static void keyMgmtKeyGroupTxDone(phostsa_private priv); +static void keyMgmtKeyPairwiseTxDone(phostsa_private priv); +static void keyMgmtKeyPairAndGroupTxDone(phostsa_private priv); + +static supplicantData_t keyMgmt_SuppData[MAX_SUPPLICANT_SESSIONS]; + +void FillKeyMaterialStruct(phostsa_private priv, + UINT16 key_len, UINT8 isPairwise, KeyData_t *pKey); +void FillGrpKeyMaterialStruct(phostsa_private priv, + UINT16 keyType, + UINT8 *pn, + UINT8 keyIdx, UINT8 keyLen, KeyData_t *pKey); +UINT16 keyMgmtFormatWpaRsnIe(phostsa_private priv, + UINT8 *pos, + IEEEtypes_MacAddr_t *pBssid, + IEEEtypes_MacAddr_t *pStaAddr, + UINT8 *pPmkid, BOOLEAN addPmkid); +t_u8 supplicantIsEnabled(void *priv); + +void +allocSupplicantData(void *priv) +{ + phostsa_private psapriv = (phostsa_private)priv; + int i = 0; + + if (psapriv->suppData) { + return; + } + //if (pm_fns->bss_type == MLAN_BSS_TYPE_STA) + { + +// int_sta = os_if_save_EnterCriticalSection(); + for (i = 0; i < MAX_SUPPLICANT_SESSIONS; i++) { + if (keyMgmt_SuppData[i].inUse == FALSE) { + keyMgmt_SuppData[i].inUse = TRUE; + supplicantInit((void *)psapriv, + &keyMgmt_SuppData[i]); + psapriv->suppData = + (void *)&keyMgmt_SuppData[i]; + break; + } + } +// os_if_save_ExitCriticalSection(int_sta); + +// os_ASSERT(connPtr->suppData); + } + +} + +void +freeSupplicantData(void *priv) +{ + phostsa_private psapriv = (phostsa_private)priv; + struct supplicantData *suppData = psapriv->suppData; + + if (suppData != NULL) { + suppData->inUse = FALSE; + suppData = NULL; + } +} + +mlan_status +initSupplicantTimer(void *priv) +{ + phostsa_private psapriv = (phostsa_private)priv; + hostsa_util_fns *util_fns = &psapriv->util_fns; + mlan_status ret = MLAN_STATUS_SUCCESS; + + if (util_fns->moal_init_timer(util_fns->pmoal_handle, + &psapriv->suppData->keyMgmtInfoSta. + rsnTimer, + keyMgmtStaRsnSecuredTimeoutHandler, + psapriv) != MLAN_STATUS_SUCCESS) { + ret = MLAN_STATUS_FAILURE; + return ret; + } + + return ret; +} + +void +freeSupplicantTimer(void *priv) +{ + phostsa_private psapriv = (phostsa_private)priv; + hostsa_util_fns *util_fns = &psapriv->util_fns; + + if (psapriv->suppData->keyMgmtInfoSta.rsnTimer) { + util_fns->moal_free_timer(util_fns->pmoal_handle, + psapriv->suppData->keyMgmtInfoSta. + rsnTimer); + psapriv->suppData->keyMgmtInfoSta.rsnTimer = NULL; + } +} + +//#if defined(PSK_SUPPLICANT) || defined (WPA_NONE) +void +keyMgmtSendDeauth2Peer(phostsa_private priv, UINT16 reason) +{ + hostsa_mlan_fns *pm_fns = &priv->mlan_fns; + + /* Assumes we are sending to AP */ + //keyMgmtSendDeauth((cm_ConnectionInfo_t*)connPtr, + // &((cm_ConnectionInfo_t*)connPtr)->suppData->localBssid, + // reason); +#if 0 + ret = wlan_prepare_cmd(priv, + HostCmd_CMD_802_11_DEAUTHENTICATE, + HostCmd_ACT_GEN_SET, + 0, NULL, &priv->suppData->localBssid); + + if (ret == MLAN_STATUS_SUCCESS) + wlan_recv_event(priv, MLAN_EVENT_ID_DRV_DEFER_HANDLING, MNULL); +#endif + pm_fns->hostsa_StaSendDeauth(pm_fns->pmlan_private, + (t_u8 *)&priv->suppData->localBssid, + reason); +} + +BOOLEAN +keyMgmtProcessMsgExt(phostsa_private priv, keyMgmtInfoSta_t *pKeyMgmtInfoSta, + EAPOL_KeyMsg_t *pKeyMsg) +{ + if (pKeyMsg->key_info.KeyType && pKeyMsg->key_info.KeyMIC) { + /* PWK Msg #3 processing */ +#ifdef DOT11R + if (supplicantAkmWpa2Ft + (priv, + &pKeyMgmtInfoSta->connPtr->suppData->customMIB_RSNConfig. + AKM)) { + if (dot11r_process_pwk_msg3(pKeyMsg) == FALSE) { + return FALSE; + } + } +#endif + +#ifdef CCX_MFP + if (ccx_mfp_process_pwk_msg3(pKeyMsg) == FALSE) { + return FALSE; + } +#endif + } + + /* + ** KDE processing for Msg#3 and for any Group Key rotations + */ + if (pKeyMsg->key_info.EncryptedKeyData) { +#ifdef DOT11W + KDE_t *pKde; + + /* Parse IGTK for 11w */ + pKde = parseKeyKDE_DataType(priv, pKeyMsg->key_data, + pKeyMsg->key_material_len, + KDE_DATA_TYPE_IGTK); + if (pKde) { + //Not install same iGtk + if (!memcmp(&priv->util_fns, + pKeyMgmtInfoSta->IGtk.Key, + (UINT8 *)(((IGtkKde_t *)pKde->data)->IGtk), + MIN(sizeof(pKeyMgmtInfoSta->IGtk.Key), + (pKde->length - 12)))) { + return TRUE; + } else { + keyMgmtSetIGtk(priv, pKeyMgmtInfoSta, + (IGtkKde_t *)pKde->data, + pKde->length); + } +#if 0 + hostEventPrintHex(assocAgent_getConnPtr(), + "SetIGTK", keyMgmtGetIGtk(), 16); +#endif + } +#endif + } + + return TRUE; +} + +#ifdef WAR_ROM_BUG50312_SIMUL_INFRA_WFD +EAPOL_KeyMsg_t * +patch_ProcessRxEAPOL_GrpMsg1(phostsa_private priv, mlan_buffer *pmbuf, + keyMgmtInfoSta_t *pKeyMgmtInfoSta) +{ + hostsa_util_fns *util_fns = &priv->util_fns; + EAPOL_KeyMsg_t *pKeyMsg; + KeyData_t GRKey; + + pKeyMsg = GetKeyMsgNonceFromEAPOL(priv, pmbuf, pKeyMgmtInfoSta); + if (!pKeyMsg) { + return NULL; + } + + KeyMgmtSta_ApplyKEK(priv, pKeyMsg, + &pKeyMgmtInfoSta->GRKey, + pKeyMgmtInfoSta->EAPOL_Encr_Key); + + pKeyMgmtInfoSta->RSNDataTrafficEnabled = 1; + //microTimerStop(pKeyMgmtInfoSta->rsnTimer); + util_fns->moal_stop_timer(util_fns->pmoal_handle, + pKeyMgmtInfoSta->rsnTimer); + //pKeyMgmtInfoSta->rsnTimer = 0; + + /* Decrypt the group key */ + if (pKeyMsg->desc_type == 2) { + /* WPA2 */ + /* handle it according to 802.11i GTK frame format */ + parseKeyDataGTK(priv, pKeyMsg->key_data, + pKeyMsg->key_material_len, &GRKey); + /* Not install same GTK */ + if (!memcmp + (util_fns, pKeyMgmtInfoSta->GRKey.Key, GRKey.Key, + TK_SIZE)) { + priv->gtk_installed = 1; + } else { + memcpy(util_fns, &pKeyMgmtInfoSta->GRKey, &GRKey, + sizeof(KeyData_t)); + pKeyMgmtInfoSta->GRKey.TxIV16 = GRKey.TxIV16; + pKeyMgmtInfoSta->GRKey.TxIV32 = 0xFFFFFFFF; + priv->gtk_installed = 0; + } + + if (keyMgmtProcessMsgExt(priv, pKeyMgmtInfoSta, pKeyMsg) == + FALSE) { + return NULL; + } + } else { + /* WPA or Dynamic WEP */ + if (!memcmp + (util_fns, pKeyMgmtInfoSta->GRKey.Key, pKeyMsg->key_data, + pKeyMsg->key_material_len)) { + priv->gtk_installed = 1; + } else { + memcpy(util_fns, pKeyMgmtInfoSta->GRKey.Key, + pKeyMsg->key_data, pKeyMsg->key_material_len); + + pKeyMgmtInfoSta->GRKey.KeyIndex = + pKeyMsg->key_info.KeyIndex; + } + } + + return pKeyMsg; +} +#endif + +#ifdef WAR_ROM_BUG50312_SIMUL_INFRA_WFD +EAPOL_KeyMsg_t * +patch_ProcessRxEAPOL_PwkMsg3(phostsa_private priv, mlan_buffer *pmbuf, + keyMgmtInfoSta_t *pKeyMgmtInfoSta) +{ + hostsa_util_fns *util_fns = &priv->util_fns; + EAPOL_KeyMsg_t *pKeyMsg; + + pKeyMsg = GetKeyMsgNonceFromEAPOL(priv, pmbuf, pKeyMgmtInfoSta); + if (!pKeyMsg) { + return NULL; + } + pKeyMgmtInfoSta->newPWKey.TxIV16 = 1; + pKeyMgmtInfoSta->newPWKey.TxIV32 = 0; + + /* look for group key once the pairwise has been plumbed */ + if (pKeyMsg->key_info.EncryptedKeyData) { + /* I think the timer stop should be moved later on + in case ramHook_Process_CCX_MFP_11r returns + FALSE + */ + +// microTimerStop(pKeyMgmtInfoSta->rsnTimer); + util_fns->moal_stop_timer(util_fns->pmoal_handle, + pKeyMgmtInfoSta->rsnTimer); +// pKeyMgmtInfoSta->rsnTimer = 0; + + KeyMgmtSta_ApplyKEK(priv, pKeyMsg, + &pKeyMgmtInfoSta->GRKey, + pKeyMgmtInfoSta->EAPOL_Encr_Key); + + if (keyMgmtProcessMsgExt(priv, pKeyMgmtInfoSta, pKeyMsg) == + FALSE) { + return NULL; + } + + parseKeyDataGTK(priv, pKeyMsg->key_data, + pKeyMsg->key_material_len, + &pKeyMgmtInfoSta->GRKey); + + } + return pKeyMsg; +} +#endif + +/* This routine must be called after mlmeStaInit_UR +** It assumes that parent session structures are initialized +** (vmacEntry_ur and mlmeStaInfo_URepeater) +*/ +void +KeyMgmtInitSta(phostsa_private priv) +{ + KeyMgmtSta_InitSession(priv, &priv->suppData->keyMgmtInfoSta); +} + +Status_e +GeneratePWKMsg2(phostsa_private priv, mlan_buffer *pmbuf, + UINT8 *pSNonce, UINT8 *pEAPOLMICKey, UINT8 forceKeyDescVersion) +{ + hostsa_mlan_fns *pm_fns = &priv->mlan_fns; + EAPOL_KeyMsg_Tx_t *pTxEapol = MNULL; + UINT16 frameLen; + UINT16 packet_len = 0; + BOOLEAN rsnIeAdded = FALSE; + EAPOL_KeyMsg_t *pRxEapol = + (EAPOL_KeyMsg_t *)(pmbuf->pbuf + pmbuf->data_offset + + sizeof(ether_hdr_t)); + struct supplicantData *suppData = priv->suppData; + pmlan_buffer newbuf = MNULL; + UINT8 intf_hr_len = + pm_fns->Hostsa_get_intf_hr_len(pm_fns->pmlan_private); + + PRINTM(MMSG, "ENTER: %s\n", __FUNCTION__); + + newbuf = pm_fns->hostsa_alloc_mlan_buffer(pm_fns->pmlan_adapter, + MLAN_TX_DATA_BUF_SIZE_2K, 0, + MOAL_MALLOC_BUFFER); + if (newbuf) { + newbuf->bss_index = pmbuf->bss_index; + newbuf->buf_type = pmbuf->buf_type; + newbuf->priority = pmbuf->priority; + newbuf->in_ts_sec = pmbuf->in_ts_sec; + newbuf->in_ts_usec = pmbuf->in_ts_usec; + newbuf->data_offset = + (sizeof(TxPD) + intf_hr_len + DMA_ALIGNMENT); + } + + if (newbuf == NULL) { + PRINTM(MERROR, "GeneratePWKMsg2 newbuf=NULL\n"); + return FAIL; + } + + pTxEapol = (EAPOL_KeyMsg_Tx_t *)(newbuf->pbuf + newbuf->data_offset); + KeyMgmtSta_PrepareEAPOLFrame(priv, pTxEapol, + pRxEapol, + (t_u8 *)&suppData->localBssid, + (t_u8 *)&suppData->localStaAddr, pSNonce); + +#ifdef DOT11R + if (dot11r_is_ft_akm(&connPtr->suppData->customMIB_RSNConfig.AKM)) { + dot11r_process_pwk_msg2(connPtr, &pTxEapol->keyMsg); + rsnIeAdded = TRUE; + } +#endif + + if (!rsnIeAdded && (pTxEapol->keyMsg.desc_type != 1)) { + /* Add the RSN/WPA IE if not dynamic WEP */ + pTxEapol->keyMsg.key_material_len + = keyMgmtFormatWpaRsnIe(priv, + (UINT8 *)&pTxEapol->keyMsg. + key_data, &suppData->localBssid, + &suppData->localStaAddr, NULL, + FALSE); + } +#ifdef CCX_MFP + ccx_mfp_process_pwk_msg2(&pTxEapol->keyMsg); +#endif + + frameLen = KeyMgmtSta_PopulateEAPOLLengthMic(priv, pTxEapol, + pEAPOLMICKey, + EAPOL_PROTOCOL_V1, + forceKeyDescVersion); + + packet_len = frameLen + sizeof(Hdr_8021x_t) + sizeof(ether_hdr_t); + UpdateEAPOLWcbLenAndTransmit(priv, newbuf, packet_len); + + PRINTM(MMSG, "LEAVE: %s\n", __FUNCTION__); + return SUCCESS; +} + +Status_e +GeneratePWKMsg4(phostsa_private priv, mlan_buffer *pmbuf, + keyMgmtInfoSta_t *pKeyMgmtInfoSta, BOOLEAN groupKeyReceived) +{ + hostsa_mlan_fns *pm_fns = &priv->mlan_fns; + struct supplicantData *suppData = priv->suppData; + EAPOL_KeyMsg_Tx_t *pTxEapol; + UINT16 frameLen; + UINT16 packet_len = 0; + EAPOL_KeyMsg_t *pRxEapol = + (EAPOL_KeyMsg_t *)(pmbuf->pbuf + pmbuf->data_offset + + sizeof(ether_hdr_t)); + pmlan_buffer newbuf = MNULL; + UINT8 intf_hr_len = + pm_fns->Hostsa_get_intf_hr_len(pm_fns->pmlan_private); + + PRINTM(MMSG, "Enter GeneratePWKMsg4\n"); + + newbuf = pm_fns->hostsa_alloc_mlan_buffer(pm_fns->pmlan_adapter, + MLAN_TX_DATA_BUF_SIZE_2K, 0, + MOAL_MALLOC_BUFFER); + if (newbuf) { + newbuf->bss_index = pmbuf->bss_index; + newbuf->buf_type = pmbuf->buf_type; + newbuf->priority = pmbuf->priority; + newbuf->in_ts_sec = pmbuf->in_ts_sec; + newbuf->in_ts_usec = pmbuf->in_ts_usec; + newbuf->data_offset = + (sizeof(TxPD) + intf_hr_len + DMA_ALIGNMENT); + } + + if (newbuf == NULL) { + PRINTM(MERROR, "GeneratePWKMsg4 newbuf=NULL\n"); + return FAIL; + } + + pTxEapol = (EAPOL_KeyMsg_Tx_t *)(newbuf->pbuf + newbuf->data_offset); + + KeyMgmtSta_PrepareEAPOLFrame(priv, pTxEapol, + pRxEapol, + (t_u8 *)&suppData->localBssid, + (t_u8 *)&suppData->localStaAddr, NULL); + + frameLen = KeyMgmtSta_PopulateEAPOLLengthMic(priv, pTxEapol, + pKeyMgmtInfoSta-> + EAPOL_MIC_Key, + EAPOL_PROTOCOL_V1, 0); + + /* Set the BuffDesc free callback so the PSK supplicant can determine + ** if the 4th message was successfully received by the AP. Allows + ** the supplicant to hold off switching/setting the new key until + ** it is sure the AP has acknowledged the handshake completion + */ +#if 0 + if (pKeyMgmtInfoSta->RSNDataTrafficEnabled) { + pBufDesc->isCB = 1; + if (groupKeyReceived) { + pBufDesc->freeCallback = keyMgmtKeyPairAndGroupTxDone; + } else { + pBufDesc->freeCallback = keyMgmtKeyPairwiseTxDone; + } + } else { +#endif + + if (groupKeyReceived) { + keyMgmtKeyPairAndGroupTxDone(priv); + } else { + keyMgmtKeyPairwiseTxDone(priv); + } +#if 0 + } +#endif + + packet_len = frameLen + sizeof(Hdr_8021x_t) + sizeof(ether_hdr_t); + UpdateEAPOLWcbLenAndTransmit(priv, newbuf, packet_len); + + PRINTM(MMSG, "Leave GeneratePWKMsg4\n"); + return SUCCESS; +} + +Status_e +GenerateGrpMsg2(phostsa_private priv, mlan_buffer *pmbuf, + keyMgmtInfoSta_t *pKeyMgmtInfoSta) +{ + hostsa_mlan_fns *pm_fns = &priv->mlan_fns; + EAPOL_KeyMsg_t *pRxEapol = + (EAPOL_KeyMsg_t *)(pmbuf->pbuf + pmbuf->data_offset + + sizeof(ether_hdr_t)); + EAPOL_KeyMsg_Tx_t *pTxEapol; + UINT16 frameLen; + UINT16 packet_len = 0; + struct supplicantData *suppData = priv->suppData; + pmlan_buffer newbuf = MNULL; + UINT8 intf_hr_len = + pm_fns->Hostsa_get_intf_hr_len(pm_fns->pmlan_private); + + PRINTM(MMSG, "ENTER: %s\n", __FUNCTION__); + + newbuf = pm_fns->hostsa_alloc_mlan_buffer(pm_fns->pmlan_adapter, + MLAN_TX_DATA_BUF_SIZE_2K, 0, + MOAL_MALLOC_BUFFER); + if (newbuf) { + newbuf->bss_index = pmbuf->bss_index; + newbuf->buf_type = pmbuf->buf_type; + newbuf->priority = pmbuf->priority; + newbuf->in_ts_sec = pmbuf->in_ts_sec; + newbuf->in_ts_usec = pmbuf->in_ts_usec; + newbuf->data_offset = + (sizeof(TxPD) + intf_hr_len + DMA_ALIGNMENT); + } + + if (newbuf == NULL) { + PRINTM(MERROR, "GenerateGrpMsg2 newbuf=NULL\n"); + return FAIL; + } + + pTxEapol = (EAPOL_KeyMsg_Tx_t *)(newbuf->pbuf + newbuf->data_offset); + + KeyMgmtSta_PrepareEAPOLFrame(priv, pTxEapol, + pRxEapol, + (t_u8 *)&suppData->localBssid, + (t_u8 *)&suppData->localStaAddr, NULL); + + frameLen = KeyMgmtSta_PopulateEAPOLLengthMic(priv, pTxEapol, + pKeyMgmtInfoSta-> + EAPOL_MIC_Key, + EAPOL_PROTOCOL_V1, 0); +// pBufDesc->isCB = 1; +// pBufDesc->freeCallback = keyMgmtKeyGroupTxDone; + keyMgmtKeyGroupTxDone(priv); + + packet_len = frameLen + sizeof(Hdr_8021x_t) + sizeof(ether_hdr_t); + UpdateEAPOLWcbLenAndTransmit(priv, newbuf, packet_len); + + PRINTM(MMSG, "LEAVE: %s\n", __FUNCTION__); + return SUCCESS; +} + +BOOLEAN +KeyMgmtStaHsk_Recvd_PWKMsg1(phostsa_private priv, mlan_buffer *pmbuf, + IEEEtypes_MacAddr_t *sa, IEEEtypes_MacAddr_t *da) +{ + EAPOL_KeyMsg_t *pKeyMsg = NULL; + struct supplicantData *suppData = priv->suppData; + keyMgmtInfoSta_t *pKeyMgmtInfoSta = &suppData->keyMgmtInfoSta; + UINT8 *pPmk; + BOOLEAN msgProcessed; + BOOLEAN genPwkMsg2; + BOOLEAN retval; + UINT32 uMaxRetry = 5; // MAX_SUPPLICANT_INIT_TIMEOUT + + PRINTM(MMSG, "ENTER: %s\n", __FUNCTION__); + + msgProcessed = FALSE; + genPwkMsg2 = TRUE; + retval = FALSE; + +//#ifdef PSK_SUPPLICANT + /* Wait for supplicant data to be initialized, which will complete + * after set channel/DPD trainign is complete + */ + while (uMaxRetry-- && (suppData->suppInitialized != TRUE)) { +// OSATaskSleep(1); + } +//#endif + + pKeyMsg = GetKeyMsgNonceFromEAPOL(priv, pmbuf, pKeyMgmtInfoSta); + if (!pKeyMsg) { + PRINTM(MERROR, "KeyMgmtStaHsk_Recvd_PWKMsg1 pKeyMsg is NULL\n"); + return FALSE; + } +#ifdef DOT11R + if (!msgProcessed && + dot11r_is_ft_akm(&connPtr->suppData->customMIB_RSNConfig.AKM)) { + dot11r_process_pwk_msg1(connPtr, + sa, + da, + pKeyMgmtInfoSta->SNonce, + pKeyMgmtInfoSta->ANonce); + + msgProcessed = TRUE; + retval = TRUE; + } +#endif +#ifdef PSK_SUPPLICANT_CCKM + if (!msgProcessed && ccx_is_cckm_enabled(connPtr)) { + retval = cckm_Recvd_PWKMsg1(connPtr, + sa, + da, + pEAPoLBufDesc, + pKeyMgmtInfoSta->SNonce, + (UINT8 *)connPtr->suppData-> + hashSsId.SsId, + connPtr->suppData->hashSsId.Len); + genPwkMsg2 = FALSE; + msgProcessed = TRUE; + } +#endif + + if (!msgProcessed + && supplicantAkmIsWpaWpa2(priv, &suppData->customMIB_RSNConfig.AKM)) + { + if (supplicantAkmIsWpaWpa2Psk(priv, + &suppData->customMIB_RSNConfig. + AKM)) { + /* Selected AKM Suite is PSK based */ + pPmk = pmkCacheFindPSK((void *)priv, + (UINT8 *)suppData->hashSsId.SsId, + suppData->hashSsId.Len); + } else { + pPmk = pmkCacheFindPMK(priv, &suppData->localBssid); + } + + if (!pPmk) { + PRINTM(MERROR, + "KeyMgmtStaHsk_Recvd_PWKMsg1 pPmk is NULL\n"); + return FALSE; + } + + KeyMgmtSta_DeriveKeys(priv, pPmk, + (UINT8 *)da, + (UINT8 *)sa, + pKeyMgmtInfoSta->ANonce, + pKeyMgmtInfoSta->SNonce, + pKeyMgmtInfoSta->EAPOL_MIC_Key, + pKeyMgmtInfoSta->EAPOL_Encr_Key, + &pKeyMgmtInfoSta->newPWKey, + supplicantAkmUsesKdf(priv, + &suppData-> + customMIB_RSNConfig. + AKM)); + + retval = TRUE; + + /* PMKID checking not used by embedded supplicant. + ** Commenting out the code in case it needs to be + ** readded later. + */ +#if 0 + /* Need to check for PMKID response */ + if (pKeyMsg->desc_type == 2) { + if (keyLen) { + KDE_t *pKde; + + /* Parse PMKID though it's _not used_ now */ + pKde = parseKeyKDE_DataType(pKeyMsg->key_data, + keyLen, + KDE_DATA_TYPE_PMKID); + + if (pKde + && gcustomMIB_RSNConfig.pmkidValid + && memcmp(pKde->data, + gcustomMIB_RSNConfig.PMKID, + sizeof(gcustomMIB_RSNConfig. + PMKID))) { + /* PMKID could be invalid if generated based on an + ** old key. A new key should have been negotiated + ** We should regenerate PMKID and check it. + */ + } + } + } +#endif + } + + if (genPwkMsg2) { + /* construct Message 2 */ + if (GeneratePWKMsg2(priv, pmbuf, + pKeyMgmtInfoSta->SNonce, + pKeyMgmtInfoSta->EAPOL_MIC_Key, + 0) != SUCCESS) { + PRINTM(MERROR, + "KeyMgmtStaHsk_Recvd_PWKMsg1 GeneratePWKMsg2 Fail\n"); + retval = FALSE; + } + } + + if (retval == TRUE) { +#ifdef MIB_STATS + INC_MIB_STAT(connPtr, eapolSentFrmFwCnt); +#endif + updateApReplayCounter(priv, pKeyMgmtInfoSta, + (UINT8 *)pKeyMsg->replay_cnt); + + pKeyMgmtInfoSta->RSNSecured = FALSE; + } + + PRINTM(MMSG, "LEAVE: %s\n", __FUNCTION__); + return retval; +} + +EAPOL_KeyMsg_t const * +KeyMgmtStaHsk_Recvd_PWKMsg3(phostsa_private priv, mlan_buffer *pmbuf) +{ + EAPOL_KeyMsg_t *pKeyMsg; +// cm_ConnectionInfo_t* connPtr = pEAPoLBufDesc->intf.connPtr; + struct supplicantData *suppData = priv->suppData; + keyMgmtInfoSta_t *pKeyMgmtInfoSta = &suppData->keyMgmtInfoSta; + + PRINTM(MMSG, "ENTER: %s\n", __FUNCTION__); + +#ifdef WAR_ROM_BUG50312_SIMUL_INFRA_WFD + pKeyMsg = patch_ProcessRxEAPOL_PwkMsg3(pEAPoLBufDesc, pKeyMgmtInfoSta); +#else + pKeyMsg = ProcessRxEAPOL_PwkMsg3(priv, pmbuf, pKeyMgmtInfoSta); +#endif + if (!pKeyMsg) { + PRINTM(MERROR, "KeyMgmtStaHsk_Recvd_PWKMsg3 pKeyMsg is NULL\n"); + return NULL; + } + + /* construct Message 4 */ + if (GeneratePWKMsg4(priv, pmbuf, + pKeyMgmtInfoSta, + (pKeyMsg->desc_type == 2)) != SUCCESS) { + PRINTM(MERROR, + "KeyMgmtStaHsk_Recvd_PWKMsg3 GeneratePWKMsg4 Fail\n"); + return pKeyMsg; + } +#ifdef MIB_STATS + INC_MIB_STAT(connPtr, eapolSentFrmFwCnt); +#endif + + updateApReplayCounter(priv, pKeyMgmtInfoSta, + (UINT8 *)pKeyMsg->replay_cnt); + + PRINTM(MMSG, "LEAVE: %s\n", __FUNCTION__); + return NULL; +} + +EAPOL_KeyMsg_t const * +KeyMgmtStaHsk_Recvd_GrpMsg1(phostsa_private priv, mlan_buffer *pmbuf) +{ + EAPOL_KeyMsg_t *pKeyMsg; + struct supplicantData *suppData = priv->suppData; + keyMgmtInfoSta_t *pKeyMgmtInfoSta = &suppData->keyMgmtInfoSta; + + PRINTM(MMSG, "ENTER: %s\n", __FUNCTION__); + +#ifdef WAR_ROM_BUG50312_SIMUL_INFRA_WFD + pKeyMsg = patch_ProcessRxEAPOL_GrpMsg1(priv, pKeyMgmtInfoSta); +#else + pKeyMsg = ProcessRxEAPOL_GrpMsg1(priv, pmbuf, pKeyMgmtInfoSta); +#endif + if (!pKeyMsg) { + PRINTM(MERROR, "KeyMgmtStaHsk_Recvd_GrpMsg1 pKeyMsg is NULL\n"); + return NULL; + } + /* construct Message Grp Msg2 */ + if (GenerateGrpMsg2(priv, pmbuf, pKeyMgmtInfoSta) != SUCCESS) { + PRINTM(MERROR, + "KeyMgmtStaHsk_Recvd_GrpMsg1 GenerateGrpMsg2 Fail\n"); + return NULL; + } +#ifdef MIB_STATS + INC_MIB_STAT(connPtr, eapolSentFrmFwCnt); +#endif + + updateApReplayCounter(priv, pKeyMgmtInfoSta, + (UINT8 *)pKeyMsg->replay_cnt); + + PRINTM(MMSG, "LEAVE: %s\n", __FUNCTION__); + return pKeyMsg; +} + +void +ProcessKeyMgmtDataSta(phostsa_private priv, mlan_buffer *pmbuf, + IEEEtypes_MacAddr_t *sa, IEEEtypes_MacAddr_t *da) +{ + UINT8 retry; + EAPOL_KeyMsg_t *pKeyMsg = + (EAPOL_KeyMsg_t *)(pmbuf->pbuf + pmbuf->data_offset + + sizeof(ether_hdr_t)); + + retry = 0; + + if (pKeyMsg->key_info.KeyType) { + /* PWK */ + if (pKeyMsg->key_info.KeyMIC) { + /* 3rd msg in seq */ + KeyMgmtStaHsk_Recvd_PWKMsg3(priv, pmbuf); + } else { + while ((KeyMgmtStaHsk_Recvd_PWKMsg1(priv, pmbuf, sa, da) + == FALSE) + && (retry < PWK_MSG1_RETRIES)) { + /* Delay and retry Msg1 processing in case failure was + ** due to the host not having time to program a PMK + ** yet for 802.1x AKMPs + */ + //hal_WaitInUs(100); + retry++; + } + //KeyMgmtStaHsk_Recvd_PWKMsg1(priv, pmbuf, sa, da); + + } + } else { + /* GRP */ + if (!KeyMgmtStaHsk_Recvd_GrpMsg1(priv, pmbuf)) { +#if defined(MEF_ENH) && defined(VISTA_802_11_DRIVER_INTERFACE) + hostsleep_initiate_wakeup_with_reason + (WOL_GRP_KEY_REFRESH_ERROR, + WOL_VACUOUS_PATTERN_ID); +#endif + } + } + +} + +#if 0 +/* +** This function send a MIC failure event to the AP +*/ +void +SendMICFailReport_sta(cm_ConnectionInfo_t * connPtr, + keyMgmtInfoSta_t *pKeyMgmtInfoSta, BOOLEAN isUnicast) +{ + EAPOL_KeyMsg_Tx_t *pTxEapol; + UINT16 frameLen; + UINT32 int_sta; + BufferDesc_t *pBufDesc; + + if (pKeyMgmtInfoSta->staCounterHi == 0xffffffff + && pKeyMgmtInfoSta->staCounterLo == 0xffffffff) { + KeyMgmtResetCounter(pKeyMgmtInfoSta); + return; + } + + int_sta = os_if_save_EnterCriticalSection(); + + /* Since there is a MIC failure drop all packets in Tx queue. */ + while ((pBufDesc = (BufferDesc_t *) getq(&wlan_data_q)) != NULL) { + /* Do nothing here. We are just dropping the packet + ** and releasing the queue. + */ + mrvl_HandleTxDone(pBufDesc, 0); + } + + os_if_save_ExitCriticalSection(int_sta); + + pBufDesc = GetTxEAPOLBuffer(connPtr, &pTxEapol, NULL); + + if (pBufDesc == NULL) { + return; + } + KeyMgmtSta_PrepareEAPOLMicErrFrame(pTxEapol, + isUnicast, + &connPtr->suppData->localBssid, + &connPtr->suppData->localStaAddr, + pKeyMgmtInfoSta); + SetEAPOLKeyDescTypeVersion(pTxEapol, + connPtr->suppData->customMIB_RSNConfig. + wpaType.wpa2, + supplicantAkmUsesKdf(&connPtr->suppData-> + customMIB_RSNConfig. + AKM), + connPtr->suppData->customMIB_RSNConfig. + ucstCipher.ccmp); + + if (pKeyMgmtInfoSta->staCounterLo++ == 0) { + pKeyMgmtInfoSta->staCounterHi++; + } + + frameLen = KeyMgmtSta_PopulateEAPOLLengthMic(pTxEapol, + pKeyMgmtInfoSta-> + EAPOL_MIC_Key, + EAPOL_PROTOCOL_V1, 0); + + UpdateEAPOLWcbLenAndTransmit(pBufDesc, frameLen); +} +#endif + +#ifdef WAR_ROM_BUG57216_QUIET_TIME_INTERVAL +/* This function assumes that argument state would be either + NO_MIC_FAILURE or FIRST_MIC_FAIL_IN_60_SEC + It must not be called with state othe than these two +*/ +#define MIC_ERROR_QUIET_TIME_INTERVAL 60000000 /* 60 sec */ +#define MIC_ERROR_CHECK_TIME_INTERVAL 60000000 + +void +KeyMgmtSta_handleMICErr(MIC_Fail_State_e state, + keyMgmtInfoSta_t *pKeyMgmtInfoSta, + MicroTimerCallback_t callback, UINT8 flags) +{ + UINT32 expiry; + UINT32 int_save = tx_interrupt_control(TX_INT_DISABLE); + + if (state == NO_MIC_FAILURE) { + /* First MIC failure */ + pKeyMgmtInfoSta->sta_MIC_Error.status = + FIRST_MIC_FAIL_IN_60_SEC; + expiry = MIC_ERROR_CHECK_TIME_INTERVAL; + } else { + /* Received 2 MIC failures within 60 sec. Do deauth from AP */ + pKeyMgmtInfoSta->sta_MIC_Error.disableStaAsso = 1; + pKeyMgmtInfoSta->sta_MIC_Error.status = + SECOND_MIC_FAIL_IN_60_SEC; + pKeyMgmtInfoSta->apCounterHi = 0; + pKeyMgmtInfoSta->apCounterLo = 0; + expiry = MIC_ERROR_QUIET_TIME_INTERVAL; + } + tx_interrupt_control(int_save); +#if 0 + microTimerStop(pKeyMgmtInfoSta->micTimer); + + microTimerStart(callback, + (UINT32)pKeyMgmtInfoSta, + expiry, &pKeyMgmtInfoSta->micTimer, flags); +#endif +} +#endif + +#if 0 +void +supplicantMICCounterMeasureInvoke(cm_ConnectionInfo_t * connPtr, + BOOLEAN isUnicast) +{ + MIC_Fail_State_e state; + keyMgmtInfoSta_t *pKeyMgmtInfoSta = &connPtr->suppData->keyMgmtInfoSta; + + if (pKeyMgmtInfoSta->sta_MIC_Error.MICCounterMeasureEnabled) { + state = pKeyMgmtInfoSta->sta_MIC_Error.status; + + /* Watchdog and clear any pending TX packets to ensure that + ** We are able to get a TX buffer + */ + tx_watchdog_recovery(); + SendMICFailReport_sta(connPtr, pKeyMgmtInfoSta, isUnicast); + + switch (state) { + case NO_MIC_FAILURE: + /* Received 1st MIC failure */ + /* Noneed to check if timer is active. It will not be active + ** cause this is the first state + */ + KeyMgmtSta_handleMICErr(state, + pKeyMgmtInfoSta, + MicErrTimerExp_Sta, + MICRO_TIMER_FLAG_KILL_ON_PS_ENTRY); + + connPtr->suppData->customMIB_RSNStats. + TKIPLocalMICFailures++; + + break; + + case FIRST_MIC_FAIL_IN_60_SEC: + /* Received 2 MIC failures within 60 sec. Do deauth from AP */ + connPtr->suppData->customMIB_RSNStats. + TKIPCounterMeasuresInvoked++; + + KeyMgmtSta_handleMICErr(state, + pKeyMgmtInfoSta, + MicErrTimerExp_Sta, + MICRO_TIMER_FLAG_KILL_ON_PS_ENTRY); + + /* Is this really needed? */ + pKeyMgmtInfoSta->connPtr = connPtr; + + KeyMgmtSta_handleMICDeauthTimer(pKeyMgmtInfoSta, + DeauthDelayTimerExp_Sta, + DEAUTH_DELAY_TIME_INTERVAL, + MICRO_TIMER_FLAG_KILL_ON_PS_ENTRY); + + break; + + case SECOND_MIC_FAIL_IN_60_SEC: + /*No need to do anything. Everything has been taken care of by + ** the above state + */ + + default: + break; + } + } + return; +} +#endif +/* + Start the key Management session +*/ +void +keyMgmtSta_StartSession(phostsa_private priv, + IEEEtypes_MacAddr_t *pBssid, + IEEEtypes_MacAddr_t *pStaAddr) +{ + hostsa_util_fns *util_fns = &priv->util_fns; + keyMgmtInfoSta_t *pKeyMgmtInfoSta = &priv->suppData->keyMgmtInfoSta; + + //pKeyMgmtInfoSta->psapriv = priv; + + memcpy(util_fns, &priv->suppData->localStaAddr, + pStaAddr, sizeof(priv->suppData->localStaAddr)); + memcpy(util_fns, &priv->suppData->localBssid, + pBssid, sizeof(priv->suppData->localBssid)); + + keyMgmtSta_StartSession_internal(priv, pKeyMgmtInfoSta, + //keyMgmtStaRsnSecuredTimeoutHandler, + RSNSECUREDTIMEOUT, 0); //MICRO_TIMER_FLAG_KILL_ON_PS_ENTRY); + +} + +void +supplicantClrEncryptKey(void *priv) +{ + phostsa_private psapriv = (phostsa_private)priv; + hostsa_mlan_fns *pm_fns = NULL; + + if (!psapriv) + return; + + pm_fns = &psapriv->mlan_fns; + pm_fns->hostsa_clr_encrypt_key(psapriv->pmlan_private); +} + +UINT32 +keyApi_UpdateKeyMaterial(void *priv, key_MgtMaterial_t *keyMgtData_p) +{ + phostsa_private psapriv = (phostsa_private)priv; + hostsa_util_fns *util_fns = &psapriv->util_fns; + hostsa_mlan_fns *pm_fns = &psapriv->mlan_fns; + //UINT8 wepKeyIndex; + mlan_ds_encrypt_key encrypt_key; + t_u8 bcast_addr[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + + memset(util_fns, &encrypt_key, 0, sizeof(mlan_ds_encrypt_key)); + + PRINTM(MMSG, "keyApi_UpdateKeyMaterial keyType=%x keyLen=%x\n", + keyMgtData_p->keyType, keyMgtData_p->keyLen); + switch (keyMgtData_p->keyType) { + case KEY_TYPE_TKIP: + case KEY_TYPE_AES: + /* The Key Info definition for TKIP and AES is the same */ + if (keyMgtData_p->keyInfo & KEY_INFO_UNICAST) { + /* Unicast Key */ + //SET_KEY_STATE_ENABLED(pwkey, + // (keyMgtData_p->keyInfo + // & KEY_INFO_ENABLED)? TRUE : FALSE); + //pwkey->hdr.keyType = keyMgtData_p->keyType; + //pwkey->hdr.keyDirection = KEY_DIRECTION_RXTX; + //pwkey->hdr.keyLen = keyMgtData_p->keyLen; + + //if (IS_KEY_STATE_ENABLED(pwkey)) + //{ + // ramHook_keyApiSta_setConnDataTrafficEnabled(connPtr, TRUE); + // ramHook_keyApiSta_setConnCurPktTxEnabled(connPtr, TRUE); + // SET_KEY_STATE_FORCE_EAPOL_UNENCRYPTED(pwkey, TRUE); + // } + encrypt_key.key_flags |= KEY_FLAG_SET_TX_KEY; + encrypt_key.key_len = keyMgtData_p->keyLen; + memcpy(util_fns, encrypt_key.mac_addr, + psapriv->suppData->localBssid, MAC_ADDR_SIZE); + + /* The Key Material is different */ + if (keyMgtData_p->keyLen && + (keyMgtData_p->keyType == KEY_TYPE_TKIP)) { + /* Update key if included */ + memcpy(util_fns, + (void *)encrypt_key.key_material, + (const void *)keyMgtData_p->keyEncypt. + TKIP.key, TK_SIZE); + memcpy(util_fns, + (void *)&encrypt_key. + key_material[TK_SIZE], + (const void *)keyMgtData_p->keyEncypt. + TKIP.txMicKey, MIC_KEY_SIZE); + memcpy(util_fns, + (void *)&encrypt_key. + key_material[TK_SIZE + MIC_KEY_SIZE], + (const void *)keyMgtData_p->keyEncypt. + TKIP.rxMicKey, MIC_KEY_SIZE); + } else if (keyMgtData_p->keyLen && + (keyMgtData_p->keyType == KEY_TYPE_AES)) { + /* Update key if included */ + memcpy(util_fns, + (uint8 *)encrypt_key.key_material, + (uint8 *)keyMgtData_p->keyEncypt.AES.key, + TK_SIZE); + + /* duplicate to group key, + * for adhoc aes to use. + */ + //if (!IS_KEY_STATE_ENABLED(gwkey)) + // { + /* Multicast Key */ + // SET_KEY_STATE_ENABLED(gwkey, + // (keyMgtData_p->keyInfo + // & KEY_INFO_ENABLED) ? TRUE : FALSE); + // gwkey->hdr.keyType = keyMgtData_p->keyType; + // gwkey->hdr.keyDirection = KEY_DIRECTION_RXTX; + // gwkey->hdr.keyLen = keyMgtData_p->keyLen; + // if (IS_KEY_STATE_ENABLED(gwkey)) + // { + // gwkey->ckd.tkip_aes.loReplayCounter16 = 0; + // gwkey->ckd.tkip_aes.hiReplayCounter32 = 0xffffffff; + // } + // memcpy((uint8*)gwkey->ckd.tkip_aes.key, + // (uint8*)keyMgtData_p->keyEncypt.AES.key, + // TK_SIZE); + // } + } + } + + if (keyMgtData_p->keyInfo & KEY_INFO_MULTICAST) { + /* Multicast Key */ + //SET_KEY_STATE_ENABLED(gwkey, + // (keyMgtData_p-> + // keyInfo & KEY_INFO_ENABLED) ? TRUE : + // FALSE); + //gwkey->hdr.keyType = keyMgtData_p->keyType; + //gwkey->hdr.keyDirection = KEY_DIRECTION_RXTX; + //gwkey->hdr.keyLen = keyMgtData_p->keyLen; + //if (IS_KEY_STATE_ENABLED(gwkey)) + //{ + // gwkey->ckd.tkip_aes.loReplayCounter16 = 0; + // gwkey->ckd.tkip_aes.hiReplayCounter32 = 0xffffffff; + + // if (!IS_KEY_STATE_ENABLED(pwkey)) + // { + // gwkey->ckd.tkip_aes.txIV32 = 0x0; + // gwkey->ckd.tkip_aes.txIV16 = 0x1; + // ramHook_keyApiSta_setConnDataTrafficEnabled(connPtr, TRUE); + // ramHook_keyApiSta_setConnCurPktTxEnabled(connPtr, TRUE); + // } + // } + encrypt_key.key_flags |= KEY_FLAG_GROUP_KEY; + encrypt_key.key_len = keyMgtData_p->keyLen; + memcpy(util_fns, encrypt_key.mac_addr, bcast_addr, + MAC_ADDR_SIZE); + + if (keyMgtData_p->keyLen && + (keyMgtData_p->keyType == KEY_TYPE_TKIP)) { + /* Update key if included */ + memcpy(util_fns, + (void *)encrypt_key.key_material, + (const void *)keyMgtData_p->keyEncypt. + TKIP.key, TK_SIZE); + memcpy(util_fns, + (void *)&encrypt_key. + key_material[TK_SIZE], + (const void *)keyMgtData_p->keyEncypt. + TKIP.txMicKey, MIC_KEY_SIZE); + memcpy(util_fns, + (void *)&encrypt_key. + key_material[TK_SIZE + MIC_KEY_SIZE], + (const void *)keyMgtData_p->keyEncypt. + TKIP.rxMicKey, MIC_KEY_SIZE); + } else if (keyMgtData_p->keyLen && + (keyMgtData_p->keyType == KEY_TYPE_AES)) { + /* Update key if included */ + memcpy(util_fns, + (uint8 *)encrypt_key.key_material, + (uint8 *)keyMgtData_p->keyEncypt.AES.key, + TK_SIZE); + } + } + /**set pn 0*/ + memset(util_fns, encrypt_key.pn, 0, sizeof(encrypt_key.pn)); + /**key flag*/ + encrypt_key.key_flags |= KEY_FLAG_RX_SEQ_VALID; + + //ramHook_keyApi_PalladiumHook1(connPtr); + /**set command to fw update key*/ + pm_fns->hostsa_set_encrypt_key((void *)psapriv->pmlan_private, + &encrypt_key); + + break; + +#ifndef WAR_ROM_BUG54733_PMF_SUPPORT + case KEY_TYPE_AES_CMAC: + if ( /*NULL != igwkey && */ + (keyMgtData_p->keyInfo & KEY_INFO_MULTICAST_IGTK)) { + /* Multicast Key */ + //SET_KEY_STATE_ENABLED(igwkey, + // (keyMgtData_p->keyInfo + // & KEY_INFO_ENABLED) ? TRUE : FALSE); + //igwkey->hdr.keyType = keyMgtData_p->keyType; + //igwkey->hdr.keyDirection = KEY_DIRECTION_RXTX; + //igwkey->hdr.keyLen = keyMgtData_p->keyLen; + if (keyMgtData_p->keyLen) { + /* Update IPN if included */ + //memcpy((UINT8 *)&igwkey->ckd.tkip_aes.loReplayCounter16, + // (UINT8 *)&keyMgtData_p->keyEncypt.iGTK.ipn[0], + // sizeof(igwkey->ckd.tkip_aes.loReplayCounter16)); + + //memcpy((UINT8 *)&igwkey->ckd.tkip_aes.hiReplayCounter32, + // (UINT8 *)&keyMgtData_p->keyEncypt.iGTK.ipn[2], + // sizeof(igwkey->ckd.tkip_aes.hiReplayCounter32)); + + /* Update key if included */ + //memcpy((UINT8 *)igwkey->ckd.tkip_aes.key, + // (UINT8 *)keyMgtData_p->keyEncypt.iGTK.key, + // CRYPTO_AES_CMAC_KEY_LEN); + memcpy(util_fns, + (uint8 *)encrypt_key.key_material, + (UINT8 *)keyMgtData_p->keyEncypt.iGTK. + key, CRYPTO_AES_CMAC_KEY_LEN); + } + /**set pn 0*/ + memset(util_fns, encrypt_key.pn, 0, + sizeof(encrypt_key.pn)); + /**key flag*/ + encrypt_key.key_flags |= KEY_FLAG_RX_SEQ_VALID; + + //ramHook_keyApi_PalladiumHook1(connPtr); + /**set command to fw update key*/ + pm_fns->hostsa_set_encrypt_key(psapriv->pmlan_private, + &encrypt_key); + } + break; +#endif + } + + return 0; +} + +void +FillKeyMaterialStruct(phostsa_private priv, + UINT16 key_len, UINT8 isPairwise, KeyData_t *pKey) +{ + key_MgtMaterial_t keyMgtData; + +#ifdef MIB_STATS + if (isPairwise) { + INC_MIB_STAT(connPtr, PTKSentFrmESUPPCnt); + } else { + INC_MIB_STAT(connPtr, GTKSentFrmESUPPCnt); + } +#endif + + FillKeyMaterialStruct_internal(priv, &keyMgtData, key_len, isPairwise, + pKey); + keyApi_UpdateKeyMaterial(priv, &keyMgtData); +} + +void +FillGrpKeyMaterialStruct(phostsa_private priv, + UINT16 keyType, + UINT8 *pn, UINT8 keyIdx, UINT8 keyLen, KeyData_t *pKey) +{ + hostsa_util_fns *util_fns = &priv->util_fns; + key_MgtMaterial_t keyMgtData; + + if (keyType == KDE_DATA_TYPE_IGTK) { + memset(util_fns, (void *)&keyMgtData, 0x00, sizeof(keyMgtData)); + + keyMgtData.keyType = KEY_TYPE_AES_CMAC; + keyMgtData.keyInfo = KEY_INFO_MULTICAST_IGTK | KEY_INFO_ENABLED; + keyMgtData.keyLen = keyLen; + + memcpy(util_fns, keyMgtData.keyEncypt.iGTK.ipn, pn, + CRYPTO_AES_CMAC_IPN_LEN); + memcpy(util_fns, keyMgtData.keyEncypt.iGTK.key, pKey->Key, + keyLen); + } else { + FillKeyMaterialStruct_internal(priv, &keyMgtData, keyLen, FALSE, + pKey); + } + + keyApi_UpdateKeyMaterial(priv, &keyMgtData); +} + +void +supplicantInitSession(void *priv, + t_u8 *pSsid, t_u16 len, t_u8 *pBssid, t_u8 *pStaAddr) +{ + phostsa_private psapriv = (phostsa_private)priv; + hostsa_util_fns *util_fns = &psapriv->util_fns; + + if (supplicantIsEnabled((void *)psapriv)) { + KeyMgmtInitSta(psapriv); + memcpy(util_fns, (void *)psapriv->suppData->hashSsId.SsId, + pSsid, len); + psapriv->suppData->hashSsId.Len = len; + keyMgmtSta_StartSession(psapriv, (IEEEtypes_MacAddr_t *)pBssid, + (IEEEtypes_MacAddr_t *)pStaAddr); + psapriv->suppData->suppInitialized = TRUE; + psapriv->gtk_installed = 0; + } +} + +UINT8 +supplicantIsCounterMeasuresActive(phostsa_private priv) +{ + return priv->suppData->keyMgmtInfoSta.sta_MIC_Error.disableStaAsso; +} + +//#endif + +void +init_customApp_mibs(phostsa_private priv, supplicantData_t *suppData) +{ + hostsa_util_fns *util_fns = &priv->util_fns; + + memset(util_fns, &suppData->customMIB_RSNStats, + 0x00, sizeof(suppData->customMIB_RSNStats)); + memset(util_fns, &suppData->customMIB_RSNConfig, + 0x00, sizeof(suppData->customMIB_RSNConfig)); + /* keep noRsn = 1 as default setting */ + suppData->customMIB_RSNConfig.wpaType.noRsn = 1; + +} + +SecurityMode_t +supplicantCurrentSecurityMode(phostsa_private priv) +{ + return (priv->suppData->customMIB_RSNConfig.wpaType); +} + +AkmSuite_t * +supplicantCurrentAkmSuite(phostsa_private priv) +{ + return &priv->suppData->customMIB_RSNConfig.AKM; +} + +//#pragma arm section code = ".wlandatapathcode" +t_u8 +supplicantIsEnabled(void *priv) +{ + phostsa_private psapriv = (phostsa_private)priv; + + if (!psapriv || psapriv->suppData == NULL) { + return 0; + } + + return (psapriv->suppData->customMIB_RSNConfig.RSNEnabled); +} + +//#pragma arm section code + +void +supplicantDisable(void *priv) +{ + phostsa_private psapriv = (phostsa_private)priv; + + if (!supplicantIsEnabled((void *)psapriv)) { + return; + } + psapriv->suppData->customMIB_RSNConfig.RSNEnabled = 0; + init_customApp_mibs(psapriv, psapriv->suppData); + + PRINTM(MMSG, "supplicantDisable RSNEnabled=%x\n", + psapriv->suppData->customMIB_RSNConfig.RSNEnabled); +} + +void +supplicantQueryPassphraseAndEnable(void *priv, t_u8 *pbuf) +{ + phostsa_private psapriv = (phostsa_private)priv; + pmkElement_t *pPMKElement = MNULL; + mlan_ssid_bssid *ssid_bssid = (mlan_ssid_bssid *)pbuf; + mlan_802_11_ssid *pssid = &ssid_bssid->ssid; + + if (!psapriv || psapriv->suppData == NULL) + return; + if (!ssid_bssid) + return; + if (!pssid->ssid_len) + return; + /* extract the PSK from the cache entry */ + pPMKElement = + pmkCacheFindPSKElement((void *)psapriv, pssid->ssid, + pssid->ssid_len); + if (pPMKElement) + psapriv->suppData->customMIB_RSNConfig.RSNEnabled = 1; + else + psapriv->suppData->customMIB_RSNConfig.RSNEnabled = 0; + + PRINTM(MMSG, + "supplicantQueryPassphraseAndEnable RSNEnabled=%x ssid=%s\n", + psapriv->suppData->customMIB_RSNConfig.RSNEnabled, pssid->ssid); +} + +void +supplicantSetAssocRsn(phostsa_private priv, + SecurityMode_t wpaType, + Cipher_t *pMcstCipher, + Cipher_t *pUcstCipher, + AkmSuite_t *pAkm, + IEEEtypes_RSNCapability_t *pRsnCap, + Cipher_t *pGrpMgmtCipher) +{ + hostsa_util_fns *util_fns = &priv->util_fns; + IEEEtypes_RSNCapability_t rsnCap; + + if (pRsnCap == NULL) { + /* It is being added as an IOT workaround for APs that + * do not properly handle association requests that omit + * the RSN Capability field in the RSN IE + */ + memset(util_fns, &rsnCap, 0x00, sizeof(rsnCap)); + pRsnCap = &rsnCap; + } + + supplicantSetAssocRsn_internal(priv, + &priv->suppData->customMIB_RSNConfig, + &priv->suppData->currParams, + wpaType, + pMcstCipher, + pUcstCipher, + pAkm, pRsnCap, pGrpMgmtCipher); +} + +UINT16 +keyMgmtFormatWpaRsnIe(phostsa_private priv, + UINT8 *pos, + IEEEtypes_MacAddr_t *pBssid, + IEEEtypes_MacAddr_t *pStaAddr, + UINT8 *pPmkid, BOOLEAN addPmkid) +{ + struct supplicantData *suppData = priv->suppData; + + return keyMgmtFormatWpaRsnIe_internal(priv, + &suppData->customMIB_RSNConfig, + pos, + pBssid, + pStaAddr, pPmkid, addPmkid); +} + +static void +install_wpa_none_keys(phostsa_private priv, UINT8 type, UINT8 unicast) +{ + UINT8 *pPMK; + key_MgtMaterial_t keyMgtData; + + pPMK = pmkCacheFindPSK((void *)priv, + (UINT8 *)priv->suppData->hashSsId.SsId, + priv->suppData->hashSsId.Len); + if (pPMK == NULL) { + return; + } + + install_wpa_none_keys_internal(priv, &keyMgtData, pPMK, type, unicast); + + keyApi_UpdateKeyMaterial(priv, &keyMgtData); + + /* there's no timer or other to initialize */ + KeyMgmtInitSta(priv); + priv->suppData->keyMgmtInfoSta.RSNSecured = TRUE; +} + +void +supplicantInstallWpaNoneKeys(phostsa_private priv) +{ + if (priv->suppData->customMIB_RSNConfig.RSNEnabled + && priv->suppData->customMIB_RSNConfig.wpaType.wpaNone) { + install_wpa_none_keys(priv, + priv->suppData->customMIB_RSNConfig. + mcstCipher.ccmp, 0); + install_wpa_none_keys(priv, + priv->suppData->customMIB_RSNConfig. + mcstCipher.ccmp, 1); + } +} + +void +supplicantSetProfile(phostsa_private priv, + SecurityMode_t wpaType, + Cipher_t mcstCipher, Cipher_t ucstCipher) +{ + priv->suppData->currParams.wpaType = wpaType; + priv->suppData->currParams.mcstCipher = mcstCipher; + priv->suppData->currParams.ucstCipher = ucstCipher; +} + +void +supplicantGetProfile(phostsa_private priv, + SecurityMode_t *pWpaType, + Cipher_t *pMcstCipher, Cipher_t *pUcstCipher) +{ + *pWpaType = priv->suppData->currParams.wpaType; + *pMcstCipher = priv->suppData->currParams.mcstCipher; + *pUcstCipher = priv->suppData->currParams.ucstCipher; +} + +void +supplicantGetProfileCurrent(phostsa_private priv, + SecurityMode_t *pWpaType, + Cipher_t *pMcstCipher, Cipher_t *pUcstCipher) +{ + *pWpaType = priv->suppData->customMIB_RSNConfig.wpaType; + *pMcstCipher = priv->suppData->customMIB_RSNConfig.mcstCipher; + *pUcstCipher = priv->suppData->customMIB_RSNConfig.ucstCipher; +} + +void +supplicantInit(void *priv, supplicantData_t *suppData) +{ + phostsa_private psapriv = (phostsa_private)priv; + hostsa_util_fns *util_fns = &psapriv->util_fns; + + init_customApp_mibs(priv, suppData); + + memset(util_fns, &suppData->currParams, 0xff, sizeof(SecurityParams_t)); + memset(util_fns, &suppData->keyMgmtInfoSta, 0, + sizeof(keyMgmtInfoSta_t)); + suppData->keyMgmtInfoSta.sta_MIC_Error.disableStaAsso = 0; + suppData->keyMgmtInfoSta.sta_MIC_Error.MICCounterMeasureEnabled = 1; + suppData->keyMgmtInfoSta.sta_MIC_Error.status = NO_MIC_FAILURE; + KeyMgmtResetCounter(&suppData->keyMgmtInfoSta); +} + +void +supplicantStopSessionTimer(void *priv) +{ + phostsa_private psapriv = (phostsa_private)priv; + hostsa_util_fns *util_fns = NULL; + + if (!psapriv || psapriv->suppData == NULL) { + return; + } + + util_fns = &psapriv->util_fns; + if (psapriv->suppData->keyMgmtInfoSta.rsnTimer) { + util_fns->moal_stop_timer(util_fns->pmoal_handle, + psapriv->suppData->keyMgmtInfoSta. + rsnTimer); + //priv->suppData->keyMgmtInfoSta.rsnTimer = 0; + } +} + +void +supplicantSmeResetNotification(phostsa_private priv) +{ + supplicantStopSessionTimer(priv); +} + +UINT16 +keyMgmtGetKeySize(phostsa_private priv, UINT8 isPairwise) +{ + return keyMgmtGetKeySize_internal(&priv->suppData->customMIB_RSNConfig, + isPairwise); +} + +void +keyMgmtSetMICKey(phostsa_private priv, UINT8 *pKey) +{ + hostsa_util_fns *util_fns = &priv->util_fns; + + memcpy(util_fns, priv->suppData->keyMgmtInfoSta.EAPOL_MIC_Key, + pKey, sizeof(priv->suppData->keyMgmtInfoSta.EAPOL_MIC_Key)); +} + +UINT8 * +keyMgmtGetMICKey(phostsa_private priv) +{ + return (priv->suppData->keyMgmtInfoSta.EAPOL_MIC_Key); +} + +void +keyMgmtSetEAPOLEncrKey(phostsa_private priv, UINT8 *pKey) +{ + hostsa_util_fns *util_fns = &priv->util_fns; + + memcpy(util_fns, priv->suppData->keyMgmtInfoSta.EAPOL_Encr_Key, + pKey, sizeof(priv->suppData->keyMgmtInfoSta.EAPOL_Encr_Key)); +} + +void +keyMgmtSetTemporalKeyOnly(phostsa_private priv, UINT8 *pTk) +{ + hostsa_util_fns *util_fns = &priv->util_fns; + + memcpy(util_fns, &priv->suppData->keyMgmtInfoSta.newPWKey.Key, + pTk, sizeof(priv->suppData->keyMgmtInfoSta.newPWKey.Key)); +} + +UINT8 * +keyMgmtGetEAPOLEncrKey(phostsa_private priv) +{ + return (priv->suppData->keyMgmtInfoSta.EAPOL_Encr_Key); +} + +void +keyMgmtSetPairwiseKey(phostsa_private priv, KeyData_t *pKey) +{ + hostsa_util_fns *util_fns = &priv->util_fns; + + memcpy(util_fns, &priv->suppData->keyMgmtInfoSta.newPWKey, + pKey, sizeof(priv->suppData->keyMgmtInfoSta.newPWKey)); +} + +void +keyMgmtSetGroupKey(phostsa_private priv, KeyData_t *pKey) +{ + hostsa_util_fns *util_fns = &priv->util_fns; + + memcpy(util_fns, &priv->suppData->keyMgmtInfoSta.GRKey, + pKey, sizeof(priv->suppData->keyMgmtInfoSta.GRKey)); + + FillKeyMaterialStruct(priv, + keyMgmtGetKeySize(priv, FALSE), FALSE, pKey); +} + +void +keyMgmtSetGtk(phostsa_private priv, IEEEtypes_GtkElement_t * pGtk, UINT8 *pKek) +{ + hostsa_util_fns *util_fns = &priv->util_fns; + UINT8 encrKeyLen; + + /* Determine the encrypted key field length from the IE length */ + encrKeyLen = pGtk->Len - (sizeof(pGtk->KeyInfo) + + sizeof(pGtk->KeyLen) + sizeof(pGtk->RSC)); + + MRVL_AesUnWrap(pKek, + 2, + encrKeyLen / 8 - 1, + (UINT8 *)pGtk->Key, NULL, (UINT8 *)pGtk->Key); + + memcpy(util_fns, &priv->suppData->keyMgmtInfoSta.GRKey.Key, + (UINT8 *)pGtk->Key, + sizeof(priv->suppData->keyMgmtInfoSta.GRKey.Key)); + + FillGrpKeyMaterialStruct(priv, + KDE_DATA_TYPE_GTK, + pGtk->RSC, + pGtk->KeyInfo.KeyId, + pGtk->KeyLen, + &priv->suppData->keyMgmtInfoSta.GRKey); +} + +void +keyMgmtSetIGtk(phostsa_private priv, keyMgmtInfoSta_t *pKeyMgmtInfoSta, + IGtkKde_t *pIGtkKde, UINT8 iGtkKdeLen) +{ + hostsa_util_fns *util_fns = &priv->util_fns; + UINT8 iGtkLen; + + iGtkLen = iGtkKdeLen - 12; /* OUI + dataType + keyId + IPN = 12 bytes */ + + memcpy(util_fns, &pKeyMgmtInfoSta->IGtk.Key, + (UINT8 *)pIGtkKde->IGtk, + MIN(sizeof(pKeyMgmtInfoSta->IGtk.Key), iGtkLen)); + + FillGrpKeyMaterialStruct(priv, + KDE_DATA_TYPE_IGTK, + pIGtkKde->IPN, + pIGtkKde->keyId[0], + iGtkLen, &pKeyMgmtInfoSta->IGtk); + +} + +UINT8 * +keyMgmtGetIGtk(phostsa_private priv) +{ + return (priv->suppData->keyMgmtInfoSta.IGtk.Key); +} + +void +keyMgmtPlumbPairwiseKey(phostsa_private priv) +{ + hostsa_util_fns *util_fns = &priv->util_fns; + + memcpy(util_fns, &priv->suppData->keyMgmtInfoSta.PWKey, + &priv->suppData->keyMgmtInfoSta.newPWKey, + sizeof(priv->suppData->keyMgmtInfoSta.PWKey)); + + FillKeyMaterialStruct(priv, + keyMgmtGetKeySize(priv, TRUE), + TRUE, &priv->suppData->keyMgmtInfoSta.PWKey); +} + +#if 0 +#pragma arm section code = ".wlandatapathcode" +void +keyMgmtSuccessfulDecryptNotify(cm_ConnectionInfo_t * connPtr, + cipher_key_t *pRxCipherKey) +{ + if (supplicantIsEnabled(connPtr)) { + connPtr->suppData->keyMgmtInfoSta.pRxDecryptKey = pRxCipherKey; + + if (connPtr->suppData->keyMgmtInfoSta.pRxDecryptKey && + (!connPtr->suppData->customMIB_RSNConfig.RSNEnabled || + (connPtr->suppData->customMIB_RSNConfig.RSNEnabled && + connPtr->suppData->keyMgmtInfoSta.pwkHandshakeComplete))) + { + SET_KEY_STATE_FORCE_EAPOL_UNENCRYPTED(connPtr-> + suppData-> + keyMgmtInfoSta. + pRxDecryptKey, + FALSE); + } + } else { + if (pRxCipherKey && + (!connPtr->cmFlags.RSNEnabled || + (connPtr->cmFlags.RSNEnabled && + connPtr->cmFlags.gDataTrafficEnabled))) { + SET_KEY_STATE_FORCE_EAPOL_UNENCRYPTED(pRxCipherKey, + FALSE); + } + } +} + +#pragma arm section code +#endif +static void +keyMgmtKeyGroupTxDone(phostsa_private priv) +{ + if (priv->gtk_installed) + return; + /* + ** if (!pBufDesc || (pBufDesc->rsvd & 0x00FF == 0)) + ** + ** Removed check to verify the 4th message was a success. If we + ** miss the ACK from the 4th(WPA2)/2nd(WPA) message, but the AP + ** received it, then it won't retry and we will be stuck waiting for + ** a session timeout. + ** + ** Could add back later if we retry the message in case of TX failure. + */ + FillKeyMaterialStruct(priv, + keyMgmtGetKeySize(priv, FALSE), + FALSE, &priv->suppData->keyMgmtInfoSta.GRKey); + + priv->suppData->keyMgmtInfoSta.RSNDataTrafficEnabled = TRUE; + + if (priv->suppData->keyMgmtInfoSta.RSNSecured == FALSE) { + priv->suppData->keyMgmtInfoSta.RSNSecured = TRUE; + + keyMgmtControlledPortOpen(priv); + } +#ifdef MULTI_CH_SW + chmgr_UnlockCh(connPtr, 0); +#endif + + //return NULL; +} + +static void +keyMgmtKeyPairwiseTxDone(phostsa_private priv) +{ + if (!priv->suppData->keyMgmtInfoSta.pwkHandshakeComplete) { + /* + ** if (!pBufDesc || (pBufDesc->rsvd & 0x00FF == 0)) + ** + ** Removed check to verify the 4th message was a success. If we + ** miss the ACK from the 4th(WPA2) message, but the AP + ** received it, then it won't retry and we will be stuck waiting for + ** a session timeout. + ** + ** Could add back later if we retry the message in case of TX failure. + */ + keyMgmtPlumbPairwiseKey(priv); + + priv->suppData->keyMgmtInfoSta.pwkHandshakeComplete = TRUE; + + if (priv->suppData->keyMgmtInfoSta.pRxDecryptKey && + priv->suppData->keyMgmtInfoSta.pwkHandshakeComplete) { + SET_KEY_STATE_FORCE_EAPOL_UNENCRYPTED(priv->suppData-> + keyMgmtInfoSta. + pRxDecryptKey, + FALSE); + } + } +} + +static + void +keyMgmtKeyPairAndGroupTxDone(phostsa_private priv) +{ + if (!priv->suppData->keyMgmtInfoSta.pwkHandshakeComplete) { + keyMgmtKeyPairwiseTxDone(priv); + keyMgmtKeyGroupTxDone(priv); + } +} + +void +keyMgmtControlledPortOpen(phostsa_private priv) +{ + hostsa_mlan_fns *pm_fns = &priv->mlan_fns; + + supplicantStopSessionTimer((void *)priv); + + pm_fns->Hostsa_StaControlledPortOpen(pm_fns->pmlan_private); +} + +#ifdef WAR_ROM_BUG42707_RSN_IE_LEN_CHECK + +BOOLEAN +patch_supplicantParseRsnIe(phostsa_private priv, IEEEtypes_RSNElement_t *pRsnIe, + SecurityMode_t *pWpaTypeOut, + Cipher_t *pMcstCipherOut, + Cipher_t *pUcstCipherOut, + AkmSuite_t *pAkmListOut, + UINT8 akmOutMax, + IEEEtypes_RSNCapability_t *pRsnCapOut, + Cipher_t *pGrpMgmtCipherOut) +{ + hostsa_util_fns *util_fns = &priv->util_fns; + UINT8 *pIeData; + UINT8 *pIeEnd; + UINT8 *pGrpKeyCipher; + UINT16 pwsKeyCnt; + UINT8 *pPwsKeyCipherList; + UINT16 authKeyCnt; + UINT8 *pAuthKeyList; + + IEEEtypes_RSNCapability_t *pRsnCap; + + UINT16 *pPMKIDCnt; + + UINT8 *pGrpMgmtCipher; + + memset(util_fns, pWpaTypeOut, 0x00, sizeof(SecurityMode_t)); + + pWpaTypeOut->wpa2 = 1; + + /* Set the start and end of the IE data */ + pIeData = (UINT8 *)&pRsnIe->Ver; + pIeEnd = pIeData + pRsnIe->Len; + + /* Skip past the version field */ + pIeData += sizeof(pRsnIe->Ver); + + /* Parse the group key cipher list */ + pGrpKeyCipher = pIeData; + pIeData += sizeof(pRsnIe->GrpKeyCipher); + supplicantParseMcstCipher(priv, pMcstCipherOut, pGrpKeyCipher); + + /* Parse the pairwise key cipher list */ + memcpy(util_fns, &pwsKeyCnt, pIeData, sizeof(pwsKeyCnt)); + pIeData += sizeof(pRsnIe->PwsKeyCnt); + + pPwsKeyCipherList = pIeData; + pIeData += pwsKeyCnt * sizeof(pRsnIe->PwsKeyCipherList); + supplicantParseUcstCipher(priv, pUcstCipherOut, pwsKeyCnt, + pPwsKeyCipherList); + + /* Parse and return the AKM list */ + memcpy(util_fns, &authKeyCnt, pIeData, sizeof(authKeyCnt)); + pIeData += sizeof(pRsnIe->AuthKeyCnt); + + pAuthKeyList = pIeData; + pIeData += authKeyCnt * sizeof(pRsnIe->AuthKeyList); + memset(util_fns, pAkmListOut, 0x00, akmOutMax * sizeof(AkmSuite_t)); + memcpy(util_fns, pAkmListOut, + pAuthKeyList, + MIN(authKeyCnt, akmOutMax) * sizeof(pRsnIe->AuthKeyList)); + + /* Check if the RSN Capability is included */ + if (pIeData < pIeEnd) { + pRsnCap = (IEEEtypes_RSNCapability_t *)pIeData; + pIeData += sizeof(pRsnIe->RsnCap); + + if (pRsnCapOut) { + memcpy(util_fns, pRsnCapOut, pRsnCap, + sizeof(IEEEtypes_RSNCapability_t)); + } + } + + /* Check if the PMKID count is included */ + if (pIeData < pIeEnd) { + pPMKIDCnt = (UINT16 *)pIeData; + pIeData += sizeof(pRsnIe->PMKIDCnt); + + /* Check if the PMKID List is included */ + if (pIeData < pIeEnd) { + /* pPMKIDList = pIeData; <-- Currently not used in parsing */ + pIeData += *pPMKIDCnt * sizeof(pRsnIe->PMKIDList); + } + } + + /* Check if the Group Mgmt Cipher is included */ + if (pIeData < pIeEnd) { + pGrpMgmtCipher = pIeData; + + if (pGrpMgmtCipherOut) { + memcpy(util_fns, pGrpMgmtCipherOut, + pGrpMgmtCipher, sizeof(pRsnIe->GrpMgmtCipher)); + } + } + + return TRUE; +} + +#endif + +//#pragma arm section code = ".init" +void +keyMgmtSta_RomInit(void) +{ +//#if defined(PSK_SUPPLICANT) || defined (WPA_NONE) + //ramHook_keyMgmtProcessMsgExt = keyMgmtProcessMsgExt; + //ramHook_keyMgmtSendDeauth = keyMgmtSendDeauth2Peer; +//#endif + +#ifdef WAR_ROM_BUG42707_RSN_IE_LEN_CHECK + supplicantParseRsnIe_hook = patch_supplicantParseRsnIe; +#endif + +} + +//#pragma arm section code + +#if defined(BTAMP) +UINT8 * +parseKeyDataField(cm_ConnectionInfo_t * connPtr, UINT8 *pKey, UINT16 len) +{ + keyMgmtInfoSta_t *pKeyMgmtInfoSta; + KDE_t *pKde; + + pKeyMgmtInfoSta = &connPtr->suppData->keyMgmtInfoSta; + + /* parse KDE GTK */ + pKde = parseKeyDataGTK(pKey, len, &pKeyMgmtInfoSta->GRKey); + + /* Parse PMKID though it's _not used_ now */ + + pKde = parseKeyKDE_DataType(pKey, len, KDE_DATA_TYPE_PMKID); + + if (pKde) { + /* PMKID KDE */ + return (UINT8 *)pKde->data; + } + + return NULL; +} + +/* Add RSN IE to a frame body */ +UINT16 +btampAddRsnIe(cm_ConnectionInfo_t * connPtr, IEEEtypes_RSNElement_t *pRsnIe) +{ + const uint8 wpa2_psk[4] = { 0x00, 0x0f, 0xac, 0x02 }; /* WPA2 PSK */ + UINT16 ieSize; + IEEEtypes_RSNCapability_t rsncap; + SecurityMode_t securityMode; + Cipher_t mcstWpa2; + Cipher_t ucstWpa2; + AkmSuite_t *pAkmWpa2; + + memset(util_fns, &securityMode, 0x00, sizeof(securityMode)); + memset(util_fns, &mcstWpa2, 0x00, sizeof(mcstWpa2)); + memset(util_fns, &ucstWpa2, 0x00, sizeof(ucstWpa2)); + memset(util_fns, &rsncap, 0, sizeof(rsncap)); + + mcstWpa2.ccmp = 1; + ucstWpa2.ccmp = 1; + securityMode.wpa2 = 1; + + pAkmWpa2 = (AkmSuite_t *)wpa2_psk; + + supplicantSetProfile(connPtr, securityMode, mcstWpa2, ucstWpa2); + + supplicantSetAssocRsn(connPtr, securityMode, &mcstWpa2, &ucstWpa2, + pAkmWpa2, &rsncap, NULL); + + ieSize = keyMgmtFormatWpaRsnIe(connPtr, + (UINT8 *)pRsnIe, + NULL, NULL, NULL, FALSE); + return ieSize; +} +#endif +#if 0 +void +supplicantParseAndFormatRsnIe(phostsa_private priv, + IEEEtypes_RSNElement_t *pRsnIe, + SecurityMode_t *pWpaTypeOut, + Cipher_t *pMcstCipherOut, + Cipher_t *pUcstCipherOut, AkmSuite_t *pAkmListOut, + UINT8 akmOutMax, + IEEEtypes_RSNCapability_t *pRsnCapOut, + Cipher_t *pGrpMgmtCipherOut) +{ + hostsa_util_fns *util_fns = &priv->util_fns; + UINT8 *pIeData; + UINT8 *pIeEnd; + UINT8 *pGrpKeyCipher; + UINT16 pwsKeyCnt; + UINT8 *pPwsKeyCipherList; + UINT16 authKeyCnt; + UINT8 *pAuthKeyList; + + IEEEtypes_RSNCapability_t *pRsnCap; + + UINT16 *pPMKIDCnt; + + UINT8 *pGrpMgmtCipher; + +//longl add + UINT8 *pos = NULL; + UINT8 cp_size = 0; +#if 0 +#if !defined(REMOVE_PATCH_HOOKS) + if (supplicantParseRsnIe_hook(pRsnIe, + pWpaTypeOut, + pMcstCipherOut, + pUcstCipherOut, + pAkmListOut, + akmOutMax, + pRsnCapOut, pGrpMgmtCipherOut)) { + return; + } +#endif +#endif + memset(util_fns, pWpaTypeOut, 0x00, sizeof(SecurityMode_t)); + memset(util_fns, (UINT8 *)priv->suppData->wpa_rsn_ie, 0x00, + MAX_IE_SIZE); + pos = (UINT8 *)priv->suppData->wpa_rsn_ie; + + pWpaTypeOut->wpa2 = 1; + + /* Set the start and end of the IE data */ + pIeData = (UINT8 *)&pRsnIe->Ver; + pIeEnd = pIeData + pRsnIe->Len; + + /* Skip past the version field */ + pIeData += sizeof(pRsnIe->Ver); + + /* Parse the group key cipher list */ + pGrpKeyCipher = pIeData; + pIeData += sizeof(pRsnIe->GrpKeyCipher); + supplicantParseMcstCipher(priv, pMcstCipherOut, pGrpKeyCipher); + + cp_size = pIeData - (UINT8 *)pRsnIe; + memcpy(util_fns, pos, (UINT8 *)pRsnIe, cp_size); + pos += cp_size; + + /* Parse the pairwise key cipher list */ + memcpy(util_fns, &pwsKeyCnt, pIeData, sizeof(pwsKeyCnt)); + pIeData += sizeof(pRsnIe->PwsKeyCnt); + + if (pwsKeyCnt > 0) { + (*(UINT16 *)pos) = (UINT16)1; + pos += sizeof(UINT16); + } + + pPwsKeyCipherList = pIeData; + pIeData += pwsKeyCnt * sizeof(pRsnIe->PwsKeyCipherList); + supplicantParseUcstCipher(priv, pUcstCipherOut, pwsKeyCnt, + pPwsKeyCipherList); + + if (pUcstCipherOut->ccmp == 1) { + memcpy(util_fns, pos, wpa2_oui04, sizeof(wpa2_oui04)); + pos += sizeof(wpa2_oui04); + } else if (pUcstCipherOut->tkip == 1) { + memcpy(util_fns, pos, wpa2_oui02, sizeof(wpa2_oui02)); + pos += sizeof(wpa2_oui02); + } + if ((pUcstCipherOut->ccmp == 1) && (pUcstCipherOut->tkip == 1)) + pUcstCipherOut->tkip = 0; + + cp_size = pIeEnd - pIeData; + memcpy(util_fns, pos, pIeData, cp_size); + pos += cp_size; + ((IEEEtypes_RSNElement_t *)(priv->suppData->wpa_rsn_ie))->Len = + pos - (UINT8 *)priv->suppData->wpa_rsn_ie - + sizeof(IEEEtypes_InfoElementHdr_t); + + /* Parse and return the AKM list */ + memcpy(util_fns, &authKeyCnt, pIeData, sizeof(authKeyCnt)); + pIeData += sizeof(pRsnIe->AuthKeyCnt); + + pAuthKeyList = pIeData; + pIeData += authKeyCnt * sizeof(pRsnIe->AuthKeyList); + memset(util_fns, pAkmListOut, 0x00, akmOutMax * sizeof(AkmSuite_t)); + memcpy(util_fns, pAkmListOut, + pAuthKeyList, + MIN(authKeyCnt, akmOutMax) * sizeof(pRsnIe->AuthKeyList)); + + DBG_HEXDUMP(MCMD_D, " pAuthKeyList", + (t_u8 *)pAuthKeyList, MIN(authKeyCnt, + akmOutMax) * + sizeof(pRsnIe->AuthKeyList)); + DBG_HEXDUMP(MCMD_D, " pAuthKeyList", (t_u8 *)pAkmListOut, + MIN(authKeyCnt, akmOutMax) * sizeof(pRsnIe->AuthKeyList)); + /* Check if the RSN Capability is included */ + if (pIeData < pIeEnd) { + pRsnCap = (IEEEtypes_RSNCapability_t *)pIeData; + pIeData += sizeof(pRsnIe->RsnCap); + + if (pRsnCapOut) { + memcpy(util_fns, pRsnCapOut, pRsnCap, + sizeof(IEEEtypes_RSNCapability_t)); + } + } + + /* Check if the PMKID count is included */ + if (pIeData < pIeEnd) { + pPMKIDCnt = (UINT16 *)pIeData; + pIeData += sizeof(pRsnIe->PMKIDCnt); + + /* Check if the PMKID List is included */ + if (pIeData < pIeEnd) { + /* pPMKIDList = pIeData; <-- Currently not used in parsing */ + pIeData += *pPMKIDCnt * sizeof(pRsnIe->PMKIDList); + } + } + + /* Check if the Group Mgmt Cipher is included */ + if (pIeData < pIeEnd) { + pGrpMgmtCipher = pIeData; + + if (pGrpMgmtCipherOut) { + memcpy(util_fns, pGrpMgmtCipherOut, + pGrpMgmtCipher, sizeof(pRsnIe->GrpMgmtCipher)); + } + } +} + +void +supplicantParseAndFormatWpaIe(phostsa_private priv, IEEEtypes_WPAElement_t *pIe, + SecurityMode_t *pWpaType, + Cipher_t *pMcstCipher, + Cipher_t *pUcstCipher, + AkmSuite_t *pAkmList, UINT8 akmOutMax) +{ + hostsa_util_fns *util_fns = &priv->util_fns; + IEEEtypes_WPAElement_t *pTemp = pIe; + int count; + int akmCount = akmOutMax; + AkmSuite_t *pAkm = pAkmList; + UINT8 *pos = NULL; + UINT8 cp_size = 0; + UINT8 *pIeEnd = + (UINT8 *)pIe + sizeof(IEEEtypes_InfoElementHdr_t) + pIe->Len; + + PRINTM(MMSG, "ENTER: %s\n", __FUNCTION__); + + memset(util_fns, pMcstCipher, 0x00, sizeof(Cipher_t)); + memset(util_fns, pUcstCipher, 0x00, sizeof(Cipher_t)); + memset(util_fns, pAkmList, 0x00, akmOutMax * sizeof(AkmSuite_t)); + memset(util_fns, pWpaType, 0x00, sizeof(SecurityMode_t)); + memset(util_fns, (UINT8 *)priv->suppData->wpa_rsn_ie, 0x00, + MAX_IE_SIZE); + pos = (UINT8 *)priv->suppData->wpa_rsn_ie; + + pWpaType->wpa = 1; + + /* record the AP's multicast cipher */ + if (!memcmp + (util_fns, (char *)pTemp->GrpKeyCipher, wpa_oui02, + sizeof(wpa_oui02))) { + /* WPA TKIP */ + pMcstCipher->tkip = 1; + } else if (!memcmp + (util_fns, (char *)pTemp->GrpKeyCipher, wpa_oui04, + sizeof(wpa_oui04))) { + /* WPA AES */ + pMcstCipher->ccmp = 1; + } else if (!memcmp + (util_fns, (char *)pTemp->GrpKeyCipher, wpa_oui01, + sizeof(wpa_oui01))) { + /* WPA WEP 40 */ + pMcstCipher->wep40 = 1; + } else if (!memcmp + (util_fns, (char *)pTemp->GrpKeyCipher, wpa_oui05, + sizeof(wpa_oui05))) { + /* WPA WEP 104 */ + pMcstCipher->wep104 = 1; + } + + cp_size = (UINT8 *)(&pTemp->PwsKeyCnt) - (UINT8 *)pIe; + memcpy(util_fns, pos, (UINT8 *)pIe, cp_size); + pos += cp_size; + + count = pTemp->PwsKeyCnt; + + if (count > 0) { + (*(UINT16 *)pos) = (UINT16)1; + pos += sizeof(UINT16); + } + + while (count) { + /* record the AP's unicast cipher */ + if (!memcmp(util_fns, (char *)pTemp->PwsKeyCipherList, + wpa_oui02, sizeof(wpa_oui02))) { + /* WPA TKIP */ + pUcstCipher->tkip = 1; + } else if (!memcmp(util_fns, (char *)pTemp->PwsKeyCipherList, + wpa_oui04, sizeof(wpa_oui04))) { + /* WPA AES */ + pUcstCipher->ccmp = 1; + } + count--; + + if (count) { + pTemp = (IEEEtypes_WPAElement_t *)((UINT8 *)pTemp + + sizeof(pTemp-> + PwsKeyCipherList)); + } + } + + if (pUcstCipher->ccmp == 1) { + memcpy(util_fns, pos, wpa_oui04, sizeof(wpa_oui04)); + pos += sizeof(wpa_oui04); + } else if (pUcstCipher->tkip == 1) { + memcpy(util_fns, pos, wpa_oui02, sizeof(wpa_oui02)); + pos += sizeof(wpa_oui02); + } + if ((pUcstCipher->ccmp == 1) && (pUcstCipher->tkip == 1)) + pUcstCipher->tkip = 0; + + cp_size = pIeEnd - (UINT8 *)(&pTemp->AuthKeyCnt); + memcpy(util_fns, pos, &pTemp->AuthKeyCnt, cp_size); + pos += cp_size; + ((IEEEtypes_RSNElement_t *)(priv->suppData->wpa_rsn_ie))->Len = + pos - (UINT8 *)priv->suppData->wpa_rsn_ie - + sizeof(IEEEtypes_InfoElementHdr_t); + + count = pTemp->AuthKeyCnt; + + while (count) { + if (akmCount) { + /* Store the AKM */ + memcpy(util_fns, pAkm, + (char *)pTemp->AuthKeyList, + sizeof(pTemp->AuthKeyList)); + pAkm++; + akmCount--; + } + + count--; + + if (count) { + pTemp = (IEEEtypes_WPAElement_t *)((UINT8 *)pTemp + + + sizeof(pTemp-> + AuthKeyList)); + } + } + + if (!memcmp(util_fns, pAkmList, wpa_oui_none, sizeof(wpa_oui_none))) { + pWpaType->wpaNone = 1; + } + +} +#endif + +void * +processRsnWpaInfo(void *priv, void *prsnwpa_ie) +{ + phostsa_private psapriv = (phostsa_private)priv; + hostsa_util_fns *util_fns = &psapriv->util_fns; + Cipher_t mcstCipher; + Cipher_t ucstCipher; + SecurityMode_t wpaType; + AkmSuite_t akmList[AKM_SUITE_MAX]; + IEEEtypes_RSNCapability_t rsnCap; + t_u8 type = ((IEEEtypes_InfoElementHdr_t *)prsnwpa_ie)->ElementId; + + if (supplicantIsEnabled((void *)psapriv)) { + + memset(util_fns, &wpaType, 0x00, sizeof(wpaType)); + + if (type == ELEM_ID_RSN || type == ELEM_ID_VENDOR_SPECIFIC) { + if (type == ELEM_ID_RSN) { + supplicantParseRsnIe(psapriv, + (IEEEtypes_RSNElement_t *) + prsnwpa_ie, &wpaType, + &mcstCipher, &ucstCipher, + akmList, + NELEMENTS(akmList), + &rsnCap, NULL); + } else if (type == ELEM_ID_VENDOR_SPECIFIC) { + supplicantParseWpaIe(psapriv, + (IEEEtypes_WPAElement_t *) + prsnwpa_ie, &wpaType, + &mcstCipher, &ucstCipher, + akmList, + NELEMENTS(akmList)); + } + + if (wpaType.wpa2 || wpaType.wpa) { + memset(util_fns, &rsnCap, 0x00, sizeof(rsnCap)); + + supplicantSetProfile(psapriv, wpaType, + mcstCipher, ucstCipher); + + supplicantSetAssocRsn(psapriv, + wpaType, + &mcstCipher, + &ucstCipher, + akmList, &rsnCap, NULL); + + memset(util_fns, + (UINT8 *)psapriv->suppData->wpa_rsn_ie, + 0x00, MAX_IE_SIZE); + if (keyMgmtFormatWpaRsnIe + (psapriv, + (UINT8 *)&psapriv->suppData->wpa_rsn_ie, + &psapriv->suppData->localBssid, + &psapriv->suppData->localStaAddr, NULL, + FALSE)) + return (void *)(psapriv->suppData-> + wpa_rsn_ie); + } + } + + } + return NULL; +} + +t_u8 +supplicantFormatRsnWpaTlv(void *priv, void *rsn_wpa_ie, void *rsn_ie_tlv) +{ + phostsa_private psapriv = (phostsa_private)priv; + hostsa_util_fns *util_fns = &psapriv->util_fns; + void *supp_rsn_wpa_ie = NULL; + MrvlIEGeneric_t *prsn_ie = (MrvlIEGeneric_t *)rsn_ie_tlv; + t_u8 total_len = 0; + + if (rsn_wpa_ie) { + supp_rsn_wpa_ie = + processRsnWpaInfo((void *)psapriv, rsn_wpa_ie); + if (!supp_rsn_wpa_ie) + return total_len; + + /* WPA_IE or RSN_IE */ + prsn_ie->IEParam.Type = + (t_u16)(((IEEEtypes_InfoElementHdr_t *) + supp_rsn_wpa_ie)->ElementId); + prsn_ie->IEParam.Type = prsn_ie->IEParam.Type & 0x00FF; + prsn_ie->IEParam.Type = wlan_cpu_to_le16(prsn_ie->IEParam.Type); + prsn_ie->IEParam.Length = + (t_u16)(((IEEEtypes_InfoElementHdr_t *) + supp_rsn_wpa_ie)->Len); + prsn_ie->IEParam.Length = prsn_ie->IEParam.Length & 0x00FF; + if (prsn_ie->IEParam.Length <= MAX_IE_SIZE) { + memcpy(util_fns, rsn_ie_tlv + sizeof(prsn_ie->IEParam), + (t_u8 *)supp_rsn_wpa_ie + + sizeof(IEEEtypes_InfoElementHdr_t), + prsn_ie->IEParam.Length); + } else + return total_len; + + HEXDUMP("ASSOC_CMD: RSN IE", (t_u8 *)rsn_ie_tlv, + sizeof(prsn_ie->IEParam) + prsn_ie->IEParam.Length); + total_len += sizeof(prsn_ie->IEParam) + prsn_ie->IEParam.Length; + prsn_ie->IEParam.Length = + wlan_cpu_to_le16(prsn_ie->IEParam.Length); + } + return total_len; +} diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/keyMgmtSta.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/keyMgmtSta.h new file mode 100644 index 000000000000..579e17150ddd --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/keyMgmtSta.h @@ -0,0 +1,79 @@ +/** @file keyMgmtSta.h + * + * @brief This file contains the defines for key management. + * + * Copyright (C) 2014-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 03/07/2014: Initial version +******************************************************/ +#ifndef _KEY_MGMT_STA_H_ +#define _KEY_MGMT_STA_H_ + +#include "keyCommonDef.h" +#include "KeyApiStaDefs.h" +#include "IEEE_types.h" +#include "keyMgmtStaTypes.h" +#include "keyMgmtSta_rom.h" + +#ifdef BTAMP +#include "btamp_config.h" +#define BTAMP_SUPPLICATNT_SESSIONS AMPHCI_MAX_PHYSICAL_LINK_SUPPORTED +#else +#define BTAMP_SUPPLICATNT_SESSIONS 0 +#endif + +//longl test +#define MAX_SUPPLICANT_SESSIONS (10) + +void keyMgmtControlledPortOpen(phostsa_private priv); + +extern BOOLEAN supplicantAkmIsWpaWpa2(phostsa_private priv, AkmSuite_t *pAkm); +extern BOOLEAN supplicantAkmIsWpaWpa2Psk(phostsa_private priv, + AkmSuite_t *pAkm); +extern BOOLEAN supplicantAkmUsesKdf(phostsa_private priv, AkmSuite_t *pAkm); +extern BOOLEAN supplicantGetPmkid(phostsa_private priv, + IEEEtypes_MacAddr_t *pBssid, + IEEEtypes_MacAddr_t *pSta, AkmSuite_t *pAkm, + UINT8 *pPMKID); + +extern void keyMgmtSetIGtk(phostsa_private priv, + keyMgmtInfoSta_t *pKeyMgmtInfoSta, + IGtkKde_t *pIGtkKde, UINT8 iGtkKdeLen); + +extern UINT8 *keyMgmtGetIGtk(phostsa_private priv); + +extern void keyMgmtSta_RomInit(void); + +#if 0 +extern BufferDesc_t *GetTxEAPOLBuffer(struct cm_ConnectionInfo *connPtr, + EAPOL_KeyMsg_Tx_t **ppTxEapol, + BufferDesc_t * pBufDesc); +#endif +extern void allocSupplicantData(void *priv); +extern void freeSupplicantData(void *priv); +extern void supplicantInit(void *priv, supplicantData_t *suppData); +extern BOOLEAN keyMgmtProcessMsgExt(phostsa_private priv, + keyMgmtInfoSta_t *pKeyMgmtInfoSta, + EAPOL_KeyMsg_t *pKeyMsg); + +extern void ProcessKeyMgmtDataSta(phostsa_private priv, mlan_buffer *pmbuf, + IEEEtypes_MacAddr_t *sa, + IEEEtypes_MacAddr_t *da); + +#endif diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/keyMgmtStaTypes.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/keyMgmtStaTypes.h new file mode 100644 index 000000000000..06148c3427fa --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/keyMgmtStaTypes.h @@ -0,0 +1,109 @@ +/** @file keyMgmtstaType.h + * + * @brief This file defines data types structrue for key managment. + * + * Copyright (C) 2014-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 03/07/2014: Initial version +******************************************************/ +#ifndef _KEYMGMTSTATYPES_H_ +#define _KEYMGMTSTATYPES_H_ + +#define WPA_AES_KEY_LEN 16 +#define WPA_TKIP_KEY_LEN 32 +#define WPA_WEP104_KEY_LEN 13 +#define WPA_WEP40_KEY_LEN 5 + +#define KEY_TYPE_WEP 0 +#define KEY_TYPE_TKIP 1 +#define KEY_TYPE_AES 2 +#define KEY_TYPE_WAPI 3 +#define KEY_TYPE_AES_CMAC 4 + +/* This struct definition is used in ROM and should be kept intact */ +typedef struct { + UINT8 wep40:1; + UINT8 wep104:1; + UINT8 tkip:1; + UINT8 ccmp:1; + + UINT8 rsvd:4; + +} Cipher_t; + +/* This struct definition is used in ROM and should be kept intact */ +typedef MLAN_PACK_START struct { + UINT16 noRsn:1; + UINT16 wepStatic:1; + UINT16 wepDynamic:1; + UINT16 wpa:1; + UINT16 wpaNone:1; + UINT16 wpa2:1; + UINT16 cckm:1; + UINT16 wapi:1; + UINT16 rsvd:8; + +} MLAN_PACK_END SecurityMode_t; + +typedef MLAN_PACK_START enum { + AKM_NONE = 0, + + AKM_1X = 1, + AKM_PSK = 2, + AKM_FT_1X = 3, + AKM_FT_PSK = 4, + AKM_SHA256_1X = 5, + AKM_SHA256_PSK = 6, + AKM_TDLS = 7, + + AKM_CCKM = 99, /* Make CCKM a unique AKM enumeration */ + + AKM_WPA_MAX = 2, /* Only AKM types 1 and 2 are expected for WPA */ + AKM_RSN_MAX = 6, /* AKM types 1 to 6 are valid for RSN */ + + AKM_SUITE_MAX = 5 /* Used to size max parsing of AKMs from a BCN */ +} MLAN_PACK_END AkmType_e; + +typedef AkmType_e AkmTypePacked_e; + +typedef struct { + UINT8 akmOui[3]; + AkmTypePacked_e akmType; + +} AkmSuite_t; + +typedef struct { + SecurityMode_t wpaType; + Cipher_t mcstCipher; + Cipher_t ucstCipher; + +} SecurityParams_t; + +typedef enum { + host_PSK_SUPP_MIC_FAIL_TIMEOUT = 0x0001, + host_PSK_SUPP_FOURWAY_HANDSHAKE_FAIL = 0x0002 +} host_psk_suppMsg_e; + +typedef struct { + UINT8 keyId[2]; + UINT8 IPN[6]; + UINT8 IGtk[32]; +} IGtkKde_t; + +#endif diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/keyMgmtSta_rom.c b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/keyMgmtSta_rom.c new file mode 100644 index 000000000000..cfb66db11cf8 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/keyMgmtSta_rom.c @@ -0,0 +1,1299 @@ +/** @file keyMgmtsta_rom.c + * + * @brief This file defines key management function for sta + * + * Copyright (C) 2014-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 03/07/2014: Initial version +******************************************************/ +#include "wltypes.h" +#include "IEEE_types.h" +#include "hostsa_ext_def.h" +#include "authenticator.h" + +#include "keyMgmtSta_rom.h" +#include "pmkCache_rom.h" +#include "crypt_new_rom.h" +#include "rc4_rom.h" +#include "aes_cmac_rom.h" +#include "sha1.h" +#include "md5.h" +#include "mrvl_sha256_crypto.h" +#include "wl_macros.h" + +#define MIC_ERROR_QUIET_TIME_INTERVAL 60000000 /* 60 sec */ +#define MIC_ERROR_CHECK_TIME_INTERVAL 60000000 + +void +supplicantSetAssocRsn_internal(phostsa_private priv, RSNConfig_t *pRsnConfig, + SecurityParams_t *pSecurityParams, + SecurityMode_t wpaType, + Cipher_t *pMcstCipher, + Cipher_t *pUcstCipher, + AkmSuite_t *pAkm, + IEEEtypes_RSNCapability_t *pRsnCap, + Cipher_t *pGrpMgmtCipher) +{ + hostsa_util_fns *util_fns = &priv->util_fns; + memset(util_fns, &pRsnConfig->wpaType, + 0x00, sizeof(pRsnConfig->wpaType)); + memset(util_fns, &pRsnConfig->ucstCipher, + 0x00, sizeof(pRsnConfig->ucstCipher)); + memset(util_fns, &pRsnConfig->mcstCipher, + 0x00, sizeof(pRsnConfig->mcstCipher)); + + pRsnConfig->pmkidValid = 0; + pRsnConfig->rsnCapValid = 0; + pRsnConfig->grpMgmtCipherValid = 0; + pRsnConfig->rsvd = 0; + + if (pSecurityParams->wpaType.wpa2 && wpaType.wpa2) { + /* encryption mode is WPA2 */ + memcpy(util_fns, &pRsnConfig->AKM, (UINT8 *)pAkm, + sizeof(pRsnConfig->AKM)); + + pRsnConfig->wpaType.wpa2 = 1; + + if (pRsnCap) { + pRsnConfig->rsnCapValid = 1; + memcpy(util_fns, &pRsnConfig->rsnCap, pRsnCap, + sizeof(pRsnConfig->rsnCap)); + } + + if (pGrpMgmtCipher) { + pRsnConfig->grpMgmtCipherValid = 1; + memcpy(util_fns, &pRsnConfig->grpMgmtCipher, + pGrpMgmtCipher, + sizeof(pRsnConfig->grpMgmtCipher)); + } + } else if (pSecurityParams->wpaType.wpaNone && wpaType.wpaNone) { + memcpy(util_fns, &pRsnConfig->AKM, + wpa_oui_none, sizeof(pRsnConfig->AKM)); + + /* encryption mode is WPA None */ + pRsnConfig->wpaType.wpaNone = 1; + + if (pSecurityParams->mcstCipher.ccmp && pMcstCipher->ccmp) { + pRsnConfig->mcstCipher.ccmp = 1; + } else { + pRsnConfig->mcstCipher.tkip = 1; + } + } else if (pSecurityParams->wpaType.wpa && wpaType.wpa) { + /* encryption mode is WPA */ + memcpy(util_fns, &pRsnConfig->AKM, (UINT8 *)pAkm, + sizeof(pRsnConfig->AKM)); + + pRsnConfig->wpaType.wpa = 1; + } else if (pSecurityParams->wpaType.noRsn) { + /* No encryption */ + pRsnConfig->wpaType.noRsn = 1; + } + + if (pRsnConfig->wpaType.wpa || pRsnConfig->wpaType.wpa2) { + if (pSecurityParams->ucstCipher.ccmp && pUcstCipher->ccmp) { + pRsnConfig->ucstCipher.ccmp = 1; + } else { + pRsnConfig->ucstCipher.tkip = 1; + } + + if (pSecurityParams->mcstCipher.ccmp && pMcstCipher->ccmp) { + pRsnConfig->mcstCipher.ccmp = 1; + } else if (pSecurityParams->mcstCipher.tkip && + pMcstCipher->tkip) { + pRsnConfig->mcstCipher.tkip = 1; + } else if (pSecurityParams->mcstCipher.wep104 && + pMcstCipher->wep104) { + pRsnConfig->mcstCipher.wep104 = 1; + } else { + pRsnConfig->mcstCipher.wep40 = 1; + } + } + +} + +UINT16 +keyMgmtFormatWpaRsnIe_internal(phostsa_private priv, RSNConfig_t *pRsnConfig, + UINT8 *pos, + IEEEtypes_MacAddr_t *pBssid, + IEEEtypes_MacAddr_t *pStaAddr, + UINT8 *pPmkid, BOOLEAN addPmkid) +{ + hostsa_util_fns *util_fns = &priv->util_fns; + IEEEtypes_WPAElement_t *pWpaIe = (IEEEtypes_WPAElement_t *)pos; + IEEEtypes_RSNElement_t *pRsnIe = (IEEEtypes_RSNElement_t *)pos; + + UINT32 ieSize = 0; +#if 0 //!defined(REMOVE_PATCH_HOOKS) + UINT16 ptr_val; + + if (keyMgmtFormatWpaRsnIe_internal_hook(pRsnConfig, + pos, + pBssid, + pStaAddr, + pPmkid, addPmkid, &ptr_val)) { + return ptr_val; + } +#endif + + if (pRsnConfig->wpaType.wpa2) { + /* encryption mode is WPA2 */ + pRsnIe->ElementId = ELEM_ID_RSN; + pRsnIe->Len = (sizeof(pRsnIe->Ver) + + sizeof(pRsnIe->GrpKeyCipher) + + sizeof(pRsnIe->PwsKeyCnt) + + sizeof(pRsnIe->PwsKeyCipherList) + + sizeof(pRsnIe->AuthKeyCnt) + + sizeof(pRsnIe->AuthKeyList)); + + memcpy(util_fns, (void *)pRsnIe->AuthKeyList, + &pRsnConfig->AKM, sizeof(pRsnIe->AuthKeyList)); + + pRsnIe->Ver = 1; + + pRsnIe->PwsKeyCnt = 1; + pRsnIe->AuthKeyCnt = 1; + + if (pRsnConfig->ucstCipher.ccmp) { + /* unicast cipher is aes */ + memcpy(util_fns, (void *)pRsnIe->PwsKeyCipherList, + wpa2_oui04, sizeof(pRsnIe->PwsKeyCipherList)); + } else { + /* if not AES the TKIP */ + memcpy(util_fns, (void *)pRsnIe->PwsKeyCipherList, + wpa2_oui02, sizeof(pRsnIe->PwsKeyCipherList)); + } + + if (pRsnConfig->mcstCipher.ccmp) { + /* multicast cipher is aes */ + memcpy(util_fns, (void *)pRsnIe->GrpKeyCipher, + wpa2_oui04, sizeof(pRsnIe->GrpKeyCipher)); + } else if (pRsnConfig->mcstCipher.tkip) { + /* multicast cipher is tkip */ + memcpy(util_fns, (void *)pRsnIe->GrpKeyCipher, + wpa2_oui02, sizeof(pRsnIe->GrpKeyCipher)); + } else if (pRsnConfig->mcstCipher.wep104) { + /* multicast cipher is WEP 104 */ + memcpy(util_fns, (void *)pRsnIe->GrpKeyCipher, + wpa2_oui05, sizeof(pRsnIe->GrpKeyCipher)); + } else { + /* multicast cipher is WEP 40 */ + memcpy(util_fns, (void *)pRsnIe->GrpKeyCipher, + wpa2_oui01, sizeof(pRsnIe->GrpKeyCipher)); + } + if (addPmkid && ((!pRsnConfig->pmkidValid && pBssid) || pPmkid)) { + if (pPmkid) { + memcpy(util_fns, pRsnConfig->PMKID, pPmkid, + sizeof(pRsnConfig->PMKID)); + pRsnConfig->pmkidValid = TRUE; + } else { + pRsnConfig->pmkidValid = + supplicantGetPmkid(priv, pBssid, + pStaAddr, + &pRsnConfig->AKM, + pRsnConfig->PMKID); + } + } + + if (pRsnConfig->rsnCapValid + || pRsnConfig->pmkidValid + || pRsnConfig->grpMgmtCipherValid) { + memcpy(util_fns, &pRsnIe->RsnCap, + &pRsnConfig->rsnCap, sizeof(pRsnIe->RsnCap)); + + pRsnIe->Len += sizeof(pRsnIe->RsnCap); + } + + if (pRsnConfig->pmkidValid || pRsnConfig->grpMgmtCipherValid) { + pRsnIe->PMKIDCnt = 0; + pRsnIe->Len += sizeof(pRsnIe->PMKIDCnt); + + if (pRsnConfig->pmkidValid) { + /* Add PMKID to the RSN if not an EAPOL msg */ + pRsnIe->PMKIDCnt = 1; + + memcpy(util_fns, (UINT8 *)pRsnIe->PMKIDList, + pRsnConfig->PMKID, + sizeof(pRsnIe->PMKIDList)); + + pRsnIe->Len += sizeof(pRsnIe->PMKIDList); + } + } + + if (pRsnConfig->grpMgmtCipherValid) { + memcpy(util_fns, pRsnIe->GrpMgmtCipher, + &pRsnConfig->grpMgmtCipher, + sizeof(pRsnIe->GrpMgmtCipher)); + + pRsnIe->Len += sizeof(pRsnIe->GrpMgmtCipher); + } + + ieSize = sizeof(pRsnIe->ElementId) + sizeof(pRsnIe->Len); + ieSize += pRsnIe->Len; + } else if (pRsnConfig->wpaType.wpaNone || pRsnConfig->wpaType.wpa) { + /* encryption mode is WPA */ + pWpaIe->ElementId = ELEM_ID_VENDOR_SPECIFIC; + pWpaIe->Len = (sizeof(IEEEtypes_WPAElement_t) + - sizeof(pWpaIe->ElementId) + - sizeof(pWpaIe->Len)); + + memcpy(util_fns, pWpaIe->OuiType, wpa_oui01, + sizeof(pWpaIe->OuiType)); + + pWpaIe->Ver = 1; + + pWpaIe->PwsKeyCnt = 1; + pWpaIe->AuthKeyCnt = 1; + + memcpy(util_fns, (void *)pWpaIe->AuthKeyList, + &pRsnConfig->AKM, sizeof(pWpaIe->AuthKeyList)); + + if (pRsnConfig->wpaType.wpaNone) { + memcpy(util_fns, (void *)pWpaIe->PwsKeyCipherList, + wpa_oui_none, sizeof(pWpaIe->PwsKeyCipherList)); + + if (pRsnConfig->mcstCipher.tkip) { + /* multicast cipher is tkip */ + memcpy(util_fns, (void *)pWpaIe->GrpKeyCipher, + wpa_oui02, sizeof(pWpaIe->GrpKeyCipher)); + } else if (pRsnConfig->mcstCipher.ccmp) { + /* multicast cipher is aes */ + memcpy(util_fns, (void *)pWpaIe->GrpKeyCipher, + wpa_oui04, sizeof(pWpaIe->GrpKeyCipher)); + } + } else { + if (pRsnConfig->ucstCipher.ccmp) { + /* unicast cipher is aes */ + memcpy(util_fns, + (void *)pWpaIe->PwsKeyCipherList, + wpa_oui04, + sizeof(pWpaIe->PwsKeyCipherList)); + } else { + /* unicast cipher is tkip */ + memcpy(util_fns, + (void *)pWpaIe->PwsKeyCipherList, + wpa_oui02, + sizeof(pWpaIe->PwsKeyCipherList)); + } + + if (pRsnConfig->mcstCipher.ccmp) { + /* multicast cipher is aes */ + memcpy(util_fns, (void *)pWpaIe->GrpKeyCipher, + wpa_oui04, sizeof(pWpaIe->GrpKeyCipher)); + } else if (pRsnConfig->mcstCipher.tkip) { + /* multicast cipher is tkip */ + memcpy(util_fns, (void *)pWpaIe->GrpKeyCipher, + wpa_oui02, sizeof(pWpaIe->GrpKeyCipher)); + } else if (pRsnConfig->mcstCipher.wep104) { + /* multicast cipher is wep 104 */ + memcpy(util_fns, (void *)pWpaIe->GrpKeyCipher, + wpa_oui05, sizeof(pWpaIe->GrpKeyCipher)); + } else { + /* multicast cipher is wep 40 */ + memcpy(util_fns, (void *)pWpaIe->GrpKeyCipher, + wpa_oui01, sizeof(pWpaIe->GrpKeyCipher)); + } + } + + ieSize = sizeof(pWpaIe->ElementId) + sizeof(pWpaIe->Len); + ieSize += pWpaIe->Len; + } + + return ieSize; +} + +void +install_wpa_none_keys_internal(phostsa_private priv, + key_MgtMaterial_t *pKeyMgtData, UINT8 *pPMK, + UINT8 type, UINT8 unicast) +{ + hostsa_util_fns *util_fns = &priv->util_fns; + +#if 0 //!defined(REMOVE_PATCH_HOOKS) + if (install_wpa_none_keys_internal_hook(pKeyMgtData, + pPMK, type, unicast)) { + return; + } +#endif + + memset(util_fns, (void *)pKeyMgtData, 0, sizeof(key_MgtMaterial_t)); + + if (unicast) { + pKeyMgtData->keyInfo = (KEY_INFO_MULTICAST | KEY_INFO_ENABLED); + } else { + pKeyMgtData->keyInfo = (KEY_INFO_UNICAST | KEY_INFO_ENABLED); + } + + if (type) { + memcpy(util_fns, (UINT8 *)pKeyMgtData->keyEncypt.AES.key, pPMK, + 16); + pKeyMgtData->keyType = KEY_TYPE_AES; + pKeyMgtData->keyLen = WPA_AES_KEY_LEN; + } else { + memcpy(util_fns, (UINT8 *)pKeyMgtData->keyEncypt.TKIP.key, pPMK, + 16); + pPMK += 16; + pKeyMgtData->keyType = KEY_TYPE_TKIP; + + /* in WPA none the TX & RX MIC key is the same */ + memcpy(util_fns, (UINT8 *)pKeyMgtData->keyEncypt.TKIP.txMicKey, + pPMK, 8); + memcpy(util_fns, (UINT8 *)pKeyMgtData->keyEncypt.TKIP.rxMicKey, + pPMK, 8); + + pKeyMgtData->keyLen = WPA_TKIP_KEY_LEN; + } + +} + +UINT16 +keyMgmtGetKeySize_internal(RSNConfig_t *pRsnConfig, UINT8 isPairwise) +{ + /* default to TKIP key size */ + UINT16 retval = 32; + +#if 0 //!defined(REMOVE_PATCH_HOOKS) + if (keyMgmtGetKeySize_internal_hook(pRsnConfig, isPairwise, &retval)) { + return retval; + } +#endif + + if (isPairwise) { + if (pRsnConfig->ucstCipher.ccmp) { + retval = 16; + } + } else { + if (pRsnConfig->mcstCipher.ccmp) { + retval = 16; + } else if (pRsnConfig->mcstCipher.wep104) { + retval = 13; + } else if (pRsnConfig->mcstCipher.wep40) { + retval = 5; + } + } + return retval; +} + +//#if defined(PSK_SUPPLICANT) || defined (WPA_NONE) +void +supplicantGenerateSha1Pmkid(phostsa_private priv, UINT8 *pPMK, + IEEEtypes_MacAddr_t *pBssid, + IEEEtypes_MacAddr_t *pSta, UINT8 *pPMKID) +{ + char pmkidString[] = "PMK Name"; + void *pText[3]; + + int len[3] = { 8, 6, 6 }; + + pText[0] = pmkidString; + pText[1] = pBssid; + pText[2] = pSta; + + Mrvl_hmac_sha1((void *)priv, (UINT8 **)pText, len, 3, pPMK, 32, /* PMK size is always 32 bytes */ + pPMKID, 16); +} + +int +isApReplayCounterFresh(phostsa_private priv, keyMgmtInfoSta_t *pKeyMgmtInfoSta, + UINT8 *pRxReplayCount) +{ + hostsa_util_fns *util_fns = &priv->util_fns; + UINT32 tmpHi; + UINT32 tmpLo; + UINT32 rxCountHi; + UINT32 rxCountLo; + + /* initialize the value as stale */ + int retVal = 0; + + memcpy(util_fns, &tmpHi, pRxReplayCount, 4); + memcpy(util_fns, &tmpLo, pRxReplayCount + 4, 4); + + rxCountHi = ntohl(tmpHi); + rxCountLo = ntohl(tmpLo); + + /* check hi dword first */ + if (rxCountHi > pKeyMgmtInfoSta->apCounterHi) { + retVal = 1; + } else if (rxCountHi == pKeyMgmtInfoSta->apCounterHi) { + /* hi dword is equal, check lo dword */ + if (rxCountLo > pKeyMgmtInfoSta->apCounterLo) { + retVal = 1; + } else if (rxCountLo == pKeyMgmtInfoSta->apCounterLo) { + + /* Counters are equal. Check special case of zero. */ + if ((rxCountHi == 0) && (rxCountLo == 0)) { + if (!pKeyMgmtInfoSta->apCounterZeroDone) { + retVal = 1; + } + } + } + } + + return retVal; +} + +void +updateApReplayCounter(phostsa_private priv, keyMgmtInfoSta_t *pKeyMgmtStaInfo, + UINT8 *pRxReplayCount) +{ + hostsa_util_fns *util_fns = &priv->util_fns; + UINT32 tmpHi; + UINT32 tmpLo; + UINT32 rxCountHi; + UINT32 rxCountLo; + + memcpy(util_fns, &tmpHi, pRxReplayCount, 4); + memcpy(util_fns, &tmpLo, pRxReplayCount + 4, 4); + + rxCountHi = ntohl(tmpHi); + rxCountLo = ntohl(tmpLo); + + pKeyMgmtStaInfo->apCounterHi = rxCountHi; + pKeyMgmtStaInfo->apCounterLo = rxCountLo; + + if ((rxCountHi == 0) && (rxCountLo == 0)) { + pKeyMgmtStaInfo->apCounterZeroDone = 1; + } +} + +void +FillKeyMaterialStruct_internal(phostsa_private priv, + key_MgtMaterial_t *pKeyMgtData, UINT16 key_len, + UINT8 isPairwise, KeyData_t *pKey) +{ + hostsa_util_fns *util_fns = &priv->util_fns; + UINT8 keyInfo; + +#if 0 //!defined(REMOVE_PATCH_HOOKS) + if (FillKeyMaterialStruct_internal_hook(pKeyMgtData, + key_len, isPairwise, pKey)) { + return; + } +#endif + + /* Update key material */ + memset(util_fns, (void *)pKeyMgtData, 0x00, sizeof(key_MgtMaterial_t)); + + /* check the key type is pairwise */ + if (isPairwise) { + keyInfo = KEY_INFO_UNICAST; + } else { + keyInfo = KEY_INFO_MULTICAST; + } + + if (key_len == (UINT16)WPA_AES_KEY_LEN) { + /* AES */ + pKeyMgtData->keyType = KEY_TYPE_AES; + pKeyMgtData->keyInfo = keyInfo | KEY_INFO_ENABLED; + + memcpy(util_fns, (UINT8 *)pKeyMgtData->keyEncypt.AES.key, + pKey->Key, key_len); + + } else if (key_len == WPA_TKIP_KEY_LEN) { + pKeyMgtData->keyType = KEY_TYPE_TKIP; + pKeyMgtData->keyInfo = keyInfo | KEY_INFO_ENABLED; + + memcpy(util_fns, (UINT8 *)pKeyMgtData->keyEncypt.TKIP.key, + pKey->Key, TK_SIZE); + memcpy(util_fns, (UINT8 *)pKeyMgtData->keyEncypt.TKIP.txMicKey, + pKey->TxMICKey, 8); + memcpy(util_fns, (UINT8 *)pKeyMgtData->keyEncypt.TKIP.rxMicKey, + pKey->RxMICKey, 8); + } else if (key_len == WPA_WEP104_KEY_LEN || + key_len == WPA_WEP40_KEY_LEN) { + pKeyMgtData->keyType = KEY_TYPE_WEP; + pKeyMgtData->keyInfo = keyInfo | KEY_INFO_ENABLED; + + if (isPairwise) { + pKeyMgtData->keyEncypt.WEP.keyIndex = 0; + pKeyMgtData->keyEncypt.WEP.isDefaultTx = 1; + } else { + /* use the Key index provided */ + pKeyMgtData->keyEncypt.WEP.keyIndex = pKey->KeyIndex; + pKeyMgtData->keyEncypt.WEP.isDefaultTx = 0; + } + memcpy(util_fns, (UINT8 *)(pKeyMgtData->keyEncypt.WEP.key), + pKey->Key, key_len); + } else { + /* Key length does not match + ** don't send down anything + */ + return; + } + + pKeyMgtData->keyLen = key_len; +} + +//#endif + +/* +** This function checks if the given element pointer parameter +** is a KDE or not (returns NULL) +*/ +KDE_t * +parseKeyKDE(phostsa_private priv, IEEEtypes_InfoElementHdr_t *pIe) +{ + hostsa_util_fns *util_fns = &priv->util_fns; + KDE_t *pKde = NULL; + + if (pIe->ElementId == ELEM_ID_VENDOR_SPECIFIC) { + pKde = (KDE_t *)pIe; + + if (pKde->length > sizeof(KDE_t) && + !memcmp(util_fns, (void *)pKde->OUI, kde_oui, + sizeof(kde_oui))) { + return pKde; + } + } + + return NULL; + +} + +/* This function searches KDE_DATA_TYPE_XXX in KDE. We can collect all such +** KDE_DATA_TYPE_XXX in one pass, however, there seems not much benefit +** as in general there would not be many KDE_DATA_TYPE_XXX present. +** Returns NULL, when KDE_DATA_TYPE_XXX not found. +*/ + +KDE_t * +parseKeyKDE_DataType(phostsa_private priv, UINT8 *pData, + SINT32 dataLen, IEEEtypes_KDEDataType_e KDEDataType) +{ + + IEEEtypes_InfoElementHdr_t *pIe; + KDE_t *pKde; +#if 0 //!defined(REMOVE_PATCH_HOOKS) + UINT32 ptr_val; + + if (parseKeyKDE_DataType_hook(pData, dataLen, KDEDataType, &ptr_val)) { + return (KDE_t *)ptr_val; + } +#endif + + if (pData == NULL) { + return NULL; + } + + while (dataLen > (SINT32)sizeof(IEEEtypes_InfoElementHdr_t)) { + pIe = (IEEEtypes_InfoElementHdr_t *)pData; + + if (pIe->ElementId == ELEM_ID_VENDOR_SPECIFIC) { + pKde = parseKeyKDE(priv, pIe); + + if ((pKde != NULL) && (pKde->dataType == KDEDataType)) { + return pKde; + } else if (pIe->Len == 0) { + /* the rest is padding, so adjust the length + ** to stop the processing loop + */ + dataLen = sizeof(IEEEtypes_InfoElementHdr_t); + } + } + + dataLen -= (pIe->Len + sizeof(IEEEtypes_InfoElementHdr_t)); + pData += (pIe->Len + sizeof(IEEEtypes_InfoElementHdr_t)); + } + + return NULL; +} + +KDE_t * +parseKeyDataGTK(phostsa_private priv, UINT8 *pKey, UINT16 len, + KeyData_t *pGRKey) +{ + hostsa_util_fns *util_fns = &priv->util_fns; + GTK_KDE_t *pGtk; + KDE_t *pKde; +#if 0 //!defined(REMOVE_PATCH_HOOKS) + UINT32 ptr_val; + + if (parseKeyDataGTK_hook(pKey, len, pGRKey, &ptr_val)) { + return (KDE_t *)ptr_val; + } +#endif + + /* parse KDE GTK */ + pKde = parseKeyKDE_DataType(priv, pKey, len, KDE_DATA_TYPE_GTK); + + if (pKde) { + /* GTK KDE */ + pGtk = (GTK_KDE_t *)pKde->data; + + /* The KDE overhead is 6 bytes */ + memcpy(util_fns, pGRKey->Key, (void *)pGtk->GTK, + pKde->length - 6); + + /* save the group key index */ + pGRKey->KeyIndex = pGtk->KeyID; + } + + return pKde; +} + +void +KeyMgmtSta_ApplyKEK(phostsa_private priv, EAPOL_KeyMsg_t *pKeyMsg, + KeyData_t *pGRKey, UINT8 *EAPOL_Encr_Key) +{ +#if 0 +#if !defined(REMOVE_PATCH_HOOKS) + if (KeyMgmtSta_ApplyKEK_hook(pKeyMsg, pGRKey, EAPOL_Encr_Key)) { + return; + } +#endif +#endif + pGRKey->TxIV16 = pKeyMsg->key_RSC[1] << 8; + pGRKey->TxIV16 |= pKeyMsg->key_RSC[0]; + pGRKey->TxIV32 = 0xFFFFFFFF; + + pKeyMsg->key_material_len = ntohs(pKeyMsg->key_material_len) & 0xFFFF; + + switch (pKeyMsg->key_info.KeyDescriptorVersion) { + /* + ** Key Descriptor Version 2 or 3: AES key wrap, defined in IETF + ** RFC 3394, shall be used to encrypt the Key Data field using + ** the KEK field from the derived PTK. + */ + case 3: + case 2: + /* CCMP */ + MRVL_AesUnWrap(EAPOL_Encr_Key, + 2, + pKeyMsg->key_material_len / 8 - 1, + (UINT8 *)pKeyMsg->key_data, + NULL, (UINT8 *)pKeyMsg->key_data); + + /* AES key wrap has 8 extra bytes that come out + ** due to the default IV + */ + pKeyMsg->key_material_len -= 8; + break; + + /* + ** Key Descriptor Version 1: ARC4 is used to encrypt the Key Data + ** field using the KEK field from the derived PTK + */ + default: + case 1: + /* TKIP or WEP */ + /* Skip the first 256 bytes of the RC4 Stream */ + RC4_Encrypt((void *)priv, EAPOL_Encr_Key, + (UINT8 *)pKeyMsg->EAPOL_key_IV, + sizeof(pKeyMsg->EAPOL_key_IV), + (UINT8 *)pKeyMsg->key_data, + pKeyMsg->key_material_len, 256); + break; + } + +} + +/* +** Verifies the received EAPOL frame during 4-way handshake or +** group key handshake +*/ +BOOLEAN +KeyMgmtSta_IsRxEAPOLValid(phostsa_private priv, + keyMgmtInfoSta_t *pKeyMgmtInfoSta, + EAPOL_KeyMsg_t *pKeyMsg) +{ + hostsa_util_fns *util_fns = &priv->util_fns; + +#if 0 //!defined(REMOVE_PATCH_HOOKS) + BOOLEAN ptr_val; + + if (KeyMgmtSta_IsRxEAPOLValid_hook(pKeyMgmtInfoSta, pKeyMsg, &ptr_val)) { + return ptr_val; + } +#endif + + if (!pKeyMgmtInfoSta || !pKeyMsg) { + PRINTM(MERROR, "KeyMgmtSta_IsRxEAPOLValid input not valid\n"); + return FALSE; + } + + if (!isApReplayCounterFresh + (priv, pKeyMgmtInfoSta, (UINT8 *)pKeyMsg->replay_cnt)) { + PRINTM(MERROR, + "KeyMgmtSta_IsRxEAPOLValid isApReplayCounterFresh Fail\n"); + return FALSE; + } + + /* Check if we have to verify MIC */ + + if (pKeyMsg->key_info.KeyMIC) { + /* We have to verify MIC, if keyType is 1 it's the 3rd message in + 4-way handshake, in that case verify ANonce. + */ + + if ((pKeyMsg->key_info.KeyType == 1) && + memcmp(util_fns, (UINT8 *)&pKeyMsg->key_nonce, + (UINT8 *)pKeyMgmtInfoSta->ANonce, NONCE_SIZE) != 0) { + /* Dropping the packet, return some error msg. */ + PRINTM(MERROR, + "KeyMgmtSta_IsRxEAPOLValid Nonce check Fail\n"); + return FALSE; + } + + if (!IsEAPOL_MICValid + (priv, pKeyMsg, pKeyMgmtInfoSta->EAPOL_MIC_Key)) { + /* MIC failed */ + PRINTM(MERROR, + "KeyMgmtSta_IsRxEAPOLValid MIC check Fail\n"); + return FALSE; + } + + } + return TRUE; + +} + +/* +** This function populates EAPOL frame fileds that are common +** to message 2, 4 of 4-way handshake and group key hadshake +** message 2. +** Any of these fields in general should not change, and if needed +** can be overwritten in the caller function. +*/ +void +KeyMgmtSta_PrepareEAPOLFrame(phostsa_private priv, EAPOL_KeyMsg_Tx_t *pTxEapol, + EAPOL_KeyMsg_t *pRxEapol, + t_u8 *da, t_u8 *sa, UINT8 *pSNonce) +{ + hostsa_util_fns *util_fns = &priv->util_fns; + + if (!pTxEapol || !pRxEapol) { + return; + } + memset(util_fns, (UINT8 *)pTxEapol, 0x00, sizeof(EAPOL_KeyMsg_Tx_t)); + + formEAPOLEthHdr(priv, pTxEapol, da, sa); + + pTxEapol->keyMsg.desc_type = pRxEapol->desc_type; + pTxEapol->keyMsg.key_info.KeyType = pRxEapol->key_info.KeyType; + pTxEapol->keyMsg.key_info.KeyMIC = 1; + pTxEapol->keyMsg.key_info.Secure = pRxEapol->key_info.Secure; + pTxEapol->keyMsg.replay_cnt[0] = pRxEapol->replay_cnt[0]; + pTxEapol->keyMsg.replay_cnt[1] = pRxEapol->replay_cnt[1]; + + pTxEapol->keyMsg.key_info.KeyDescriptorVersion + = pRxEapol->key_info.KeyDescriptorVersion; + + // Only for 4-w handshake message 2. + if (pSNonce) { + memcpy(util_fns, (UINT8 *)pTxEapol->keyMsg.key_nonce, pSNonce, + NONCE_SIZE); + } +} + +void +KeyMgmtSta_PrepareEAPOLMicErrFrame(phostsa_private priv, + EAPOL_KeyMsg_Tx_t *pTxEapol, + BOOLEAN isUnicast, IEEEtypes_MacAddr_t *da, + IEEEtypes_MacAddr_t *sa, + keyMgmtInfoSta_t *pKeyMgmtInfoSta) +{ + hostsa_util_fns *util_fns = &priv->util_fns; + + if (!pTxEapol || !pKeyMgmtInfoSta) { + return; + } + + memset(util_fns, (UINT8 *)pTxEapol, 0x00, sizeof(EAPOL_KeyMsg_Tx_t)); + + formEAPOLEthHdr(priv, pTxEapol, (t_u8 *)da, (t_u8 *)sa); + + pTxEapol->keyMsg.key_info.KeyType = isUnicast; + pTxEapol->keyMsg.key_info.KeyMIC = 1; + pTxEapol->keyMsg.key_info.Secure = 1; + pTxEapol->keyMsg.key_info.Error = 1; + pTxEapol->keyMsg.key_info.Request = 1; + pTxEapol->keyMsg.replay_cnt[0] = htonl(pKeyMgmtInfoSta->staCounterHi); + pTxEapol->keyMsg.replay_cnt[1] = htonl(pKeyMgmtInfoSta->staCounterLo); + +} + +BOOLEAN +supplicantAkmIsWpaWpa2(phostsa_private priv, AkmSuite_t *pAkm) +{ + hostsa_util_fns *util_fns = &priv->util_fns; + + if (!memcmp(util_fns, pAkm->akmOui, wpa_oui, sizeof(wpa_oui)) || + !memcmp(util_fns, pAkm->akmOui, kde_oui, sizeof(kde_oui))) { + return TRUE; + } + + return FALSE; +} + +BOOLEAN +supplicantAkmIsWpa2(phostsa_private priv, AkmSuite_t *pAkm) +{ + hostsa_util_fns *util_fns = &priv->util_fns; + + if (memcmp(util_fns, pAkm->akmOui, kde_oui, sizeof(kde_oui)) == 0) { + return TRUE; + } + + return FALSE; +} + +BOOLEAN +supplicantAkmIsWpaWpa2Psk(phostsa_private priv, AkmSuite_t *pAkm) +{ + if (supplicantAkmIsWpaWpa2(priv, pAkm)) { + return ((pAkm->akmType == AKM_PSK) || + (pAkm->akmType == AKM_SHA256_PSK) || + (pAkm->akmType == AKM_FT_PSK)); + } + + return FALSE; +} + +BOOLEAN +supplicantAkmUsesKdf(phostsa_private priv, AkmSuite_t *pAkm) +{ + if (supplicantAkmIsWpa2(priv, pAkm)) { + if ((pAkm->akmType == AKM_SHA256_PSK) || + (pAkm->akmType == AKM_SHA256_1X) || + (pAkm->akmType == AKM_FT_PSK) || + (pAkm->akmType == AKM_FT_1X)) { + return TRUE; + } + } + + return FALSE; +} + +BOOLEAN +supplicantAkmWpa2Ft(phostsa_private priv, AkmSuite_t *pAkm) +{ + if (supplicantAkmIsWpa2(priv, pAkm)) { + if ((pAkm->akmType == AKM_FT_PSK) || + (pAkm->akmType == AKM_FT_1X)) { + return TRUE; + } + } + + return FALSE; +} + +BOOLEAN +supplicantAkmUsesSha256Pmkid(phostsa_private priv, AkmSuite_t *pAkm) +{ + if (supplicantAkmIsWpa2(priv, pAkm)) { + if ((pAkm->akmType == AKM_SHA256_PSK) || + (pAkm->akmType == AKM_SHA256_1X)) { + return TRUE; + } + } + + return FALSE; +} + +void +supplicantGenerateSha256Pmkid(phostsa_private priv, UINT8 *pPMK, + IEEEtypes_MacAddr_t *pBssid, + IEEEtypes_MacAddr_t *pSta, UINT8 *pPMKID) +{ + hostsa_util_fns *util_fns = &priv->util_fns; + UINT8 *pOutput; + UINT8 *vectors[3]; + size_t vectLen[NELEMENTS(vectors)]; + UINT8 buf[500] = { 0 }; + + pOutput = (UINT8 *)buf; + + /* + ** PMKID = Truncate-128(SHA-256("PMK Name" || AA || SPA) + */ + vectors[0] = (UINT8 *)"PMK Name"; + vectLen[0] = 8; /* strlen("PMK Name") */ + + vectors[1] = (UINT8 *)pBssid; + vectLen[1] = sizeof(IEEEtypes_MacAddr_t); + + vectors[2] = (UINT8 *)pSta; + vectLen[2] = sizeof(IEEEtypes_MacAddr_t); + + mrvl_sha256_crypto_vector((void *)priv, NELEMENTS(vectors), vectors, + vectLen, pOutput); + + memcpy(util_fns, pPMKID, pOutput, PMKID_LEN); + +} + +BOOLEAN +supplicantGetPmkid(phostsa_private priv, IEEEtypes_MacAddr_t *pBssid, + IEEEtypes_MacAddr_t *pStaAddr, + AkmSuite_t *pAkm, UINT8 *pPMKID) +{ + BOOLEAN retval; + UINT8 *pPMK; + + retval = FALSE; + + if (!supplicantAkmIsWpaWpa2Psk(priv, pAkm)) { + pPMK = pmkCacheFindPMK((void *)priv, pBssid); + + /* found the PMK so generate the PMKID */ + if (pPMK) { + if (supplicantAkmUsesSha256Pmkid(priv, pAkm)) { + supplicantGenerateSha256Pmkid(priv, pPMK, + pBssid, pStaAddr, + pPMKID); + } else { + supplicantGenerateSha1Pmkid(priv, pPMK, pBssid, + pStaAddr, pPMKID); + } + + retval = TRUE; + } + } + + return retval; +} + +EAPOL_KeyMsg_t * +GetKeyMsgNonceFromEAPOL(phostsa_private priv, mlan_buffer *pmbuf, + keyMgmtInfoSta_t *pKeyMgmtInfoSta) +{ + hostsa_util_fns *util_fns = &priv->util_fns; + EAPOL_KeyMsg_t *pKeyMsg = + (EAPOL_KeyMsg_t *)(pmbuf->pbuf + pmbuf->data_offset + + sizeof(ether_hdr_t));; + + if (!KeyMgmtSta_IsRxEAPOLValid(priv, pKeyMgmtInfoSta, pKeyMsg)) { + PRINTM(MERROR, "KeyMgmtSta_IsRxEAPOLValid Fail\n"); + return NULL; + } + /* Generate Nonce if this is first PWK Message */ + if (pKeyMsg->key_info.KeyMIC == 0) { + memcpy(util_fns, pKeyMgmtInfoSta->ANonce, + pKeyMsg->key_nonce, NONCE_SIZE); + + supplicantGenerateRand(priv, pKeyMgmtInfoSta->SNonce, + NONCE_SIZE); + } + return pKeyMsg; +} + +#ifndef WAR_ROM_BUG50312_SIMUL_INFRA_WFD +EAPOL_KeyMsg_t * +ProcessRxEAPOL_PwkMsg3(phostsa_private priv, mlan_buffer *pmbuf, + keyMgmtInfoSta_t *pKeyMgmtInfoSta) +{ + hostsa_util_fns *util_fns = &priv->util_fns; + + EAPOL_KeyMsg_t *pKeyMsg; + + pKeyMsg = GetKeyMsgNonceFromEAPOL(priv, pmbuf, pKeyMgmtInfoSta); + if (!pKeyMsg) { + PRINTM(MERROR, "ProcessRxEAPOL_PwkMsg3 pKeyMsg is NULL\n"); + return NULL; + } + pKeyMgmtInfoSta->newPWKey.TxIV16 = 1; + pKeyMgmtInfoSta->newPWKey.TxIV32 = 0; + + /* look for group key once the pairwise has been plumbed */ + if (pKeyMsg->key_info.EncryptedKeyData) { + /* I think the timer stop should be moved later on + in case ramHook_Process_CCX_MFP_11r returns + FALSE + */ + +// microTimerStop(pKeyMgmtInfoSta->rsnTimer); + util_fns->moal_stop_timer(util_fns->pmoal_handle, + pKeyMgmtInfoSta->rsnTimer); + //pKeyMgmtInfoSta->rsnTimer = 0; + + KeyMgmtSta_ApplyKEK(priv, pKeyMsg, + &pKeyMgmtInfoSta->GRKey, + pKeyMgmtInfoSta->EAPOL_Encr_Key); +#if 0 + if (ramHook_keyMgmtProcessMsgExt(pKeyMgmtInfoSta, pKeyMsg) == + FALSE) { + return NULL; + } +#endif + parseKeyDataGTK(priv, pKeyMsg->key_data, + pKeyMsg->key_material_len, + &pKeyMgmtInfoSta->GRKey); + + } + return pKeyMsg; +} +#endif + +#ifndef WAR_ROM_BUG50312_SIMUL_INFRA_WFD +EAPOL_KeyMsg_t * +ProcessRxEAPOL_GrpMsg1(phostsa_private priv, mlan_buffer *pmbuf, + keyMgmtInfoSta_t *pKeyMgmtInfoSta) +{ + hostsa_util_fns *util_fns = &priv->util_fns; + EAPOL_KeyMsg_t *pKeyMsg; + KeyData_t GRKey; + pKeyMsg = GetKeyMsgNonceFromEAPOL(priv, pmbuf, pKeyMgmtInfoSta); + if (!pKeyMsg) { + return NULL; + } + + KeyMgmtSta_ApplyKEK(priv, pKeyMsg, + &pKeyMgmtInfoSta->GRKey, + pKeyMgmtInfoSta->EAPOL_Encr_Key); + + pKeyMgmtInfoSta->RSNDataTrafficEnabled = 1; + //microTimerStop(pKeyMgmtInfoSta->rsnTimer); + util_fns->moal_stop_timer(util_fns->pmoal_handle, + pKeyMgmtInfoSta->rsnTimer); + //pKeyMgmtInfoSta->rsnTimer = 0; + + /* Decrypt the group key */ + if (pKeyMsg->desc_type == 2) { + /* WPA2 */ + /* handle it according to 802.11i GTK frame format */ + parseKeyDataGTK(priv, pKeyMsg->key_data, + pKeyMsg->key_material_len, &GRKey); + /* Not install same GTK */ + if (!memcmp + (util_fns, pKeyMgmtInfoSta->GRKey.Key, GRKey.Key, + TK_SIZE)) { + priv->gtk_installed = 1; + } else { + memcpy(util_fns, &pKeyMgmtInfoSta->GRKey, &GRKey, + sizeof(KeyData_t)); + pKeyMgmtInfoSta->GRKey.TxIV16 = GRKey.TxIV16; + pKeyMgmtInfoSta->GRKey.TxIV32 = 0xFFFFFFFF; + priv->gtk_installed = 0; + } +#if 0 + if (ramHook_keyMgmtProcessMsgExt(pKeyMgmtInfoSta, pKeyMsg) == + FALSE) { + return NULL; + } +#endif + } else { + /* WPA or Dynamic WEP */ + if (!memcmp + (util_fns, pKeyMgmtInfoSta->GRKey.Key, pKeyMsg->key_data, + pKeyMsg->key_material_len)) { + priv->gtk_installed = 1; + } else { + memcpy(util_fns, pKeyMgmtInfoSta->GRKey.Key, + pKeyMsg->key_data, pKeyMsg->key_material_len); + + pKeyMgmtInfoSta->GRKey.KeyIndex = + pKeyMsg->key_info.KeyIndex; + } + } + + return pKeyMsg; +} +#endif + +void +KeyMgmtResetCounter(keyMgmtInfoSta_t *pKeyMgmtInfo) +{ + if (pKeyMgmtInfo) { + pKeyMgmtInfo->staCounterHi = 0; + pKeyMgmtInfo->staCounterLo = 0; + } +} + +/* +** This code executes when the MIC failure timer timesout +*/ +void +MicErrTimerExp_Sta(t_void *context) +{ + phostsa_private psapriv = (phostsa_private)context; + keyMgmtInfoSta_t *pKeyMgmtInfo = &psapriv->suppData->keyMgmtInfoSta; + + if (pKeyMgmtInfo) { + //if (pKeyMgmtInfo->micTimer == timerId) + { + if (pKeyMgmtInfo->sta_MIC_Error.status + == SECOND_MIC_FAIL_IN_60_SEC) { + //ramHook_keyMgmtSendTkipQuietOver(data); + } + + pKeyMgmtInfo->sta_MIC_Error.status = NO_MIC_FAILURE; + pKeyMgmtInfo->sta_MIC_Error.disableStaAsso = 0; + } + + pKeyMgmtInfo->micTimer = 0; + } +} + +void +DeauthDelayTimerExp_Sta(t_void *context) +{ + phostsa_private psapriv = (phostsa_private)context; + keyMgmtInfoSta_t *pKeyMgmtInfoSta = &psapriv->suppData->keyMgmtInfoSta; + + if (pKeyMgmtInfoSta) { + //if (pKeyMgmtInfoSta->deauthDelayTimer == timerId) + { + if (pKeyMgmtInfoSta->sta_MIC_Error.status + == SECOND_MIC_FAIL_IN_60_SEC) { + //ramHook_keyMgmtSendDeauth(psapriv, + // IEEEtypes_REASON_MIC_FAILURE); + keyMgmtSendDeauth2Peer(psapriv, + IEEEtypes_REASON_MIC_FAILURE); + } + } + + pKeyMgmtInfoSta->deauthDelayTimer = 0; + } +} + +/* +** Key Management timeout handler +*/ +void +keyMgmtStaRsnSecuredTimeoutHandler(t_void *context) +{ + phostsa_private psapriv = (phostsa_private)context; + keyMgmtInfoSta_t *pKeyMgmtInfoSta = &psapriv->suppData->keyMgmtInfoSta; + + if (pKeyMgmtInfoSta) { + //if (pKeyMgmtInfoSta->rsnTimer == timerId) + { + if (pKeyMgmtInfoSta->RSNSecured == FALSE) { + /* Clear timer before calling the timeout so the rsnTimer + ** can't be cancelled during the sme state clearing. + ** (caused timer re-entrancy failure). + */ + //pKeyMgmtInfoSta->rsnTimer = 0; + //ramHook_keyMgmtSendDeauth( + // psapriv, + // IEEEtypes_REASON_4WAY_HANDSHK_TIMEOUT); + keyMgmtSendDeauth2Peer(psapriv, + IEEEtypes_REASON_4WAY_HANDSHK_TIMEOUT); + } + } + //pKeyMgmtInfoSta->rsnTimer = 0; + } +} + +void +keyMgmtSta_StartSession_internal(phostsa_private priv, + keyMgmtInfoSta_t *pKeyMgmtInfoSta, + //MicroTimerCallback_t callback, + UINT32 expiry, UINT8 flags) +{ + hostsa_util_fns *util_fns = &priv->util_fns; + + if (!pKeyMgmtInfoSta->sta_MIC_Error.disableStaAsso) { + //microTimerStop(pKeyMgmtInfoSta->rsnTimer); + util_fns->moal_stop_timer(util_fns->pmoal_handle, + pKeyMgmtInfoSta->rsnTimer); + //pKeyMgmtInfoSta->rsnTimer = 0; + //microTimerStart(callback, + // (UINT32)pKeyMgmtInfoSta, + // expiry, + // &pKeyMgmtInfoSta->rsnTimer, + // flags); + util_fns->moal_start_timer(util_fns->pmoal_handle, + pKeyMgmtInfoSta->rsnTimer, MFALSE, + expiry); + } + + /* reset the authenticator replay counter */ + pKeyMgmtInfoSta->apCounterLo = 0; + pKeyMgmtInfoSta->apCounterHi = 0; + pKeyMgmtInfoSta->apCounterZeroDone = 0; +} + +void +KeyMgmtSta_handleMICDeauthTimer(keyMgmtInfoSta_t *pKeyMgmtInfoSta, + MicroTimerCallback_t callback, + UINT32 expiry, UINT8 flags) +{ +#if 0 + microTimerStop(pKeyMgmtInfoSta->deauthDelayTimer); + + microTimerStart(callback, + (UINT32)pKeyMgmtInfoSta, + expiry, &pKeyMgmtInfoSta->deauthDelayTimer, flags); +#endif +} + +#ifndef WAR_ROM_BUG57216_QUIET_TIME_INTERVAL +/* This function assumes that argument state would be either + NO_MIC_FAILURE or FIRST_MIC_FAIL_IN_60_SEC + It must not be called with state othe than these two +*/ +void +KeyMgmtSta_handleMICErr(MIC_Fail_State_e state, + keyMgmtInfoSta_t *pKeyMgmtInfoSta, + MicroTimerCallback_t callback, UINT8 flags) +{ + UINT32 expiry; +// UINT32 int_save = tx_interrupt_control(TX_INT_DISABLE); + + if (state == NO_MIC_FAILURE) { + /* First MIC failure */ + pKeyMgmtInfoSta->sta_MIC_Error.status = + FIRST_MIC_FAIL_IN_60_SEC; + expiry = MIC_ERROR_CHECK_TIME_INTERVAL; + } else { + /* Received 2 MIC failures within 60 sec. Do deauth from AP */ + pKeyMgmtInfoSta->sta_MIC_Error.disableStaAsso = 1; + pKeyMgmtInfoSta->sta_MIC_Error.status = + SECOND_MIC_FAIL_IN_60_SEC; + pKeyMgmtInfoSta->apCounterHi = 0; + pKeyMgmtInfoSta->apCounterLo = 0; + expiry = MIC_ERROR_QUIET_TIME_INTERVAL; + } +// tx_interrupt_control(int_save); +#if 0 + microTimerStop(pKeyMgmtInfoSta->micTimer); + + microTimerStart(callback, + (UINT32)pKeyMgmtInfoSta, + expiry, &pKeyMgmtInfoSta->micTimer, flags); +#endif +} +#endif + +/* +** Initialize the Key Mgmt session +*/ +void +KeyMgmtSta_InitSession(phostsa_private priv, keyMgmtInfoSta_t *pKeyMgmtInfoSta) +{ + hostsa_util_fns *util_fns = &priv->util_fns; + + pKeyMgmtInfoSta->RSNDataTrafficEnabled = FALSE; + pKeyMgmtInfoSta->RSNSecured = FALSE; + pKeyMgmtInfoSta->pRxDecryptKey = NULL; + pKeyMgmtInfoSta->pwkHandshakeComplete = FALSE; + + if (!pKeyMgmtInfoSta->sta_MIC_Error.disableStaAsso) { +// microTimerStop(pKeyMgmtInfoSta->micTimer); + pKeyMgmtInfoSta->micTimer = 0; +// microTimerStop(pKeyMgmtInfoSta->deauthDelayTimer); + pKeyMgmtInfoSta->deauthDelayTimer = 0; + } +// microTimerStop(pKeyMgmtInfoSta->rsnTimer); + util_fns->moal_stop_timer(util_fns->pmoal_handle, + pKeyMgmtInfoSta->rsnTimer); + + //pKeyMgmtInfoSta->rsnTimer = 0; +} diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/keyMgmtSta_rom.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/keyMgmtSta_rom.h new file mode 100644 index 000000000000..8d5c2864ad46 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/keyMgmtSta_rom.h @@ -0,0 +1,243 @@ +/** @file keyMgntsta_rom.h + * + * @brief This file contains key management function for sta + * + * Copyright (C) 2014-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 03/07/2014: Initial version +******************************************************/ +#ifndef _KEY_MGMT_STA_ROM_H_ +#define _KEY_MGMT_STA_ROM_H_ + +#include "keyCommonDef.h" +#include "KeyApiStaDefs.h" +#include "IEEE_types.h" +#include "keyApiStaTypes.h" +//#include "keyMgmtStaHostTypes.h" +#include "authenticator.h" +#include "keyMgmtApStaCommon.h" + +/* Timer ID passed back to the caller when starting a timer. */ +typedef UINT32 MicroTimerId_t; + +/* Callback function registered when starting a timer */ +typedef void (*MicroTimerCallback_t) (MicroTimerId_t, UINT32); +#define PMKID_LEN 16 + +extern void updateApReplayCounter(phostsa_private priv, + keyMgmtInfoSta_t *pKeyMgmtStaInfo, + UINT8 *pRxReplayCount); + +extern KDE_t *parseKeyKDE(phostsa_private priv, + IEEEtypes_InfoElementHdr_t *pIe); + +extern void supplicantGenerateSha1Pmkid(phostsa_private priv, UINT8 *pPMK, + IEEEtypes_MacAddr_t *pBssid, + IEEEtypes_MacAddr_t *pSta, + UINT8 *pPMKID); + +extern void FillKeyMaterialStruct_internal(phostsa_private priv, + key_MgtMaterial_t *p_keyMgtData, + UINT16 key_len, UINT8 isPairwise, + KeyData_t *pKey); + +extern BOOLEAN (*supplicantSetAssocRsn_internal_hook) (RSNConfig_t *pRsnConfig, + SecurityParams_t + *pSecurityParams, + SecurityMode_t wpaType, + Cipher_t *pMcstCipher, + Cipher_t *pUcstCipher, + AkmSuite_t *pAkm, + IEEEtypes_RSNCapability_t + *pRsnCap, + Cipher_t + *pGrpMgmtCipher); + +extern void supplicantSetAssocRsn_internal(phostsa_private priv, + RSNConfig_t *pRsnConfig, + SecurityParams_t *pSecurityParams, + SecurityMode_t wpaType, + Cipher_t *pMcstCipher, + Cipher_t *pUcstCipher, + AkmSuite_t *pAkm, + IEEEtypes_RSNCapability_t *pRsnCap, + Cipher_t *pGrpMgmtCipher); +#if 0 +extern BOOLEAN (*keyMgmtFormatWpaRsnIe_internal_hook) (RSNConfig_t *pRsnConfig, + UINT8 *pos, + IEEEtypes_MacAddr_t + *pBssid, + IEEEtypes_MacAddr_t + *pStaAddr, UINT8 *pPmkid, + BOOLEAN addPmkid, + UINT16 *ptr_val); +#endif +extern UINT16 keyMgmtFormatWpaRsnIe_internal(phostsa_private priv, + RSNConfig_t *pRsnConfig, + UINT8 *pos, + IEEEtypes_MacAddr_t *pBssid, + IEEEtypes_MacAddr_t *pStaAddr, + UINT8 *pPmkid, BOOLEAN addPmkid); +#if 0 +extern BOOLEAN (*install_wpa_none_keys_internal_hook) (key_MgtMaterial_t + *p_keyMgtData, + UINT8 *pPMK, UINT8 type, + UINT8 unicast); +#endif +extern void install_wpa_none_keys_internal(phostsa_private priv, + key_MgtMaterial_t *p_keyMgtData, + UINT8 *pPMK, UINT8 type, + UINT8 unicast); +#if 0 +extern BOOLEAN (*keyMgmtGetKeySize_internal_hook) (RSNConfig_t *pRsnConfig, + UINT8 isPairwise, + UINT16 *ptr_val); +#endif +extern UINT16 keyMgmtGetKeySize_internal(RSNConfig_t *pRsnConfig, + UINT8 isPairwise); +extern BOOLEAN supplicantAkmIsWpaWpa2(phostsa_private priv, AkmSuite_t *pAkm); +extern BOOLEAN supplicantAkmIsWpaWpa2Psk(phostsa_private priv, + AkmSuite_t *pAkm); +extern BOOLEAN supplicantAkmUsesKdf(phostsa_private priv, AkmSuite_t *pAkm); +#if 0 +extern +BOOLEAN (*parseKeyKDE_DataType_hook) (UINT8 *pData, + SINT32 dataLen, + IEEEtypes_KDEDataType_e KDEDataType, + UINT32 *ptr_val); +#endif +extern KDE_t *parseKeyKDE_DataType(phostsa_private priv, UINT8 *pData, + SINT32 dataLen, + IEEEtypes_KDEDataType_e KDEDataType); +#if 0 +extern BOOLEAN (*parseKeyDataGTK_hook) (UINT8 *pKey, + UINT16 len, + KeyData_t *pGRKey, UINT32 *ptr_val); +#endif +extern KDE_t *parseKeyDataGTK(phostsa_private priv, UINT8 *pKey, UINT16 len, + KeyData_t *pGRKey); + +extern BOOLEAN IsEAPOL_MICValid(phostsa_private priv, EAPOL_KeyMsg_t *pKeyMsg, + UINT8 *pMICKey); +#if 0 +extern BOOLEAN (*KeyMgmtSta_ApplyKEK_hook) (EAPOL_KeyMsg_t *pKeyMsg, + KeyData_t *pGRKey, + UINT8 *EAPOL_Encr_Key); +#endif +extern void KeyMgmtSta_ApplyKEK(phostsa_private priv, EAPOL_KeyMsg_t *pKeyMsg, + KeyData_t *pGRKey, UINT8 *EAPOL_Encr_Key); +#if 0 +extern +BOOLEAN (*KeyMgmtSta_IsRxEAPOLValid_hook) (keyMgmtInfoSta_t *pKeyMgmtInfoSta, + EAPOL_KeyMsg_t *pKeyMsg, + BOOLEAN *ptr_val); +#endif +extern BOOLEAN KeyMgmtSta_IsRxEAPOLValid(phostsa_private priv, + keyMgmtInfoSta_t *pKeyMgmtInfoSta, + EAPOL_KeyMsg_t *pKeyMsg); +extern void KeyMgmtSta_PrepareEAPOLFrame(phostsa_private priv, + EAPOL_KeyMsg_Tx_t *pTxEapol, + EAPOL_KeyMsg_t *pRxEapol, t_u8 *da, + t_u8 *sa, UINT8 *pSNonce); +extern UINT16 KeyMgmtSta_PopulateEAPOLLengthMic(phostsa_private priv, + EAPOL_KeyMsg_Tx_t *pTxEapol, + UINT8 *pEAPOLMICKey, + UINT8 eapolProtocolVersion, + UINT8 forceKeyDescVersion); +extern +void KeyMgmtSta_PrepareEAPOLMicErrFrame(phostsa_private priv, + EAPOL_KeyMsg_Tx_t *pTxEapol, + BOOLEAN isUnicast, + IEEEtypes_MacAddr_t *da, + IEEEtypes_MacAddr_t *sa, + keyMgmtInfoSta_t *pKeyMgmtInfoSta); + +extern BOOLEAN supplicantAkmWpa2Ft(phostsa_private priv, AkmSuite_t *pAkm); + +extern BOOLEAN supplicantAkmUsesSha256Pmkid(phostsa_private priv, + AkmSuite_t *pAkm); + +extern void supplicantGenerateSha256Pmkid(phostsa_private priv, UINT8 *pPMK, + IEEEtypes_MacAddr_t *pBssid, + IEEEtypes_MacAddr_t *pSta, + UINT8 *pPMKID); + +extern BOOLEAN supplicantGetPmkid(phostsa_private priv, + IEEEtypes_MacAddr_t *pBssid, + IEEEtypes_MacAddr_t *pStaAddr, + AkmSuite_t *pAkm, UINT8 *pPMKID); + +extern void KeyMgmt_DerivePTK(phostsa_private priv, UINT8 *pAddr1, + UINT8 *pAddr2, + UINT8 *pNonce1, + UINT8 *pNonce2, + UINT8 *pPTK, UINT8 *pPMK, BOOLEAN use_kdf); + +extern void SetEAPOLKeyDescTypeVersion(EAPOL_KeyMsg_Tx_t *pTxEapol, + BOOLEAN isWPA2, + BOOLEAN isKDF, BOOLEAN nonTKIP); + +extern void KeyMgmtResetCounter(keyMgmtInfoSta_t *pKeyMgmtInfo); + +extern void keyMgmtSta_StartSession_internal(phostsa_private priv, + keyMgmtInfoSta_t *pKeyMgmtInfoSta, + //MicroTimerCallback_t callback, + UINT32 expiry, UINT8 flags); + +extern void KeyMgmtSta_handleMICDeauthTimer(keyMgmtInfoSta_t *pKeyMgmtInfoSta, + MicroTimerCallback_t callback, + UINT32 expiry, UINT8 flags); + +extern void KeyMgmtSta_handleMICErr(MIC_Fail_State_e state, + keyMgmtInfoSta_t *pKeyMgmtInfoSta, + MicroTimerCallback_t callback, UINT8 flags); + +extern void DeauthDelayTimerExp_Sta(t_void *context); +extern void keyMgmtStaRsnSecuredTimeoutHandler(t_void *context); +extern void keyMgmtSendDeauth2Peer(phostsa_private priv, UINT16 reason); +extern void supplicantGenerateRand(hostsa_private *priv, UINT8 *dataOut, + UINT32 length); + +extern EAPOL_KeyMsg_t *GetKeyMsgNonceFromEAPOL(phostsa_private priv, + mlan_buffer *pmbuf, + keyMgmtInfoSta_t + *pKeyMgmtInfoSta); + +extern EAPOL_KeyMsg_t *ProcessRxEAPOL_PwkMsg3(phostsa_private priv, + mlan_buffer *pmbuf, + keyMgmtInfoSta_t + *pKeyMgmtInfoSta); + +extern EAPOL_KeyMsg_t *ProcessRxEAPOL_GrpMsg1(phostsa_private priv, + mlan_buffer *pmbuf, + keyMgmtInfoSta_t + *pKeyMgmtInfoSta); + +extern void KeyMgmtSta_InitSession(phostsa_private priv, + keyMgmtInfoSta_t *pKeyMgmtInfoSta); + +extern void supplicantParseMcstCipher(phostsa_private priv, + Cipher_t *pMcstCipherOut, + UINT8 *pGrpKeyCipher); + +extern void supplicantParseUcstCipher(phostsa_private priv, + Cipher_t *pUcstCipherOut, UINT8 pwsKeyCnt, + UINT8 *pPwsKeyCipherList); + +#endif diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/supplicant.c b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/supplicant.c new file mode 100644 index 000000000000..b2992580dbb6 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/esa/supplicant.c @@ -0,0 +1,151 @@ +/** @file supplicant.c + * + * @brief This file defines the API for supplicant + * + * Copyright (C) 2014-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 03/07/2014: Initial version +******************************************************/ + +#include "wltypes.h" +#include "keyMgmtSta.h" +#include "keyCommonDef.h" +#include "keyMgmtSta.h" +#include "pmkCache.h" + +t_u8 +EAPoLKeyPkt_Validation(mlan_buffer *pmbuf) +{ + t_u32 recvd_pkt_len, eapol_pkt_len; + EAPOL_KeyMsg_t *pKeyMsg = NULL; + + pKeyMsg = + (EAPOL_KeyMsg_t *)(pmbuf->pbuf + pmbuf->data_offset + + sizeof(ether_hdr_t)); + /* Received eapol pkt length: DataLen - 802.3 header */ + recvd_pkt_len = pmbuf->data_len - sizeof(ether_hdr_t); + /* 8021.X header + EAPOL key pkt header */ + eapol_pkt_len = sizeof(EAPOL_KeyMsg_t) - sizeof(pKeyMsg->key_data); + + if (recvd_pkt_len < eapol_pkt_len) { + PRINTM(MERROR, + "Invalid EAPOL Key Msg, received length: %u, least length: %u\n", + recvd_pkt_len, eapol_pkt_len); + return 1; + } + /* Todo: other validation check */ + + return 0; +} + +static __inline void +ProcessEAPoLKeyPkt(phostsa_private priv, mlan_buffer *pmbuf, + IEEEtypes_MacAddr_t *sa, IEEEtypes_MacAddr_t *da) +{ + hostsa_mlan_fns *pm_fns = &priv->mlan_fns; + t_u8 bss_role = pm_fns->Hostsa_get_bss_role(pm_fns->pmlan_private); + + PRINTM(MMSG, "ProcessEAPoLKeyPk bss_type=%x bss_role=%x\n", + pm_fns->bss_type, bss_role); + +#ifdef MIB_STATS + INC_MIB_STAT(connPtr, eapolRxForESUPPCnt); +#endif + + if (EAPoLKeyPkt_Validation(pmbuf) != 0) + return; + + switch (bss_role) { +#ifdef BTAMP + case CONNECTION_TYPE_BTAMP: + ProcessKeyMgmtDataAmp(bufDesc); + break; +#endif + + case MLAN_BSS_ROLE_STA: + /*key data */ + ProcessKeyMgmtDataSta(priv, pmbuf, sa, da); + break; + + default: +#ifdef AUTHENTICATOR + if (WIFI_DIRECT_MODE_GO == connPtr->DeviceMode) { + ProcessKeyMgmtDataAp(bufDesc); + } +#endif + + break; + } +} + +t_u8 +ProcessEAPoLPkt(void *priv, mlan_buffer *pmbuf) +{ + phostsa_private psapriv = (phostsa_private)priv; + ether_hdr_t *pEthHdr = + (ether_hdr_t *)(pmbuf->pbuf + pmbuf->data_offset); + EAP_PacketMsg_t *pEapPkt = NULL; + UINT8 fPacketProcessed = 0; + + pEapPkt = (EAP_PacketMsg_t *)((t_u8 *)pEthHdr + sizeof(ether_hdr_t)); + switch (pEapPkt->hdr_8021x.pckt_type) { + case IEEE_8021X_PACKET_TYPE_EAPOL_KEY: + ProcessEAPoLKeyPkt(psapriv, pmbuf, + (IEEEtypes_MacAddr_t *)pEthHdr->sa, + (IEEEtypes_MacAddr_t *)pEthHdr->da); + fPacketProcessed = 1; + break; + +#if 0 + case IEEE_8021X_PACKET_TYPE_EAP_PACKET: + { + if (WIFI_DIRECT_MODE_CLIENT == connPtr->DeviceMode + || WIFI_DIRECT_MODE_DEVICE == connPtr->DeviceMode) { + if (pEapPkt->code == + IEEE_8021X_CODE_TYPE_REQUEST) { + assocAgent_eapRequestRx(sa); + } + } + } + break; +#endif + default: + break; + } +// CLEAN_FLUSH_CACHED_SQMEM((UINT32)(pEapPkt), bufDesc->DataLen); + return fPacketProcessed; +} + +Status_e +supplicantRestoreDefaults(void *priv) +{ + pmkCacheInit(priv); + return SUCCESS; +} + +/* This can also be removed*/ +//#pragma arm section code = ".init" +void +supplicantFuncInit(void *priv) +{ + supplicantRestoreDefaults(priv); +} + +//#pragma arm section code +//#endif /* PSK_SUPPLICANT */ diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan.h new file mode 100644 index 000000000000..05d71a3bcd36 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan.h @@ -0,0 +1,35 @@ +/** @file mlan.h + * + * @brief This file declares all APIs that will be called from MOAL module. + * It also defines the data structures used for APIs between MLAN and MOAL. + * + * Copyright (C) 2008-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 10/13/2008: initial version + 11/07/2008: split mlan.h into mlan_decl.h & mlan_ioctl.h +******************************************************/ + +#ifndef _MLAN_H_ +#define _MLAN_H_ + +#include "mlan_decl.h" +#include "mlan_ioctl.h" +#include "mlan_ieee.h" + +#endif /* !_MLAN_H_ */ diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_11d.c b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_11d.c new file mode 100644 index 000000000000..57e5fa8613f8 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_11d.c @@ -0,0 +1,1603 @@ +/** @file mlan_11d.c + * + * @brief This file contains functions for 802.11D. + * + * Copyright (C) 2008-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ +/******************************************************** +Change log: + 10/21/2008: initial version +********************************************************/ + +#include "mlan.h" +#include "mlan_join.h" +#include "mlan_util.h" +#include "mlan_fw.h" +#include "mlan_main.h" +#include "mlan_11h.h" + +/******************************************************** + Local Variables +********************************************************/ + +#ifdef STA_SUPPORT +/** Region code mapping */ +typedef struct _region_code_mapping { + /** Region */ + t_u8 region[COUNTRY_CODE_LEN]; + /** Code */ + t_u8 code; +} region_code_mapping_t; + +/** Region code mapping table */ +static region_code_mapping_t region_code_mapping[] = { + {"US ", 0x10}, /* US FCC */ + {"CA ", 0x20}, /* IC Canada */ + {"SG ", 0x10}, /* Singapore */ + {"EU ", 0x30}, /* ETSI */ + {"AU ", 0x30}, /* Australia */ + {"KR ", 0x30}, /* Republic Of Korea */ + {"FR ", 0x32}, /* France */ + {"JP ", 0x40}, /* Japan */ + {"JP ", 0x41}, /* Japan */ + {"CN ", 0x50}, /* China */ + {"JP ", 0xFE}, /* Japan */ + {"JP ", 0xFF}, /* Japan special */ +}; + +/** Default Tx power */ +#define TX_PWR_DEFAULT 10 + +/** Universal region code */ +#define UNIVERSAL_REGION_CODE 0xff + +/* Following two structures define the supported channels */ +/** Channels for 802.11b/g */ +static chan_freq_power_t channel_freq_power_UN_BG[] = { + {1, 2412, TX_PWR_DEFAULT}, + {2, 2417, TX_PWR_DEFAULT}, + {3, 2422, TX_PWR_DEFAULT}, + {4, 2427, TX_PWR_DEFAULT}, + {5, 2432, TX_PWR_DEFAULT}, + {6, 2437, TX_PWR_DEFAULT}, + {7, 2442, TX_PWR_DEFAULT}, + {8, 2447, TX_PWR_DEFAULT}, + {9, 2452, TX_PWR_DEFAULT}, + {10, 2457, TX_PWR_DEFAULT}, + {11, 2462, TX_PWR_DEFAULT}, + {12, 2467, TX_PWR_DEFAULT}, + {13, 2472, TX_PWR_DEFAULT}, + {14, 2484, TX_PWR_DEFAULT} +}; + +/** Channels for 802.11a/j */ +static chan_freq_power_t channel_freq_power_UN_AJ[] = { + {8, 5040, TX_PWR_DEFAULT}, + {12, 5060, TX_PWR_DEFAULT}, + {16, 5080, TX_PWR_DEFAULT}, + {34, 5170, TX_PWR_DEFAULT}, + {38, 5190, TX_PWR_DEFAULT}, + {42, 5210, TX_PWR_DEFAULT}, + {46, 5230, TX_PWR_DEFAULT}, + {36, 5180, TX_PWR_DEFAULT}, + {40, 5200, TX_PWR_DEFAULT}, + {44, 5220, TX_PWR_DEFAULT}, + {48, 5240, TX_PWR_DEFAULT}, + {52, 5260, TX_PWR_DEFAULT}, + {56, 5280, TX_PWR_DEFAULT}, + {60, 5300, TX_PWR_DEFAULT}, + {64, 5320, TX_PWR_DEFAULT}, + {100, 5500, TX_PWR_DEFAULT}, + {104, 5520, TX_PWR_DEFAULT}, + {108, 5540, TX_PWR_DEFAULT}, + {112, 5560, TX_PWR_DEFAULT}, + {116, 5580, TX_PWR_DEFAULT}, + {120, 5600, TX_PWR_DEFAULT}, + {124, 5620, TX_PWR_DEFAULT}, + {128, 5640, TX_PWR_DEFAULT}, + {132, 5660, TX_PWR_DEFAULT}, + {136, 5680, TX_PWR_DEFAULT}, + {140, 5700, TX_PWR_DEFAULT}, + {149, 5745, TX_PWR_DEFAULT}, + {153, 5765, TX_PWR_DEFAULT}, + {157, 5785, TX_PWR_DEFAULT}, + {161, 5805, TX_PWR_DEFAULT}, + {165, 5825, TX_PWR_DEFAULT}, +/* {240, 4920, TX_PWR_DEFAULT}, + {244, 4940, TX_PWR_DEFAULT}, + {248, 4960, TX_PWR_DEFAULT}, + {252, 4980, TX_PWR_DEFAULT}, +channels for 11J JP 10M channel gap */ +}; +#endif /* STA_SUPPORT */ + +/******************************************************** + Global Variables +********************************************************/ + +/******************************************************** + Local Functions +********************************************************/ +#ifdef STA_SUPPORT +/** + * @brief This function converts integer code to region string + * + * @param pmadapter A pointer to mlan_adapter structure + * @param code Region code + * + * @return Region string + */ +static t_u8 * +wlan_11d_code_2_region(pmlan_adapter pmadapter, t_u8 code) +{ + t_u8 i; + + ENTER(); + + /* Look for code in mapping table */ + for (i = 0; i < NELEMENTS(region_code_mapping); i++) { + if (region_code_mapping[i].code == code) { + LEAVE(); + return region_code_mapping[i].region; + } + } + + LEAVE(); + /* Default is US */ + return region_code_mapping[0].region; +} + +/** + * @brief This function Checks if channel txpwr is learned from AP/IBSS + * + * @param pmadapter A pointer to mlan_adapter structure + * @param band Band number + * @param chan Channel number + * @param parsed_region_chan Pointer to parsed_region_chan_11d_t + * + * @return MTRUE or MFALSE + */ +static t_u8 +wlan_11d_channel_known(pmlan_adapter pmadapter, + t_u8 band, + t_u8 chan, parsed_region_chan_11d_t *parsed_region_chan) +{ + chan_power_11d_t *pchan_pwr = parsed_region_chan->chan_pwr; + t_u8 no_of_chan = parsed_region_chan->no_of_chan; + t_u8 i = 0; + t_u8 ret = MFALSE; + mlan_private *pmpriv; + + ENTER(); + + HEXDUMP("11D: parsed_region_chan", (t_u8 *)pchan_pwr, + sizeof(chan_power_11d_t) * no_of_chan); + + /* Search channel */ + for (i = 0; i < no_of_chan; i++) { + if (chan == pchan_pwr[i].chan && band == pchan_pwr[i].band) { + PRINTM(MINFO, "11D: Found channel:%d (band:%d)\n", chan, + band); + ret = MTRUE; + + if (band & BAND_A) { + /* If chan is a DFS channel, we need to see an AP on it */ + pmpriv = wlan_get_priv(pmadapter, + MLAN_BSS_ROLE_STA); + if (pmpriv && + wlan_11h_radar_detect_required(pmpriv, + chan)) { + PRINTM(MINFO, + "11H: DFS channel %d, and ap_seen=%d\n", + chan, pchan_pwr[i].ap_seen); + ret = pchan_pwr[i].ap_seen; + } + } + + LEAVE(); + return ret; + } + } + + PRINTM(MINFO, "11D: Could not find channel:%d (band:%d)\n", chan, band); + LEAVE(); + return ret; +} + +/** + * @brief This function generates parsed_region_chan from Domain Info + * learned from AP/IBSS + * + * @param pmadapter Pointer to mlan_adapter structure + * @param region_chan Pointer to region_chan_t + * @param parsed_region_chan Pointer to parsed_region_chan_11d_t + * + * @return N/A + */ +static t_void +wlan_11d_generate_parsed_region_chan(pmlan_adapter pmadapter, + region_chan_t *region_chan, + parsed_region_chan_11d_t + *parsed_region_chan) +{ + chan_freq_power_t *cfp; + t_u8 i; + + ENTER(); + + /* Region channel must be provided */ + if (!region_chan) { + PRINTM(MWARN, "11D: region_chan is MNULL\n"); + LEAVE(); + return; + } + + /* Get channel-frequency-power trio */ + cfp = region_chan->pcfp; + if (!cfp) { + PRINTM(MWARN, "11D: cfp equal MNULL\n"); + LEAVE(); + return; + } + + /* Set channel, band and power */ + for (i = 0; i < region_chan->num_cfp; i++, cfp++) { + parsed_region_chan->chan_pwr[i].chan = (t_u8)cfp->channel; + parsed_region_chan->chan_pwr[i].band = region_chan->band; + parsed_region_chan->chan_pwr[i].pwr = (t_u8)cfp->max_tx_power; + PRINTM(MINFO, "11D: Chan[%d] Band[%d] Pwr[%d]\n", + parsed_region_chan->chan_pwr[i].chan, + parsed_region_chan->chan_pwr[i].band, + parsed_region_chan->chan_pwr[i].pwr); + } + parsed_region_chan->no_of_chan = region_chan->num_cfp; + + PRINTM(MINFO, "11D: no_of_chan[%d]\n", parsed_region_chan->no_of_chan); + + LEAVE(); + return; +} + +/** + * @brief This function generates domain_info from parsed_region_chan + * + * @param pmadapter Pointer to mlan_adapter structure + * @param parsed_region_chan Pointer to parsed_region_chan_11d_t + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_11d_generate_domain_info(pmlan_adapter pmadapter, + parsed_region_chan_11d_t *parsed_region_chan) +{ + t_u8 no_of_sub_band = 0; + t_u8 no_of_chan = parsed_region_chan->no_of_chan; + t_u8 no_of_parsed_chan = 0; + t_u8 first_chan = 0, next_chan = 0, max_pwr = 0; + t_u8 i, flag = MFALSE; + wlan_802_11d_domain_reg_t *domain_info = &pmadapter->domain_reg; + + ENTER(); + + /* Should be only place that clear domain_reg (besides init) */ + memset(pmadapter, domain_info, 0, sizeof(wlan_802_11d_domain_reg_t)); + + /* Set country code */ + memcpy(pmadapter, domain_info->country_code, + wlan_11d_code_2_region(pmadapter, (t_u8)pmadapter->region_code), + COUNTRY_CODE_LEN); + + PRINTM(MINFO, "11D: Number of channel = %d\n", no_of_chan); + HEXDUMP("11D: parsed_region_chan", (t_u8 *)parsed_region_chan, + sizeof(parsed_region_chan_11d_t)); + + /* Set channel and power */ + for (i = 0; i < no_of_chan; i++) { + if (!flag) { + flag = MTRUE; + next_chan = first_chan = + parsed_region_chan->chan_pwr[i].chan; + max_pwr = parsed_region_chan->chan_pwr[i].pwr; + no_of_parsed_chan = 1; + continue; + } + + if (parsed_region_chan->chan_pwr[i].chan == next_chan + 1 && + parsed_region_chan->chan_pwr[i].pwr == max_pwr) { + next_chan++; + no_of_parsed_chan++; + } else { + domain_info->sub_band[no_of_sub_band].first_chan = + first_chan; + domain_info->sub_band[no_of_sub_band].no_of_chan = + no_of_parsed_chan; + domain_info->sub_band[no_of_sub_band].max_tx_pwr = + max_pwr; + no_of_sub_band++; + no_of_parsed_chan = 1; + next_chan = first_chan = + parsed_region_chan->chan_pwr[i].chan; + max_pwr = parsed_region_chan->chan_pwr[i].pwr; + } + } + + if (flag) { + domain_info->sub_band[no_of_sub_band].first_chan = first_chan; + domain_info->sub_band[no_of_sub_band].no_of_chan = + no_of_parsed_chan; + domain_info->sub_band[no_of_sub_band].max_tx_pwr = max_pwr; + no_of_sub_band++; + } + domain_info->no_of_sub_band = no_of_sub_band; + + PRINTM(MINFO, "11D: Number of sub-band =0x%x\n", + domain_info->no_of_sub_band); + HEXDUMP("11D: domain_info", (t_u8 *)domain_info, + COUNTRY_CODE_LEN + 1 + + sizeof(IEEEtypes_SubbandSet_t) * no_of_sub_band); + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function updates the channel power table with the channel + * present in BSSDescriptor. + * + * @param pmpriv A pointer to mlan_private structure + * @param pbss_desc A pointer to BSSDescriptor_t + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status +wlan_11d_update_chan_pwr_table(mlan_private *pmpriv, BSSDescriptor_t *pbss_desc) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + parsed_region_chan_11d_t *parsed_region_chan = + &pmadapter->parsed_region_chan; + t_u16 i; + t_u8 tx_power = 0; + t_u8 chan; + + ENTER(); + + chan = pbss_desc->phy_param_set.ds_param_set.current_chan; + + tx_power = wlan_get_txpwr_of_chan_from_cfp(pmpriv, chan); + + if (!tx_power) { + PRINTM(MMSG, "11D: Invalid channel\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + /* Check whether the channel already exists in channel power table of + parsed region */ + for (i = 0; ((i < parsed_region_chan->no_of_chan) && + (i < MAX_NO_OF_CHAN)); i++) { + if (parsed_region_chan->chan_pwr[i].chan == chan + && parsed_region_chan->chan_pwr[i].band == + pbss_desc->bss_band) { + /* Channel already exists, use minimum of existing and + tx_power */ + parsed_region_chan->chan_pwr[i].pwr = + MIN(parsed_region_chan->chan_pwr[i].pwr, + tx_power); + parsed_region_chan->chan_pwr[i].ap_seen = MTRUE; + break; + } + } + + if (i == parsed_region_chan->no_of_chan && i < MAX_NO_OF_CHAN) { + /* Channel not found. Update the channel in the channel-power + table */ + parsed_region_chan->chan_pwr[i].chan = chan; + parsed_region_chan->chan_pwr[i].band = + (t_u8)pbss_desc->bss_band; + parsed_region_chan->chan_pwr[i].pwr = tx_power; + parsed_region_chan->chan_pwr[i].ap_seen = MTRUE; + parsed_region_chan->no_of_chan++; + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function finds the no_of_chan-th chan after the first_chan + * + * @param pmadapter A pointer to mlan_adapter structure + * @param band Band + * @param first_chan First channel number + * @param no_of_chan Number of channels + * @param chan Pointer to the returned no_of_chan-th chan number + * + * @return MTRUE or MFALSE + */ +static t_u8 +wlan_11d_get_chan(pmlan_adapter pmadapter, t_u8 band, t_u8 first_chan, + t_u8 no_of_chan, t_u8 *chan) +{ + chan_freq_power_t *cfp = MNULL; + t_u8 i; + t_u8 cfp_no = 0; + + ENTER(); + if (band & (BAND_B | BAND_G | BAND_GN)) { + cfp = channel_freq_power_UN_BG; + cfp_no = NELEMENTS(channel_freq_power_UN_BG); + } else if (band & (BAND_A | BAND_AN)) { + cfp = channel_freq_power_UN_AJ; + cfp_no = NELEMENTS(channel_freq_power_UN_AJ); + } else { + PRINTM(MERROR, "11D: Wrong Band[%d]\n", band); + LEAVE(); + return MFALSE; + } + /* Locate the first_chan */ + for (i = 0; i < cfp_no; i++) { + if (cfp && ((cfp + i)->channel == first_chan)) { + PRINTM(MINFO, "11D: first_chan found\n"); + break; + } + } + + if (i < cfp_no) { + /* Check if beyond the boundary */ + if (i + no_of_chan < cfp_no) { + /* Get first_chan + no_of_chan */ + *chan = (t_u8)(cfp + i + no_of_chan)->channel; + LEAVE(); + return MTRUE; + } + } + + LEAVE(); + return MFALSE; +} + +/** + * @brief This function processes the country info present in BSSDescriptor. + * + * @param pmpriv A pointer to mlan_private structure + * @param pbss_desc A pointer to BSSDescriptor_t + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status +wlan_11d_process_country_info(mlan_private *pmpriv, BSSDescriptor_t *pbss_desc) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + parsed_region_chan_11d_t region_chan; + parsed_region_chan_11d_t *parsed_region_chan = + &pmadapter->parsed_region_chan; + t_u16 i, j, num_chan_added = 0; + + ENTER(); + + memset(pmadapter, ®ion_chan, 0, sizeof(parsed_region_chan_11d_t)); + + /* Parse 11D country info */ + if (wlan_11d_parse_domain_info(pmadapter, &pbss_desc->country_info, + (t_u8)pbss_desc->bss_band, + ®ion_chan) != MLAN_STATUS_SUCCESS) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + if (parsed_region_chan->no_of_chan != 0) { + /* + * Check if the channel number already exists in the + * chan-power table of parsed_region_chan + */ + for (i = 0; (i < region_chan.no_of_chan && i < MAX_NO_OF_CHAN); + i++) { + for (j = 0; + (j < parsed_region_chan->no_of_chan && + j < MAX_NO_OF_CHAN); j++) { + /* + * Channel already exists, update the tx power + * with new tx power, since country IE is valid + * here. + */ + if (region_chan.chan_pwr[i].chan == + parsed_region_chan->chan_pwr[j].chan && + region_chan.chan_pwr[i].band == + parsed_region_chan->chan_pwr[j].band) { + parsed_region_chan->chan_pwr[j].pwr = + region_chan.chan_pwr[i].pwr; + break; + } + } + + if (j == parsed_region_chan->no_of_chan && + (j + num_chan_added) < MAX_NO_OF_CHAN) { + /* + * Channel does not exist in the channel power + * table, update this new chan and tx_power + * to the channel power table + */ + parsed_region_chan-> + chan_pwr[parsed_region_chan-> + no_of_chan + + num_chan_added].chan = + region_chan.chan_pwr[i].chan; + parsed_region_chan-> + chan_pwr[parsed_region_chan-> + no_of_chan + + num_chan_added].band = + region_chan.chan_pwr[i].band; + parsed_region_chan-> + chan_pwr[parsed_region_chan-> + no_of_chan + + num_chan_added].pwr = + region_chan.chan_pwr[i].pwr; + parsed_region_chan-> + chan_pwr[parsed_region_chan-> + no_of_chan + + num_chan_added].ap_seen = + MFALSE; + num_chan_added++; + } + } + parsed_region_chan->no_of_chan += num_chan_added; + } else { + /* Parsed region is empty, copy the first one */ + memcpy(pmadapter, parsed_region_chan, + ®ion_chan, sizeof(parsed_region_chan_11d_t)); + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This helper function copies chan_power_11d_t element + * + * @param chan_dst Pointer to destination of chan_power + * @param chan_src Pointer to source of chan_power + * + * @return N/A + */ +static t_void +wlan_11d_copy_chan_power(chan_power_11d_t *chan_dst, chan_power_11d_t *chan_src) +{ + ENTER(); + + chan_dst->chan = chan_src->chan; + chan_dst->band = chan_src->band; + chan_dst->pwr = chan_src->pwr; + chan_dst->ap_seen = chan_src->ap_seen; + + LEAVE(); + return; +} + +/** + * @brief This function sorts parsed_region_chan in ascending + * channel number. + * + * @param parsed_region_chan Pointer to parsed_region_chan_11d_t + * + * @return N/A + */ +static t_void +wlan_11d_sort_parsed_region_chan(parsed_region_chan_11d_t *parsed_region_chan) +{ + int i, j; + chan_power_11d_t temp; + chan_power_11d_t *pchan_power = parsed_region_chan->chan_pwr; + + ENTER(); + + PRINTM(MINFO, "11D: Number of channel = %d\n", + parsed_region_chan->no_of_chan); + + /* Use insertion sort method */ + for (i = 1; i < parsed_region_chan->no_of_chan; i++) { + wlan_11d_copy_chan_power(&temp, pchan_power + i); + for (j = i; j > 0 && (pchan_power + j - 1)->chan > temp.chan; + j--) + wlan_11d_copy_chan_power(pchan_power + j, + pchan_power + j - 1); + wlan_11d_copy_chan_power(pchan_power + j, &temp); + } + + HEXDUMP("11D: parsed_region_chan", (t_u8 *)parsed_region_chan, + sizeof(parsed_region_chan_11d_t)); + + LEAVE(); + return; +} +#endif /* STA_SUPPORT */ + +/** + * @brief This function sends domain info to FW + * + * @param pmpriv A pointer to mlan_private structure + * @param pioctl_buf A pointer to MLAN IOCTL Request buffer + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status +wlan_11d_send_domain_info(mlan_private *pmpriv, t_void *pioctl_buf) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + /* Send cmd to FW to set domain info */ + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_802_11D_DOMAIN_INFO, + HostCmd_ACT_GEN_SET, + 0, (t_void *)pioctl_buf, MNULL); + if (ret) + PRINTM(MERROR, "11D: Failed to download domain Info\n"); + + LEAVE(); + return ret; +} + +/** + * @brief This function overwrites domain_info + * + * @param pmadapter Pointer to mlan_adapter structure + * @param band Intended operating band + * @param country_code Intended country code + * @param num_sub_band Count of tuples in list below + * @param sub_band_list List of sub_band tuples + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_11d_set_domain_info(mlan_private *pmpriv, + t_u8 band, + t_u8 country_code[COUNTRY_CODE_LEN], + t_u8 num_sub_band, + IEEEtypes_SubbandSet_t *sub_band_list) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + wlan_802_11d_domain_reg_t *pdomain = &pmadapter->domain_reg; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + memset(pmadapter, pdomain, 0, sizeof(wlan_802_11d_domain_reg_t)); + memcpy(pmadapter, pdomain->country_code, country_code, + COUNTRY_CODE_LEN); + pdomain->band = band; + pdomain->no_of_sub_band = num_sub_band; + memcpy(pmadapter, pdomain->sub_band, sub_band_list, + MIN(MRVDRV_MAX_SUBBAND_802_11D, + num_sub_band) * sizeof(IEEEtypes_SubbandSet_t)); + + LEAVE(); + return ret; +} + +/******************************************************** + Global functions +********************************************************/ + +/** + * @brief This function gets if priv is a station (STA) + * + * @param pmpriv Pointer to mlan_private structure + * + * @return MTRUE or MFALSE + */ +t_bool +wlan_is_station(mlan_private *pmpriv) +{ + ENTER(); + LEAVE(); + return (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_STA) ? MTRUE : MFALSE; +} + +/** + * @brief This function gets if 11D is enabled + * + * @param pmpriv Pointer to mlan_private structure + * + * @return MTRUE or MFALSE + */ +t_bool +wlan_11d_is_enabled(mlan_private *pmpriv) +{ + ENTER(); + LEAVE(); + return (pmpriv->state_11d.enable_11d == ENABLE_11D) ? MTRUE : MFALSE; +} + +/** + * @brief Initialize interface variable for 11D + * + * @param pmpriv Pointer to mlan_private structure + * + * @return N/A + */ +t_void +wlan_11d_priv_init(mlan_private *pmpriv) +{ + wlan_802_11d_state_t *state = &pmpriv->state_11d; + + ENTER(); + + /* Start in disabled mode */ + state->enable_11d = DISABLE_11D; + if (!pmpriv->adapter->init_para.cfg_11d) + state->user_enable_11d = DEFAULT_11D_STATE; + else + state->user_enable_11d = + (pmpriv->adapter->init_para.cfg_11d == + MLAN_INIT_PARA_DISABLED) ? DISABLE_11D : ENABLE_11D; + + LEAVE(); + return; +} + +/** + * @brief Initialize device variable for 11D + * + * @param pmadapter Pointer to mlan_adapter structure + * + * @return N/A + */ +t_void +wlan_11d_init(mlan_adapter *pmadapter) +{ + ENTER(); + +#ifdef STA_SUPPORT + memset(pmadapter, &(pmadapter->parsed_region_chan), 0, + sizeof(parsed_region_chan_11d_t)); + memset(pmadapter, &(pmadapter->universal_channel), 0, + sizeof(region_chan_t)); +#endif + memset(pmadapter, &(pmadapter->domain_reg), 0, + sizeof(wlan_802_11d_domain_reg_t)); + + LEAVE(); + return; +} + +/** + * @brief This function enable/disable 11D + * + * @param pmpriv A pointer to mlan_private structure + * @param pioctl_buf A pointer to MLAN IOCTL Request buffer + * @param flag 11D status + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_11d_enable(mlan_private *pmpriv, t_void *pioctl_buf, state_11d_t flag) +{ +#ifdef STA_SUPPORT + mlan_adapter *pmadapter = pmpriv->adapter; +#endif + mlan_status ret = MLAN_STATUS_SUCCESS; + state_11d_t enable = flag; + + ENTER(); + + /* Send cmd to FW to enable/disable 11D function */ + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_802_11_SNMP_MIB, + HostCmd_ACT_GEN_SET, + Dot11D_i, (t_void *)pioctl_buf, &enable); + + if (ret) { + PRINTM(MERROR, "11D: Failed to %s 11D\n", + (flag) ? "enable" : "disable"); + } +#ifdef STA_SUPPORT + else { + /* clear parsed table regardless of flag */ + memset(pmadapter, &(pmadapter->parsed_region_chan), 0, + sizeof(parsed_region_chan_11d_t)); + } +#endif + + LEAVE(); + return ret; +} + +/** + * @brief This function implements command CMD_802_11D_DOMAIN_INFO + * + * @param pmpriv A pointer to mlan_private structure + * @param pcmd A pointer to HostCmd_DS_COMMAND structure of + * command buffer + * @param cmd_action Command action + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_cmd_802_11d_domain_info(mlan_private *pmpriv, + HostCmd_DS_COMMAND *pcmd, t_u16 cmd_action) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + HostCmd_DS_802_11D_DOMAIN_INFO *pdomain_info = + &pcmd->params.domain_info; + MrvlIEtypes_DomainParamSet_t *domain = &pdomain_info->domain; + t_u8 no_of_sub_band = pmadapter->domain_reg.no_of_sub_band; + + ENTER(); + + PRINTM(MINFO, "11D: number of sub-band=0x%x\n", no_of_sub_band); + + pcmd->command = wlan_cpu_to_le16(HostCmd_CMD_802_11D_DOMAIN_INFO); + pdomain_info->action = wlan_cpu_to_le16(cmd_action); + if (cmd_action == HostCmd_ACT_GEN_GET) { + /* Dump domain info */ + pcmd->size = + wlan_cpu_to_le16(sizeof(pdomain_info->action) + + S_DS_GEN); + HEXDUMP("11D: 802_11D_DOMAIN_INFO", (t_u8 *)pcmd, + wlan_le16_to_cpu(pcmd->size)); + LEAVE(); + return MLAN_STATUS_SUCCESS; + } + + /* Set domain info fields */ + domain->header.type = wlan_cpu_to_le16(TLV_TYPE_DOMAIN); + memcpy(pmadapter, domain->country_code, + pmadapter->domain_reg.country_code, + sizeof(domain->country_code)); + + domain->header.len = + ((no_of_sub_band * sizeof(IEEEtypes_SubbandSet_t)) + + sizeof(domain->country_code)); + + if (no_of_sub_band) { + memcpy(pmadapter, domain->sub_band, + pmadapter->domain_reg.sub_band, + MIN(MRVDRV_MAX_SUBBAND_802_11D, + no_of_sub_band) * sizeof(IEEEtypes_SubbandSet_t)); + + pcmd->size = wlan_cpu_to_le16(sizeof(pdomain_info->action) + + domain->header.len + + sizeof(MrvlIEtypesHeader_t) + + S_DS_GEN); + } else { + pcmd->size = + wlan_cpu_to_le16(sizeof(pdomain_info->action) + + S_DS_GEN); + } + domain->header.len = wlan_cpu_to_le16(domain->header.len); + + HEXDUMP("11D: 802_11D_DOMAIN_INFO", (t_u8 *)pcmd, + wlan_le16_to_cpu(pcmd->size)); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handle response of CMD_802_11D_DOMAIN_INFO + * + * @param pmpriv A pointer to mlan_private structure + * @param resp Pointer to command response buffer + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_ret_802_11d_domain_info(mlan_private *pmpriv, HostCmd_DS_COMMAND *resp) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + HostCmd_DS_802_11D_DOMAIN_INFO_RSP *domain_info = + &resp->params.domain_info_resp; + MrvlIEtypes_DomainParamSet_t *domain = &domain_info->domain; + t_u16 action = wlan_le16_to_cpu(domain_info->action); + t_u8 no_of_sub_band = 0; + + ENTER(); + + /* Dump domain info response data */ + HEXDUMP("11D: DOMAIN Info Rsp Data", (t_u8 *)resp, resp->size); + + no_of_sub_band = + (t_u8)((wlan_le16_to_cpu(domain->header.len) - COUNTRY_CODE_LEN) + / sizeof(IEEEtypes_SubbandSet_t)); + + PRINTM(MINFO, "11D Domain Info Resp: number of sub-band=%d\n", + no_of_sub_band); + + if (no_of_sub_band > MRVDRV_MAX_SUBBAND_802_11D) { + PRINTM(MWARN, "11D: Invalid number of subbands %d returned!!\n", + no_of_sub_band); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + switch (action) { + case HostCmd_ACT_GEN_SET: /* Proc Set Action */ + break; + case HostCmd_ACT_GEN_GET: + break; + default: + PRINTM(MERROR, "11D: Invalid Action:%d\n", domain_info->action); + ret = MLAN_STATUS_FAILURE; + break; + } + + LEAVE(); + return ret; +} + +#ifdef STA_SUPPORT +/** + * @brief This function parses country information for region channel + * + * @param pmadapter Pointer to mlan_adapter structure + * @param country_info Country information + * @param band Chan band + * @param parsed_region_chan Pointer to parsed_region_chan_11d_t + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_11d_parse_domain_info(pmlan_adapter pmadapter, + IEEEtypes_CountryInfoFullSet_t *country_info, + t_u8 band, + parsed_region_chan_11d_t *parsed_region_chan) +{ + t_u8 no_of_sub_band, no_of_chan; + t_u8 last_chan, first_chan, cur_chan = 0; + t_u8 idx = 0; + t_u8 j, i; + + ENTER(); + + /* + * Validation Rules: + * 1. Valid Region Code + * 2. First Chan increment + * 3. Channel range no overlap + * 4. Channel is valid? + * 5. Channel is supported by Region? + * 6. Others + */ + + HEXDUMP("country_info", (t_u8 *)country_info, 30); + + /* Step 1: Check region_code */ + if (!(*(country_info->country_code)) || + (country_info->len <= COUNTRY_CODE_LEN)) { + /* No region info or wrong region info: treat as no 11D info */ + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + no_of_sub_band = (country_info->len - COUNTRY_CODE_LEN) / + sizeof(IEEEtypes_SubbandSet_t); + + for (j = 0, last_chan = 0; j < no_of_sub_band; j++) { + + if (country_info->sub_band[j].first_chan <= last_chan) { + /* Step2&3: Check First Chan Num increment and no overlap */ + PRINTM(MINFO, "11D: Chan[%d>%d] Overlap\n", + country_info->sub_band[j].first_chan, last_chan); + continue; + } + + first_chan = country_info->sub_band[j].first_chan; + no_of_chan = country_info->sub_band[j].no_of_chan; + + for (i = 0; idx < MAX_NO_OF_CHAN && i < no_of_chan; i++) { + /* Step 4 : Channel is supported? */ + if (wlan_11d_get_chan + (pmadapter, band, first_chan, i, + &cur_chan) == MFALSE) { + /* Chan is not found in UN table */ + PRINTM(MWARN, + "11D: channel is not supported: %d\n", + i); + break; + } + + last_chan = cur_chan; + + /* Step 5: We don't need to check if cur_chan is + supported by mrvl in region */ + parsed_region_chan->chan_pwr[idx].chan = cur_chan; + parsed_region_chan->chan_pwr[idx].band = band; + parsed_region_chan->chan_pwr[idx].pwr = + country_info->sub_band[j].max_tx_pwr; + idx++; + } + + /* Step 6: Add other checking if any */ + } + + parsed_region_chan->no_of_chan = idx; + + PRINTM(MINFO, "11D: number of channel=0x%x\n", + parsed_region_chan->no_of_chan); + HEXDUMP("11D: parsed_region_chan", (t_u8 *)parsed_region_chan->chan_pwr, + sizeof(chan_power_11d_t) * idx); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function converts channel to frequency + * + * @param pmadapter A pointer to mlan_adapter structure + * @param chan Channel number + * @param band Band + * + * @return Channel frequency + */ +t_u32 +wlan_11d_chan_2_freq(pmlan_adapter pmadapter, t_u8 chan, t_u8 band) +{ + chan_freq_power_t *cf; + t_u16 cnt; + t_u16 i; + t_u32 freq = 0; + + ENTER(); + + /* Get channel-frequency-power trios */ + if (band & (BAND_A | BAND_AN)) { + cf = channel_freq_power_UN_AJ; + cnt = NELEMENTS(channel_freq_power_UN_AJ); + } else { + cf = channel_freq_power_UN_BG; + cnt = NELEMENTS(channel_freq_power_UN_BG); + } + + /* Locate channel and return corresponding frequency */ + for (i = 0; i < cnt; i++) { + if (chan == cf[i].channel) + freq = cf[i].freq; + } + + LEAVE(); + return freq; +} + +/** + * @brief This function setups scan channels + * + * @param pmpriv Pointer to mlan_private structure + * @param band Band + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_11d_set_universaltable(mlan_private *pmpriv, t_u8 band) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + t_u16 i = 0; + + ENTER(); + + memset(pmadapter, pmadapter->universal_channel, 0, + sizeof(pmadapter->universal_channel)); + + if (band & (BAND_B | BAND_G | BAND_GN)) + /* If band B, G or N */ + { + /* Set channel-frequency-power */ + pmadapter->universal_channel[i].num_cfp = + NELEMENTS(channel_freq_power_UN_BG); + PRINTM(MINFO, "11D: BG-band num_cfp=%d\n", + pmadapter->universal_channel[i].num_cfp); + + pmadapter->universal_channel[i].pcfp = channel_freq_power_UN_BG; + pmadapter->universal_channel[i].valid = MTRUE; + + /* Set region code */ + pmadapter->universal_channel[i].region = UNIVERSAL_REGION_CODE; + + /* Set band */ + if (band & BAND_GN) + pmadapter->universal_channel[i].band = BAND_G; + else + pmadapter->universal_channel[i].band = + (band & BAND_G) ? BAND_G : BAND_B; + i++; + } + + if (band & (BAND_A | BAND_AN)) { + /* If band A */ + + /* Set channel-frequency-power */ + pmadapter->universal_channel[i].num_cfp = + NELEMENTS(channel_freq_power_UN_AJ); + PRINTM(MINFO, "11D: AJ-band num_cfp=%d\n", + pmadapter->universal_channel[i].num_cfp); + + pmadapter->universal_channel[i].pcfp = channel_freq_power_UN_AJ; + + pmadapter->universal_channel[i].valid = MTRUE; + + /* Set region code */ + pmadapter->universal_channel[i].region = UNIVERSAL_REGION_CODE; + + /* Set band */ + pmadapter->universal_channel[i].band = BAND_A; + i++; + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function calculates the scan type for channels + * + * @param pmadapter A pointer to mlan_adapter structure + * @param band Band number + * @param chan Chan number + * @param parsed_region_chan Pointer to parsed_region_chan_11d_t + * + * @return PASSIVE if chan is unknown; ACTIVE + * if chan is known + */ +t_u8 +wlan_11d_get_scan_type(pmlan_adapter pmadapter, + t_u8 band, + t_u8 chan, parsed_region_chan_11d_t *parsed_region_chan) +{ + t_u8 scan_type = MLAN_SCAN_TYPE_PASSIVE; + + ENTER(); + + if (wlan_11d_channel_known(pmadapter, band, chan, parsed_region_chan)) { + /* Channel found */ + PRINTM(MINFO, "11D: Channel found and doing Active Scan\n"); + scan_type = MLAN_SCAN_TYPE_ACTIVE; + } else + PRINTM(MINFO, + "11D: Channel not found and doing Passive Scan\n"); + + LEAVE(); + return scan_type; +} + +/** + * @brief This function clears the parsed region table, if 11D is enabled + * + * @param pmpriv A pointer to mlan_private structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_11d_clear_parsedtable(mlan_private *pmpriv) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (wlan_11d_is_enabled(pmpriv)) + memset(pmadapter, &(pmadapter->parsed_region_chan), 0, + sizeof(parsed_region_chan_11d_t)); + else + ret = MLAN_STATUS_FAILURE; + + LEAVE(); + return ret; +} + +/** + * @brief This function generates 11D info from user specified regioncode + * and download to FW + * + * @param pmpriv A pointer to mlan_private structure + * @param band Band to create + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_11d_create_dnld_countryinfo(mlan_private *pmpriv, t_u8 band) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_adapter *pmadapter = pmpriv->adapter; + region_chan_t *region_chan; + parsed_region_chan_11d_t parsed_region_chan; + t_u8 j; + + ENTER(); + + /* Only valid if 11D is enabled */ + if (wlan_11d_is_enabled(pmpriv)) { + + PRINTM(MINFO, "11D: Band[%d]\n", band); + + /* Update parsed_region_chan; download domain info to FW */ + + /* Find region channel */ + for (j = 0; j < MAX_REGION_CHANNEL_NUM; j++) { + region_chan = &pmadapter->region_channel[j]; + + PRINTM(MINFO, "11D: [%d] region_chan->Band[%d]\n", j, + region_chan->band); + + if (!region_chan || !region_chan->valid || + !region_chan->pcfp) + continue; + switch (region_chan->band) { + case BAND_A: + switch (band) { + case BAND_A: + case BAND_AN: + case BAND_A | BAND_AN: + break; + default: + continue; + } + break; + case BAND_B: + case BAND_G: + switch (band) { + case BAND_B: + case BAND_G: + case BAND_G | BAND_B: + case BAND_GN: + case BAND_G | BAND_GN: + case BAND_B | BAND_G | BAND_GN: + break; + default: + continue; + } + break; + default: + continue; + } + break; + } + + /* Check if region channel found */ + if (j >= MAX_REGION_CHANNEL_NUM) { + PRINTM(MERROR, "11D: region_chan not found. Band[%d]\n", + band); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + /* Generate parsed region channel info from region channel */ + memset(pmadapter, &parsed_region_chan, 0, + sizeof(parsed_region_chan_11d_t)); + wlan_11d_generate_parsed_region_chan(pmadapter, region_chan, + &parsed_region_chan); + + /* Generate domain info from parsed region channel info */ + wlan_11d_generate_domain_info(pmadapter, &parsed_region_chan); + + /* Set domain info */ + ret = wlan_11d_send_domain_info(pmpriv, MNULL); + if (ret) { + PRINTM(MERROR, + "11D: Error sending domain info to FW\n"); + } + } + + LEAVE(); + return ret; +} + +/** + * @brief This function parses country info from AP and + * download country info to FW + * + * @param pmpriv A pointer to mlan_private structure + * @param pbss_desc A pointer to BSS descriptor + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_11d_parse_dnld_countryinfo(mlan_private *pmpriv, + BSSDescriptor_t *pbss_desc) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_adapter *pmadapter = pmpriv->adapter; + parsed_region_chan_11d_t region_chan; + parsed_region_chan_11d_t bssdesc_region_chan; + t_u32 i, j; + + ENTER(); + + /* Only valid if 11D is enabled */ + if (wlan_11d_is_enabled(pmpriv)) { + + memset(pmadapter, ®ion_chan, 0, + sizeof(parsed_region_chan_11d_t)); + memset(pmadapter, &bssdesc_region_chan, 0, + sizeof(parsed_region_chan_11d_t)); + + memcpy(pmadapter, ®ion_chan, + &pmadapter->parsed_region_chan, + sizeof(parsed_region_chan_11d_t)); + + if (pbss_desc) { + /* Parse domain info if available */ + ret = wlan_11d_parse_domain_info(pmadapter, + &pbss_desc-> + country_info, + (t_u8)pbss_desc-> + bss_band, + &bssdesc_region_chan); + + if (ret == MLAN_STATUS_SUCCESS) { + /* Update the channel-power table */ + for (i = 0; + ((i < bssdesc_region_chan.no_of_chan) + && (i < MAX_NO_OF_CHAN)); i++) { + + for (j = 0; + ((j < region_chan.no_of_chan) + && (j < MAX_NO_OF_CHAN)); j++) { + /* + * Channel already exists, use minimum + * of existing tx power and tx_power + * received from country info of the + * current AP + */ + if (region_chan.chan_pwr[i]. + chan == + bssdesc_region_chan. + chan_pwr[j].chan && + region_chan.chan_pwr[i]. + band == + bssdesc_region_chan. + chan_pwr[j].band) { + region_chan.chan_pwr[j]. + pwr = + MIN(region_chan. + chan_pwr[j]. + pwr, + bssdesc_region_chan. + chan_pwr[i]. + pwr); + break; + } + } + } + } + } + + /* Generate domain info */ + wlan_11d_generate_domain_info(pmadapter, ®ion_chan); + + /* Set domain info */ + ret = wlan_11d_send_domain_info(pmpriv, MNULL); + if (ret) { + PRINTM(MERROR, + "11D: Error sending domain info to FW\n"); + } + } + + LEAVE(); + return ret; +} + +/** + * @brief This function prepares domain info from scan table and + * downloads the domain info command to the FW. + * + * @param pmpriv A pointer to mlan_private structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_11d_prepare_dnld_domain_info_cmd(mlan_private *pmpriv) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_adapter *pmadapter = pmpriv->adapter; + IEEEtypes_CountryInfoFullSet_t *pcountry_full = MNULL; + t_u32 idx; + + ENTER(); + + /* Only valid if 11D is enabled */ + if (wlan_11d_is_enabled(pmpriv) && pmadapter->num_in_scan_table != 0) { + for (idx = 0; idx < pmadapter->num_in_scan_table; idx++) { + pcountry_full = + &pmadapter->pscan_table[idx].country_info; + + ret = wlan_11d_update_chan_pwr_table(pmpriv, + &pmadapter-> + pscan_table[idx]); + + if (*(pcountry_full->country_code) != 0 && + (pcountry_full->len > COUNTRY_CODE_LEN)) { + /* Country info found in the BSS Descriptor */ + ret = wlan_11d_process_country_info(pmpriv, + &pmadapter-> + pscan_table + [idx]); + } + } + + /* Sort parsed_region_chan in ascending channel number */ + wlan_11d_sort_parsed_region_chan(&pmadapter-> + parsed_region_chan); + + /* Check if connected */ + if (pmpriv->media_connected == MTRUE) { + ret = wlan_11d_parse_dnld_countryinfo(pmpriv, + &pmpriv-> + curr_bss_params. + bss_descriptor); + } else { + ret = wlan_11d_parse_dnld_countryinfo(pmpriv, MNULL); + } + } + + LEAVE(); + return ret; +} + +/** + * @brief This function checks country code and maps it when needed + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pcountry_code Pointer to the country code string + * + * @return Pointer to the mapped country code string + */ +static t_u8 * +wlan_11d_map_country_code(IN pmlan_adapter pmadapter, IN t_u8 *pcountry_code) +{ + /* Since firmware can only recognize EU as ETSI domain and there is no memory left + * for some devices to convert it in firmware, driver need to convert it before + * passing country code to firmware through tlv + */ + + if (wlan_is_etsi_country(pmadapter, pcountry_code)) + return ("EU "); + else + return pcountry_code; +} + +/** + * @brief This function sets up domain_reg and downloads CMD to FW + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req Pointer to the IOCTL request buffer + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_11d_cfg_domain_info(IN pmlan_adapter pmadapter, + IN mlan_ioctl_req *pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_11d_domain_info *domain_info = MNULL; + mlan_ds_11d_cfg *cfg_11d = MNULL; + t_u8 cfp_bg = 0, cfp_a = 0; + + ENTER(); + + if (pmadapter->otp_region && pmadapter->otp_region->force_reg) { + PRINTM(MERROR, + "ForceRegionRule is set in the on-chip OTP memory\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + cfg_11d = (mlan_ds_11d_cfg *)pioctl_req->pbuf; + domain_info = &cfg_11d->param.domain_info; + memcpy(pmadapter, pmadapter->country_code, domain_info->country_code, + COUNTRY_CODE_LEN); + wlan_11d_set_domain_info(pmpriv, domain_info->band, + wlan_11d_map_country_code(pmadapter, + domain_info-> + country_code), + domain_info->no_of_sub_band, + (IEEEtypes_SubbandSet_t *)domain_info-> + sub_band); + ret = wlan_11d_send_domain_info(pmpriv, pioctl_req); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + /* Update region code and table based on country code */ + if (wlan_misc_country_2_cfp_table_code(pmadapter, + domain_info->country_code, + &cfp_bg, &cfp_a)) { + PRINTM(MIOCTL, "Country code %c%c not found!\n", + domain_info->country_code[0], + domain_info->country_code[1]); + goto done; + } + pmadapter->cfp_code_bg = cfp_bg; + pmadapter->cfp_code_a = cfp_a; + if (cfp_bg && cfp_a && (cfp_bg == cfp_a)) + pmadapter->region_code = cfp_a; + else + pmadapter->region_code = 0; + if (wlan_set_regiontable(pmpriv, pmadapter->region_code, + pmadapter->config_bands | pmadapter-> + adhoc_start_band)) { + PRINTM(MIOCTL, "Fail to set regiontabl\n"); + goto done; + } +done: + LEAVE(); + return ret; +} +#endif /* STA_SUPPORT */ + +#if defined(UAP_SUPPORT) +/** + * @brief This function handles domain info data from UAP interface. + * Checks conditions, sets up domain_reg, then downloads CMD. + * + * @param pmpriv A pointer to mlan_private structure + * @param band Band interface is operating on + * @param domain_tlv Pointer to domain_info tlv + * @param pioctl_buf Pointer to the IOCTL buffer + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_11d_handle_uap_domain_info(mlan_private *pmpriv, + t_u8 band, t_u8 *domain_tlv, t_void *pioctl_buf) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_adapter *pmadapter = pmpriv->adapter; + MrvlIEtypes_DomainParamSet_t *pdomain_tlv; + t_u8 num_sub_band = 0; + t_u8 cfp_bg = 0, cfp_a = 0; + + ENTER(); + + pdomain_tlv = (MrvlIEtypes_DomainParamSet_t *)domain_tlv; + + /* update region code & table based on country string */ + if (wlan_misc_country_2_cfp_table_code(pmadapter, + pdomain_tlv->country_code, + &cfp_bg, + &cfp_a) == MLAN_STATUS_SUCCESS) { + pmadapter->cfp_code_bg = cfp_bg; + pmadapter->cfp_code_a = cfp_a; + if (cfp_bg && cfp_a && (cfp_bg == cfp_a)) + pmadapter->region_code = cfp_a; + else + pmadapter->region_code = 0; + if (wlan_set_regiontable(pmpriv, pmadapter->region_code, + pmadapter->config_bands | pmadapter-> + adhoc_start_band)) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + } + + memcpy(pmadapter, pmadapter->country_code, pdomain_tlv->country_code, + COUNTRY_CODE_LEN); + num_sub_band = + ((pdomain_tlv->header.len - + COUNTRY_CODE_LEN) / sizeof(IEEEtypes_SubbandSet_t)); + + /* TODO: don't just clobber pmadapter->domain_reg. + * Add some checking or merging between STA & UAP domain_info + */ + wlan_11d_set_domain_info(pmpriv, band, pdomain_tlv->country_code, + num_sub_band, pdomain_tlv->sub_band); + ret = wlan_11d_send_domain_info(pmpriv, pioctl_buf); + +done: + LEAVE(); + return ret; +} +#endif diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_11h.c b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_11h.c new file mode 100644 index 000000000000..eb6c70a99a68 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_11h.c @@ -0,0 +1,3978 @@ +/** @file mlan_11h.c + * + * @brief This file contains functions for 802.11H. + * + * Copyright (C) 2008-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +/************************************************************* +Change Log: + 03/26/2009: initial version +************************************************************/ + +#include "mlan.h" +#include "mlan_join.h" +#include "mlan_util.h" +#include "mlan_fw.h" +#include "mlan_main.h" +#include "mlan_ioctl.h" +#include "mlan_11h.h" +#include "mlan_11n.h" +#ifdef UAP_SUPPORT +#include "mlan_uap.h" +#endif + +/******************************************************** + Local Variables +********************************************************/ + +/** Default IBSS DFS recovery interval (in TBTTs); used for adhoc start */ +#define WLAN_11H_DEFAULT_DFS_RECOVERY_INTERVAL 100 + +/** Default 11h power constraint used to offset the maximum transmit power */ +#define WLAN_11H_TPC_POWERCONSTRAINT 0 + +/** 11h TPC Power capability minimum setting, sent in TPC_INFO command to fw */ +#define WLAN_11H_TPC_POWERCAPABILITY_MIN 5 + +/** 11h TPC Power capability maximum setting, sent in TPC_INFO command to fw */ +#define WLAN_11H_TPC_POWERCAPABILITY_MAX 20 + +/** Regulatory requirement for the duration of a channel availability check */ +#define WLAN_11H_CHANNEL_AVAIL_CHECK_DURATION 60000 /* in ms */ + +/** Starting Frequency for 11A band */ +#define START_FREQ_11A_BAND 5000 /* in MHz */ + +/** DFS Channel Move Time */ +#define DFS_CHAN_MOVE_TIME 10 /* in sec */ + +/** Regulatory requirement for the duration of a non-occupancy period */ +#define WLAN_11H_NON_OCCUPANCY_PERIOD 1800 /* in sec (30mins) */ + +/** Maximum allowable age (seconds) on DFS report data */ +#define MAX_DFS_REPORT_USABLE_AGE_SEC (120) /* 2 minutes */ + +/** Minimum delay for CHAN_SW IE to broadcast by FW */ +#define MIN_RDH_CHAN_SW_IE_PERIOD_MSEC (400) /* 4 beacons @ 100ms */ + +/** Maximum delay for CHAN_SW IE to broadcast by FW */ +#define MAX_RDH_CHAN_SW_IE_PERIOD_MSEC (3000) /* 5 beacons @ 600ms */ + +/** Maximum retries on selecting new random channel */ +#define MAX_RANDOM_CHANNEL_RETRIES (20) + +/** Maximum retries on selecting new random non-dfs channel */ +#define MAX_SWITCH_CHANNEL_RETRIES (30) + +/** Value for undetermined priv_curr_idx on first entry to new RDH stage */ +#define RDH_STAGE_FIRST_ENTRY_PRIV_IDX (0xff) + +/** Region codes 0x10, 0x20: channels 1 thru 11 supported */ +static const +IEEEtypes_SupportChan_Subband_t wlan_11h_2_4G_region_FCC = { 1, 11 }; + +/** Region codes 0x30, 0x32, 0x41, 0x50: channels 1 thru 13 supported */ +static const +IEEEtypes_SupportChan_Subband_t wlan_11h_2_4G_region_EU = { 1, 13 }; + +/** Region code 0x40: only channel 14 supported */ +static const +IEEEtypes_SupportChan_Subband_t wlan_11h_2_4G_region_JPN40 = { 14, 1 }; + +/** JPN sub-band config : Start Channel = 8, NumChans = 3 */ +static const +IEEEtypes_SupportChan_Subband_t wlan_11h_JPN_bottom_band = { 8, 3 }; + +/** U-NII sub-band config : Start Channel = 36, NumChans = 4 */ +static const +IEEEtypes_SupportChan_Subband_t wlan_11h_unii_lower_band = { 36, 4 }; + +/** U-NII sub-band config : Start Channel = 52, NumChans = 4 */ +static const +IEEEtypes_SupportChan_Subband_t wlan_11h_unii_middle_band = { 52, 4 }; + +/** U-NII sub-band config : Start Channel = 100, NumChans = 11 */ +static const +IEEEtypes_SupportChan_Subband_t wlan_11h_unii_mid_upper_band = { 100, 11 }; + +/** U-NII sub-band config : Start Channel = 149, NumChans = 5 */ +static const +IEEEtypes_SupportChan_Subband_t wlan_11h_unii_upper_band = { 149, 5 }; + +/** Internally passed structure used to send a CMD_802_11_TPC_INFO command */ +typedef struct { + t_u8 chan; + /**< Channel to which the power constraint applies */ + t_u8 power_constraint; + /**< Local power constraint to send to firmware */ +} wlan_11h_tpc_info_param_t; + +/******************************************************** + Global Variables +********************************************************/ + +/******************************************************** + Local Functions +********************************************************/ + +/** + * @brief Utility function to get a random number based on the underlying OS + * + * @param pmadapter Pointer to mlan_adapter + * @return random integer + */ +static t_u32 +wlan_11h_get_random_num(pmlan_adapter pmadapter) +{ + t_u32 sec, usec; + + ENTER(); + pmadapter->callbacks.moal_get_system_time(pmadapter->pmoal_handle, &sec, + &usec); + sec = (sec & 0xFFFF) + (sec >> 16); + usec = (usec & 0xFFFF) + (usec >> 16); + + LEAVE(); + return (usec << 16) | sec; +} + +/** + * @brief Convert an IEEE formatted IE to 16-bit ID/Len Marvell + * proprietary format + * + * @param pmadapter Pointer to mlan_adapter + * @param pout_buf Output parameter: Buffer to output Marvell formatted IE + * @param pin_ie Pointer to IEEE IE to be converted to Marvell format + * + * @return Number of bytes output to pout_buf parameter return + */ +static t_u32 +wlan_11h_convert_ieee_to_mrvl_ie(mlan_adapter *pmadapter, + t_u8 *pout_buf, const t_u8 *pin_ie) +{ + MrvlIEtypesHeader_t mrvl_ie_hdr; + t_u8 *ptmp_buf = pout_buf; + + ENTER(); + /* Assign the Element Id and Len to the Marvell struct attributes */ + mrvl_ie_hdr.type = wlan_cpu_to_le16(pin_ie[0]); + mrvl_ie_hdr.len = wlan_cpu_to_le16(pin_ie[1]); + + /* If the element ID is zero, return without doing any copying */ + if (!mrvl_ie_hdr.type) { + LEAVE(); + return 0; + } + + /* Copy the header to the buffer pointer */ + memcpy(pmadapter, ptmp_buf, &mrvl_ie_hdr, sizeof(mrvl_ie_hdr)); + + /* Increment the temp buffer pointer by the size appended */ + ptmp_buf += sizeof(mrvl_ie_hdr); + + /* Append the data section of the IE; length given by the IEEE IE length */ + memcpy(pmadapter, ptmp_buf, pin_ie + 2, pin_ie[1]); + + LEAVE(); + /* Return the number of bytes appended to pout_buf */ + return sizeof(mrvl_ie_hdr) + pin_ie[1]; +} + +#ifdef STA_SUPPORT +/** + * @brief Setup the IBSS DFS element passed to the firmware in adhoc start + * and join commands + * + * The DFS Owner and recovery fields are set to be our MAC address and + * a predetermined constant recovery value. If we are joining an adhoc + * network, these values are replaced with the existing IBSS values. + * They are valid only when starting a new IBSS. + * + * The IBSS DFS Element is variable in size based on the number of + * channels supported in our current region. + * + * @param priv Private driver information structure + * @param pdfs Output parameter: Pointer to the IBSS DFS element setup by + * this function. + * + * @return + * - Length of the returned element in pdfs output parameter + * - 0 if returned element is not setup + */ +static t_u32 +wlan_11h_set_ibss_dfs_ie(mlan_private *priv, IEEEtypes_IBSS_DFS_t *pdfs) +{ + t_u8 num_chans = 0; + MeasRptBasicMap_t initial_map; + mlan_adapter *adapter = priv->adapter; + + ENTER(); + + memset(adapter, pdfs, 0x00, sizeof(IEEEtypes_IBSS_DFS_t)); + + /* + * A basic measurement report is included with each channel in the + * map field. Initial value for the map for each supported channel + * is with only the unmeasured bit set. + */ + memset(adapter, &initial_map, 0x00, sizeof(initial_map)); + initial_map.unmeasured = 1; + + /* Set the DFS Owner and recovery interval fields */ + memcpy(adapter, pdfs->dfs_owner, priv->curr_addr, + sizeof(pdfs->dfs_owner)); + pdfs->dfs_recovery_interval = WLAN_11H_DEFAULT_DFS_RECOVERY_INTERVAL; + + for (; (num_chans < adapter->parsed_region_chan.no_of_chan) + && (num_chans < WLAN_11H_MAX_IBSS_DFS_CHANNELS); num_chans++) { + pdfs->channel_map[num_chans].channel_number = + adapter->parsed_region_chan.chan_pwr[num_chans].chan; + + /* + * Set the initial map field with a basic measurement + */ + pdfs->channel_map[num_chans].rpt_map = initial_map; + } + + /* + * If we have an established channel map, include it and return + * a valid DFS element + */ + if (num_chans) { + PRINTM(MINFO, "11h: Added %d channels to IBSS DFS Map\n", + num_chans); + + pdfs->element_id = IBSS_DFS; + pdfs->len = + (sizeof(pdfs->dfs_owner) + + sizeof(pdfs->dfs_recovery_interval) + + num_chans * sizeof(IEEEtypes_ChannelMap_t)); + + LEAVE(); + return pdfs->len + sizeof(pdfs->len) + sizeof(pdfs->element_id); + } + + /* Ensure the element is zeroed out for an invalid return */ + memset(adapter, pdfs, 0x00, sizeof(IEEEtypes_IBSS_DFS_t)); + + LEAVE(); + return 0; +} +#endif + +/** + * @brief Setup the Supported Channel IE sent in association requests + * + * The Supported Channels IE is required to be sent when the spectrum + * management capability (11h) is enabled. The element contains a + * starting channel and number of channels tuple for each sub-band + * the STA supports. This information is based on the operating region. + * + * @param priv Private driver information structure + * @param band Band in use + * @param psup_chan Output parameter: Pointer to the Supported Chan element + * setup by this function. + * + * @return + * - Length of the returned element in psup_chan output parameter + * - 0 if returned element is not setup + */ +static + t_u16 +wlan_11h_set_supp_channels_ie(mlan_private *priv, + t_u8 band, + IEEEtypes_SupportedChannels_t *psup_chan) +{ + t_u16 num_subbands = 0; + t_u16 ret_len = 0; + t_u8 cfp_bg, cfp_a; + + ENTER(); + memset(priv->adapter, psup_chan, 0x00, + sizeof(IEEEtypes_SupportedChannels_t)); + + cfp_bg = cfp_a = priv->adapter->region_code; + if (!priv->adapter->region_code) { + /* Invalid region code, use CFP code */ + cfp_bg = priv->adapter->cfp_code_bg; + cfp_a = priv->adapter->cfp_code_a; + } + + if ((band & BAND_B) || (band & BAND_G)) { + /* + * Channels are contiguous in 2.4GHz, usually only one subband. + */ + switch (cfp_bg) { + case 0x10: /* USA FCC */ + case 0x20: /* Canada IC */ + default: + psup_chan->subband[num_subbands++] = + wlan_11h_2_4G_region_FCC; + break; + case 0x30: /* Europe ETSI */ + case 0x41: /* Japan */ + case 0x50: /* China */ + psup_chan->subband[num_subbands++] = + wlan_11h_2_4G_region_EU; + break; + case 0x40: /* Japan */ + psup_chan->subband[num_subbands++] = + wlan_11h_2_4G_region_JPN40; + break; + case 0xff: /* Japan special */ + psup_chan->subband[num_subbands++] = + wlan_11h_2_4G_region_EU; + psup_chan->subband[num_subbands++] = + wlan_11h_2_4G_region_JPN40; + break; + } + } else if (band & BAND_A) { + /* + * Set the supported channel elements based on the region code, + * incrementing num_subbands for each sub-band we append to the + * element. + */ + switch (cfp_a) { + case 0x10: /* USA FCC */ + case 0x20: /* Canada IC */ + case 0x30: /* Europe ETSI */ + default: + psup_chan->subband[num_subbands++] = + wlan_11h_unii_lower_band; + psup_chan->subband[num_subbands++] = + wlan_11h_unii_middle_band; + psup_chan->subband[num_subbands++] = + wlan_11h_unii_mid_upper_band; + psup_chan->subband[num_subbands++] = + wlan_11h_unii_upper_band; + break; + case 0x50: /* China */ + psup_chan->subband[num_subbands++] = + wlan_11h_unii_lower_band; + psup_chan->subband[num_subbands++] = + wlan_11h_unii_middle_band; + psup_chan->subband[num_subbands++] = + wlan_11h_unii_upper_band; + break; + case 0x40: /* Japan */ + case 0x41: /* Japan */ + case 0xff: /* Japan special */ + psup_chan->subband[num_subbands++] = + wlan_11h_JPN_bottom_band; + psup_chan->subband[num_subbands++] = + wlan_11h_unii_lower_band; + psup_chan->subband[num_subbands++] = + wlan_11h_unii_middle_band; + psup_chan->subband[num_subbands++] = + wlan_11h_unii_mid_upper_band; + break; + case 0x1: /* Low band (5150-5250 MHz) channels */ + psup_chan->subband[num_subbands++] = + wlan_11h_unii_lower_band; + break; + case 0x2: /* Lower middle band (5250-5350 MHz) channels */ + psup_chan->subband[num_subbands++] = + wlan_11h_unii_middle_band; + break; + case 0x3: /* Upper middle band (5470-5725 MHz) channels */ + psup_chan->subband[num_subbands++] = + wlan_11h_unii_mid_upper_band; + break; + case 0x4: /* High band (5725-5850 MHz) channels */ + psup_chan->subband[num_subbands++] = + wlan_11h_unii_upper_band; + break; + case 0x5: /* Low band (5150-5250 MHz) and High band (5725-5850 MHz) channels */ + psup_chan->subband[num_subbands++] = + wlan_11h_unii_lower_band; + psup_chan->subband[num_subbands++] = + wlan_11h_unii_upper_band; + break; + case 0x6: /* Low band (5150-5250 MHz) and Lower middle band (5250-5350 MHz) and High band (5725-5850 MHz) channels */ + psup_chan->subband[num_subbands++] = + wlan_11h_unii_lower_band; + psup_chan->subband[num_subbands++] = + wlan_11h_unii_middle_band; + psup_chan->subband[num_subbands++] = + wlan_11h_unii_upper_band; + break; + } + } + + /* + * If we have setup any supported subbands in the element, return a + * valid IE along with its size, else return 0. + */ + if (num_subbands) { + psup_chan->element_id = SUPPORTED_CHANNELS; + psup_chan->len = + num_subbands * sizeof(IEEEtypes_SupportChan_Subband_t); + + ret_len = (t_u16)(psup_chan->len + + sizeof(psup_chan->len) + + sizeof(psup_chan->element_id)); + + HEXDUMP("11h: SupChan", (t_u8 *)psup_chan, ret_len); + } + + LEAVE(); + return ret_len; +} + +/** + * @brief Prepare CMD_802_11_TPC_ADAPT_REQ firmware command + * + * @param priv Private driver information structure + * @param pcmd_ptr Output parameter: Pointer to the command being prepared + * for the firmware + * @param pinfo_buf HostCmd_DS_802_11_TPC_ADAPT_REQ passed as void data block + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_11h_cmd_tpc_request(mlan_private *priv, + HostCmd_DS_COMMAND *pcmd_ptr, const t_void *pinfo_buf) +{ + ENTER(); + + memcpy(priv->adapter, &pcmd_ptr->params.tpc_req, pinfo_buf, + sizeof(HostCmd_DS_802_11_TPC_ADAPT_REQ)); + + pcmd_ptr->params.tpc_req.req.timeout = + wlan_cpu_to_le16(pcmd_ptr->params.tpc_req.req.timeout); + + /* Converted to little endian in wlan_11h_cmd_process */ + pcmd_ptr->size = sizeof(HostCmd_DS_802_11_TPC_ADAPT_REQ) + S_DS_GEN; + + HEXDUMP("11h: 11_TPC_ADAPT_REQ:", (t_u8 *)pcmd_ptr, + (t_u32)pcmd_ptr->size); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Prepare CMD_802_11_TPC_INFO firmware command + * + * @param priv Private driver information structure + * @param pcmd_ptr Output parameter: Pointer to the command being prepared + * for the firmware + * @param pinfo_buf wlan_11h_tpc_info_param_t passed as void data block + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_11h_cmd_tpc_info(mlan_private *priv, + HostCmd_DS_COMMAND *pcmd_ptr, const t_void *pinfo_buf) +{ + HostCmd_DS_802_11_TPC_INFO *ptpc_info = &pcmd_ptr->params.tpc_info; + MrvlIEtypes_LocalPowerConstraint_t *pconstraint = + &ptpc_info->local_constraint; + MrvlIEtypes_PowerCapability_t *pcap = &ptpc_info->power_cap; + + wlan_11h_device_state_t *pstate = &priv->adapter->state_11h; + const wlan_11h_tpc_info_param_t *ptpc_info_param = + (wlan_11h_tpc_info_param_t *)pinfo_buf; + + ENTER(); + + pcap->min_power = pstate->min_tx_power_capability; + pcap->max_power = pstate->max_tx_power_capability; + pcap->header.len = wlan_cpu_to_le16(2); + pcap->header.type = wlan_cpu_to_le16(TLV_TYPE_POWER_CAPABILITY); + + pconstraint->chan = ptpc_info_param->chan; + pconstraint->constraint = ptpc_info_param->power_constraint; + pconstraint->header.type = wlan_cpu_to_le16(TLV_TYPE_POWER_CONSTRAINT); + pconstraint->header.len = wlan_cpu_to_le16(2); + + /* Converted to little endian in wlan_11h_cmd_process */ + pcmd_ptr->size = sizeof(HostCmd_DS_802_11_TPC_INFO) + S_DS_GEN; + + HEXDUMP("11h: TPC INFO", (t_u8 *)pcmd_ptr, (t_u32)pcmd_ptr->size); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Prepare CMD_802_11_CHAN_SW_ANN firmware command + * + * @param priv Private driver information structure + * @param pcmd_ptr Output parameter: Pointer to the command being + * prepared to for firmware + * @param pinfo_buf HostCmd_DS_802_11_CHAN_SW_ANN passed as void data block + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_11h_cmd_chan_sw_ann(mlan_private *priv, + HostCmd_DS_COMMAND *pcmd_ptr, const t_void *pinfo_buf) +{ + const HostCmd_DS_802_11_CHAN_SW_ANN *pch_sw_ann = + (HostCmd_DS_802_11_CHAN_SW_ANN *)pinfo_buf; + + ENTER(); + + /* Converted to little endian in wlan_11h_cmd_process */ + pcmd_ptr->size = sizeof(HostCmd_DS_802_11_CHAN_SW_ANN) + S_DS_GEN; + + memcpy(priv->adapter, &pcmd_ptr->params.chan_sw_ann, pch_sw_ann, + sizeof(HostCmd_DS_802_11_CHAN_SW_ANN)); + + PRINTM(MINFO, "11h: ChSwAnn: %#x-%u, Seq=%u, Ret=%u\n", + pcmd_ptr->command, pcmd_ptr->size, pcmd_ptr->seq_num, + pcmd_ptr->result); + PRINTM(MINFO, "11h: ChSwAnn: Ch=%d, Cnt=%d, Mode=%d\n", + pch_sw_ann->new_chan, pch_sw_ann->switch_count, + pch_sw_ann->switch_mode); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Prepare CMD_CHAN_REPORT_REQUEST firmware command + * + * @param priv Private driver information structure + * @param pcmd_ptr Output parameter: Pointer to the command being + * prepared to for firmware + * @param pinfo_buf HostCmd_DS_CHAN_RPT_REQ passed as void data block + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_PENDING + */ +static mlan_status +wlan_11h_cmd_chan_rpt_req(mlan_private *priv, + HostCmd_DS_COMMAND *pcmd_ptr, const t_void *pinfo_buf) +{ + const HostCmd_DS_CHAN_RPT_REQ *pchan_rpt_req = + (HostCmd_DS_CHAN_RPT_REQ *)pinfo_buf; + wlan_dfs_device_state_t *pstate_dfs = &priv->adapter->state_dfs; + MrvlIEtypes_ChanRpt11hBasic_t *ptlv_basic; + t_bool is_cancel_req = MFALSE; + + /* + * pchan_rpt_req->millisec_dwell_time would be zero if the chan_rpt_req + * is to cancel current ongoing report + */ + if (pchan_rpt_req->millisec_dwell_time == 0) + is_cancel_req = MTRUE; + + ENTER(); + if (pstate_dfs->dfs_check_pending && !is_cancel_req) { + PRINTM(MERROR, + "11h: ChanRptReq - previous CMD_CHAN_REPORT_REQUEST has" + " not returned its result yet (as EVENT_CHANNEL_READY)." + " This command will be dropped.\n"); + LEAVE(); + return MLAN_STATUS_PENDING; + } + + /* Converted to little endian in wlan_11h_cmd_process */ + pcmd_ptr->size = sizeof(HostCmd_DS_CHAN_RPT_REQ) + S_DS_GEN; + + memcpy(priv->adapter, &pcmd_ptr->params.chan_rpt_req, pchan_rpt_req, + sizeof(HostCmd_DS_CHAN_RPT_REQ)); + pcmd_ptr->params.chan_rpt_req.chan_desc.startFreq = + wlan_cpu_to_le16(pchan_rpt_req->chan_desc.startFreq); + pcmd_ptr->params.chan_rpt_req.millisec_dwell_time = + wlan_cpu_to_le32(pchan_rpt_req->millisec_dwell_time); + + /* if DFS channel, add BASIC report TLV, and set radar bit */ + if (!is_cancel_req && + wlan_11h_radar_detect_required(priv, + pchan_rpt_req->chan_desc.chanNum)) { + ptlv_basic = + (MrvlIEtypes_ChanRpt11hBasic_t *)(((t_u8 *)(pcmd_ptr)) + + pcmd_ptr->size); + ptlv_basic->Header.type = + wlan_cpu_to_le16(TLV_TYPE_CHANRPT_11H_BASIC); + ptlv_basic->Header.len = + wlan_cpu_to_le16(sizeof(MeasRptBasicMap_t)); + memset(priv->adapter, &ptlv_basic->map, 0, + sizeof(MeasRptBasicMap_t)); + ptlv_basic->map.radar = 1; + pcmd_ptr->size += sizeof(MrvlIEtypes_ChanRpt11hBasic_t); + } + + /* update dfs sturcture. + * dfs_check_pending is set when we receive CMD_RESP == SUCCESS */ + pstate_dfs->dfs_check_pending = MFALSE; + pstate_dfs->dfs_radar_found = MFALSE; + pstate_dfs->dfs_check_priv = MNULL; + + if (!is_cancel_req) + pstate_dfs->dfs_check_channel = + pchan_rpt_req->chan_desc.chanNum; + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Set the local power capability and constraint TLV + * + * @param ppbuffer The buffer to add these two TLVs + * @param channel Channel to which the power constraint applies + * @param power_constraint Power constraint to be applied on the channel + * @param min_tx_power_capability Min. Tx Power in Power Capability IE + * @param max_tx_power_capability Max. Tx Power in Power Capability IE + * + * @return The len increased + */ +static t_u32 +wlan_11h_set_local_power_constraint_tlv(t_u8 **ppbuffer, + t_u8 channel, + t_u8 power_constraint, + t_u8 min_tx_power_capability, + t_u8 max_tx_power_capability) +{ + MrvlIEtypes_PowerCapability_t *pcap; + MrvlIEtypes_LocalPowerConstraint_t *pconstraint; + t_u8 *start_ptr = MNULL; + + ENTER(); + + /* Null Checks */ + if ((ppbuffer == MNULL) || (((t_u8 *)(*ppbuffer)) == MNULL)) { + LEAVE(); + return 0; + } + + start_ptr = (t_u8 *)(*ppbuffer); + + PRINTM(MINFO, + "11h: Set local power constraint = %d channel=%d min_tx_pwr=%d max_tx_pwr=%d\n", + power_constraint, channel, min_tx_power_capability, + max_tx_power_capability); + + pcap = (MrvlIEtypes_PowerCapability_t *)*ppbuffer; + pcap->header.type = wlan_cpu_to_le16(TLV_TYPE_POWER_CAPABILITY); + pcap->header.len = wlan_cpu_to_le16(2); + pcap->min_power = min_tx_power_capability; + pcap->max_power = max_tx_power_capability; + *ppbuffer += sizeof(MrvlIEtypesHeader_t) + 2; + + pconstraint = (MrvlIEtypes_LocalPowerConstraint_t *)*ppbuffer; + pconstraint->header.type = wlan_cpu_to_le16(TLV_TYPE_POWER_CONSTRAINT); + pconstraint->header.len = wlan_cpu_to_le16(2); + pconstraint->chan = channel; + pconstraint->constraint = power_constraint; + *ppbuffer += sizeof(MrvlIEtypesHeader_t) + 2; + + LEAVE(); + return (t_u32)(*ppbuffer - start_ptr); +} + +/** + * @brief Utility function to process a join to an infrastructure BSS + * + * @param priv Private driver information structure + * @param ppbuffer Output parameter: Pointer to the TLV output buffer, + * modified on return to point after the appended 11h TLVs + * @param band Band on which we are joining the BSS + * @param channel Channel on which we are joining the BSS + * @param p11h_bss_info Pointer to the 11h BSS information for this network + * that was parsed out of the scan response. + * + * @return Integer number of bytes appended to the TLV output + * buffer (ppbuffer) + */ +static t_u32 +wlan_11h_process_infra_join(mlan_private *priv, + t_u8 **ppbuffer, + t_u8 band, + t_u32 channel, wlan_11h_bss_info_t *p11h_bss_info) +{ + MrvlIEtypesHeader_t ie_header; + IEEEtypes_SupportedChannels_t sup_chan_ie; + t_u32 ret_len = 0; + t_u16 sup_chan_len = 0; + + ENTER(); + + /* Null Checks */ + if ((ppbuffer == MNULL) || (((t_u8 *)(*ppbuffer)) == MNULL)) { + LEAVE(); + return 0; + } + + ret_len += + wlan_11h_set_local_power_constraint_tlv(ppbuffer, (t_u8)channel, + (t_u8)p11h_bss_info-> + power_constraint. + local_constraint, + (t_u8)priv->adapter-> + state_11h. + min_tx_power_capability, + (t_u8)priv->adapter-> + state_11h. + max_tx_power_capability); + + /* Setup the Supported Channels IE */ + sup_chan_len = wlan_11h_set_supp_channels_ie(priv, band, &sup_chan_ie); + + /* + * If we returned a valid Supported Channels IE, wrap and append it + */ + if (sup_chan_len) { + /* Wrap the supported channels IE with a passthrough TLV type */ + ie_header.type = wlan_cpu_to_le16(TLV_TYPE_PASSTHROUGH); + ie_header.len = wlan_cpu_to_le16(sup_chan_len); + memcpy(priv->adapter, *ppbuffer, &ie_header, sizeof(ie_header)); + + /* + * Increment the return size and the return buffer + * pointer param + */ + *ppbuffer += sizeof(ie_header); + ret_len += sizeof(ie_header); + + /* + * Copy the supported channels IE to the output buf, + * advance pointer + */ + memcpy(priv->adapter, *ppbuffer, &sup_chan_ie, sup_chan_len); + *ppbuffer += sup_chan_len; + ret_len += sup_chan_len; + } + + LEAVE(); + return ret_len; +} + +/** + * @brief Utility function to process a start or join to an adhoc network + * + * Add the elements to the TLV buffer needed in the start/join adhoc commands: + * - IBSS DFS IE + * - Quiet IE + * + * Also send the local constraint to the firmware in a TPC_INFO command. + * + * @param priv Private driver information structure + * @param ppbuffer Output parameter: Pointer to the TLV output buffer, + * modified on return to point after the appended 11h TLVs + * @param channel Channel on which we are starting/joining the IBSS + * @param p11h_bss_info Pointer to the 11h BSS information for this network + * that was parsed out of the scan response. NULL + * indicates we are starting the adhoc network + * + * @return Integer number of bytes appended to the TLV output + * buffer (ppbuffer) + */ +static t_u32 +wlan_11h_process_adhoc(mlan_private *priv, + t_u8 **ppbuffer, + t_u32 channel, wlan_11h_bss_info_t *p11h_bss_info) +{ + IEEEtypes_IBSS_DFS_t dfs_elem; + t_u32 size_appended; + t_u32 ret_len = 0; + t_s8 local_constraint = 0; + mlan_adapter *adapter = priv->adapter; + + ENTER(); + +#ifdef STA_SUPPORT + /* Format our own IBSS DFS Element. Include our channel map fields */ + wlan_11h_set_ibss_dfs_ie(priv, &dfs_elem); +#endif + + if (p11h_bss_info) { + /* + * Copy the DFS Owner/Recovery Interval from the BSS + * we are joining + */ + memcpy(adapter, dfs_elem.dfs_owner, + p11h_bss_info->ibss_dfs.dfs_owner, + sizeof(dfs_elem.dfs_owner)); + dfs_elem.dfs_recovery_interval = + p11h_bss_info->ibss_dfs.dfs_recovery_interval; + } + + /* Append the dfs element to the TLV buffer */ + size_appended = wlan_11h_convert_ieee_to_mrvl_ie(adapter, + (t_u8 *)*ppbuffer, + (t_u8 *)&dfs_elem); + + HEXDUMP("11h: IBSS-DFS", (t_u8 *)*ppbuffer, size_appended); + *ppbuffer += size_appended; + ret_len += size_appended; + + /* + * Check to see if we are joining a network. Join is indicated by the + * BSS Info pointer being valid (not NULL) + */ + if (p11h_bss_info) { + /* + * If there was a quiet element, include it in + * adhoc join command + */ + if (p11h_bss_info->quiet.element_id == QUIET) { + size_appended + = + wlan_11h_convert_ieee_to_mrvl_ie(adapter, + (t_u8 *) + *ppbuffer, + (t_u8 *) + &p11h_bss_info-> + quiet); + HEXDUMP("11h: Quiet", (t_u8 *)*ppbuffer, size_appended); + *ppbuffer += size_appended; + ret_len += size_appended; + } + + /* Copy the local constraint from the network */ + local_constraint = + p11h_bss_info->power_constraint.local_constraint; + } else { + /* + * If we are the adhoc starter, we can add a quiet element + */ + if (adapter->state_11h.quiet_ie.quiet_period) { + size_appended = + wlan_11h_convert_ieee_to_mrvl_ie(adapter, + (t_u8 *) + *ppbuffer, + (t_u8 *) + &adapter-> + state_11h. + quiet_ie); + HEXDUMP("11h: Quiet", (t_u8 *)*ppbuffer, size_appended); + *ppbuffer += size_appended; + ret_len += size_appended; + } + /* Use the local_constraint configured in the driver state */ + local_constraint = adapter->state_11h.usr_def_power_constraint; + } + + PRINTM(MINFO, "WEILIE 1: ppbuffer = %p\n", *ppbuffer); + + ret_len += + wlan_11h_set_local_power_constraint_tlv(ppbuffer, (t_u8)channel, + (t_u8)local_constraint, + (t_u8)priv->adapter-> + state_11h. + min_tx_power_capability, + (t_u8)priv->adapter-> + state_11h. + max_tx_power_capability); + PRINTM(MINFO, "WEILIE 2: ppbuffer = %p\n", *ppbuffer); + + LEAVE(); + return ret_len; +} + +/** + * @brief Return whether the driver has enabled 11h for the interface + * + * Association/Join commands are dynamic in that they enable 11h in the + * driver/firmware when they are detected in the existing BSS. + * + * @param priv Private driver information structure + * + * @return + * - MTRUE if 11h is enabled + * - MFALSE otherwise + */ +static t_bool +wlan_11h_is_enabled(mlan_private *priv) +{ + ENTER(); + LEAVE(); + return priv->intf_state_11h.is_11h_enabled; +} + +/** + * @brief Return whether the device has activated slave radar detection. + * + * @param priv Private driver information structure + * + * @return + * - MTRUE if slave radar detection is enabled in firmware + * - MFALSE otherwise + */ +static t_bool +wlan_11h_is_slave_radar_det_active(mlan_private *priv) +{ + ENTER(); + LEAVE(); + return priv->adapter->state_11h.is_slave_radar_det_active; +} + +/** + * @brief Return whether the slave interface is active, and on DFS channel. + * priv is assumed to already be a dfs slave interface, doesn't check this. + * + * @param priv Private driver information structure + * + * @return + * - MTRUE if priv is slave, and meets both conditions + * - MFALSE otherwise + */ +static t_bool +wlan_11h_is_slave_active_on_dfs_chan(mlan_private *priv) +{ + t_bool ret = MFALSE; + + ENTER(); + if ((priv->media_connected == MTRUE) && + (priv->curr_bss_params.band & BAND_A) && + wlan_11h_radar_detect_required(priv, + priv->curr_bss_params.bss_descriptor. + channel)) + ret = MTRUE; + + LEAVE(); + return ret; +} + +/** + * @brief Return whether the master interface is active, and on DFS channel. + * priv is assumed to already be a dfs master interface, doesn't check this. + * + * @param priv Private driver information structure + * + * @return + * - MTRUE if priv is master, and meets both conditions + * - MFALSE otherwise + */ +static t_bool +wlan_11h_is_master_active_on_dfs_chan(mlan_private *priv) +{ + t_bool ret = MFALSE; + + ENTER(); + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) { + /* Ad-hoc creator */ + if (((priv->media_connected == MTRUE) + || (priv->adhoc_state == ADHOC_STARTING)) && + (priv->adapter->adhoc_start_band & BAND_A) && + wlan_11h_radar_detect_required(priv, priv->adhoc_channel)) + ret = MTRUE; + } else if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) { + /* UAP */ +#ifdef UAP_SUPPORT + if ((priv->uap_bss_started == MTRUE) && + (priv->uap_state_chan_cb.bandcfg.chanBand == BAND_5GHZ) && + wlan_11h_radar_detect_required(priv, + priv->uap_state_chan_cb. + channel)) + ret = MTRUE; +#endif + } + LEAVE(); + return ret; +} + +/** + * @brief Determine if priv is DFS Master interface + * + * @param priv Pointer to mlan_private + * + * @return MTRUE or MFALSE + */ +static t_bool +wlan_11h_is_dfs_master(mlan_private *priv) +{ + t_bool ret = MFALSE; + + ENTER(); + /* UAP: all are master */ + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) + ret = MTRUE; + + /* STA: only ad-hoc creator is master */ + else if ((GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) && + (priv->bss_mode == MLAN_BSS_MODE_IBSS) && + (priv->adhoc_state == ADHOC_STARTED || + priv->adhoc_state == ADHOC_STARTING)) + ret = MTRUE; + + /* all other cases = slave interface */ + LEAVE(); + return ret; +} + +/* Need this as function to pass to wlan_count_priv_cond() */ +/** + * @brief Determine if priv is DFS Slave interface + * + * @param priv Pointer to mlan_private + * + * @return MTRUE or MFALSE + */ + +static t_bool +wlan_11h_is_dfs_slave(mlan_private *priv) +{ + t_bool ret = MFALSE; + ENTER(); + ret = !wlan_11h_is_dfs_master(priv); + LEAVE(); + return ret; +} + +/** + * @brief This function checks if interface is active. + * + * @param pmpriv A pointer to mlan_private structure + * + * @return MTRUE or MFALSE + */ +t_bool +wlan_is_intf_active(mlan_private *pmpriv) +{ + t_bool ret = MFALSE; + ENTER(); + +#ifdef UAP_SUPPORT + if (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_UAP) + /* + * NOTE: UAP's media_connected == true only after first STA + * associated. Need different variable to tell if UAP + * has been started. + */ + ret = pmpriv->uap_bss_started; + else +#endif + if (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_STA) + ret = pmpriv->media_connected; + + LEAVE(); + return ret; +} + +/** + * @brief This function gets current radar detect flags + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return 11H MIB setting for radar detect + */ +static t_u32 +wlan_11h_get_current_radar_detect_flags(mlan_adapter *pmadapter) +{ + t_u32 radar_det_flags = 0; + + ENTER(); + if (pmadapter->state_11h.is_master_radar_det_active) + radar_det_flags |= MASTER_RADAR_DET_MASK; + if (pmadapter->state_11h.is_slave_radar_det_active) + radar_det_flags |= SLAVE_RADAR_DET_MASK; + + PRINTM(MINFO, "%s: radar_det_state_curr=0x%x\n", + __func__, radar_det_flags); + + LEAVE(); + return radar_det_flags; +} + +/** + * @brief This function checks if radar detect flags have/should be changed. + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pnew_state Output param with new state, if return MTRUE. + * + * @return MTRUE (need update) or MFALSE (no change in flags) + */ +static t_bool +wlan_11h_check_radar_det_state(mlan_adapter *pmadapter, OUT t_u32 *pnew_state) +{ + t_u32 radar_det_state_new = 0; + t_bool ret; + + ENTER(); + PRINTM(MINFO, "%s: master_radar_det_pending=%d, " + " slave_radar_det_pending=%d\n", __func__, + pmadapter->state_11h.master_radar_det_enable_pending, + pmadapter->state_11h.slave_radar_det_enable_pending); + + /* new state comes from evaluating interface states & pending starts */ + if (pmadapter->state_11h.master_radar_det_enable_pending || + (wlan_count_priv_cond(pmadapter, + wlan_11h_is_master_active_on_dfs_chan, + wlan_11h_is_dfs_master) > 0)) + radar_det_state_new |= MASTER_RADAR_DET_MASK; + if (pmadapter->state_11h.slave_radar_det_enable_pending || + (wlan_count_priv_cond(pmadapter, + wlan_11h_is_slave_active_on_dfs_chan, + wlan_11h_is_dfs_slave) > 0)) + radar_det_state_new |= SLAVE_RADAR_DET_MASK; + + PRINTM(MINFO, "%s: radar_det_state_new=0x%x\n", + __func__, radar_det_state_new); + + /* now compare flags with current state */ + ret = (wlan_11h_get_current_radar_detect_flags(pmadapter) + != radar_det_state_new) ? MTRUE : MFALSE; + if (ret) + *pnew_state = radar_det_state_new; + + LEAVE(); + return ret; +} + +/** + * @brief Prepare ioctl for add/remove CHAN_SW IE - RADAR_DETECTED event handling + * + * @param pmadapter Pointer to mlan_adapter + * @param pioctl_req Pointer to completed mlan_ioctl_req (allocated inside) + * @param is_adding_ie CHAN_SW IE is to be added (MTRUE), or removed (MFALSE) + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status +wlan_11h_prepare_custom_ie_chansw(IN mlan_adapter *pmadapter, + OUT mlan_ioctl_req **ppioctl_req, + IN t_bool is_adding_ie) +{ + mlan_ioctl_req *pioctl_req = MNULL; + mlan_ds_misc_cfg *pds_misc_cfg = MNULL; + custom_ie *pcust_chansw_ie = MNULL; + IEEEtypes_ChanSwitchAnn_t *pchansw_ie = MNULL; + mlan_status ret; + + ENTER(); + + if (pmadapter == MNULL || ppioctl_req == MNULL) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + /* allocate buffer for mlan_ioctl_req and mlan_ds_misc_cfg */ + /* FYI - will be freed as part of cmd_response handler */ + ret = pmadapter->callbacks.moal_malloc(pmadapter->pmoal_handle, + sizeof(mlan_ioctl_req) + + sizeof(mlan_ds_misc_cfg), + MLAN_MEM_DEF, + (t_u8 **)&pioctl_req); + if ((ret != MLAN_STATUS_SUCCESS) || !pioctl_req) { + PRINTM(MERROR, "%s(): Could not allocate ioctl req\n", + __func__); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + pds_misc_cfg = (mlan_ds_misc_cfg *)((t_u8 *)pioctl_req + + sizeof(mlan_ioctl_req)); + + /* prepare mlan_ioctl_req */ + memset(pmadapter, pioctl_req, 0x00, sizeof(mlan_ioctl_req)); + pioctl_req->req_id = MLAN_IOCTL_MISC_CFG; + pioctl_req->action = MLAN_ACT_SET; + pioctl_req->pbuf = (t_u8 *)pds_misc_cfg; + pioctl_req->buf_len = sizeof(mlan_ds_misc_cfg); + + /* prepare mlan_ds_misc_cfg */ + memset(pmadapter, pds_misc_cfg, 0x00, sizeof(mlan_ds_misc_cfg)); + pds_misc_cfg->sub_command = MLAN_OID_MISC_CUSTOM_IE; + pds_misc_cfg->param.cust_ie.type = TLV_TYPE_MGMT_IE; + pds_misc_cfg->param.cust_ie.len = (sizeof(custom_ie) - MAX_IE_SIZE); + + /* configure custom_ie api settings */ + pcust_chansw_ie = + (custom_ie *)&pds_misc_cfg->param.cust_ie.ie_data_list[0]; + pcust_chansw_ie->ie_index = 0xffff; /* Auto index */ + pcust_chansw_ie->ie_length = sizeof(IEEEtypes_ChanSwitchAnn_t); + pcust_chansw_ie->mgmt_subtype_mask = (is_adding_ie) + ? MBIT(8) | MBIT(5) /* add IE for BEACON | PROBE_RSP */ + : 0; /* remove IE */ + + /* prepare CHAN_SW IE inside ioctl */ + pchansw_ie = (IEEEtypes_ChanSwitchAnn_t *)pcust_chansw_ie->ie_buffer; + pchansw_ie->element_id = CHANNEL_SWITCH_ANN; + pchansw_ie->len = + sizeof(IEEEtypes_ChanSwitchAnn_t) - sizeof(IEEEtypes_Header_t); + pchansw_ie->chan_switch_mode = 1; /* STA should not transmit */ + pchansw_ie->new_channel_num = pmadapter->state_rdh.new_channel; + + pchansw_ie->chan_switch_count = pmadapter->dfs_cs_count; + PRINTM(MCMD_D, "New Channel = %d Channel switch count = %d\n", + pmadapter->state_rdh.new_channel, pchansw_ie->chan_switch_count); + + pds_misc_cfg->param.cust_ie.len += pcust_chansw_ie->ie_length; + DBG_HEXDUMP(MCMD_D, "11h: custom_ie containing CHAN_SW IE", + (t_u8 *)pcust_chansw_ie, pds_misc_cfg->param.cust_ie.len); + + /* assign output pointer before returning */ + *ppioctl_req = pioctl_req; + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +#ifdef UAP_SUPPORT +/** Bits 2,3 of band config define the band width */ +#define UAP_BAND_WIDTH_MASK 0x0C + +/** + * @brief Check if start channel 165 is allowed to operate in + * previous uAP channel's band config + * + * @param start_chn Random Start channel choosen after radar detection + * @param uap_band_cfg Private driver uAP band configuration information structure + * + * @return MFALSE if the channel is not allowed in given band + */ +static t_bool +wlan_11h_is_band_valid(t_u8 start_chn, Band_Config_t uap_band_cfg) +{ + + /* if band width is not 20MHZ (either 40 or 80MHz) + * return MFALSE, 165 is not allowed in bands other than 20MHZ + */ + if (start_chn == 165 && (uap_band_cfg.chanWidth != CHAN_BW_20MHZ)) { + return MFALSE; + } + return MTRUE; +} + +/** + * @brief Retrieve a randomly selected starting channel if needed for 11h + * + * If 11h is enabled and 5GHz band is selected in band_config + * return a random channel in A band, else one from BG band. + * + * @param priv Private driver information structure + * @param uap_band_cfg Private driver information structure + * + * @return Starting channel + */ +static t_u8 +wlan_11h_get_uap_start_channel(mlan_private *priv, Band_Config_t uap_band_cfg) +{ + t_u8 start_chn; + mlan_adapter *adapter = priv->adapter; + t_u32 region; + t_u32 rand_entry; + region_chan_t *chn_tbl; + t_u8 rand_tries = 0; + + /*TODO: right now mostly a copy of wlan_11h_get_adhoc_start_channel. + * Improve to be more specfic to UAP, e.g. + * 1. take into account COUNTRY_CODE -> region_code + * 2. check domain_info for value channels + */ + ENTER(); + + /* + * Set start_chn to the Default. + * Used if 11h is disabled or the band + * does not require 11h support. + */ + start_chn = DEFAULT_AD_HOC_CHANNEL; + + /* + * Check that we are looking for a channel in the A Band + */ + if (uap_band_cfg.chanBand == BAND_5GHZ) { + /* + * Set default to the A Band default. + * Used if random selection fails + * or if 11h is not enabled + */ + start_chn = DEFAULT_AD_HOC_CHANNEL_A; + + /* + * Check that 11h is enabled in the driver + */ + if (wlan_11h_is_enabled(priv)) { + /* + * Search the region_channel tables for a channel table + * that is marked for the A Band. + */ + for (region = 0; (region < MAX_REGION_CHANNEL_NUM); + region++) { + chn_tbl = &adapter->region_channel[region]; + + /* Check if table is valid and marked for A Band */ + if (chn_tbl->valid + && chn_tbl->region == adapter->region_code + && chn_tbl->band & BAND_A) { + /* + * Set the start channel. Get a random + * number and use it to pick an entry + * in the table between 0 and the number + * of channels in the table (NumCFP). + */ + rand_entry = + wlan_11h_get_random_num(adapter) + % chn_tbl->num_cfp; + start_chn = + (t_u8)chn_tbl->pcfp[rand_entry]. + channel; + /* Loop until a non-dfs channel is found with compatible band + * bounded by chn_tbl->num_cfp entries in the channel table + */ + while ((wlan_11h_is_channel_under_nop + (adapter, start_chn) || + ((adapter->state_rdh.stage == + RDH_GET_INFO_CHANNEL) && + wlan_11h_radar_detect_required + (priv, start_chn)) || + !(wlan_11h_is_band_valid + (start_chn, uap_band_cfg))) && + (++rand_tries < + chn_tbl->num_cfp)) { + rand_entry++; + rand_entry = + rand_entry % + chn_tbl->num_cfp; + start_chn = + (t_u8)chn_tbl-> + pcfp[rand_entry]. + channel; + PRINTM(MINFO, + "start chan=%d rand_entry=%d\n", + start_chn, rand_entry); + } + + if (rand_tries == chn_tbl->num_cfp) { + PRINTM(MERROR, + "Failed to get UAP start channel\n"); + start_chn = 0; + } + } + } + } + } + + PRINTM(MCMD_D, "11h: UAP Get Start Channel %d\n", start_chn); + LEAVE(); + return start_chn; +} +#endif /* UAP_SUPPORT */ + +#ifdef DEBUG_LEVEL1 +static const char *DFS_TS_REPR_STRINGS[] = { "", + "NOP_start", + "CAC_completed" +}; +#endif + +/** + * @brief Search for a dfs timestamp in the list with desired channel. + * + * Assumes there will only be one timestamp per channel in the list. + * + * @param pmadapter Pointer to mlan_adapter + * @param channel Channel number + * + * @return Pointer to timestamp if found, or MNULL + */ +static wlan_dfs_timestamp_t * +wlan_11h_find_dfs_timestamp(mlan_adapter *pmadapter, t_u8 channel) +{ + wlan_dfs_timestamp_t *pts = MNULL, *pts_found = MNULL; + + ENTER(); + pts = (wlan_dfs_timestamp_t *)util_peek_list(pmadapter->pmoal_handle, + &pmadapter->state_dfs. + dfs_ts_head, MNULL, MNULL); + + while (pts && + pts != + (wlan_dfs_timestamp_t *)&pmadapter->state_dfs.dfs_ts_head) { + PRINTM(MINFO, + "dfs_timestamp(@ %p) - chan=%d, repr=%d(%s)," + " time(sec.usec)=%lu.%06lu\n", pts, pts->channel, + pts->represents, DFS_TS_REPR_STRINGS[pts->represents], + pts->ts_sec, pts->ts_usec); + + if (pts->channel == channel) { + pts_found = pts; + break; + } + pts = pts->pnext; + } + + LEAVE(); + return pts_found; +} + +/** + * @brief Removes dfs timestamp from list. + * + * @param pmadapter Pointer to mlan_adapter + * @param pdfs_ts Pointer to dfs_timestamp to remove + */ +static t_void +wlan_11h_remove_dfs_timestamp(mlan_adapter *pmadapter, + wlan_dfs_timestamp_t *pdfs_ts) +{ + ENTER(); + /* dequeue and delete timestamp */ + util_unlink_list(pmadapter->pmoal_handle, + &pmadapter->state_dfs.dfs_ts_head, + (pmlan_linked_list)pdfs_ts, MNULL, MNULL); + pmadapter->callbacks.moal_mfree(pmadapter->pmoal_handle, + (t_u8 *)pdfs_ts); + LEAVE(); +} + +/** + * @brief Add a dfs timestamp to the list + * + * Assumes there will only be one timestamp per channel in the list, + * and that timestamp modes (represents) are mutually exclusive. + * + * @param pmadapter Pointer to mlan_adapter + * @param repr Timestamp 'represents' value (see _dfs_timestamp_repr_e) + * @param channel Channel number + * + * @return Pointer to timestamp if found, or MNULL + */ +static mlan_status +wlan_11h_add_dfs_timestamp(mlan_adapter *pmadapter, t_u8 repr, t_u8 channel) +{ + wlan_dfs_timestamp_t *pdfs_ts = MNULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + pdfs_ts = wlan_11h_find_dfs_timestamp(pmadapter, channel); + + if (!pdfs_ts) { + /* need to allocate new timestamp */ + ret = pmadapter->callbacks.moal_malloc(pmadapter->pmoal_handle, + sizeof + (wlan_dfs_timestamp_t), + MLAN_MEM_DEF, + (t_u8 **)&pdfs_ts); + if ((ret != MLAN_STATUS_SUCCESS) || !pdfs_ts) { + PRINTM(MERROR, "%s(): Could not allocate dfs_ts\n", + __func__); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + memset(pmadapter, (t_u8 *)pdfs_ts, 0, + sizeof(wlan_dfs_timestamp_t)); + + util_enqueue_list_tail(pmadapter->pmoal_handle, + &pmadapter->state_dfs.dfs_ts_head, + (pmlan_linked_list)pdfs_ts, MNULL, + MNULL); + pdfs_ts->channel = channel; + } + /* (else, use existing timestamp for channel; see assumptions above) */ + + /* update params */ + pmadapter->callbacks.moal_get_system_time(pmadapter->pmoal_handle, + &pdfs_ts->ts_sec, + &pdfs_ts->ts_usec); + pdfs_ts->represents = repr; + + PRINTM(MCMD_D, "11h: add/update dfs_timestamp - chan=%d, repr=%d(%s)," + " time(sec.usec)=%lu.%06lu\n", pdfs_ts->channel, + pdfs_ts->represents, DFS_TS_REPR_STRINGS[pdfs_ts->represents], + pdfs_ts->ts_sec, pdfs_ts->ts_usec); + + LEAVE(); + return ret; +} + +/******************************************************** + Global functions +********************************************************/ + +/** + * @brief Return whether the device has activated master radar detection. + * + * @param priv Private driver information structure + * + * @return + * - MTRUE if master radar detection is enabled in firmware + * - MFALSE otherwise + */ +t_bool +wlan_11h_is_master_radar_det_active(mlan_private *priv) +{ + ENTER(); + LEAVE(); + return priv->adapter->state_11h.is_master_radar_det_active; +} + +/** + * @brief Configure master radar detection. + * Call wlan_11h_check_update_radar_det_state() afterwards + * to push this to firmware. + * + * @param priv Private driver information structure + * @param enable Whether to enable or disable master radar detection + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + * + * @sa wlan_11h_check_update_radar_det_state + */ +mlan_status +wlan_11h_config_master_radar_det(mlan_private *priv, t_bool enable) +{ + mlan_status ret = MLAN_STATUS_FAILURE; + + /* Force disable master radar detection on in-AP interfaces */ + if (priv->adapter->dfs_repeater) + enable = MFALSE; + + ENTER(); + if (wlan_11h_is_dfs_master(priv) && + priv->adapter->init_para.dfs_master_radar_det_en) { + priv->adapter->state_11h.master_radar_det_enable_pending = + enable; + ret = MLAN_STATUS_SUCCESS; + } + + LEAVE(); + return ret; +} + +/** + * @brief Configure slave radar detection. + * Call wlan_11h_check_update_radar_det_state() afterwards + * to push this to firmware. + * + * @param priv Private driver information structure + * @param enable Whether to enable or disable slave radar detection + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + * + * @sa wlan_11h_check_update_radar_det_state + */ +mlan_status +wlan_11h_config_slave_radar_det(mlan_private *priv, t_bool enable) +{ + mlan_status ret = MLAN_STATUS_FAILURE; + + /* Force disable radar detection on STA interfaces */ + if (priv->adapter->dfs_repeater) + enable = MFALSE; + + ENTER(); + if (wlan_11h_is_dfs_slave(priv) && + priv->adapter->init_para.dfs_slave_radar_det_en) { + priv->adapter->state_11h.slave_radar_det_enable_pending = + enable; + ret = MLAN_STATUS_SUCCESS; + } + LEAVE(); + return ret; +} + +/** + * @brief Checks all interfaces and determines if radar_detect flag states + * have/should be changed. If so, sends SNMP_MIB 11H command to FW. + * Call this function on any interface enable/disable/channel change. + * + * @param pmpriv Pointer to mlan_private structure + * + * @return MLAN_STATUS_SUCCESS (update or not) + * or MLAN_STATUS_FAILURE (cmd failure) + * + * @sa wlan_11h_check_radar_det_state + */ +mlan_status +wlan_11h_check_update_radar_det_state(mlan_private *pmpriv) +{ + t_u32 new_radar_det_state = 0; + t_u32 mib_11h = 0; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (wlan_11h_check_radar_det_state(pmpriv->adapter, + &new_radar_det_state)) { + PRINTM(MCMD_D, "%s: radar_det_state being updated.\n", + __func__); + + mib_11h |= new_radar_det_state; + /* keep priv's existing 11h state */ + if (pmpriv->intf_state_11h.is_11h_active) + mib_11h |= ENABLE_11H_MASK; + + /* Send cmd to FW to enable/disable 11h function in firmware */ + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_802_11_SNMP_MIB, + HostCmd_ACT_GEN_SET, + Dot11H_i, MNULL, &mib_11h); + if (ret) + ret = MLAN_STATUS_FAILURE; + } + + /* updated state sent OR no change, thus no longer pending */ + pmpriv->adapter->state_11h.master_radar_det_enable_pending = MFALSE; + pmpriv->adapter->state_11h.slave_radar_det_enable_pending = MFALSE; + + LEAVE(); + return ret; +} + +/** + * @brief Query 11h firmware enabled state. + * + * Return whether the firmware currently has 11h extensions enabled + * + * @param priv Private driver information structure + * + * @return + * - MTRUE if 11h has been activated in the firmware + * - MFALSE otherwise + * + * @sa wlan_11h_activate + */ +t_bool +wlan_11h_is_active(mlan_private *priv) +{ + ENTER(); + LEAVE(); + return priv->intf_state_11h.is_11h_active; +} + +/** + * @brief Enable the transmit interface and record the state. + * + * @param priv Private driver information structure + * + * @return N/A + */ +t_void +wlan_11h_tx_enable(mlan_private *priv) +{ + ENTER(); + if (priv->intf_state_11h.tx_disabled) { + if (priv->media_connected == MTRUE) { + wlan_recv_event(priv, MLAN_EVENT_ID_FW_START_TX, MNULL); + priv->intf_state_11h.tx_disabled = MFALSE; + } + } + LEAVE(); +} + +/** + * @brief Disable the transmit interface and record the state. + * + * @param priv Private driver information structure + * + * @return N/A + */ +t_void +wlan_11h_tx_disable(mlan_private *priv) +{ + ENTER(); + if (!priv->intf_state_11h.tx_disabled) { + if (priv->media_connected == MTRUE) { + priv->intf_state_11h.tx_disabled = MTRUE; + wlan_recv_event(priv, MLAN_EVENT_ID_FW_STOP_TX, MNULL); + } + } + LEAVE(); +} + +/** + * @brief Enable or Disable the 11h extensions in the firmware + * + * @param priv Private driver information structure + * @param pioctl_buf A pointer to MLAN IOCTL Request buffer + * @param flag Enable 11h if MTRUE, disable otherwise + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_11h_activate(mlan_private *priv, t_void *pioctl_buf, t_bool flag) +{ + t_u32 enable = flag & ENABLE_11H_MASK; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + /* add bits for master/slave radar detect into enable. */ + enable |= wlan_11h_get_current_radar_detect_flags(priv->adapter); + + /* Whenever repeater mode is on make sure + * we do not enable master or slave radar det mode. + * HW will not detect radar in dfs_repeater mode. + */ + if (priv->adapter->dfs_repeater) { + enable &= ~(MASTER_RADAR_DET_MASK | SLAVE_RADAR_DET_MASK); + } + + /* + * Send cmd to FW to enable/disable 11h function in firmware + */ + ret = wlan_prepare_cmd(priv, + HostCmd_CMD_802_11_SNMP_MIB, + HostCmd_ACT_GEN_SET, + Dot11H_i, (t_void *)pioctl_buf, &enable); + if (ret) + ret = MLAN_STATUS_FAILURE; + else + /* Set boolean flag in driver 11h state */ + priv->intf_state_11h.is_11h_active = flag; + + PRINTM(MINFO, "11h: %s\n", flag ? "Activate" : "Deactivate"); + + LEAVE(); + return ret; +} + +/** + * @brief Initialize the 11h parameters and enable 11h when starting an IBSS + * + * @param adapter mlan_adapter structure + * + * @return N/A + */ +t_void +wlan_11h_init(mlan_adapter *adapter) +{ + wlan_11h_device_state_t *pstate_11h = &adapter->state_11h; + IEEEtypes_Quiet_t *pquiet = &adapter->state_11h.quiet_ie; + wlan_dfs_device_state_t *pstate_dfs = &adapter->state_dfs; + wlan_radar_det_hndlg_state_t *pstate_rdh = &adapter->state_rdh; +#ifdef DFS_TESTING_SUPPORT + wlan_dfs_testing_settings_t *pdfs_test = &adapter->dfs_test_params; +#endif + + ENTER(); + + /* Initialize 11H struct */ + pstate_11h->usr_def_power_constraint = WLAN_11H_TPC_POWERCONSTRAINT; + pstate_11h->min_tx_power_capability = WLAN_11H_TPC_POWERCAPABILITY_MIN; + pstate_11h->max_tx_power_capability = WLAN_11H_TPC_POWERCAPABILITY_MAX; + + pstate_11h->recvd_chanswann_event = MFALSE; + pstate_11h->master_radar_det_enable_pending = MFALSE; + pstate_11h->slave_radar_det_enable_pending = MFALSE; + pstate_11h->is_master_radar_det_active = MFALSE; + pstate_11h->is_slave_radar_det_active = MFALSE; + + /*Initialize quiet_ie */ + memset(adapter, pquiet, 0, sizeof(IEEEtypes_Quiet_t)); + pquiet->element_id = QUIET; + pquiet->len = + (sizeof(pquiet->quiet_count) + sizeof(pquiet->quiet_period) + + sizeof(pquiet->quiet_duration) + + sizeof(pquiet->quiet_offset)); + + /* Initialize DFS struct */ + pstate_dfs->dfs_check_pending = MFALSE; + pstate_dfs->dfs_radar_found = MFALSE; + pstate_dfs->dfs_check_channel = 0; + pstate_dfs->dfs_report_time_sec = 0; + util_init_list((pmlan_linked_list)&pstate_dfs->dfs_ts_head); + + /* Initialize RDH struct */ + pstate_rdh->stage = RDH_OFF; + pstate_rdh->priv_list_count = 0; + pstate_rdh->priv_curr_idx = 0; + pstate_rdh->curr_channel = 0; + pstate_rdh->new_channel = 0; + memset(adapter, &(pstate_rdh->uap_band_cfg), 0, + sizeof(pstate_rdh->uap_band_cfg)); + pstate_rdh->max_bcn_dtim_ms = 0; + memset(adapter, pstate_rdh->priv_list, 0, + sizeof(pstate_rdh->priv_list)); + + /* Initialize dfs channel switch count */ +#define DFS_CS_COUNT 5 + adapter->dfs_cs_count = DFS_CS_COUNT; + +#ifdef DFS_TESTING_SUPPORT + /* Initialize DFS testing struct */ + pdfs_test->user_cac_period_msec = 0; + pdfs_test->user_nop_period_sec = 0; + pdfs_test->no_channel_change_on_radar = MFALSE; + pdfs_test->fixed_new_channel_on_radar = 0; +#endif + + LEAVE(); +} + +/** + * @brief Cleanup for the 11h parameters that allocated memory, etc. + * + * @param adapter mlan_adapter structure + * + * @return N/A + */ +t_void +wlan_11h_cleanup(mlan_adapter *adapter) +{ + wlan_dfs_device_state_t *pstate_dfs = &adapter->state_dfs; + wlan_dfs_timestamp_t *pdfs_ts; + + ENTER(); + + /* cleanup dfs_timestamp list */ + pdfs_ts = (wlan_dfs_timestamp_t *)util_peek_list(adapter->pmoal_handle, + &pstate_dfs-> + dfs_ts_head, MNULL, + MNULL); + while (pdfs_ts) { + util_unlink_list(adapter->pmoal_handle, + &pstate_dfs->dfs_ts_head, + (pmlan_linked_list)pdfs_ts, MNULL, MNULL); + adapter->callbacks.moal_mfree(adapter->pmoal_handle, + (t_u8 *)pdfs_ts); + + pdfs_ts = + (wlan_dfs_timestamp_t *)util_peek_list(adapter-> + pmoal_handle, + &pstate_dfs-> + dfs_ts_head, + MNULL, MNULL); + } + + LEAVE(); +} + +/** + * @brief Initialize the 11h parameters and enable 11h when starting an IBSS + * + * @param pmpriv Pointer to mlan_private structure + * + * @return N/A + */ +t_void +wlan_11h_priv_init(mlan_private *pmpriv) +{ + wlan_11h_interface_state_t *pistate_11h = &pmpriv->intf_state_11h; + + ENTER(); + + pistate_11h->is_11h_enabled = MTRUE; + pistate_11h->is_11h_active = MFALSE; + pistate_11h->adhoc_auto_sel_chan = MTRUE; + pistate_11h->tx_disabled = MFALSE; + pistate_11h->dfs_slave_csa_chan = 0; + pistate_11h->dfs_slave_csa_expire_at_sec = 0; + + LEAVE(); +} + +/** + * @brief Retrieve a randomly selected starting channel if needed for 11h + * + * If 11h is enabled and an A-Band channel start band preference + * configured in the driver, the start channel must be random in order + * to meet with + * + * @param priv Private driver information structure + * + * @return Starting channel + */ +t_u8 +wlan_11h_get_adhoc_start_channel(mlan_private *priv) +{ + t_u8 start_chn; + mlan_adapter *adapter = priv->adapter; + t_u32 region; + t_u32 rand_entry; + region_chan_t *chn_tbl; + t_u8 rand_tries = 0; + + ENTER(); + + /* + * Set start_chn to the Default. Used if 11h is disabled or the band + * does not require 11h support. + */ + start_chn = DEFAULT_AD_HOC_CHANNEL; + + /* + * Check that we are looking for a channel in the A Band + */ + if ((adapter->adhoc_start_band & BAND_A) + || (adapter->adhoc_start_band & BAND_AN) + ) { + /* + * Set default to the A Band default. + * Used if random selection fails + * or if 11h is not enabled + */ + start_chn = DEFAULT_AD_HOC_CHANNEL_A; + + /* + * Check that 11h is enabled in the driver + */ + if (wlan_11h_is_enabled(priv)) { + /* + * Search the region_channel tables for a channel table + * that is marked for the A Band. + */ + for (region = 0; (region < MAX_REGION_CHANNEL_NUM); + region++) { + chn_tbl = &adapter->region_channel[region]; + + /* Check if table is valid and marked for A Band */ + if (chn_tbl->valid + && chn_tbl->region == adapter->region_code + && chn_tbl->band & BAND_A) { + /* + * Set the start channel. Get a random + * number and use it to pick an entry + * in the table between 0 and the number + * of channels in the table (NumCFP). + */ + do { + rand_entry = + wlan_11h_get_random_num + (adapter) % + chn_tbl->num_cfp; + start_chn = + (t_u8)chn_tbl-> + pcfp[rand_entry]. + channel; + } while ((wlan_11h_is_channel_under_nop + (adapter, start_chn) || + ((adapter->state_rdh.stage == + RDH_GET_INFO_CHANNEL) && + wlan_11h_radar_detect_required + (priv, start_chn))) + && (++rand_tries < + MAX_RANDOM_CHANNEL_RETRIES)); + } + } + } + } + + PRINTM(MINFO, "11h: %s: AdHoc Channel set to %u\n", + wlan_11h_is_enabled(priv) ? "Enabled" : "Disabled", start_chn); + + LEAVE(); + return start_chn; +} + +/** + * @brief Retrieve channel closed for operation by Channel Switch Announcement + * + * After receiving CSA, we must not transmit in any form on the original + * channel for a certain duration. This checks the time, and returns + * the channel if valid. + * + * @param priv Private driver information structure + * + * @return Closed channel, else 0 + */ +t_u8 +wlan_11h_get_csa_closed_channel(mlan_private *priv) +{ + t_u32 sec, usec; + + ENTER(); + + if (!priv->intf_state_11h.dfs_slave_csa_chan) { + LEAVE(); + return 0; + } + + /* have csa channel, check if expired or not */ + priv->adapter->callbacks.moal_get_system_time(priv->adapter-> + pmoal_handle, &sec, + &usec); + if (sec > priv->intf_state_11h.dfs_slave_csa_expire_at_sec) { + /* expired: remove channel from blacklist table, and clear vars */ + wlan_set_chan_blacklist(priv, BAND_A, + priv->intf_state_11h.dfs_slave_csa_chan, + MFALSE); + priv->intf_state_11h.dfs_slave_csa_chan = 0; + priv->intf_state_11h.dfs_slave_csa_expire_at_sec = 0; + } + + LEAVE(); + return priv->intf_state_11h.dfs_slave_csa_chan; +} + +/** + * @brief Check if the current region's regulations require the input channel + * to be scanned for radar. + * + * Based on statically defined requirements for sub-bands per regulatory + * agency requirements. + * + * Used in adhoc start to determine if channel availability check is required + * + * @param priv Private driver information structure + * @param channel Channel to determine radar detection requirements + * + * @return + * - MTRUE if radar detection is required + * - MFALSE otherwise + */ +/** @sa wlan_11h_issue_radar_detect + */ +t_bool +wlan_11h_radar_detect_required(mlan_private *priv, t_u8 channel) +{ + t_bool required = MFALSE; + + ENTER(); + + /* + * No checks for 11h or measurement code being enabled is placed here + * since regulatory requirements exist whether we support them or not. + */ + + required = wlan_get_cfp_radar_detect(priv, channel); + + if (!priv->adapter->region_code) + PRINTM(MINFO, "11h: Radar detection in CFP code[BG:%#x, A:%#x] " + "is %srequired for channel %d\n", + priv->adapter->cfp_code_bg, priv->adapter->cfp_code_a, + (required ? "" : "not "), channel); + else + PRINTM(MINFO, "11h: Radar detection in region %#02x " + "is %srequired for channel %d\n", + priv->adapter->region_code, (required ? "" : "not "), + channel); + + if (required == MTRUE && priv->media_connected == MTRUE + && priv->curr_bss_params.bss_descriptor.channel == channel) { + required = MFALSE; + + PRINTM(MINFO, "11h: Radar detection not required. " + "Already operating on the channel\n"); + } + + LEAVE(); + return required; +} + +/** + * @brief Perform a radar measurement if required on given channel + * + * Check to see if the provided channel requires a channel availability + * check (60 second radar detection measurement). If required, perform + * measurement, stalling calling thread until the measurement completes + * and then report result. + * + * Used when starting an adhoc or AP network. + * + * @param priv Private driver information structure + * @param pioctl_req Pointer to IOCTL request buffer + * @param channel Channel on which to perform radar measurement + * @param bandcfg Channel Band config structure + * + * @return + * - MTRUE if radar measurement request was successfully issued + * - MFALSE if radar detection is not required + * - < 0 for error during radar detection (if performed) + * + * @sa wlan_11h_radar_detect_required + */ +t_s32 +wlan_11h_issue_radar_detect(mlan_private *priv, + pmlan_ioctl_req pioctl_req, + t_u8 channel, Band_Config_t bandcfg) +{ + t_s32 ret; + HostCmd_DS_CHAN_RPT_REQ chan_rpt_req; + mlan_adapter *pmadapter = priv->adapter; + mlan_ds_11h_cfg *ds_11hcfg = MNULL; + + ENTER(); + + ret = wlan_11h_radar_detect_required(priv, channel); + if (ret) { + /* Prepare and issue CMD_CHAN_RPT_REQ. */ + memset(priv->adapter, &chan_rpt_req, 0x00, + sizeof(chan_rpt_req)); + + chan_rpt_req.chan_desc.startFreq = START_FREQ_11A_BAND; + + if (pmadapter->chanrpt_param_bandcfg) { + chan_rpt_req.chan_desc.bandcfg = bandcfg; + } else { + *((t_u8 *)&chan_rpt_req.chan_desc.bandcfg) = + (t_u8)bandcfg.chanWidth; + } + + chan_rpt_req.chan_desc.chanNum = channel; + chan_rpt_req.millisec_dwell_time = + WLAN_11H_CHANNEL_AVAIL_CHECK_DURATION; + + /* ETSI new requirement for ch 120, 124 and 128 */ + if (wlan_is_etsi_country(pmadapter, pmadapter->country_code)) { + if (channel == 120 || channel == 124 || channel == 128) { + chan_rpt_req.millisec_dwell_time = + WLAN_11H_CHANNEL_AVAIL_CHECK_DURATION * + 10; + } + if (channel == 116 && + ((bandcfg.chanWidth == CHAN_BW_40MHZ) + )) { + chan_rpt_req.millisec_dwell_time = + WLAN_11H_CHANNEL_AVAIL_CHECK_DURATION * + 10; + } + } + + /* Save dwell time information to be used later in moal */ + if (pioctl_req) { + ds_11hcfg = (mlan_ds_11h_cfg *)pioctl_req->pbuf; + if (!ds_11hcfg->param.chan_rpt_req.host_based) { + ds_11hcfg->param.chan_rpt_req. + millisec_dwell_time = + chan_rpt_req.millisec_dwell_time; + } + } +#ifdef DFS_TESTING_SUPPORT + if (priv->adapter->dfs_test_params.user_cac_period_msec) { + PRINTM(MCMD_D, + "dfs_testing - user CAC period=%d (msec)\n", + priv->adapter->dfs_test_params. + user_cac_period_msec); + chan_rpt_req.millisec_dwell_time = + priv->adapter->dfs_test_params. + user_cac_period_msec; + } +#endif + + PRINTM(MMSG, "11h: issuing DFS Radar check for channel=%d." + " Please wait for response...\n", channel); + + ret = wlan_prepare_cmd(priv, HostCmd_CMD_CHAN_REPORT_REQUEST, + HostCmd_ACT_GEN_SET, 0, + (t_void *)pioctl_req, + (t_void *)&chan_rpt_req); + } + + LEAVE(); + return ret; +} + +/** + * @brief Checks if a radar measurement was performed on channel, + * and if so, whether radar was detected on it. + * + * Used when starting an adhoc network. + * + * @param priv Private driver information structure + * @param chan Channel to check upon + * + * @return + * - MLAN_STATUS_SUCCESS if no radar on channel + * - MLAN_STATUS_FAILURE if radar was found on channel + * - (TBD??) MLAN_STATUS_PENDING if radar report NEEDS TO BE REISSUED + * + * @sa wlan_11h_issue_radar_detect + * @sa wlan_11h_process_start + */ +mlan_status +wlan_11h_check_chan_report(mlan_private *priv, t_u8 chan) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + wlan_dfs_device_state_t *pstate_dfs = &priv->adapter->state_dfs; + t_u32 sec, usec; + + ENTER(); + + /* check report we hold is valid or not */ + priv->adapter->callbacks.moal_get_system_time(priv->adapter-> + pmoal_handle, &sec, + &usec); + + PRINTM(MINFO, "11h: %s()\n", __func__); + PRINTM(MINFO, "- sec_now=%d, sec_report=%d.\n", + sec, pstate_dfs->dfs_report_time_sec); + PRINTM(MINFO, "- rpt_channel=%d, rpt_radar=%d.\n", + pstate_dfs->dfs_check_channel, pstate_dfs->dfs_radar_found); + + if ((!pstate_dfs->dfs_check_pending) && + (chan == pstate_dfs->dfs_check_channel) && + ((sec - pstate_dfs->dfs_report_time_sec) < + MAX_DFS_REPORT_USABLE_AGE_SEC)) { + /* valid and not out-dated, check if radar */ + if (pstate_dfs->dfs_radar_found) { + PRINTM(MMSG, "Radar was detected on channel %d.\n", + chan); + ret = MLAN_STATUS_FAILURE; + } + } else { + /* When Cache is not valid. This is required during extending cache + * validity during bss_stop + */ + pstate_dfs->dfs_check_channel = 0; + + /*TODO: reissue report request if not pending. + * BUT HOW to make the code wait for it??? + * For now, just fail since we don't have the info. */ + + ret = MLAN_STATUS_PENDING; + } + + LEAVE(); + return ret; +} + +/** + * @brief Process an TLV buffer for a pending BSS Adhoc start command. + * + * Activate 11h functionality in the firmware if driver has is enabled + * for 11h (configured by the application via IOCTL). + * + * @param priv Private driver information structure + * @param ppbuffer Output parameter: Pointer to the TLV output buffer, + * modified on return to point after the appended 11h TLVs + * @param pcap_info Pointer to the capability info for the BSS to join + * @param channel Channel on which we are starting the IBSS + * @param p11h_bss_info Input/Output parameter: Pointer to the 11h BSS + * information for this network that we are establishing. + * 11h sensed flag set on output if warranted. + * + * @return + * - MLAN_STATUS_SUCCESS if 11h is disabled + * - Integer number of bytes appended to the TLV output buffer (ppbuffer) + * - < 0 for error (e.g. radar detected on channel) + */ +t_s32 +wlan_11h_process_start(mlan_private *priv, + t_u8 **ppbuffer, + IEEEtypes_CapInfo_t *pcap_info, + t_u32 channel, wlan_11h_bss_info_t *p11h_bss_info) +{ + mlan_adapter *adapter = priv->adapter; + t_s32 ret = MLAN_STATUS_SUCCESS; + t_bool is_dfs_chan = MFALSE; + + ENTER(); + if (wlan_11h_is_enabled(priv) + && ((adapter->adhoc_start_band & BAND_A) + || (adapter->adhoc_start_band & BAND_AN) + ) + ) { + if (!wlan_11d_is_enabled(priv)) { + /* No use having 11h enabled without 11d enabled */ + wlan_11d_enable(priv, MNULL, ENABLE_11D); +#ifdef STA_SUPPORT + wlan_11d_create_dnld_countryinfo(priv, + adapter-> + adhoc_start_band); +#endif + } + + /* + * Activate 11h functions in firmware, + * turns on capability bit + */ + wlan_11h_activate(priv, MNULL, MTRUE); + pcap_info->spectrum_mgmt = MTRUE; + + /* If using a DFS channel, enable radar detection. */ + is_dfs_chan = wlan_11h_radar_detect_required(priv, channel); + if (is_dfs_chan) { + if (!wlan_11h_is_master_radar_det_active(priv)) + wlan_11h_config_master_radar_det(priv, MTRUE); + } + wlan_11h_check_update_radar_det_state(priv); + + /* Set flag indicating this BSS we are starting is using 11h */ + p11h_bss_info->sensed_11h = MTRUE; + + if (is_dfs_chan) { + /* check if this channel is under NOP */ + if (wlan_11h_is_channel_under_nop(adapter, channel)) + ret = MLAN_STATUS_FAILURE; + /* check last channel report, if this channel is free of radar */ + if (ret == MLAN_STATUS_SUCCESS) + ret = wlan_11h_check_chan_report(priv, channel); + } + if (ret == MLAN_STATUS_SUCCESS) + ret = wlan_11h_process_adhoc(priv, ppbuffer, channel, + MNULL); + else + ret = MLAN_STATUS_FAILURE; + } else { + /* Deactivate 11h functions in the firmware */ + wlan_11h_activate(priv, MNULL, MFALSE); + pcap_info->spectrum_mgmt = MFALSE; + wlan_11h_check_update_radar_det_state(priv); + } + LEAVE(); + return ret; +} + +/** + * @brief Process an TLV buffer for a pending BSS Join command for + * both adhoc and infra networks + * + * The TLV command processing for a BSS join for either adhoc or + * infrastructure network is performed with this function. The + * capability bits are inspected for the IBSS flag and the appropriate + * local routines are called to setup the necessary TLVs. + * + * Activate 11h functionality in the firmware if the spectrum management + * capability bit is found in the network information for the BSS we are + * joining. + * + * @param priv Private driver information structure + * @param ppbuffer Output parameter: Pointer to the TLV output buffer, + * modified on return to point after the appended 11h TLVs + * @param pcap_info Pointer to the capability info for the BSS to join + * @param band Band on which we are joining the BSS + * @param channel Channel on which we are joining the BSS + * @param p11h_bss_info Pointer to the 11h BSS information for this + * network that was parsed out of the scan response. + * + * @return Integer number of bytes appended to the TLV output + * buffer (ppbuffer), MLAN_STATUS_FAILURE (-1), + * or MLAN_STATUS_SUCCESS (0) + */ +t_s32 +wlan_11h_process_join(mlan_private *priv, + t_u8 **ppbuffer, + IEEEtypes_CapInfo_t *pcap_info, + t_u8 band, + t_u32 channel, wlan_11h_bss_info_t *p11h_bss_info) +{ + t_s32 ret = 0; + + ENTER(); + + if (priv->media_connected == MTRUE) { + if (wlan_11h_is_active(priv) == p11h_bss_info->sensed_11h) { + /* + * Assume DFS parameters are the same for roaming as long as + * the current & next APs have the same spectrum mgmt capability + * bit setting + */ + ret = MLAN_STATUS_SUCCESS; + + } else { + /* No support for roaming between DFS/non-DFS yet */ + ret = MLAN_STATUS_FAILURE; + } + + LEAVE(); + return ret; + } + + if (p11h_bss_info->sensed_11h) { + if (!wlan_11d_is_enabled(priv)) { + /* No use having 11h enabled without 11d enabled */ + wlan_11d_enable(priv, MNULL, ENABLE_11D); +#ifdef STA_SUPPORT + wlan_11d_parse_dnld_countryinfo(priv, + priv-> + pattempted_bss_desc); +#endif + } + /* + * Activate 11h functions in firmware, + * turns on capability bit + */ + wlan_11h_activate(priv, MNULL, MTRUE); + pcap_info->spectrum_mgmt = MTRUE; + + /* If using a DFS channel, enable radar detection. */ + if ((band & BAND_A) && + wlan_11h_radar_detect_required(priv, channel)) { + if (!wlan_11h_is_slave_radar_det_active(priv)) + wlan_11h_config_slave_radar_det(priv, MTRUE); + } + wlan_11h_check_update_radar_det_state(priv); + + if (pcap_info->ibss) { + PRINTM(MINFO, "11h: Adhoc join: Sensed\n"); + ret = wlan_11h_process_adhoc(priv, ppbuffer, channel, + p11h_bss_info); + } else { + PRINTM(MINFO, "11h: Infra join: Sensed\n"); + ret = wlan_11h_process_infra_join(priv, ppbuffer, band, + channel, + p11h_bss_info); + } + } else { + /* Deactivate 11h functions in the firmware */ + wlan_11h_activate(priv, MNULL, MFALSE); + pcap_info->spectrum_mgmt = MFALSE; + wlan_11h_check_update_radar_det_state(priv); + } + + LEAVE(); + return ret; +} + +/** + * + * @brief Prepare the HostCmd_DS_Command structure for an 11h command. + * + * Use the Command field to determine if the command being set up is for + * 11h and call one of the local command handlers accordingly for: + * + * - HostCmd_CMD_802_11_TPC_ADAPT_REQ + * - HostCmd_CMD_802_11_TPC_INFO + * - HostCmd_CMD_802_11_CHAN_SW_ANN + */ +/** - HostCmd_CMD_CHAN_REPORT_REQUEST + */ +/** + * @param priv Private driver information structure + * @param pcmd_ptr Output parameter: Pointer to the command being prepared + * for the firmware + * @param pinfo_buf Void buffer pass through with data necessary for a + * specific command type + */ +/** @return MLAN_STATUS_SUCCESS, MLAN_STATUS_FAILURE + * or MLAN_STATUS_PENDING + */ +/** @sa wlan_11h_cmd_tpc_request + * @sa wlan_11h_cmd_tpc_info + * @sa wlan_11h_cmd_chan_sw_ann + */ +/** @sa wlan_11h_cmd_chan_report_req + */ +mlan_status +wlan_11h_cmd_process(mlan_private *priv, + HostCmd_DS_COMMAND *pcmd_ptr, const t_void *pinfo_buf) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + switch (pcmd_ptr->command) { + case HostCmd_CMD_802_11_TPC_ADAPT_REQ: + ret = wlan_11h_cmd_tpc_request(priv, pcmd_ptr, pinfo_buf); + break; + case HostCmd_CMD_802_11_TPC_INFO: + ret = wlan_11h_cmd_tpc_info(priv, pcmd_ptr, pinfo_buf); + break; + case HostCmd_CMD_802_11_CHAN_SW_ANN: + ret = wlan_11h_cmd_chan_sw_ann(priv, pcmd_ptr, pinfo_buf); + break; + case HostCmd_CMD_CHAN_REPORT_REQUEST: + ret = wlan_11h_cmd_chan_rpt_req(priv, pcmd_ptr, pinfo_buf); + break; + default: + ret = MLAN_STATUS_FAILURE; + } + + pcmd_ptr->command = wlan_cpu_to_le16(pcmd_ptr->command); + pcmd_ptr->size = wlan_cpu_to_le16(pcmd_ptr->size); + + LEAVE(); + return ret; +} + +/** + * @brief Handle the command response from the firmware if from an 11h command + * + * Use the Command field to determine if the command response being + * is for 11h. Call the local command response handler accordingly for: + * + * - HostCmd_CMD_802_11_TPC_ADAPT_REQ + * - HostCmd_CMD_802_11_TPC_INFO + * - HostCmd_CMD_802_11_CHAN_SW_ANN + */ +/** - HostCmd_CMD_CHAN_REPORT_REQUEST + */ +/** + * @param priv Private driver information structure + * @param resp HostCmd_DS_COMMAND struct returned from the firmware + * command + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_11h_cmdresp_process(mlan_private *priv, const HostCmd_DS_COMMAND *resp) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + switch (resp->command) { + case HostCmd_CMD_802_11_TPC_ADAPT_REQ: + HEXDUMP("11h: TPC REQUEST Rsp:", (t_u8 *)resp, + (t_u32)resp->size); + memcpy(priv->adapter, priv->adapter->curr_cmd->pdata_buf, + &resp->params.tpc_req, + sizeof(HostCmd_DS_802_11_TPC_ADAPT_REQ)); + break; + + case HostCmd_CMD_802_11_TPC_INFO: + HEXDUMP("11h: TPC INFO Rsp Data:", (t_u8 *)resp, + (t_u32)resp->size); + break; + + case HostCmd_CMD_802_11_CHAN_SW_ANN: + PRINTM(MINFO, "11h: Ret ChSwAnn: Sz=%u, Seq=%u, Ret=%u\n", + resp->size, resp->seq_num, resp->result); + break; + + case HostCmd_CMD_CHAN_REPORT_REQUEST: + priv->adapter->state_dfs.dfs_check_priv = priv; + priv->adapter->state_dfs.dfs_check_pending = MTRUE; + + if (resp->params.chan_rpt_req.millisec_dwell_time == 0) { + /* from wlan_11h_ioctl_dfs_cancel_chan_report */ + priv->adapter->state_dfs.dfs_check_pending = MFALSE; + priv->adapter->state_dfs.dfs_check_priv = MNULL; + priv->adapter->state_dfs.dfs_check_channel = 0; + PRINTM(MINFO, "11h: Cancelling Chan Report \n"); + } else { + PRINTM(MERROR, + "11h: Ret ChanRptReq. Set dfs_check_pending and wait" + " for EVENT_CHANNEL_REPORT.\n"); + } + + break; + + default: + ret = MLAN_STATUS_FAILURE; + } + + LEAVE(); + return ret; +} + +/** + * @brief Process an element from a scan response, copy relevant info for 11h + * + * @param pmadapter Pointer to mlan_adapter + * @param p11h_bss_info Output parameter: Pointer to the 11h BSS information + * for the network that is being processed + * @param pelement Pointer to the current IE we are inspecting for 11h + * relevance + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_11h_process_bss_elem(mlan_adapter *pmadapter, + wlan_11h_bss_info_t *p11h_bss_info, + const t_u8 *pelement) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u8 element_len = *((t_u8 *)pelement + 1); + + ENTER(); + switch (*pelement) { + case POWER_CONSTRAINT: + PRINTM(MINFO, "11h: Power Constraint IE Found\n"); + p11h_bss_info->sensed_11h = MTRUE; + memcpy(pmadapter, &p11h_bss_info->power_constraint, pelement, + MIN((element_len + sizeof(IEEEtypes_Header_t)), + sizeof(IEEEtypes_PowerConstraint_t))); + p11h_bss_info->power_constraint.len = + MIN(element_len, (sizeof(IEEEtypes_PowerConstraint_t) + - sizeof(IEEEtypes_Header_t))); + break; + + case POWER_CAPABILITY: + PRINTM(MINFO, "11h: Power Capability IE Found\n"); + p11h_bss_info->sensed_11h = MTRUE; + memcpy(pmadapter, &p11h_bss_info->power_capability, pelement, + MIN((element_len + sizeof(IEEEtypes_Header_t)), + sizeof(IEEEtypes_PowerCapability_t))); + p11h_bss_info->power_capability.len = + MIN(element_len, (sizeof(IEEEtypes_PowerCapability_t) + - sizeof(IEEEtypes_Header_t))); + break; + + case TPC_REPORT: + PRINTM(MINFO, "11h: Tpc Report IE Found\n"); + p11h_bss_info->sensed_11h = MTRUE; + memcpy(pmadapter, &p11h_bss_info->tpc_report, pelement, + MIN((element_len + sizeof(IEEEtypes_Header_t)), + sizeof(IEEEtypes_TPCReport_t))); + p11h_bss_info->tpc_report.len = + MIN(element_len, (sizeof(IEEEtypes_TPCReport_t) + - sizeof(IEEEtypes_Header_t))); + break; + + case CHANNEL_SWITCH_ANN: + PRINTM(MINFO, "11h: Channel Switch Ann IE Found\n"); + p11h_bss_info->sensed_11h = MTRUE; + memcpy(pmadapter, &p11h_bss_info->chan_switch_ann, pelement, + MIN((element_len + sizeof(IEEEtypes_Header_t)), + sizeof(IEEEtypes_ChanSwitchAnn_t))); + p11h_bss_info->chan_switch_ann.len = + MIN(element_len, (sizeof(IEEEtypes_ChanSwitchAnn_t) + - sizeof(IEEEtypes_Header_t))); + break; + + case QUIET: + PRINTM(MINFO, "11h: Quiet IE Found\n"); + p11h_bss_info->sensed_11h = MTRUE; + memcpy(pmadapter, &p11h_bss_info->quiet, pelement, + MIN((element_len + sizeof(IEEEtypes_Header_t)), + sizeof(IEEEtypes_Quiet_t))); + p11h_bss_info->quiet.len = + MIN(element_len, (sizeof(IEEEtypes_Quiet_t) + - sizeof(IEEEtypes_Header_t))); + break; + + case IBSS_DFS: + PRINTM(MINFO, "11h: Ibss Dfs IE Found\n"); + p11h_bss_info->sensed_11h = MTRUE; + memcpy(pmadapter, &p11h_bss_info->ibss_dfs, pelement, + MIN((element_len + sizeof(IEEEtypes_Header_t)), + sizeof(IEEEtypes_IBSS_DFS_t))); + p11h_bss_info->ibss_dfs.len = + MIN(element_len, (sizeof(IEEEtypes_IBSS_DFS_t) + - sizeof(IEEEtypes_Header_t))); + break; + + case SUPPORTED_CHANNELS: + case TPC_REQUEST: + /* + * These elements are not in beacons/probe responses. + * Included here to cover set of enumerated 11h elements. + */ + break; + + default: + ret = MLAN_STATUS_FAILURE; + } + + LEAVE(); + return ret; +} + +/** + * @brief Driver handling for CHANNEL_SWITCH_ANN event + * + * @param priv Pointer to mlan_private + * + * @return MLAN_STATUS_SUCCESS, MLAN_STATUS_FAILURE or MLAN_STATUS_PENDING + */ +mlan_status +wlan_11h_handle_event_chanswann(mlan_private *priv) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; +#ifdef STA_SUPPORT + mlan_deauth_param deauth_param; +#endif + t_u32 sec, usec; + + ENTER(); + priv->adapter->state_11h.recvd_chanswann_event = MTRUE; + + /* unlikely: clean up previous csa if still on-going */ + if (priv->intf_state_11h.dfs_slave_csa_chan) { + wlan_set_chan_blacklist(priv, BAND_A, + priv->intf_state_11h.dfs_slave_csa_chan, + MFALSE); + } + + /* record channel and time of occurence */ + priv->intf_state_11h.dfs_slave_csa_chan = + priv->curr_bss_params.bss_descriptor.channel; + priv->adapter->callbacks.moal_get_system_time(priv->adapter-> + pmoal_handle, &sec, + &usec); + priv->intf_state_11h.dfs_slave_csa_expire_at_sec = + sec + DFS_CHAN_MOVE_TIME; + +#ifdef STA_SUPPORT + /* do directed deauth. recvd_chanswann_event flag will cause different reason code */ + PRINTM(MINFO, "11h: handle_event_chanswann() - sending deauth\n"); + memcpy(priv->adapter, deauth_param.mac_addr, + &priv->curr_bss_params.bss_descriptor.mac_address, + MLAN_MAC_ADDR_LENGTH); + deauth_param.reason_code = DEF_DEAUTH_REASON_CODE; + ret = wlan_disconnect(priv, MNULL, &deauth_param); + + /* clear region table so next scan will be all passive */ + PRINTM(MINFO, "11h: handle_event_chanswann() - clear region table\n"); + wlan_11d_clear_parsedtable(priv); + + /* add channel to blacklist table */ + PRINTM(MINFO, + "11h: handle_event_chanswann() - scan blacklist csa channel\n"); + wlan_set_chan_blacklist(priv, BAND_A, + priv->intf_state_11h.dfs_slave_csa_chan, MTRUE); +#endif + + priv->adapter->state_11h.recvd_chanswann_event = MFALSE; + LEAVE(); + return ret; +} + +#ifdef DFS_TESTING_SUPPORT +/** + * @brief 802.11h DFS Testing configuration + * + * @param pmadapter Pointer to mlan_adapter + * @param pioctl_req Pointer to mlan_ioctl_req + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_11h_ioctl_dfs_testing(pmlan_adapter pmadapter, pmlan_ioctl_req pioctl_req) +{ + mlan_ds_11h_cfg *ds_11hcfg = MNULL; + mlan_ds_11h_dfs_testing *dfs_test = MNULL; + wlan_dfs_testing_settings_t *pdfs_test_params = MNULL; + + ENTER(); + + ds_11hcfg = (mlan_ds_11h_cfg *)pioctl_req->pbuf; + dfs_test = &ds_11hcfg->param.dfs_testing; + pdfs_test_params = &pmadapter->dfs_test_params; + + if (pioctl_req->action == MLAN_ACT_GET) { + dfs_test->usr_cac_period_msec = + pdfs_test_params->user_cac_period_msec; + dfs_test->usr_nop_period_sec = + pdfs_test_params->user_nop_period_sec; + dfs_test->usr_no_chan_change = + pdfs_test_params->no_channel_change_on_radar; + dfs_test->usr_fixed_new_chan = + pdfs_test_params->fixed_new_channel_on_radar; + } else { + pdfs_test_params->user_cac_period_msec = + dfs_test->usr_cac_period_msec; + pdfs_test_params->user_nop_period_sec = + dfs_test->usr_nop_period_sec; + pdfs_test_params->no_channel_change_on_radar = + dfs_test->usr_no_chan_change; + pdfs_test_params->fixed_new_channel_on_radar = + dfs_test->usr_fixed_new_chan; + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief 802.11h IOCTL to handle channel NOP status check + * @brief If given channel is under NOP, return a new non-dfs + * @brief channel + * + * @param pmadapter Pointer to mlan_adapter + * @param pioctl_req Pointer to mlan_ioctl_req + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_11h_ioctl_get_channel_nop_info(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + pmlan_private pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_11h_cfg *ds_11hcfg = MNULL; + t_s32 ret = MLAN_STATUS_FAILURE; + mlan_ds_11h_chan_nop_info *ch_nop_info = MNULL; + + ENTER(); + + if (pioctl_req) { + ds_11hcfg = (mlan_ds_11h_cfg *)pioctl_req->pbuf; + ch_nop_info = &ds_11hcfg->param.ch_nop_info; + + if (pioctl_req->action == MLAN_ACT_GET) { + ch_nop_info->chan_under_nop = + wlan_11h_is_channel_under_nop(pmadapter, + ch_nop_info-> + curr_chan); + if (ch_nop_info->chan_under_nop) { + wlan_11h_switch_non_dfs_chan(pmpriv, + &ch_nop_info-> + new_chan.channel); + if (ch_nop_info->chan_width == CHAN_BW_40MHZ) + wlan_11h_update_bandcfg(&ch_nop_info-> + new_chan. + bandcfg, + ch_nop_info-> + new_chan. + channel); + } + } + ret = MLAN_STATUS_SUCCESS; + } + + LEAVE(); + return ret; +} +#endif /* DFS_TESTING_SUPPORT */ + +/** + * @brief 802.11h DFS Channel Switch Count Configuration + * + * @param pmadapter Pointer to mlan_adapter + * @param pioctl_req Pointer to mlan_ioctl_req + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_11h_ioctl_chan_switch_count(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_ds_11h_cfg *ds_11hcfg = MNULL; + t_s32 ret = MLAN_STATUS_FAILURE; + + ENTER(); + + if (pioctl_req) { + ds_11hcfg = (mlan_ds_11h_cfg *)pioctl_req->pbuf; + + if (pioctl_req->action == MLAN_ACT_GET) { + ds_11hcfg->param.cs_count = pmadapter->dfs_cs_count; + } else { + pmadapter->dfs_cs_count = ds_11hcfg->param.cs_count; + } + ret = MLAN_STATUS_SUCCESS; + } + + LEAVE(); + return ret; +} + +/** + * @brief 802.11h DFS cancel chan report + * + * @param priv Pointer to mlan_private + * @param pioctl_req Pointer to mlan_ioctl_req + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_11h_ioctl_dfs_cancel_chan_report(mlan_private *priv, + pmlan_ioctl_req pioctl_req) +{ + mlan_ds_11h_cfg *ds_11hcfg = MNULL; + HostCmd_DS_CHAN_RPT_REQ *chan_rpt_req = MNULL; + t_s32 ret; + + ENTER(); + + ds_11hcfg = (mlan_ds_11h_cfg *)pioctl_req->pbuf; + + chan_rpt_req = + (HostCmd_DS_CHAN_RPT_REQ *)&ds_11hcfg->param.chan_rpt_req; + + ret = wlan_prepare_cmd(priv, HostCmd_CMD_CHAN_REPORT_REQUEST, + HostCmd_ACT_GEN_SET, 0, + (t_void *)pioctl_req, (t_void *)chan_rpt_req); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Check if channel is under NOP (Non-Occupancy Period) + * If so, the channel should not be used until the period expires. + * + * @param pmadapter Pointer to mlan_adapter + * @param channel Channel number + * + * @return MTRUE or MFALSE + */ +t_bool +wlan_11h_is_channel_under_nop(mlan_adapter *pmadapter, t_u8 channel) +{ + wlan_dfs_timestamp_t *pdfs_ts = MNULL; + t_u32 now_sec, now_usec; + t_bool ret = MFALSE; + + ENTER(); + pdfs_ts = wlan_11h_find_dfs_timestamp(pmadapter, channel); + + if (pdfs_ts && (pdfs_ts->channel == channel) + && (pdfs_ts->represents == DFS_TS_REPR_NOP_START)) { + /* found NOP_start timestamp entry on channel */ + pmadapter->callbacks.moal_get_system_time(pmadapter-> + pmoal_handle, + &now_sec, &now_usec); +#ifdef DFS_TESTING_SUPPORT + if (pmadapter->dfs_test_params.user_nop_period_sec) { + PRINTM(MCMD_D, + "dfs_testing - user NOP period=%d (sec)\n", + pmadapter->dfs_test_params.user_nop_period_sec); + if ((now_sec - pdfs_ts->ts_sec) <= + pmadapter->dfs_test_params.user_nop_period_sec) { + ret = MTRUE; + } + } else +#endif + { + if ((now_sec - pdfs_ts->ts_sec) <= + WLAN_11H_NON_OCCUPANCY_PERIOD) + ret = MTRUE; + } + + /* if entry is expired, remove it */ + if (!ret) + wlan_11h_remove_dfs_timestamp(pmadapter, pdfs_ts); + else + PRINTM(MMSG, + "11h: channel %d is under NOP - can't use.\n", + channel); + } + + LEAVE(); + return ret; +} + +/** + * @brief Driver handling for CHANNEL_REPORT_RDY event + * This event will have the channel report data appended. + * + * @param priv Pointer to mlan_private + * @param pevent Pointer to mlan_event + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_11h_handle_event_chanrpt_ready(mlan_private *priv, mlan_event *pevent) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + HostCmd_DS_CHAN_RPT_RSP *pchan_rpt_rsp; + MrvlIEtypes_Data_t *ptlv; + MeasRptBasicMap_t *pmeas_rpt_basic; + t_u8 *pbuffer; + t_s32 evt_len; + t_u16 tlv_len; + t_u32 sec, usec; + wlan_dfs_device_state_t *pstate_dfs = &priv->adapter->state_dfs; + + ENTER(); + pchan_rpt_rsp = (HostCmd_DS_CHAN_RPT_RSP *)&pevent->event_buf; + DBG_HEXDUMP(MCMD_D, "11h: Event ChanRptReady (HostCmd_DS_CHAN_RPT_RSP)", + (t_u8 *)pchan_rpt_rsp, pevent->event_len); + + if (wlan_le32_to_cpu(pchan_rpt_rsp->cmd_result) == + MLAN_CMD_RESULT_SUCCESS) { + pbuffer = (t_u8 *)&pchan_rpt_rsp->tlv_buffer; + evt_len = pevent->event_len; + evt_len -= + sizeof(HostCmd_DS_CHAN_RPT_RSP) - + sizeof(pchan_rpt_rsp->tlv_buffer); + + while (evt_len >= sizeof(MrvlIEtypesHeader_t)) { + ptlv = (MrvlIEtypes_Data_t *)pbuffer; + tlv_len = wlan_le16_to_cpu(ptlv->header.len); + + switch (wlan_le16_to_cpu(ptlv->header.type)) { + case TLV_TYPE_CHANRPT_11H_BASIC: + pmeas_rpt_basic = + (MeasRptBasicMap_t *)&ptlv->data; + if (pmeas_rpt_basic->radar) { + pstate_dfs->dfs_radar_found = MTRUE; + PRINTM(MMSG, + "RADAR Detected on channel %d!\n", + pstate_dfs->dfs_check_channel); + /* add channel to NOP list */ + wlan_11h_add_dfs_timestamp(priv-> + adapter, + DFS_TS_REPR_NOP_START, + pstate_dfs-> + dfs_check_channel); + } + break; + + default: + break; + } + + pbuffer += (tlv_len + sizeof(ptlv->header)); + evt_len -= (tlv_len + sizeof(ptlv->header)); + evt_len = (evt_len > 0) ? evt_len : 0; + } + } else { + ret = MLAN_STATUS_FAILURE; + } + + /* Update DFS structure. */ + priv->adapter->callbacks.moal_get_system_time(priv->adapter-> + pmoal_handle, &sec, + &usec); + pstate_dfs->dfs_report_time_sec = sec; + pstate_dfs->dfs_check_pending = MFALSE; + pstate_dfs->dfs_check_priv = MNULL; + + LEAVE(); + return ret; +} + +/** + * @brief Check if RADAR_DETECTED handling is blocking data tx + * + * @param pmadapter Pointer to mlan_adapter + * + * @return MTRUE or MFALSE + */ +t_bool +wlan_11h_radar_detected_tx_blocked(mlan_adapter *pmadapter) +{ + if (pmadapter->state_rdh.tx_block) + return MTRUE; + switch (pmadapter->state_rdh.stage) { + case RDH_OFF: + case RDH_CHK_INTFS: + case RDH_STOP_TRAFFIC: + return MFALSE; + } + return MTRUE; +} + +/** + * @brief Callback for RADAR_DETECTED event driver handling + * + * @param priv Void pointer to mlan_private + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_11h_radar_detected_callback(t_void *priv) +{ + mlan_status ret; + ENTER(); + ret = wlan_11h_radar_detected_handling(((mlan_private *)(priv))-> + adapter, (mlan_private *)priv); + LEAVE(); + return ret; +} + +/** + * @brief Function for handling sta disconnect event in dfs_repeater mode + * + * @param pmadapter pointer to mlan_adapter + * + * @return NONE + */ +void +wlan_dfs_rep_disconnect(mlan_adapter *pmadapter) +{ + mlan_private *priv_list[MLAN_MAX_BSS_NUM]; + mlan_private *pmpriv = MNULL; + t_u8 pcount, i; + + memset(pmadapter, priv_list, 0x00, sizeof(priv_list)); + pcount = wlan_get_privs_by_cond(pmadapter, wlan_is_intf_active, + priv_list); + + /* Stop all the active BSSes */ + for (i = 0; i < pcount; i++) { + pmpriv = priv_list[i]; + + if (GET_BSS_ROLE(pmpriv) != MLAN_BSS_ROLE_UAP) + continue; + + if (wlan_11h_radar_detect_required(pmpriv, + pmadapter->dfsr_channel)) { + wlan_prepare_cmd(pmpriv, HOST_CMD_APCMD_BSS_STOP, + HostCmd_ACT_GEN_SET, 0, MNULL, MNULL); + } + } +} + +/** + * @brief Function for handling sta BW change event in dfs_repeater mode + * + * @param pmadapter pointer to mlan_adapter + * + * @return NONE + */ +void +wlan_dfs_rep_bw_change(mlan_adapter *pmadapter) +{ + mlan_private *priv_list[MLAN_MAX_BSS_NUM]; + mlan_private *pmpriv = MNULL; + t_u8 pcount, i; + + memset(pmadapter, priv_list, 0x00, sizeof(priv_list)); + pcount = wlan_get_privs_by_cond(pmadapter, wlan_is_intf_active, + priv_list); + if (pcount == 1) { + pmpriv = priv_list[0]; + if (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_STA) { + PRINTM(MMSG, "dfs-repeater: BW change detected\n" + "no active priv's, skip event handling.\n"); + return; + } + } + + /* Stop all the active BSSes */ + for (i = 0; i < pcount; i++) { + pmpriv = priv_list[i]; + + if (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_UAP) { + + /* Check if uAPs running on non-dfs channel. If they do + * then there is no need to restart the uAPs + */ + if (!wlan_11h_radar_detect_required(pmpriv, + pmadapter-> + dfsr_channel)) + return; + + wlan_prepare_cmd(pmpriv, HOST_CMD_APCMD_BSS_STOP, + HostCmd_ACT_GEN_SET, 0, MNULL, MNULL); + } + } + + /* Start all old active BSSes */ + for (i = 0; i < pcount; i++) { + pmpriv = priv_list[i]; + + if (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_UAP) { + wlan_prepare_cmd(pmpriv, HOST_CMD_APCMD_BSS_START, + HostCmd_ACT_GEN_SET, 0, MNULL, MNULL); + } + } +} + +/** + * @brief Update band config for the new channel + * + * @param uap_band_cfg uap's old channel's band configuration + * @param new_channel new channel that the device is switching to + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE or MLAN_STATUS_PENDING + */ +void +wlan_11h_update_bandcfg(IN Band_Config_t *uap_band_cfg, IN t_u8 new_channel) +{ + t_u8 chan_offset; + ENTER(); + + /* Update the channel offset for 20MHz, 40MHz and 80MHz + * Clear the channel bandwidth for 20MHz + * since channel switch could be happening from 40/80MHz to 20MHz + */ + chan_offset = wlan_get_second_channel_offset(new_channel); + uap_band_cfg->chan2Offset = chan_offset; + + if (!chan_offset) { /* 40MHz/80MHz */ + PRINTM(MCMD_D, "20MHz channel, clear channel bandwidth\n"); + uap_band_cfg->chanWidth = CHAN_BW_20MHZ; + } + LEAVE(); +} + +/** + * @brief Get priv current index -- this is used to enter correct rdh_state during radar handling + * + * @param pmpriv Pointer to mlan_private + * @param pstate_rdh Pointer to radar detected state handler + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_11h_get_priv_curr_idx(mlan_private *pmpriv, + wlan_radar_det_hndlg_state_t *pstate_rdh) +{ + t_bool found = MFALSE; + ENTER(); + + PRINTM(MINFO, "%s:pmpriv =%p\n", __func__, pmpriv); + while ((++pstate_rdh->priv_curr_idx) < pstate_rdh->priv_list_count) { + if (pmpriv == pstate_rdh->priv_list[pstate_rdh->priv_curr_idx]) { + PRINTM(MINFO, "found matching priv: priv_idx=%d\n", + pstate_rdh->priv_curr_idx); + found = MTRUE; + break; + } + } + return (found == MTRUE) ? MLAN_STATUS_SUCCESS : MLAN_STATUS_FAILURE; +} + +/** + * @brief Driver handling for RADAR_DETECTED event + * + * @param pmadapter Pointer to mlan_adapter + * @param pmpriv Pointer to mlan_private + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE or MLAN_STATUS_PENDING + */ +mlan_status +wlan_11h_radar_detected_handling(mlan_adapter *pmadapter, mlan_private *pmpriv) +{ +#ifdef DEBUG_LEVEL1 + const char *rdh_stage_str[] = { + "RDH_OFF", + "RDH_CHK_INTFS", + "RDH_STOP_TRAFFIC", + "RDH_GET_INFO_CHANNEL", + "RDH_GET_INFO_BEACON_DTIM", + "RDH_SET_CUSTOM_IE", + "RDH_REM_CUSTOM_IE", + "RDH_STOP_INTFS", + "RDH_SET_NEW_CHANNEL", + "RDH_RESTART_INTFS", + "RDH_RESTART_TRAFFIC" + }; +#endif + + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u32 i; + wlan_radar_det_hndlg_state_t *pstate_rdh = &pmadapter->state_rdh; + + ENTER(); + + if (!pmpriv) { + PRINTM(MERROR, "Invalid radar priv -- Exit radar handling\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + switch (pstate_rdh->stage) { + case RDH_CHK_INTFS: + PRINTM(MCMD_D, "%s(): stage(%d)=%s\n", + __func__, pstate_rdh->stage, + rdh_stage_str[pstate_rdh->stage]); + + /* get active interfaces */ + memset(pmadapter, pstate_rdh->priv_list, 0x00, + sizeof(pstate_rdh->priv_list)); + pstate_rdh->priv_list_count = wlan_get_privs_by_cond(pmadapter, + wlan_is_intf_active, + pstate_rdh-> + priv_list); + PRINTM(MCMD_D, "%s(): priv_list_count = %d\n", __func__, + pstate_rdh->priv_list_count); + for (i = 0; i < pstate_rdh->priv_list_count; i++) + PRINTM(MINFO, "%s(): priv_list[%d] = %p\n", + __func__, i, pstate_rdh->priv_list[i]); + + if (pstate_rdh->priv_list_count == 0) { + /* no interfaces active... nothing to do */ + PRINTM(MMSG, "11h: Radar Detected - no active priv's," + " skip event handling.\n"); + pstate_rdh->stage = RDH_OFF; + PRINTM(MCMD_D, "%s(): finished - stage(%d)=%s\n", + __func__, pstate_rdh->stage, + rdh_stage_str[pstate_rdh->stage]); + break; /* EXIT CASE */ + } + + /* else: start handling */ + pstate_rdh->curr_channel = 0; + pstate_rdh->new_channel = 0; + memset(pmadapter, &(pstate_rdh->uap_band_cfg), 0, + sizeof(pstate_rdh->uap_band_cfg)); + pstate_rdh->max_bcn_dtim_ms = 0; + pstate_rdh->priv_curr_idx = RDH_STAGE_FIRST_ENTRY_PRIV_IDX; + pstate_rdh->stage = RDH_STOP_TRAFFIC; + /* FALL THROUGH TO NEXT STAGE */ + + case RDH_STOP_TRAFFIC: + PRINTM(MCMD_D, "%s(): stage(%d)=%s\n", + __func__, pstate_rdh->stage, + rdh_stage_str[pstate_rdh->stage]); + + PRINTM(MMSG, + "11h: Radar Detected - stopping host tx traffic.\n"); + for (i = 0; i < pstate_rdh->priv_list_count; i++) + wlan_11h_tx_disable(pstate_rdh->priv_list[i]); + + pstate_rdh->priv_curr_idx = RDH_STAGE_FIRST_ENTRY_PRIV_IDX; + pstate_rdh->stage = RDH_GET_INFO_CHANNEL; + /* FALL THROUGH TO NEXT STAGE */ + + case RDH_GET_INFO_CHANNEL: + PRINTM(MCMD_D, "%s(): stage(%d)=%s, priv_idx=%d\n", + __func__, pstate_rdh->stage, + rdh_stage_str[pstate_rdh->stage], + pstate_rdh->priv_curr_idx); + + /* here, prefer STA info over UAP info - one less CMD to send */ + if (pstate_rdh->priv_curr_idx == RDH_STAGE_FIRST_ENTRY_PRIV_IDX) { +#ifdef UAP_SUPPORT + if (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_UAP) { + ret = wlan_11h_get_priv_curr_idx(pmpriv, + pstate_rdh); + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, + "Unable to locate pmpriv in current active priv_list\n"); + break; /* EXIT CASE */ + } + + /* send cmd to get first UAP's info */ + pmpriv->uap_state_chan_cb.pioctl_req_curr = + MNULL; + pmpriv->uap_state_chan_cb.get_chan_callback = + wlan_11h_radar_detected_callback; + ret = wlan_uap_get_channel(pmpriv); + break; /* EXIT CASE */ + } else +#endif + { + /* Assume all STAs on same channel, find first STA */ + MASSERT(pstate_rdh->priv_list_count > 0); + for (i = 0; i < pstate_rdh->priv_list_count; + i++) { + pmpriv = pstate_rdh->priv_list[i]; + if (GET_BSS_ROLE(pmpriv) == + MLAN_BSS_ROLE_STA) + break; + } + /* STA info kept in driver, just copy */ + pstate_rdh->curr_channel = + pmpriv->curr_bss_params.bss_descriptor. + channel; + } + } +#ifdef UAP_SUPPORT + else if (pstate_rdh->priv_curr_idx < + pstate_rdh->priv_list_count) { + /* repeat entry: UAP return with info */ + pstate_rdh->curr_channel = + pmpriv->uap_state_chan_cb.channel; + pstate_rdh->uap_band_cfg = + pmpriv->uap_state_chan_cb.bandcfg; + PRINTM(MCMD_D, + "%s(): uap_band_cfg=0x%02x curr_chan=%d, curr_idx=%d bss_role=%d\n", + __func__, pstate_rdh->uap_band_cfg, + pstate_rdh->curr_channel, + pstate_rdh->priv_curr_idx, GET_BSS_ROLE(pmpriv)); + } +#endif + + /* add channel to NOP list */ + wlan_11h_add_dfs_timestamp(pmadapter, DFS_TS_REPR_NOP_START, + pstate_rdh->curr_channel); + + /* choose new channel (!= curr channel) and move on */ +#ifdef UAP_SUPPORT + if (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_UAP) + pstate_rdh->new_channel = + wlan_11h_get_uap_start_channel(pmpriv, + pmpriv-> + uap_state_chan_cb. + bandcfg); + else +#endif + pstate_rdh->new_channel = + wlan_11h_get_adhoc_start_channel(pmpriv); + + if (!pstate_rdh->new_channel || (pstate_rdh->new_channel == pstate_rdh->curr_channel)) { /* report error */ + PRINTM(MERROR, + "%s(): ERROR - Failed to choose new_chan" + " (!= curr_chan) !!\n", __func__); +#ifdef UAP_SUPPORT + if (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_UAP) { + ret = wlan_prepare_cmd(pmpriv, + HOST_CMD_APCMD_BSS_STOP, + HostCmd_ACT_GEN_SET, 0, + MNULL, MNULL); + PRINTM(MERROR, + "STOP UAP and exit radar handling...\n"); + pstate_rdh->stage = RDH_OFF; + break; /* leads to exit case */ + } +#endif + } +#ifdef DFS_TESTING_SUPPORT + if (!pmadapter->dfs_test_params.no_channel_change_on_radar && + pmadapter->dfs_test_params.fixed_new_channel_on_radar) { + PRINTM(MCMD_D, "dfs_testing - user fixed new_chan=%d\n", + pmadapter->dfs_test_params. + fixed_new_channel_on_radar); + pstate_rdh->new_channel = + pmadapter->dfs_test_params. + fixed_new_channel_on_radar; + } + /* applies to DFS with ECSA support */ + if (pmadapter->dfs_test_params.no_channel_change_on_radar) { + pstate_rdh->new_channel = pstate_rdh->curr_channel; + } +#endif + PRINTM(MCMD_D, "%s(): curr_chan=%d, new_chan=%d\n", + __func__, pstate_rdh->curr_channel, + pstate_rdh->new_channel); + + pstate_rdh->priv_curr_idx = RDH_STAGE_FIRST_ENTRY_PRIV_IDX; + pstate_rdh->stage = RDH_GET_INFO_BEACON_DTIM; + /* FALL THROUGH TO NEXT STAGE */ + + case RDH_GET_INFO_BEACON_DTIM: + PRINTM(MCMD_D, "%s(): stage(%d)=%s, priv_idx=%d\n", + __func__, pstate_rdh->stage, + rdh_stage_str[pstate_rdh->stage], + pstate_rdh->priv_curr_idx); + +#ifdef UAP_SUPPORT + /* check all intfs in this stage to find longest period */ + /* UAP intf callback returning with info */ + if (pstate_rdh->priv_curr_idx < pstate_rdh->priv_list_count) { + t_u16 bcn_dtim_msec; + pmpriv = pstate_rdh->priv_list[pstate_rdh-> + priv_curr_idx]; + PRINTM(MCMD_D, "%s(): uap.bcn_pd=%d, uap.dtim_pd=%d\n", + __func__, + pmpriv->uap_state_chan_cb.beacon_period, + pmpriv->uap_state_chan_cb.dtim_period); + bcn_dtim_msec = + (pmpriv->uap_state_chan_cb.beacon_period * + pmpriv->uap_state_chan_cb.dtim_period); + if (bcn_dtim_msec > pstate_rdh->max_bcn_dtim_ms) + pstate_rdh->max_bcn_dtim_ms = bcn_dtim_msec; + } +#endif + + /* check next intf */ + while ((++pstate_rdh->priv_curr_idx) < + pstate_rdh->priv_list_count) { + pmpriv = pstate_rdh->priv_list[pstate_rdh-> + priv_curr_idx]; + +#ifdef UAP_SUPPORT + if (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_UAP) { + pmpriv->uap_state_chan_cb.pioctl_req_curr = + MNULL; + pmpriv->uap_state_chan_cb.get_chan_callback = + wlan_11h_radar_detected_callback; + ret = wlan_uap_get_beacon_dtim(pmpriv); + break; /* leads to exit case */ + } else +#endif + { /* get STA info from driver and compare here */ + t_u16 bcn_pd_msec = 100; + t_u16 dtim_pd_msec = 1; + t_u16 bcn_dtim_msec; + + /* adhoc creator */ + if (wlan_11h_is_dfs_master(pmpriv)) { + bcn_pd_msec = pmpriv->beacon_period; + } else { + bcn_pd_msec = + pmpriv->curr_bss_params. + bss_descriptor.beacon_period; + /* if (priv->bss_mode != MLAN_BSS_MODE_IBSS) */ + /* TODO: mlan_scan.c needs to parse TLV 0x05 (TIM) for dtim_period */ + } + PRINTM(MCMD_D, + "%s(): sta.bcn_pd=%d, sta.dtim_pd=%d\n", + __func__, bcn_pd_msec, dtim_pd_msec); + bcn_dtim_msec = (bcn_pd_msec * dtim_pd_msec); + if (bcn_dtim_msec > pstate_rdh->max_bcn_dtim_ms) + pstate_rdh->max_bcn_dtim_ms = + bcn_dtim_msec; + } + } + + if (pstate_rdh->priv_curr_idx < pstate_rdh->priv_list_count) + break; /* EXIT CASE (for UAP) */ + /* else */ + pstate_rdh->priv_curr_idx = RDH_STAGE_FIRST_ENTRY_PRIV_IDX; + pstate_rdh->stage = RDH_SET_CUSTOM_IE; + /* FALL THROUGH TO NEXT STAGE */ + + case RDH_SET_CUSTOM_IE: + PRINTM(MCMD_D, "%s(): stage(%d)=%s, priv_idx=%d\n", + __func__, pstate_rdh->stage, + rdh_stage_str[pstate_rdh->stage], + pstate_rdh->priv_curr_idx); + + /* add CHAN_SW IE - firmware will accept on any interface, and apply to all */ + if (pstate_rdh->priv_curr_idx == RDH_STAGE_FIRST_ENTRY_PRIV_IDX) { + mlan_ioctl_req *pioctl_req = MNULL; + + ret = wlan_11h_prepare_custom_ie_chansw(pmadapter, + &pioctl_req, + MTRUE); + if ((ret != MLAN_STATUS_SUCCESS) || !pioctl_req) { + PRINTM(MERROR, + "%s(): Error in preparing CHAN_SW IE.\n", + __func__); + break; /* EXIT CASE */ + } + + PRINTM(MMSG, + "11h: Radar Detected - adding CHAN_SW IE to interfaces.\n"); + ret = wlan_11h_get_priv_curr_idx(pmpriv, pstate_rdh); + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, + "Unable to locate pmpriv in current active priv_list\n"); + break; /* EXIT CASE */ + } + + pioctl_req->bss_index = pmpriv->bss_index; + ret = wlan_misc_ioctl_custom_ie_list(pmadapter, + pioctl_req, + MFALSE); + if (ret != MLAN_STATUS_SUCCESS && + ret != MLAN_STATUS_PENDING) { + PRINTM(MERROR, + "%s(): Could not set IE for priv=%p [priv_bss_idx=%d]!\n", + __func__, pmpriv, pmpriv->bss_index); + /* TODO: how to handle this error case?? ignore & continue? */ + } + /* free ioctl buffer memory before we leave */ + pmadapter->callbacks.moal_mfree(pmadapter->pmoal_handle, + (t_u8 *)pioctl_req); + break; /* EXIT CASE */ + } + /* else */ + pstate_rdh->priv_curr_idx = RDH_STAGE_FIRST_ENTRY_PRIV_IDX; + pstate_rdh->stage = RDH_REM_CUSTOM_IE; + /* FALL THROUGH TO NEXT STAGE */ + + case RDH_REM_CUSTOM_IE: + PRINTM(MCMD_D, "%s(): stage(%d)=%s, priv_idx=%d\n", + __func__, pstate_rdh->stage, + rdh_stage_str[pstate_rdh->stage], + pstate_rdh->priv_curr_idx); + + /* remove CHAN_SW IE - firmware will accept on any interface, + and apply to all */ + if (pstate_rdh->priv_curr_idx == RDH_STAGE_FIRST_ENTRY_PRIV_IDX) { + mlan_ioctl_req *pioctl_req = MNULL; + + /* + * first entry to this stage, do delay + * DFS requires a minimum of 5 chances for clients to hear this IE. + * Use delay: 5 beacons <= (BCN_DTIM_MSEC*5) <= 3 seconds). + */ + t_u16 delay_ms = MAX(MIN_RDH_CHAN_SW_IE_PERIOD_MSEC, + MIN((4 * + pstate_rdh->max_bcn_dtim_ms), + MAX_RDH_CHAN_SW_IE_PERIOD_MSEC)); + PRINTM(MMSG, + "11h: Radar Detected - delay %d ms for FW to" + " broadcast CHAN_SW IE.\n", delay_ms); + wlan_mdelay(pmadapter, delay_ms); + PRINTM(MMSG, + "11h: Radar Detected - delay over, removing" + " CHAN_SW IE from interfaces.\n"); + + ret = wlan_11h_prepare_custom_ie_chansw(pmadapter, + &pioctl_req, + MFALSE); + if ((ret != MLAN_STATUS_SUCCESS) || !pioctl_req) { + PRINTM(MERROR, + "%s(): Error in preparing CHAN_SW IE.\n", + __func__); + break; /* EXIT CASE */ + } + + ret = wlan_11h_get_priv_curr_idx(pmpriv, pstate_rdh); + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, + "Unable to locate pmpriv in current active priv_list\n"); + break; /* EXIT CASE */ + } + + pioctl_req->bss_index = pmpriv->bss_index; + ret = wlan_misc_ioctl_custom_ie_list(pmadapter, + pioctl_req, + MFALSE); + if (ret != MLAN_STATUS_SUCCESS && + ret != MLAN_STATUS_PENDING) { + PRINTM(MERROR, + "%s(): Could not remove IE for priv=%p [priv_bss_idx=%d]!\n", + __func__, pmpriv, pmpriv->bss_index); + /* TODO: hiow to handle this error case?? ignore & continue? */ + } + /* free ioctl buffer memory before we leave */ + pmadapter->callbacks.moal_mfree(pmadapter->pmoal_handle, + (t_u8 *)pioctl_req); + break; /* EXIT CASE */ + } + /* else */ + pstate_rdh->priv_curr_idx = RDH_STAGE_FIRST_ENTRY_PRIV_IDX; + pstate_rdh->stage = RDH_STOP_INTFS; + /* FALL THROUGH TO NEXT STAGE */ + + case RDH_STOP_INTFS: + PRINTM(MCMD_D, "%s(): stage(%d)=%s, priv_idx=%d\n", + __func__, pstate_rdh->stage, + rdh_stage_str[pstate_rdh->stage], + pstate_rdh->priv_curr_idx); + + /* issues one cmd (DEAUTH/ADHOC_STOP/BSS_STOP) to each intf */ + while ((++pstate_rdh->priv_curr_idx) < + pstate_rdh->priv_list_count) { + pmpriv = pstate_rdh->priv_list[pstate_rdh-> + priv_curr_idx]; +#ifdef UAP_SUPPORT + if (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_UAP) { + ret = wlan_prepare_cmd(pmpriv, + HOST_CMD_APCMD_BSS_STOP, + HostCmd_ACT_GEN_SET, 0, + MNULL, MNULL); + break; /* leads to exit case */ + } +#endif +#ifdef STA_SUPPORT + if (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_STA) { + if (wlan_11h_is_dfs_master(pmpriv)) { + /* Save ad-hoc creator state before stop clears it */ + pmpriv->adhoc_state_prev = + pmpriv->adhoc_state; + } + if (pmpriv->media_connected == MTRUE) { + wlan_disconnect(pmpriv, MNULL, MNULL); + break; /* leads to exit case */ + } + } +#endif + } + + if (pstate_rdh->priv_curr_idx < pstate_rdh->priv_list_count || + ret == MLAN_STATUS_FAILURE) + break; /* EXIT CASE */ + /* else */ + pstate_rdh->priv_curr_idx = RDH_STAGE_FIRST_ENTRY_PRIV_IDX; + pstate_rdh->stage = RDH_SET_NEW_CHANNEL; + +#ifdef DFS_TESTING_SUPPORT + if (pmadapter->dfs_test_params.no_channel_change_on_radar) { + PRINTM(MCMD_D, + "dfs_testing - no channel change on radar." + " Overwrite new_chan = curr_chan.\n"); + pstate_rdh->new_channel = pstate_rdh->curr_channel; + pstate_rdh->priv_curr_idx = + RDH_STAGE_FIRST_ENTRY_PRIV_IDX; + pstate_rdh->stage = RDH_RESTART_INTFS; + goto rdh_restart_intfs; /* skip next stage */ + } +#endif + /* FALL THROUGH TO NEXT STAGE */ + + case RDH_SET_NEW_CHANNEL: + PRINTM(MCMD_D, "%s(): stage(%d)=%s, priv_idx=%d\n", + __func__, pstate_rdh->stage, + rdh_stage_str[pstate_rdh->stage], + pstate_rdh->priv_curr_idx); + + /* only set new channel for UAP intfs */ + while ((++pstate_rdh->priv_curr_idx) < + pstate_rdh->priv_list_count) { + pmpriv = pstate_rdh->priv_list[pstate_rdh-> + priv_curr_idx]; +#ifdef UAP_SUPPORT + if (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_UAP) { + + pmpriv->uap_state_chan_cb.pioctl_req_curr = + MNULL; + pmpriv->uap_state_chan_cb.get_chan_callback = + wlan_11h_radar_detected_callback; + + /* DFS only in 5GHz */ + wlan_11h_update_bandcfg(&pstate_rdh-> + uap_band_cfg, + pstate_rdh-> + new_channel); + PRINTM(MCMD_D, + "RDH_SET_NEW_CHANNEL: uAP band config = 0x%x channel=%d\n", + pstate_rdh->uap_band_cfg, + pstate_rdh->new_channel); + + ret = wlan_uap_set_channel(pmpriv, + pstate_rdh-> + uap_band_cfg, + pstate_rdh-> + new_channel); + break; /* leads to exit case */ + } +#endif + } + + if (pstate_rdh->priv_curr_idx < pstate_rdh->priv_list_count || + ret == MLAN_STATUS_FAILURE) + break; /* EXIT CASE (for UAP) */ + /* else */ + pstate_rdh->priv_curr_idx = RDH_STAGE_FIRST_ENTRY_PRIV_IDX; + pstate_rdh->stage = RDH_RESTART_INTFS; + /* FALL THROUGH TO NEXT STAGE */ + + case RDH_RESTART_INTFS: +#ifdef DFS_TESTING_SUPPORT +rdh_restart_intfs: +#endif + PRINTM(MCMD_D, "%s(): stage(%d)=%s, priv_idx=%d\n", + __func__, pstate_rdh->stage, + rdh_stage_str[pstate_rdh->stage], + pstate_rdh->priv_curr_idx); + + /* can only restart master intfs */ + while ((++pstate_rdh->priv_curr_idx) < + pstate_rdh->priv_list_count) { + pmpriv = pstate_rdh->priv_list[pstate_rdh-> + priv_curr_idx]; +#ifdef UAP_SUPPORT + if (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_UAP) { + if (wlan_11h_radar_detect_required(pmpriv, + pstate_rdh-> + new_channel)) + { + /* Radar detection is required for this channel, + make sure 11h is activated in the firmware */ + ret = wlan_11h_activate(pmpriv, MNULL, + MTRUE); + ret = wlan_11h_config_master_radar_det + (pmpriv, MTRUE); + ret = wlan_11h_check_update_radar_det_state(pmpriv); + } + ret = wlan_prepare_cmd(pmpriv, + HOST_CMD_APCMD_BSS_START, + HostCmd_ACT_GEN_SET, 0, + MNULL, MNULL); + break; /* leads to exit case */ + } +#endif +#ifdef STA_SUPPORT + if (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_STA) { + /* Check previous state to find former + * Ad-hoc creator interface. Set new + * state to Starting, so it'll be seen + * as a DFS master. */ + if (pmpriv->adhoc_state_prev == ADHOC_STARTED) { + pmpriv->adhoc_state = ADHOC_STARTING; + pmpriv->adhoc_state_prev = ADHOC_IDLE; + } + if (wlan_11h_is_dfs_master(pmpriv)) { + /* set new adhoc channel here */ + pmpriv->adhoc_channel = + pstate_rdh->new_channel; + if (wlan_11h_radar_detect_required + (pmpriv, pstate_rdh->new_channel)) { + /* Radar detection is required for this channel, + make sure 11h is activated in the firmware */ + ret = wlan_11h_activate(pmpriv, + MNULL, + MTRUE); + if (ret) + break; + ret = wlan_11h_config_master_radar_det(pmpriv, MTRUE); + if (ret) + break; + ret = wlan_11h_check_update_radar_det_state(pmpriv); + if (ret) + break; + } + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_802_11_AD_HOC_START, + HostCmd_ACT_GEN_SET, + 0, MNULL, + &pmpriv-> + adhoc_last_start_ssid); + break; /* leads to exit case */ + } + + /* NOTE: DON'T reconnect slave STA intfs - infra/adhoc_joiner + * Do we want to return to same AP/network (on radar channel)? + * If want to connect back, depend on either: + * 1. driver's reassoc thread + * 2. wpa_supplicant, or other user-space app + */ + } +#endif + } + + if (pstate_rdh->priv_curr_idx < pstate_rdh->priv_list_count || + ret == MLAN_STATUS_FAILURE) + break; /* EXIT CASE (for UAP) */ + /* else */ + pstate_rdh->priv_curr_idx = RDH_STAGE_FIRST_ENTRY_PRIV_IDX; + pstate_rdh->stage = RDH_RESTART_TRAFFIC; + /* FALL THROUGH TO NEXT STAGE */ + + case RDH_RESTART_TRAFFIC: + PRINTM(MCMD_D, "%s(): stage(%d)=%s\n", + __func__, pstate_rdh->stage, + rdh_stage_str[pstate_rdh->stage]); + /* remove custome ie */ + if (pmadapter->ecsa_enable) { + mlan_ioctl_req *pioctl_req = MNULL; + ret = wlan_11h_prepare_custom_ie_chansw(pmadapter, + &pioctl_req, + MFALSE); + if ((ret != MLAN_STATUS_SUCCESS) || !pioctl_req) { + PRINTM(MERROR, + "%s(): Error in preparing CHAN_SW IE.\n", + __func__); + break; /* EXIT CASE */ + } + + pmpriv = pstate_rdh->priv_list[0]; + pstate_rdh->priv_curr_idx = 0; + pioctl_req->bss_index = pmpriv->bss_index; + ret = wlan_misc_ioctl_custom_ie_list(pmadapter, + pioctl_req, + MFALSE); + if (ret != MLAN_STATUS_SUCCESS && + ret != MLAN_STATUS_PENDING) { + PRINTM(MERROR, + "%s(): Could not set IE for priv=%p [priv_bss_idx=%d]!\n", + __func__, pmpriv, pmpriv->bss_index); + /* TODO: hiow to handle this error case?? ignore & continue? */ + } + /* free ioctl buffer memory before we leave */ + pmadapter->callbacks.moal_mfree(pmadapter->pmoal_handle, + (t_u8 *)pioctl_req); + } + /* continue traffic for reactivated interfaces */ + PRINTM(MMSG, + "11h: Radar Detected - restarting host tx traffic.\n"); + for (i = 0; i < pstate_rdh->priv_list_count; i++) + wlan_11h_tx_enable(pstate_rdh->priv_list[i]); + + pstate_rdh->stage = RDH_OFF; /* DONE! */ + PRINTM(MCMD_D, "%s(): finished - stage(%d)=%s\n", + __func__, pstate_rdh->stage, + rdh_stage_str[pstate_rdh->stage]); + + break; + + default: + pstate_rdh->stage = RDH_OFF; /* cancel RDH to unblock Tx packets */ + break; + } + + LEAVE(); + return ret; +} + +/** + * @brief DFS Event Preprocessing. + * Operates directly on pmadapter variables. + * + * 1. EVENT_RADAR_DETECTED comes from firmware without specific + * bss_num/bss_type. Find it an appropriate interface and + * update event_cause field in event_buf. + * + * @param pmadapter Pointer to mlan_adapter + * + * @return MLAN_STATUS_SUCCESS (update successful) + * or MLAN_STATUS_FAILURE (no change) + */ +mlan_status +wlan_11h_dfs_event_preprocessing(mlan_adapter *pmadapter) +{ + mlan_status ret = MLAN_STATUS_FAILURE; + mlan_private *pmpriv = MNULL; + mlan_private *priv_list[MLAN_MAX_BSS_NUM]; + + ENTER(); + switch (pmadapter->event_cause & EVENT_ID_MASK) { + case EVENT_RADAR_DETECTED: + /* find active intf: prefer dfs_master over dfs_slave */ + if (wlan_get_privs_by_two_cond(pmadapter, + wlan_11h_is_master_active_on_dfs_chan, + wlan_11h_is_dfs_master, + MTRUE, priv_list)) { + pmpriv = priv_list[0]; + PRINTM(MINFO, "%s: found dfs_master priv=%p\n", + __func__, pmpriv); + } else if (wlan_get_privs_by_two_cond(pmadapter, + wlan_11h_is_slave_active_on_dfs_chan, + wlan_11h_is_dfs_slave, + MTRUE, priv_list)) { + pmpriv = priv_list[0]; + PRINTM(MINFO, "%s: found dfs_slave priv=%p\n", + __func__, pmpriv); + } else if (pmadapter->state_dfs.dfs_check_pending) { + pmpriv = (mlan_private *)(pmadapter->state_dfs. + dfs_check_priv); + PRINTM(MINFO, "%s: found dfs priv=%p\n", __func__, + pmpriv); + } + + /* update event_cause if we found an appropriate priv */ + if (pmpriv) { + pmlan_buffer pmevbuf = pmadapter->pmlan_buffer_event; + t_u32 new_event_cause = + pmadapter->event_cause & EVENT_ID_MASK; + new_event_cause |= + ((GET_BSS_NUM(pmpriv) & 0xff) << 16) | + ((pmpriv->bss_type & 0xff) << 24); + PRINTM(MINFO, "%s: priv - bss_num=%d, bss_type=%d\n", + __func__, GET_BSS_NUM(pmpriv), pmpriv->bss_type); + memcpy(pmadapter, pmevbuf->pbuf + pmevbuf->data_offset, + &new_event_cause, sizeof(new_event_cause)); + ret = MLAN_STATUS_SUCCESS; + } else { + PRINTM(MERROR, + "Failed to find dfs master/slave priv\n"); + ret = MLAN_STATUS_FAILURE; + } + break; + } + + LEAVE(); + return ret; +} + +/** + * @brief try to switch to a non-dfs channel + * + * @param priv Void pointer to mlan_private + * + * @param chan pointer to channel + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE or MLAN_STATUS_PENDING + */ +mlan_status +wlan_11h_switch_non_dfs_chan(mlan_private *priv, t_u8 *chan) +{ + mlan_status ret = MLAN_STATUS_FAILURE; + t_u32 i; + t_u32 rand_entry; + t_u8 def_chan; + t_u8 rand_tries = 0; + region_chan_t *chn_tbl = MNULL; + pmlan_adapter pmadapter = priv->adapter; + + ENTER(); + +#ifdef DFS_TESTING_SUPPORT + if (!pmadapter->dfs_test_params.no_channel_change_on_radar && + pmadapter->dfs_test_params.fixed_new_channel_on_radar) { + PRINTM(MCMD_D, "dfs_testing - user fixed new_chan=%d\n", + pmadapter->dfs_test_params.fixed_new_channel_on_radar); + *chan = pmadapter->dfs_test_params.fixed_new_channel_on_radar; + + LEAVE(); + return MLAN_STATUS_SUCCESS; + } +#endif + + /*get the channel table first */ + for (i = 0; i < MAX_REGION_CHANNEL_NUM; i++) { + if (pmadapter->region_channel[i].band == BAND_A + && pmadapter->region_channel[i].valid) { + chn_tbl = &pmadapter->region_channel[i]; + break; + } + } + + if (!chn_tbl || !chn_tbl->pcfp) + goto done; + + do { + rand_entry = + wlan_11h_get_random_num(pmadapter) % chn_tbl->num_cfp; + def_chan = (t_u8)chn_tbl->pcfp[rand_entry].channel; + rand_tries++; + } while ((wlan_11h_is_channel_under_nop(pmadapter, def_chan) || + chn_tbl->pcfp[rand_entry].passive_scan_or_radar_detect == + MTRUE) && (rand_tries < MAX_SWITCH_CHANNEL_RETRIES)); + + /* meet max retries, use the lowest non-dfs channel */ + if (rand_tries == MAX_SWITCH_CHANNEL_RETRIES) { + for (i = 0; i < chn_tbl->num_cfp; i++) { + if (chn_tbl->pcfp[i].passive_scan_or_radar_detect == + MFALSE && + !wlan_11h_is_channel_under_nop(pmadapter, + (t_u8)chn_tbl-> + pcfp[i].channel)) { + def_chan = (t_u8)chn_tbl->pcfp[i].channel; + break; + } + } + if (i == chn_tbl->num_cfp) + goto done; + } + + *chan = def_chan; + ret = MLAN_STATUS_SUCCESS; +done: + LEAVE(); + return ret; +} diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_11h.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_11h.h new file mode 100644 index 000000000000..6028853af0df --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_11h.h @@ -0,0 +1,189 @@ +/** @file mlan_11h.h + * + * @brief This header file contains data structures and + * function declarations of 802.11h + * + * Copyright (C) 2008-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +/************************************************************* +Change Log: + 03/26/2009: initial creation +*************************************************************/ + +#ifndef _MLAN_11H_ +#define _MLAN_11H_ + +/** 11H OID bitmasks */ +#define ENABLE_11H_MASK MBIT(0) +#define MASTER_RADAR_DET_MASK MBIT(1) +#define SLAVE_RADAR_DET_MASK MBIT(2) + +/** DFS Master Radar Detect global enable */ +#define DFS_MASTER_RADAR_DETECT_EN (MTRUE) +/** DFS Slave Radar Detect global enable */ +#define DFS_SLAVE_RADAR_DETECT_EN (MFALSE) + +#define CHANNEL_OFFSET_MASK 0x30 +#define CHANNEL_BANDWIDTH_MASK 0x0C + +/** + * 11H APIs + */ + +/* Is master radar detection enabled in firmware? */ +extern t_bool wlan_11h_is_master_radar_det_active(mlan_private *priv); + +/** Configure master radar detection. + * Need call wlan_11h_check_update_radar_det_state() after. + */ +extern mlan_status wlan_11h_config_master_radar_det(mlan_private *priv, + t_bool enable); + +/** Configure slave radar detection. + * Need call wlan_11h_check_update_radar_det_state() after. + */ +extern mlan_status wlan_11h_config_slave_radar_det(mlan_private *priv, + t_bool enable); + +/** Checks all interfaces and updates radar detect flags if necessary */ +extern mlan_status wlan_11h_check_update_radar_det_state(mlan_private *pmpriv); + +/** Return 1 if 11h is active in the firmware, 0 if it is inactive */ +extern t_bool wlan_11h_is_active(mlan_private *priv); + +/** Enable the tx interface and record the new transmit state */ +extern void wlan_11h_tx_enable(mlan_private *priv); + +/** Disable the tx interface and record the new transmit state */ +extern void wlan_11h_tx_disable(mlan_private *priv); + +/** Activate 11h extensions in the firmware */ +extern mlan_status wlan_11h_activate(mlan_private *priv, t_void *pioctl_buf, + t_bool flag); + +/** Initialize the 11h device structure */ +extern void wlan_11h_init(mlan_adapter *pmadapter); + +/** Cleanup for the 11h device structure */ +extern void wlan_11h_cleanup(mlan_adapter *pmadapter); + +/** Initialize the 11h interface structure */ +extern void wlan_11h_priv_init(mlan_private *pmpriv); + +/** Get an initial random channel to start an adhoc network on */ +extern t_u8 wlan_11h_get_adhoc_start_channel(mlan_private *priv); + +/** Get channel that has been closed via Channel Switch Announcement */ +extern t_u8 wlan_11h_get_csa_closed_channel(mlan_private *priv); + +/** Check if radar detection is required on the specified channel */ +extern t_bool wlan_11h_radar_detect_required(mlan_private *priv, t_u8 channel); + +/** Perform a standard availibility check on the specified channel */ +extern t_s32 wlan_11h_issue_radar_detect(mlan_private *priv, + pmlan_ioctl_req pioctl_req, + t_u8 channel, Band_Config_t bandcfg); + +/** Check previously issued radar report for a channel */ +extern mlan_status wlan_11h_check_chan_report(mlan_private *priv, t_u8 chan); + +/** Add any 11h TLVs necessary to complete an adhoc start command */ +extern t_s32 wlan_11h_process_start(mlan_private *priv, + t_u8 **ppbuffer, + IEEEtypes_CapInfo_t *pcap_info, + t_u32 channel, + wlan_11h_bss_info_t *p11h_bss_info); + +/** Add any 11h TLVs necessary to complete a join command (adhoc or infra) */ +extern t_s32 wlan_11h_process_join(mlan_private *priv, + t_u8 **ppbuffer, + IEEEtypes_CapInfo_t *pcap_info, + t_u8 band, + t_u32 channel, + wlan_11h_bss_info_t *p11h_bss_info); + +/** Complete the firmware command preparation for an 11h command function */ +extern mlan_status wlan_11h_cmd_process(mlan_private *priv, + HostCmd_DS_COMMAND *pcmd_ptr, + const t_void *pinfo_buf); + +/** Process the response of an 11h firmware command */ +extern mlan_status wlan_11h_cmdresp_process(mlan_private *priv, + const HostCmd_DS_COMMAND *resp); + +/** Receive IEs from scan processing and record any needed info for 11h */ +extern mlan_status wlan_11h_process_bss_elem(mlan_adapter *pmadapter, + wlan_11h_bss_info_t *p11h_bss_info, + const t_u8 *pelement); + +/** Handler for EVENT_CHANNEL_SWITCH_ANN */ +extern mlan_status wlan_11h_handle_event_chanswann(mlan_private *priv); + +/** Handler for EVENT_CHANNEL_REPORT_RDY */ +extern mlan_status wlan_11h_handle_event_chanrpt_ready(mlan_private *priv, + mlan_event *pevent); + +#ifdef DFS_TESTING_SUPPORT +/** Handler for DFS_TESTING IOCTL */ +extern mlan_status wlan_11h_ioctl_dfs_testing(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); +extern mlan_status wlan_11h_ioctl_get_channel_nop_info(pmlan_adapter pmadapter, + pmlan_ioctl_req + pioctl_req); +#endif + +extern mlan_status + +wlan_11h_ioctl_dfs_cancel_chan_report(mlan_private *priv, + pmlan_ioctl_req pioctl_req); +extern +mlan_status wlan_11h_ioctl_chan_switch_count(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); + +/** Check if channel is under a NOP duration (should not be used) */ +extern t_bool wlan_11h_is_channel_under_nop(mlan_adapter *pmadapter, + t_u8 channel); + +/** Check if RADAR_DETECTED handling is blocking data tx */ +extern t_bool wlan_11h_radar_detected_tx_blocked(mlan_adapter *pmadapter); + +/** Callback for RADAR_DETECTED (for UAP cmdresp) */ +extern mlan_status wlan_11h_radar_detected_callback(t_void *priv); + +/** BW_change event Handler for dfs_repeater */ +void wlan_dfs_rep_bw_change(mlan_adapter *pmadapter); + +/** disconnect event Handler for dfs_repeater */ +void wlan_dfs_rep_disconnect(mlan_adapter *pmadapter); + +/** Handler for RADAR_DETECTED */ +extern mlan_status wlan_11h_radar_detected_handling(mlan_adapter *pmadapter, + mlan_private *priv); +/** DFS Event pre-processing */ +extern mlan_status wlan_11h_dfs_event_preprocessing(mlan_adapter *pmadapter); + +/** DFS switch to non-DFS channel */ +extern mlan_status wlan_11h_switch_non_dfs_chan(mlan_private *priv, t_u8 *chan); + +extern void wlan_11h_update_bandcfg(IN Band_Config_t *uap_band_cfg, + IN t_u8 new_channel); + +/** function checks if interface is active. **/ +extern t_bool wlan_is_intf_active(mlan_private *pmpriv); + +#endif /*_MLAN_11H_ */ diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_11n.c b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_11n.c new file mode 100644 index 000000000000..d204c201114b --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_11n.c @@ -0,0 +1,3151 @@ +/** @file mlan_11n.c + * + * @brief This file contains functions for 11n handling. + * + * Copyright (C) 2008-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +/******************************************************** +Change log: + 11/10/2008: initial version +********************************************************/ + +#include "mlan.h" +#include "mlan_join.h" +#include "mlan_util.h" +#include "mlan_fw.h" +#include "mlan_main.h" +#include "mlan_wmm.h" +#include "mlan_11n.h" +/******************************************************** + Local Variables +********************************************************/ + +/******************************************************** + Global Variables +********************************************************/ + +/******************************************************** + Local Functions +********************************************************/ + +/** + * + * @brief set/get max tx buf size + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +static mlan_status +wlan_11n_ioctl_max_tx_buf_size(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_11n_cfg *cfg = MNULL; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + + ENTER(); + cfg = (mlan_ds_11n_cfg *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_SET) { + if (cfg->param.tx_buf_size == 0xffff) { + PRINTM(MIOCTL, "Send reconfigure tx buf to FW\n"); + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_RECONFIGURE_TX_BUFF, + HostCmd_ACT_GEN_SET, 0, + (t_void *)pioctl_req, + &cfg->param.tx_buf_size); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + LEAVE(); + return ret; + } + } + cfg->param.tx_buf_size = (t_u32)pmadapter->max_tx_buf_size; + pioctl_req->data_read_written = sizeof(t_u32) + MLAN_SUB_COMMAND_SIZE; + + LEAVE(); + return ret; +} + +/** + * @brief Set/get htcapinfo configuration + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +static mlan_status +wlan_11n_ioctl_htusrcfg(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_11n_cfg *cfg = MNULL; + + ENTER(); + + cfg = (mlan_ds_11n_cfg *)pioctl_req->pbuf; + + if (pioctl_req->action == MLAN_ACT_SET) { + if (((cfg->param.htcap_cfg.htcap & ~IGN_HW_DEV_CAP) & + pmpriv->adapter->hw_dot_11n_dev_cap) + != (cfg->param.htcap_cfg.htcap & ~IGN_HW_DEV_CAP)) { + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_FAILURE; + } else { + if (cfg->param.htcap_cfg.misc_cfg == BAND_SELECT_BG) { + pmpriv->usr_dot_11n_dev_cap_bg = + cfg->param.htcap_cfg.htcap; + PRINTM(MINFO, + "Set: UsrDot11nCap for 2.4GHz 0x%x\n", + pmpriv->usr_dot_11n_dev_cap_bg); + } + if (cfg->param.htcap_cfg.misc_cfg == BAND_SELECT_A) { + pmpriv->usr_dot_11n_dev_cap_a = + cfg->param.htcap_cfg.htcap; + PRINTM(MINFO, + "Set: UsrDot11nCap for 5GHz 0x%x\n", + pmpriv->usr_dot_11n_dev_cap_a); + } + if (cfg->param.htcap_cfg.misc_cfg == BAND_SELECT_BOTH) { + pmpriv->usr_dot_11n_dev_cap_bg = + cfg->param.htcap_cfg.htcap; + pmpriv->usr_dot_11n_dev_cap_a = + cfg->param.htcap_cfg.htcap; + PRINTM(MINFO, + "Set: UsrDot11nCap for 2.4GHz and 5GHz 0x%x\n", + cfg->param.htcap_cfg.htcap); + } + } + } else { + /* Hardware 11N device capability required */ + if (cfg->param.htcap_cfg.hw_cap_req) + cfg->param.htcap_cfg.htcap = + pmadapter->hw_dot_11n_dev_cap; + else { + if (cfg->param.htcap_cfg.misc_cfg == BAND_SELECT_BG) { + cfg->param.htcap_cfg.htcap = + pmpriv->usr_dot_11n_dev_cap_bg; + PRINTM(MINFO, + "Get: UsrDot11nCap for 2.4GHz 0x%x\n", + cfg->param.htcap_cfg.htcap); + } + if (cfg->param.htcap_cfg.misc_cfg == BAND_SELECT_A) { + cfg->param.htcap_cfg.htcap = + pmpriv->usr_dot_11n_dev_cap_a; + PRINTM(MINFO, + "Get: UsrDot11nCap for 5GHz 0x%x\n", + cfg->param.htcap_cfg.htcap); + } + } + } + + LEAVE(); + return ret; +} + +/** + * @brief Enable/Disable AMSDU AGGR CTRL + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status +wlan_11n_ioctl_amsdu_aggr_ctrl(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_11n_cfg *cfg = MNULL; + t_u16 cmd_action = 0; + + ENTER(); + + cfg = (mlan_ds_11n_cfg *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_SET) + cmd_action = HostCmd_ACT_GEN_SET; + else + cmd_action = HostCmd_ACT_GEN_GET; + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_AMSDU_AGGR_CTRL, + cmd_action, + 0, + (t_void *)pioctl_req, + (t_void *)&cfg->param.amsdu_aggr_ctrl); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Set/get 11n configuration + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status +wlan_11n_ioctl_httxcfg(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_11n_cfg *cfg = MNULL; + t_u16 cmd_action = 0; + + ENTER(); + + cfg = (mlan_ds_11n_cfg *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_SET) + cmd_action = HostCmd_ACT_GEN_SET; + else + cmd_action = HostCmd_ACT_GEN_GET; + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_11N_CFG, + cmd_action, + 0, + (t_void *)pioctl_req, + (t_void *)&cfg->param.tx_cfg); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Set/get TX beamforming capabilities + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success + */ +static mlan_status +wlan_11n_ioctl_tx_bf_cap(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_11n_cfg *cfg = MNULL; + + ENTER(); + + cfg = (mlan_ds_11n_cfg *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_SET) + pmpriv->tx_bf_cap = cfg->param.tx_bf_cap; + else + cfg->param.tx_bf_cap = pmpriv->tx_bf_cap; + + LEAVE(); + return ret; +} + +/** + * @brief Set/get TX beamforming configurations + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status +wlan_11n_ioctl_tx_bf_cfg(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_11n_cfg *cfg = MNULL; + t_u16 cmd_action = 0; + + ENTER(); + + cfg = (mlan_ds_11n_cfg *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_SET) + cmd_action = HostCmd_ACT_GEN_SET; + else + cmd_action = HostCmd_ACT_GEN_GET; + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_TX_BF_CFG, + cmd_action, + 0, + (t_void *)pioctl_req, + (t_void *)&cfg->param.tx_bf); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Set/get control to coex RX window size configuration + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +static mlan_status +wlan_11n_ioctl_coex_rx_winsize(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_11n_cfg *cfg = MNULL; + + ENTER(); + + cfg = (mlan_ds_11n_cfg *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_GET) + cfg->param.coex_rx_winsize = pmadapter->coex_rx_winsize; + else if (pioctl_req->action == MLAN_ACT_SET) + pmadapter->coex_rx_winsize = (t_u8)cfg->param.coex_rx_winsize; + + LEAVE(); + return ret; +} + +/** + * @brief This function will send delba request to + * the peer in the TxBAStreamTbl + * + * @param priv A pointer to mlan_private + * @param ra MAC Address to send DELBA + * + * @return N/A + */ +void +wlan_11n_send_delba_to_peer(mlan_private *priv, t_u8 *ra) +{ + + TxBAStreamTbl *ptx_tbl; + + ENTER(); + wlan_request_ralist_lock(priv); + ptx_tbl = (TxBAStreamTbl *)util_peek_list(priv->adapter->pmoal_handle, + &priv->tx_ba_stream_tbl_ptr, + MNULL, MNULL); + if (!ptx_tbl) { + wlan_release_ralist_lock(priv); + LEAVE(); + return; + } + + while (ptx_tbl != (TxBAStreamTbl *)&priv->tx_ba_stream_tbl_ptr) { + if (!memcmp + (priv->adapter, ptx_tbl->ra, ra, MLAN_MAC_ADDR_LENGTH)) { + PRINTM(MIOCTL, "Tx:Send delba to tid=%d, " MACSTR "\n", + ptx_tbl->tid, MAC2STR(ptx_tbl->ra)); + wlan_send_delba(priv, MNULL, ptx_tbl->tid, ptx_tbl->ra, + 1); + } + ptx_tbl = ptx_tbl->pnext; + } + wlan_release_ralist_lock(priv); + /* Signal MOAL to trigger mlan_main_process */ + wlan_recv_event(priv, MLAN_EVENT_ID_DRV_DEFER_HANDLING, MNULL); + LEAVE(); + return; +} + +/** + * @brief Set/Get control to TX AMPDU configuration on infra link + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +static mlan_status +wlan_11n_ioctl_txaggrctrl(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_11n_cfg *cfg = MNULL; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + + ENTER(); + + cfg = (mlan_ds_11n_cfg *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_GET) + cfg->param.txaggrctrl = pmpriv->txaggrctrl; + else if (pioctl_req->action == MLAN_ACT_SET) + pmpriv->txaggrctrl = (t_u8)cfg->param.txaggrctrl; + + if (pmpriv->media_connected == MTRUE) { + if (pioctl_req->action == MLAN_ACT_SET + && !pmpriv->txaggrctrl + && pmpriv->adapter->tdls_status != TDLS_NOT_SETUP) + wlan_11n_send_delba_to_peer(pmpriv, + pmpriv->curr_bss_params. + bss_descriptor.mac_address); + } + LEAVE(); + return ret; +} + +/** + * @brief This function will resend addba request to all + * the peer in the TxBAStreamTbl + * + * @param priv A pointer to mlan_private + * + * @return N/A + */ +static void +wlan_11n_update_addba_request(mlan_private *priv) +{ + + TxBAStreamTbl *ptx_tbl; + + ENTER(); + + wlan_request_ralist_lock(priv); + ptx_tbl = (TxBAStreamTbl *)util_peek_list(priv->adapter->pmoal_handle, + &priv->tx_ba_stream_tbl_ptr, + MNULL, MNULL); + if (!ptx_tbl) { + wlan_release_ralist_lock(priv); + LEAVE(); + return; + } + + while (ptx_tbl != (TxBAStreamTbl *)&priv->tx_ba_stream_tbl_ptr) { + wlan_send_addba(priv, ptx_tbl->tid, ptx_tbl->ra); + ptx_tbl = ptx_tbl->pnext; + } + wlan_release_ralist_lock(priv); + /* Signal MOAL to trigger mlan_main_process */ + wlan_recv_event(priv, MLAN_EVENT_ID_DRV_DEFER_HANDLING, MNULL); + LEAVE(); + return; +} + +/** + * @brief Set/get addba parameter + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success + */ +static mlan_status +wlan_11n_ioctl_addba_param(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_11n_cfg *cfg = MNULL; + t_u32 timeout; + + ENTER(); + + cfg = (mlan_ds_11n_cfg *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_GET) { + cfg->param.addba_param.timeout = pmpriv->add_ba_param.timeout; + cfg->param.addba_param.txwinsize = + pmpriv->add_ba_param.tx_win_size; + cfg->param.addba_param.rxwinsize = + pmpriv->add_ba_param.rx_win_size; + cfg->param.addba_param.txamsdu = pmpriv->add_ba_param.tx_amsdu; + cfg->param.addba_param.rxamsdu = pmpriv->add_ba_param.rx_amsdu; + } else { + timeout = pmpriv->add_ba_param.timeout; + pmpriv->add_ba_param.timeout = cfg->param.addba_param.timeout; + pmpriv->add_ba_param.tx_win_size = + cfg->param.addba_param.txwinsize; + + pmpriv->add_ba_param.rx_win_size = + cfg->param.addba_param.rxwinsize; + pmpriv->user_rxwinsize = pmpriv->add_ba_param.rx_win_size; + pmpriv->add_ba_param.tx_amsdu = cfg->param.addba_param.txamsdu; + pmpriv->add_ba_param.rx_amsdu = cfg->param.addba_param.rxamsdu; + if (timeout != pmpriv->add_ba_param.timeout) + wlan_11n_update_addba_request(pmpriv); + } + + LEAVE(); + return ret; +} + +/** + * @brief This function send delba to specific tid + * + * @param priv A pointer to mlan_priv + * @param tid tid + * @return N/A + */ +void +wlan_11n_delba(mlan_private *priv, int tid) +{ + RxReorderTbl *rx_reor_tbl_ptr; + + ENTER(); + + rx_reor_tbl_ptr = + (RxReorderTbl *)util_peek_list(priv->adapter->pmoal_handle, + &priv->rx_reorder_tbl_ptr, + priv->adapter->callbacks. + moal_spin_lock, + priv->adapter->callbacks. + moal_spin_unlock); + if (!rx_reor_tbl_ptr) { + LEAVE(); + return; + } + + while (rx_reor_tbl_ptr != (RxReorderTbl *)&priv->rx_reorder_tbl_ptr) { + if (rx_reor_tbl_ptr->tid == tid) { + PRINTM(MIOCTL, "Send delba to tid=%d, " MACSTR "\n", + tid, MAC2STR(rx_reor_tbl_ptr->ta)); + wlan_send_delba(priv, MNULL, tid, rx_reor_tbl_ptr->ta, + 0); + LEAVE(); + return; + } + rx_reor_tbl_ptr = rx_reor_tbl_ptr->pnext; + } + + LEAVE(); + return; +} + +/** + * @brief Set/get addba reject set + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +static mlan_status +wlan_11n_ioctl_addba_reject(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + int i = 0; + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_11n_cfg *cfg = MNULL; + + ENTER(); + + cfg = (mlan_ds_11n_cfg *)pioctl_req->pbuf; + + if (pioctl_req->action == MLAN_ACT_GET) { + PRINTM(MINFO, "Get Addba reject\n"); + memcpy(pmadapter, cfg->param.addba_reject, pmpriv->addba_reject, + MAX_NUM_TID); + } else { + for (i = 0; i < MAX_NUM_TID; i++) { + /* For AMPDU */ + if (cfg->param.addba_reject[i] > + ADDBA_RSP_STATUS_REJECT) { + pioctl_req->status_code = + MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_FAILURE; + break; + } + + pmpriv->addba_reject[i] = cfg->param.addba_reject[i]; + } + if (pmpriv->media_connected == MTRUE) { + for (i = 0; i < MAX_NUM_TID; i++) { + if (cfg->param.addba_reject[i] == + ADDBA_RSP_STATUS_REJECT) { + PRINTM(MIOCTL, + "Receive addba reject: tid=%d\n", + i); + wlan_11n_delba(pmpriv, i); + } + } + wlan_recv_event(pmpriv, + MLAN_EVENT_ID_DRV_DEFER_HANDLING, + MNULL); + } + } + + LEAVE(); + return ret; +} + +/** + * @brief Set/get ibss ampdu param + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +static mlan_status +wlan_11n_ioctl_ibss_ampdu_param(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + int i = 0; + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_11n_cfg *cfg = MNULL; + + ENTER(); + + cfg = (mlan_ds_11n_cfg *)pioctl_req->pbuf; + + if (pioctl_req->action == MLAN_ACT_GET) { + PRINTM(MINFO, "Get IBSS AMPDU param\n"); + for (i = 0; i < MAX_NUM_TID; i++) { + cfg->param.ibss_ampdu.ampdu[i] = pmpriv->ibss_ampdu[i]; + cfg->param.ibss_ampdu.addba_reject[i] = + pmpriv->ibss_addba_reject[i]; + } + } else { + for (i = 0; i < MAX_NUM_TID; i++) { + /* For AMPDU RX */ + if (cfg->param.ibss_ampdu.addba_reject[i] > + ADDBA_RSP_STATUS_REJECT) { + pioctl_req->status_code = + MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_FAILURE; + break; + } + pmpriv->ibss_addba_reject[i] = + cfg->param.ibss_ampdu.addba_reject[i]; + /* For AMPDU TX */ + if ((cfg->param.ibss_ampdu.ampdu[i] > HIGH_PRIO_TID) && + (cfg->param.ibss_ampdu.ampdu[i] != + BA_STREAM_NOT_ALLOWED)) { + pioctl_req->status_code = + MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_FAILURE; + break; + } + pmpriv->ibss_ampdu[i] = cfg->param.ibss_ampdu.ampdu[i]; + } + PRINTM(MMSG, "IBSS addba reject: %d %d %d %d %d %d %d %d\n", + pmpriv->ibss_addba_reject[0], + pmpriv->ibss_addba_reject[1], + pmpriv->ibss_addba_reject[2], + pmpriv->ibss_addba_reject[3], + pmpriv->ibss_addba_reject[4], + pmpriv->ibss_addba_reject[5], + pmpriv->ibss_addba_reject[6], + pmpriv->ibss_addba_reject[7]); + PRINTM(MMSG, "IBSS ampdu %d %d %d %d %d %d %d %d\n", + pmpriv->ibss_ampdu[0], pmpriv->ibss_ampdu[1], + pmpriv->ibss_ampdu[2], pmpriv->ibss_ampdu[3], + pmpriv->ibss_ampdu[4], pmpriv->ibss_ampdu[5], + pmpriv->ibss_ampdu[6], pmpriv->ibss_ampdu[7]); + } + LEAVE(); + return ret; +} + +/** + * @brief This function will send DELBA to entries in the priv's + * Tx BA stream table + * + * @param priv A pointer to mlan_private + * @param pioctl_req A pointer to ioctl request buffer + * @param tid TID + * @param peer_address A pointer to peer address + * @param last_tx_ba_to_delete A pointer to the last entry in TxBAStreamTbl + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_PENDING + */ +static mlan_status +wlan_send_delba_to_entry_in_txbastream_tbl(pmlan_private priv, + pmlan_ioctl_req pioctl_req, t_u8 tid, + t_u8 *peer_address, + TxBAStreamTbl *last_tx_ba_to_delete) +{ + pmlan_adapter pmadapter = priv->adapter; + TxBAStreamTbl *tx_ba_stream_tbl_ptr; + t_u8 zero_mac[MLAN_MAC_ADDR_LENGTH] = { 0 }; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + wlan_request_ralist_lock(priv); + tx_ba_stream_tbl_ptr = + (TxBAStreamTbl *)util_peek_list(pmadapter->pmoal_handle, + &priv->tx_ba_stream_tbl_ptr, + MNULL, MNULL); + if (!tx_ba_stream_tbl_ptr) { + wlan_release_ralist_lock(priv); + LEAVE(); + return ret; + } + + while (tx_ba_stream_tbl_ptr != + (TxBAStreamTbl *)&priv->tx_ba_stream_tbl_ptr) { + if (tx_ba_stream_tbl_ptr->ba_status == BA_STREAM_SETUP_COMPLETE) { + if (((tid == DELBA_ALL_TIDS) || + (tid == tx_ba_stream_tbl_ptr->tid)) && + (!memcmp + (pmadapter, peer_address, zero_mac, + MLAN_MAC_ADDR_LENGTH) || + !memcmp(pmadapter, peer_address, + tx_ba_stream_tbl_ptr->ra, + MLAN_MAC_ADDR_LENGTH))) { + if (last_tx_ba_to_delete && + (tx_ba_stream_tbl_ptr == + last_tx_ba_to_delete)) + ret = wlan_send_delba(priv, pioctl_req, + tx_ba_stream_tbl_ptr-> + tid, + tx_ba_stream_tbl_ptr-> + ra, 1); + else + ret = wlan_send_delba(priv, MNULL, + tx_ba_stream_tbl_ptr-> + tid, + tx_ba_stream_tbl_ptr-> + ra, 1); + } + } + tx_ba_stream_tbl_ptr = tx_ba_stream_tbl_ptr->pnext; + } + wlan_release_ralist_lock(priv); + + LEAVE(); + return ret; +} + +/** + * @brief This function will send DELBA to entries in the priv's + * rx reordering table + * + * @param priv A pointer to mlan_private + * @param pioctl_req A pointer to ioctl request buffer + * @param tid TID + * @param peer_address A pointer to peer address + * @param last_rx_ba_to_delete A pointer to the last entry in RxReorderTbl + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_PENDING + */ +static mlan_status +wlan_send_delba_to_entry_in_reorder_tbl(pmlan_private priv, + pmlan_ioctl_req pioctl_req, t_u8 tid, + t_u8 *peer_address, + RxReorderTbl *last_rx_ba_to_delete) +{ + pmlan_adapter pmadapter = priv->adapter; + RxReorderTbl *rx_reor_tbl_ptr; + t_u8 zero_mac[MLAN_MAC_ADDR_LENGTH] = { 0 }; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + rx_reor_tbl_ptr = + (RxReorderTbl *)util_peek_list(pmadapter->pmoal_handle, + &priv->rx_reorder_tbl_ptr, + pmadapter->callbacks. + moal_spin_lock, + pmadapter->callbacks. + moal_spin_unlock); + if (!rx_reor_tbl_ptr) { + LEAVE(); + return ret; + } + + while (rx_reor_tbl_ptr != (RxReorderTbl *)&priv->rx_reorder_tbl_ptr) { + if (rx_reor_tbl_ptr->ba_status == BA_STREAM_SETUP_COMPLETE) { + if (((tid == DELBA_ALL_TIDS) || + (tid == rx_reor_tbl_ptr->tid)) && + (!memcmp + (pmadapter, peer_address, zero_mac, + MLAN_MAC_ADDR_LENGTH) || + !memcmp(pmadapter, peer_address, + rx_reor_tbl_ptr->ta, + MLAN_MAC_ADDR_LENGTH))) { + if (last_rx_ba_to_delete && + (rx_reor_tbl_ptr == last_rx_ba_to_delete)) + ret = wlan_send_delba(priv, pioctl_req, + rx_reor_tbl_ptr-> + tid, + rx_reor_tbl_ptr-> + ta, 0); + else + ret = wlan_send_delba(priv, MNULL, + rx_reor_tbl_ptr-> + tid, + rx_reor_tbl_ptr-> + ta, 0); + } + } + rx_reor_tbl_ptr = rx_reor_tbl_ptr->pnext; + } + + LEAVE(); + return ret; +} + +/** + * @brief IOCTL to delete BA + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +static mlan_status +wlan_11n_ioctl_delba(IN pmlan_adapter pmadapter, IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_11n_cfg *cfg = MNULL; + TxBAStreamTbl *tx_ba_stream_tbl_ptr, *last_tx_ba_to_delete = MNULL; + RxReorderTbl *rx_reor_tbl_ptr, *last_rx_ba_to_delete = MNULL; + t_u8 zero_mac[MLAN_MAC_ADDR_LENGTH] = { 0 }; + t_u8 tid, *peer_address; + + ENTER(); + + cfg = (mlan_ds_11n_cfg *)pioctl_req->pbuf; + tid = cfg->param.del_ba.tid; + peer_address = cfg->param.del_ba.peer_mac_addr; + + PRINTM(MINFO, "DelBA: direction %d, TID %d, peer address " MACSTR "\n", + cfg->param.del_ba.direction, tid, MAC2STR(peer_address)); + + if (cfg->param.del_ba.direction & DELBA_RX) { + rx_reor_tbl_ptr = + (RxReorderTbl *)util_peek_list(pmadapter->pmoal_handle, + &pmpriv-> + rx_reorder_tbl_ptr, + pmadapter->callbacks. + moal_spin_lock, + pmadapter->callbacks. + moal_spin_unlock); + + if (rx_reor_tbl_ptr) { + while (rx_reor_tbl_ptr != + (RxReorderTbl *)&pmpriv->rx_reorder_tbl_ptr) { + if (rx_reor_tbl_ptr->ba_status == + BA_STREAM_SETUP_COMPLETE) { + if (((tid == DELBA_ALL_TIDS) || + (tid == rx_reor_tbl_ptr->tid)) && + (!memcmp + (pmadapter, peer_address, zero_mac, + MLAN_MAC_ADDR_LENGTH) || + !memcmp(pmadapter, peer_address, + rx_reor_tbl_ptr->ta, + MLAN_MAC_ADDR_LENGTH))) { + /* Found RX BA to delete */ + last_rx_ba_to_delete = + rx_reor_tbl_ptr; + } + } + rx_reor_tbl_ptr = rx_reor_tbl_ptr->pnext; + } + } + } + + if ((last_rx_ba_to_delete == MNULL) && + (cfg->param.del_ba.direction & DELBA_TX)) { + wlan_request_ralist_lock(pmpriv); + tx_ba_stream_tbl_ptr = + (TxBAStreamTbl *)util_peek_list(pmadapter->pmoal_handle, + &pmpriv-> + tx_ba_stream_tbl_ptr, + MNULL, MNULL); + + if (tx_ba_stream_tbl_ptr) { + while (tx_ba_stream_tbl_ptr != + (TxBAStreamTbl *)&pmpriv->tx_ba_stream_tbl_ptr) { + if (tx_ba_stream_tbl_ptr->ba_status == + BA_STREAM_SETUP_COMPLETE) { + if (((tid == DELBA_ALL_TIDS) || + (tid == tx_ba_stream_tbl_ptr->tid)) + && + (!memcmp + (pmadapter, peer_address, zero_mac, + MLAN_MAC_ADDR_LENGTH) || + !memcmp(pmadapter, peer_address, + tx_ba_stream_tbl_ptr->ra, + MLAN_MAC_ADDR_LENGTH))) { + /* Found TX BA to delete */ + last_tx_ba_to_delete = + tx_ba_stream_tbl_ptr; + } + } + tx_ba_stream_tbl_ptr = + tx_ba_stream_tbl_ptr->pnext; + } + } + wlan_release_ralist_lock(pmpriv); + } + + if (cfg->param.del_ba.direction & DELBA_TX) { + if (last_rx_ba_to_delete) + ret = wlan_send_delba_to_entry_in_txbastream_tbl(pmpriv, + MNULL, + tid, + peer_address, + MNULL); + else + ret = wlan_send_delba_to_entry_in_txbastream_tbl(pmpriv, + pioctl_req, + tid, + peer_address, + last_tx_ba_to_delete); + } + if (last_rx_ba_to_delete) { + ret = wlan_send_delba_to_entry_in_reorder_tbl(pmpriv, + pioctl_req, tid, + peer_address, + last_rx_ba_to_delete); + } + + LEAVE(); + return ret; +} + +/** + * @brief IOCTL to reject addba req + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +static mlan_status +wlan_11n_ioctl_rejectaddbareq(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_11n_cfg *cfg = MNULL; + t_u16 cmd_action = 0; + + ENTER(); + + cfg = (mlan_ds_11n_cfg *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_SET) + cmd_action = HostCmd_ACT_GEN_SET; + else + cmd_action = HostCmd_ACT_GEN_GET; + + /* Send command to firmware */ + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_REJECT_ADDBA_REQ, + cmd_action, + 0, + (t_void *)pioctl_req, + &cfg->param.reject_addba_req); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief This function will send DELBA to entries in the priv's + * Tx BA stream table + * + * @param priv A pointer to mlan_private + * @param tid TID + * + * @return N/A + */ +static void +wlan_send_delba_txbastream_tbl(pmlan_private priv, t_u8 tid) +{ + pmlan_adapter pmadapter = priv->adapter; + TxBAStreamTbl *tx_ba_stream_tbl_ptr; + + ENTER(); + + wlan_request_ralist_lock(priv); + tx_ba_stream_tbl_ptr = + (TxBAStreamTbl *)util_peek_list(pmadapter->pmoal_handle, + &priv->tx_ba_stream_tbl_ptr, + MNULL, MNULL); + if (!tx_ba_stream_tbl_ptr) { + wlan_release_ralist_lock(priv); + LEAVE(); + return; + } + + while (tx_ba_stream_tbl_ptr != + (TxBAStreamTbl *)&priv->tx_ba_stream_tbl_ptr) { + if (tx_ba_stream_tbl_ptr->ba_status == BA_STREAM_SETUP_COMPLETE) { + if (tid == tx_ba_stream_tbl_ptr->tid) { + PRINTM(MIOCTL, + "Tx:Send delba to tid=%d, " MACSTR "\n", + tid, MAC2STR(tx_ba_stream_tbl_ptr->ra)); + wlan_release_ralist_lock(priv); + wlan_send_delba(priv, MNULL, + tx_ba_stream_tbl_ptr->tid, + tx_ba_stream_tbl_ptr->ra, 1); + LEAVE(); + return; + } + } + tx_ba_stream_tbl_ptr = tx_ba_stream_tbl_ptr->pnext; + } + wlan_release_ralist_lock(priv); + + LEAVE(); + return; +} + +/** + * @brief update station list for the new aggr_prio_tbl setting + * + * @param priv A pointer to mlan_private structure + * + * + * @return N/A + */ +void +wlan_update_all_stations_ampdu(mlan_private *priv) +{ + sta_node *sta_ptr; + mlan_adapter *pmadapter = priv->adapter; + int i = 0; + + ENTER(); + pmadapter->callbacks.moal_spin_lock(pmadapter->pmoal_handle, + priv->wmm.ra_list_spinlock); + sta_ptr = (sta_node *)util_peek_list(pmadapter->pmoal_handle, + &priv->sta_list, MNULL, MNULL); + if (!sta_ptr) { + pmadapter->callbacks.moal_spin_unlock(pmadapter->pmoal_handle, + priv->wmm. + ra_list_spinlock); + LEAVE(); + return; + } + while (sta_ptr != (sta_node *)&priv->sta_list) { + for (i = 0; i < MAX_NUM_TID; i++) { + if (sta_ptr->is_11n_enabled) + sta_ptr->ampdu_sta[i] = + priv->aggr_prio_tbl[i].ampdu_user; + } + sta_ptr = sta_ptr->pnext; + } + pmadapter->callbacks.moal_spin_unlock(pmadapter->pmoal_handle, + priv->wmm.ra_list_spinlock); + LEAVE(); + return; +} + +/** + * @brief Set/get aggr_prio_tbl + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +static mlan_status +wlan_11n_ioctl_aggr_prio_tbl(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + int i = 0; + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_11n_cfg *cfg = MNULL; + + ENTER(); + + cfg = (mlan_ds_11n_cfg *)pioctl_req->pbuf; + + if (pioctl_req->action == MLAN_ACT_GET) { + for (i = 0; i < MAX_NUM_TID; i++) { + cfg->param.aggr_prio_tbl.ampdu[i] = + pmpriv->aggr_prio_tbl[i].ampdu_user; + cfg->param.aggr_prio_tbl.amsdu[i] = + pmpriv->aggr_prio_tbl[i].amsdu; + } + } else { + for (i = 0; i < MAX_NUM_TID; i++) { + /* For AMPDU */ + if ((cfg->param.aggr_prio_tbl.ampdu[i] > + HIGH_PRIO_TID)&&(cfg->param.aggr_prio_tbl. + ampdu[i] != + BA_STREAM_NOT_ALLOWED)) { + pioctl_req->status_code = + MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_FAILURE; + break; + } + + pmpriv->aggr_prio_tbl[i].ampdu_ap = + pmpriv->aggr_prio_tbl[i].ampdu_user = + cfg->param.aggr_prio_tbl.ampdu[i]; + + /* For AMSDU */ + if ((cfg->param.aggr_prio_tbl.amsdu[i] > HIGH_PRIO_TID + && cfg->param.aggr_prio_tbl.amsdu[i] != + BA_STREAM_NOT_ALLOWED)) { + pioctl_req->status_code = + MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_FAILURE; + break; + } else { + pmpriv->aggr_prio_tbl[i].amsdu = + cfg->param.aggr_prio_tbl.amsdu[i]; + } + } + if (pmpriv->media_connected == MTRUE) { + for (i = 0; i < MAX_NUM_TID; i++) { + if (cfg->param.aggr_prio_tbl.ampdu[i] == + BA_STREAM_NOT_ALLOWED) { + PRINTM(MIOCTL, + "Receive aggrpriotbl: BA not allowed tid=%d\n", + i); + wlan_send_delba_txbastream_tbl(pmpriv, + i); + } + } + wlan_update_all_stations_ampdu(pmpriv); + wlan_recv_event(pmpriv, + MLAN_EVENT_ID_DRV_DEFER_HANDLING, + MNULL); + } + } + + LEAVE(); + return ret; +} + +/** + * @brief This function update all the tx_win_size + * + * @param pmadapter A pointer to mlan_adapter + * + * + * @return N/A + */ +void +wlan_update_ampdu_txwinsize(pmlan_adapter pmadapter) +{ + t_u8 i; + t_u32 tx_win_size = 0; + pmlan_private priv = MNULL; + + ENTER(); + + for (i = 0; i < pmadapter->priv_num; i++) { + if (pmadapter->priv[i]) { + priv = pmadapter->priv[i]; + tx_win_size = priv->add_ba_param.tx_win_size; +#ifdef STA_SUPPORT + if (priv->bss_type == MLAN_BSS_TYPE_STA) + priv->add_ba_param.tx_win_size = + MLAN_STA_AMPDU_DEF_TXWINSIZE; +#endif +#ifdef WIFI_DIRECT_SUPPORT + if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT) + priv->add_ba_param.tx_win_size = + MLAN_WFD_AMPDU_DEF_TXRXWINSIZE; +#endif +#ifdef UAP_SUPPORT + if (priv->bss_type == MLAN_BSS_TYPE_UAP) + priv->add_ba_param.tx_win_size = + MLAN_UAP_AMPDU_DEF_TXWINSIZE; +#endif + if (pmadapter->coex_win_size && + pmadapter->coex_tx_win_size) + priv->add_ba_param.tx_win_size = + pmadapter->coex_tx_win_size; + + if (tx_win_size != priv->add_ba_param.tx_win_size) { + if (priv->media_connected == MTRUE) { + for (i = 0; i < MAX_NUM_TID; i++) + wlan_send_delba_txbastream_tbl + (priv, i); + wlan_recv_event(priv, + MLAN_EVENT_ID_DRV_DEFER_HANDLING, + MNULL); + } + } + } + } + LEAVE(); + return; +} + +/** + * @brief Get supported MCS set + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +static mlan_status +wlan_11n_ioctl_supported_mcs_set(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_ds_11n_cfg *cfg = MNULL; + int rx_mcs_supp; + t_u8 mcs_set[NUM_MCS_FIELD]; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + + ENTER(); + + if (pioctl_req->action == MLAN_ACT_SET) { + PRINTM(MERROR, "Set operation is not supported\n"); + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + LEAVE(); + return MLAN_STATUS_FAILURE; + } + rx_mcs_supp = GET_RXMCSSUPP(pmpriv->usr_dev_mcs_support); + /* Set MCS for 1x1/2x2 */ + memset(pmadapter, (t_u8 *)mcs_set, 0xff, rx_mcs_supp); + /* Clear all the other values */ + memset(pmadapter, (t_u8 *)&mcs_set[rx_mcs_supp], 0, + NUM_MCS_FIELD - rx_mcs_supp); + /* Set MCS32 with 40MHz support */ + if ((ISSUPP_CHANWIDTH40(pmpriv->usr_dot_11n_dev_cap_bg) + || ISSUPP_CHANWIDTH40(pmpriv->usr_dot_11n_dev_cap_a) + ) && !(pmpriv->curr_chan_flags & CHAN_FLAGS_NO_HT40PLUS + && pmpriv->curr_chan_flags & CHAN_FLAGS_NO_HT40MINUS)) + SETHT_MCS32(mcs_set); + + cfg = (mlan_ds_11n_cfg *)pioctl_req->pbuf; + memcpy(pmadapter, cfg->param.supported_mcs_set, mcs_set, NUM_MCS_FIELD); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function checks if the given pointer is valid entry of + * Tx BA Stream table + * + * @param priv Pointer to mlan_private + * @param ptxtblptr Pointer to tx ba stream entry + * + * @return MTRUE or MFALSE + */ +static int +wlan_is_txbastreamptr_valid(mlan_private *priv, TxBAStreamTbl *ptxtblptr) +{ + TxBAStreamTbl *ptx_tbl; + + ENTER(); + + ptx_tbl = (TxBAStreamTbl *)util_peek_list(priv->adapter->pmoal_handle, + &priv->tx_ba_stream_tbl_ptr, + MNULL, MNULL); + if (!ptx_tbl) { + LEAVE(); + return MFALSE; + } + + while (ptx_tbl != (TxBAStreamTbl *)&priv->tx_ba_stream_tbl_ptr) { + if (ptx_tbl == ptxtblptr) { + LEAVE(); + return MTRUE; + } + ptx_tbl = ptx_tbl->pnext; + } + LEAVE(); + return MFALSE; +} + +/** + * @brief This function will return the pointer to a entry in BA Stream + * table which matches the ba_status requested + * + * @param priv A pointer to mlan_private + * @param ba_status Current status of the BA stream + * + * @return A pointer to first entry matching status in BA stream + * NULL if not found + */ +static TxBAStreamTbl * +wlan_11n_get_txbastream_status(mlan_private *priv, baStatus_e ba_status) +{ + TxBAStreamTbl *ptx_tbl; + + ENTER(); + + ptx_tbl = (TxBAStreamTbl *)util_peek_list(priv->adapter->pmoal_handle, + &priv->tx_ba_stream_tbl_ptr, + MNULL, MNULL); + if (!ptx_tbl) { + LEAVE(); + return MNULL; + } + + while (ptx_tbl != (TxBAStreamTbl *)&priv->tx_ba_stream_tbl_ptr) { + if (ptx_tbl->ba_status == ba_status) { + LEAVE(); + return ptx_tbl; + } + ptx_tbl = ptx_tbl->pnext; + } + + LEAVE(); + return MNULL; +} + +/******************************************************** + Global Functions +********************************************************/ + +#ifdef STA_SUPPORT +/** + * @brief This function fills the cap info + * + * @param priv A pointer to mlan_private structure + * @param pht_cap A pointer to MrvlIETypes_HTCap_t structure + * @param bands Band configuration + * + * @return N/A + */ +static void +wlan_fill_cap_info(mlan_private *priv, HTCap_t *ht_cap, t_u8 bands) +{ + t_u32 usr_dot_11n_dev_cap; + + ENTER(); + + if (bands & BAND_A) + usr_dot_11n_dev_cap = priv->usr_dot_11n_dev_cap_a; + else + usr_dot_11n_dev_cap = priv->usr_dot_11n_dev_cap_bg; + + if (ISSUPP_CHANWIDTH40(usr_dot_11n_dev_cap)) + SETHT_SUPPCHANWIDTH(ht_cap->ht_cap_info); + else + RESETHT_SUPPCHANWIDTH(ht_cap->ht_cap_info); + + if (ISSUPP_GREENFIELD(usr_dot_11n_dev_cap)) + SETHT_GREENFIELD(ht_cap->ht_cap_info); + else + RESETHT_GREENFIELD(ht_cap->ht_cap_info); + + if (ISSUPP_SHORTGI20(usr_dot_11n_dev_cap)) + SETHT_SHORTGI20(ht_cap->ht_cap_info); + else + RESETHT_SHORTGI20(ht_cap->ht_cap_info); + + if (ISSUPP_SHORTGI40(usr_dot_11n_dev_cap)) + SETHT_SHORTGI40(ht_cap->ht_cap_info); + else + RESETHT_SHORTGI40(ht_cap->ht_cap_info); + if (ISSUPP_RXSTBC(usr_dot_11n_dev_cap)) + SETHT_RXSTBC(ht_cap->ht_cap_info, 1); + else + RESETHT_RXSTBC(ht_cap->ht_cap_info); + + if (ISENABLED_40MHZ_INTOLARENT(usr_dot_11n_dev_cap)) + SETHT_40MHZ_INTOLARANT(ht_cap->ht_cap_info); + else + RESETHT_40MHZ_INTOLARANT(ht_cap->ht_cap_info); + + /** if current channel only allow 20Mhz, we should cler 40Mhz support */ + if (priv->curr_chan_flags & CHAN_FLAGS_NO_HT40PLUS && + priv->curr_chan_flags & CHAN_FLAGS_NO_HT40MINUS) { + RESETHT_SUPPCHANWIDTH(ht_cap->ht_cap_info); + RESETHT_SHORTGI40(ht_cap->ht_cap_info); + RESETHT_40MHZ_INTOLARANT(ht_cap->ht_cap_info); + } + /* No user config for LDPC coding capability yet */ + if (ISSUPP_RXLDPC(usr_dot_11n_dev_cap)) + SETHT_LDPCCODINGCAP(ht_cap->ht_cap_info); + else + RESETHT_LDPCCODINGCAP(ht_cap->ht_cap_info); + + /* No user config for TX STBC yet */ + if (ISSUPP_TXSTBC(usr_dot_11n_dev_cap)) + SETHT_TXSTBC(ht_cap->ht_cap_info); + else + RESETHT_TXSTBC(ht_cap->ht_cap_info); + + /* No user config for Delayed BACK yet */ + RESETHT_DELAYEDBACK(ht_cap->ht_cap_info); + + /* Need change to support 8k AMSDU receive */ + RESETHT_MAXAMSDU(ht_cap->ht_cap_info); + /* SM power save */ + if (ISSUPP_MIMOPS(priv->adapter->hw_dot_11n_dev_cap)) + RESETHT_SM_POWERSAVE(ht_cap->ht_cap_info); /* Enable HT SMPS */ + else + SETHT_STATIC_SMPS(ht_cap->ht_cap_info); /* Disable HT SMPS */ + + LEAVE(); +} + +/** + * @brief This function clear the bit in cap info which we don't support + * + * @param priv A pointer to mlan_private structure + * @param pht_cap A pointer to MrvlIETypes_HTCap_t structure + * @param bands Band configuration + * + * @return N/A + */ +static void +wlan_reset_cap_info(mlan_private *priv, HTCap_t *ht_cap, t_u8 bands) +{ + t_u32 usr_dot_11n_dev_cap; + + ENTER(); + + if (bands & BAND_A) + usr_dot_11n_dev_cap = priv->usr_dot_11n_dev_cap_a; + else + usr_dot_11n_dev_cap = priv->usr_dot_11n_dev_cap_bg; + + if (!ISSUPP_CHANWIDTH40(usr_dot_11n_dev_cap)) + RESETHT_SUPPCHANWIDTH(ht_cap->ht_cap_info); + + if (!ISSUPP_GREENFIELD(usr_dot_11n_dev_cap)) + RESETHT_GREENFIELD(ht_cap->ht_cap_info); + + if (!ISSUPP_SHORTGI20(usr_dot_11n_dev_cap)) + RESETHT_SHORTGI20(ht_cap->ht_cap_info); + + if (!ISSUPP_SHORTGI40(usr_dot_11n_dev_cap)) + RESETHT_SHORTGI40(ht_cap->ht_cap_info); + if (!ISSUPP_RXSTBC(usr_dot_11n_dev_cap)) + RESETHT_RXSTBC(ht_cap->ht_cap_info); + + if (!ISENABLED_40MHZ_INTOLARENT(usr_dot_11n_dev_cap)) + RESETHT_40MHZ_INTOLARANT(ht_cap->ht_cap_info); + + /** if current channel only allow 20Mhz, we should cler 40Mhz support */ + if (priv->curr_chan_flags & CHAN_FLAGS_NO_HT40PLUS && + priv->curr_chan_flags & CHAN_FLAGS_NO_HT40MINUS) { + RESETHT_SUPPCHANWIDTH(ht_cap->ht_cap_info); + RESETHT_SHORTGI40(ht_cap->ht_cap_info); + RESETHT_40MHZ_INTOLARANT(ht_cap->ht_cap_info); + } + /* No user config for LDPC coding capability yet */ + if (!ISSUPP_RXLDPC(usr_dot_11n_dev_cap)) + RESETHT_LDPCCODINGCAP(ht_cap->ht_cap_info); + + /* No user config for TX STBC yet */ + if (!ISSUPP_TXSTBC(usr_dot_11n_dev_cap)) + RESETHT_TXSTBC(ht_cap->ht_cap_info); + + /* No user config for Delayed BACK yet */ + RESETHT_DELAYEDBACK(ht_cap->ht_cap_info); + + /* Need change to support 8k AMSDU receive */ + RESETHT_MAXAMSDU(ht_cap->ht_cap_info); + /* SM power save */ + if (!ISSUPP_MIMOPS(priv->adapter->hw_dot_11n_dev_cap)) + SETHT_STATIC_SMPS(ht_cap->ht_cap_info); /* Disable HT SMPS */ + + LEAVE(); +} + +/** + * @brief This function fills the HT cap tlv + * + * @param priv A pointer to mlan_private structure + * @param pht_cap A pointer to MrvlIETypes_HTCap_t structure + * @param bands Band configuration + * @param fill A flag for fill the htcap info + * + * @return N/A + */ +void +wlan_fill_ht_cap_tlv(mlan_private *priv, + MrvlIETypes_HTCap_t *pht_cap, t_u8 bands, t_u8 fill) +{ + mlan_adapter *pmadapter = priv->adapter; + int rx_mcs_supp; + t_u32 usr_dot_11n_dev_cap; + + ENTER(); + + if (bands & BAND_A) + usr_dot_11n_dev_cap = priv->usr_dot_11n_dev_cap_a; + else + usr_dot_11n_dev_cap = priv->usr_dot_11n_dev_cap_bg; + + /* Fill HT cap info */ + if (fill) + wlan_fill_cap_info(priv, &pht_cap->ht_cap, bands); + else + wlan_reset_cap_info(priv, &pht_cap->ht_cap, bands); + + pht_cap->ht_cap.ht_cap_info = + wlan_cpu_to_le16(pht_cap->ht_cap.ht_cap_info); + + /* Set ampdu param */ + SETAMPDU_SIZE(pht_cap->ht_cap.ampdu_param, AMPDU_FACTOR_64K); + SETAMPDU_SPACING(pht_cap->ht_cap.ampdu_param, 0); + + rx_mcs_supp = GET_RXMCSSUPP(priv->usr_dev_mcs_support); + /* Clear all the other values to get the minimum mcs set btw STA and AP */ + memset(pmadapter, + (t_u8 *)&pht_cap->ht_cap.supported_mcs_set[rx_mcs_supp], 0, + NUM_MCS_FIELD - rx_mcs_supp); + /* Set MCS32 with 40MHz support */ + /* if current channel only support 20MHz, we should not set 40Mz supprot */ + if (ISSUPP_CHANWIDTH40(usr_dot_11n_dev_cap) && + !(priv->curr_chan_flags & CHAN_FLAGS_NO_HT40PLUS + && priv->curr_chan_flags & CHAN_FLAGS_NO_HT40MINUS)) + SETHT_MCS32(pht_cap->ht_cap.supported_mcs_set); + + /* Clear RD responder bit */ + RESETHT_EXTCAP_RDG(pht_cap->ht_cap.ht_ext_cap); + pht_cap->ht_cap.ht_ext_cap = + wlan_cpu_to_le16(pht_cap->ht_cap.ht_ext_cap); + + /* Set Tx BF cap */ + pht_cap->ht_cap.tx_bf_cap = wlan_cpu_to_le32(priv->tx_bf_cap); + + LEAVE(); + return; +} + +/** + * @brief This function fills the HT cap ie + * + * @param priv A pointer to mlan_private structure + * @param pht_cap A pointer to IEEEtypes_HTCap_t structure + * @param bands Band configuration + * + * @return N/A + */ +void +wlan_fill_ht_cap_ie(mlan_private *priv, IEEEtypes_HTCap_t *pht_cap, t_u8 bands) +{ + mlan_adapter *pmadapter = priv->adapter; + int rx_mcs_supp; + t_u32 usr_dot_11n_dev_cap; + + ENTER(); + + pht_cap->ieee_hdr.element_id = HT_CAPABILITY; + pht_cap->ieee_hdr.len = sizeof(HTCap_t); + if (bands & BAND_A) + usr_dot_11n_dev_cap = priv->usr_dot_11n_dev_cap_a; + else + usr_dot_11n_dev_cap = priv->usr_dot_11n_dev_cap_bg; + + /* Fill HT cap info */ + wlan_fill_cap_info(priv, &pht_cap->ht_cap, bands); + + /* Set ampdu param */ + SETAMPDU_SIZE(pht_cap->ht_cap.ampdu_param, AMPDU_FACTOR_64K); + SETAMPDU_SPACING(pht_cap->ht_cap.ampdu_param, 0); + + rx_mcs_supp = GET_RXMCSSUPP(priv->usr_dev_mcs_support); + memset(pmadapter, (t_u8 *)pht_cap->ht_cap.supported_mcs_set, 0xff, + rx_mcs_supp); + /* Clear all the other values to get the minimum mcs set btw STA and AP */ + memset(pmadapter, + (t_u8 *)&pht_cap->ht_cap.supported_mcs_set[rx_mcs_supp], 0, + NUM_MCS_FIELD - rx_mcs_supp); + /* Set MCS32 with 40MHz support */ + /* if current channel only support 20MHz, we should not set 40Mz supprot */ + if (ISSUPP_CHANWIDTH40(usr_dot_11n_dev_cap) && + !(priv->curr_chan_flags & CHAN_FLAGS_NO_HT40PLUS + && priv->curr_chan_flags & CHAN_FLAGS_NO_HT40MINUS)) + SETHT_MCS32(pht_cap->ht_cap.supported_mcs_set); + + /* Clear RD responder bit */ + RESETHT_EXTCAP_RDG(pht_cap->ht_cap.ht_ext_cap); + + /* Set Tx BF cap */ + pht_cap->ht_cap.tx_bf_cap = priv->tx_bf_cap; + + LEAVE(); + return; +} +#endif /* STA_SUPPORT */ + +/** + * @brief This function prints the 802.11n device capability + * + * @param pmadapter A pointer to mlan_adapter structure + * @param cap Capability value + * + * @return N/A + */ +void +wlan_show_dot11ndevcap(pmlan_adapter pmadapter, t_u32 cap) +{ + ENTER(); + + PRINTM(MINFO, "GET_HW_SPEC: Maximum MSDU length = %s octets\n", + (ISSUPP_MAXAMSDU(cap) ? "7935" : "3839")); + PRINTM(MINFO, "GET_HW_SPEC: Beam forming %s\n", + (ISSUPP_BEAMFORMING(cap) ? "supported" : "not supported")); + PRINTM(MINFO, "GET_HW_SPEC: Greenfield preamble %s\n", + (ISSUPP_GREENFIELD(cap) ? "supported" : "not supported")); + PRINTM(MINFO, "GET_HW_SPEC: AMPDU %s\n", + (ISSUPP_AMPDU(cap) ? "supported" : "not supported")); + PRINTM(MINFO, "GET_HW_SPEC: MIMO Power Save %s\n", + (ISSUPP_MIMOPS(cap) ? "supported" : "not supported")); + PRINTM(MINFO, "GET_HW_SPEC: Rx STBC %s\n", + (ISSUPP_RXSTBC(cap) ? "supported" : "not supported")); + PRINTM(MINFO, "GET_HW_SPEC: Tx STBC %s\n", + (ISSUPP_TXSTBC(cap) ? "supported" : "not supported")); + PRINTM(MINFO, "GET_HW_SPEC: Short GI for 40 Mhz %s\n", + (ISSUPP_SHORTGI40(cap) ? "supported" : "not supported")); + PRINTM(MINFO, "GET_HW_SPEC: Short GI for 20 Mhz %s\n", + (ISSUPP_SHORTGI20(cap) ? "supported" : "not supported")); + PRINTM(MINFO, "GET_HW_SPEC: LDPC coded packet receive %s\n", + (ISSUPP_RXLDPC(cap) ? "supported" : "not supported")); + + PRINTM(MINFO, "GET_HW_SPEC: Number of Tx BA streams supported = %d\n", + ISSUPP_GETTXBASTREAM(cap)); + PRINTM(MINFO, "GET_HW_SPEC: 40 Mhz channel width %s\n", + (ISSUPP_CHANWIDTH40(cap) ? "supported" : "not supported")); + PRINTM(MINFO, "GET_HW_SPEC: 20 Mhz channel width %s\n", + (ISSUPP_CHANWIDTH20(cap) ? "supported" : "not supported")); + PRINTM(MINFO, "GET_HW_SPEC: 10 Mhz channel width %s\n", + (ISSUPP_CHANWIDTH10(cap) ? "supported" : "not supported")); + + if (ISSUPP_RXANTENNAA(cap)) + PRINTM(MINFO, "GET_HW_SPEC: Presence of Rx antenna A\n"); + if (ISSUPP_RXANTENNAB(cap)) + PRINTM(MINFO, "GET_HW_SPEC: Presence of Rx antenna B\n"); + if (ISSUPP_RXANTENNAC(cap)) + PRINTM(MINFO, "GET_HW_SPEC: Presence of Rx antenna C\n"); + if (ISSUPP_RXANTENNAD(cap)) + PRINTM(MINFO, "GET_HW_SPEC: Presence of Rx antenna D\n"); + if (ISSUPP_TXANTENNAA(cap)) + PRINTM(MINFO, "GET_HW_SPEC: Presence of Tx antenna A\n"); + if (ISSUPP_TXANTENNAB(cap)) + PRINTM(MINFO, "GET_HW_SPEC: Presence of Tx antenna B\n"); + if (ISSUPP_TXANTENNAC(cap)) + PRINTM(MINFO, "GET_HW_SPEC: Presence of Tx antenna C\n"); + if (ISSUPP_TXANTENNAD(cap)) + PRINTM(MINFO, "GET_HW_SPEC: Presence of Tx antenna D\n"); + + LEAVE(); + return; +} + +/** + * @brief This function prints the 802.11n device MCS + * + * @param pmadapter A pointer to mlan_adapter structure + * @param support Support value + * + * @return N/A + */ +void +wlan_show_devmcssupport(pmlan_adapter pmadapter, t_u8 support) +{ + ENTER(); + + PRINTM(MINFO, "GET_HW_SPEC: MCSs for %dx%d MIMO\n", + GET_RXMCSSUPP(support), GET_TXMCSSUPP(support)); + + LEAVE(); + return; +} + +/** + * @brief This function handles the command response of + * delete a block ack request + * + * @param priv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_ret_11n_delba(mlan_private *priv, HostCmd_DS_COMMAND *resp) +{ + int tid; + TxBAStreamTbl *ptx_ba_tbl; + HostCmd_DS_11N_DELBA *pdel_ba = + (HostCmd_DS_11N_DELBA *)&resp->params.del_ba; + + ENTER(); + + pdel_ba->del_ba_param_set = wlan_le16_to_cpu(pdel_ba->del_ba_param_set); + pdel_ba->reason_code = wlan_le16_to_cpu(pdel_ba->reason_code); + + tid = pdel_ba->del_ba_param_set >> DELBA_TID_POS; + if (pdel_ba->del_result == BA_RESULT_SUCCESS) { + mlan_11n_delete_bastream_tbl(priv, tid, pdel_ba->peer_mac_addr, + TYPE_DELBA_SENT, + INITIATOR_BIT(pdel_ba-> + del_ba_param_set), + 0); + wlan_request_ralist_lock(priv); + ptx_ba_tbl = wlan_11n_get_txbastream_status(priv, + BA_STREAM_SETUP_INPROGRESS); + wlan_release_ralist_lock(priv); + if (ptx_ba_tbl) + wlan_send_addba(priv, ptx_ba_tbl->tid, ptx_ba_tbl->ra); + } else { /* + * In case of failure, recreate + * the deleted stream in case + * we initiated the ADDBA + */ + if (INITIATOR_BIT(pdel_ba->del_ba_param_set)) { + wlan_request_ralist_lock(priv); + if (!wlan_11n_get_txbastream_tbl + (priv, tid, pdel_ba->peer_mac_addr, MFALSE)) + wlan_11n_create_txbastream_tbl(priv, + pdel_ba-> + peer_mac_addr, + tid, + BA_STREAM_SETUP_INPROGRESS); + ptx_ba_tbl = + wlan_11n_get_txbastream_status(priv, + BA_STREAM_SETUP_INPROGRESS); + wlan_release_ralist_lock(priv); + if (ptx_ba_tbl) { + mlan_11n_delete_bastream_tbl(priv, + ptx_ba_tbl->tid, + ptx_ba_tbl->ra, + TYPE_DELBA_SENT, + MTRUE, 0); + } + + } + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of + * add a block ack request + * + * @param priv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_ret_11n_addba_req(mlan_private *priv, HostCmd_DS_COMMAND *resp) +{ + t_u8 tid; + HostCmd_DS_11N_ADDBA_RSP *padd_ba_rsp = + (HostCmd_DS_11N_ADDBA_RSP *)&resp->params.add_ba_rsp; + TxBAStreamTbl *ptx_ba_tbl; + raListTbl *ra_list = MNULL; + int tid_down; + + ENTER(); + + padd_ba_rsp->block_ack_param_set = + wlan_le16_to_cpu(padd_ba_rsp->block_ack_param_set); + padd_ba_rsp->block_ack_tmo = + wlan_le16_to_cpu(padd_ba_rsp->block_ack_tmo); + padd_ba_rsp->ssn = (wlan_le16_to_cpu(padd_ba_rsp->ssn)) & SSN_MASK; + padd_ba_rsp->status_code = wlan_le16_to_cpu(padd_ba_rsp->status_code); + + tid = (padd_ba_rsp->block_ack_param_set & BLOCKACKPARAM_TID_MASK) + >> BLOCKACKPARAM_TID_POS; + tid_down = wlan_get_wmm_tid_down(priv, tid); + ra_list = + wlan_wmm_get_ralist_node(priv, tid_down, + padd_ba_rsp->peer_mac_addr); + if (padd_ba_rsp->status_code == BA_RESULT_SUCCESS) { + ptx_ba_tbl = wlan_11n_get_txbastream_tbl(priv, tid, + padd_ba_rsp-> + peer_mac_addr, MTRUE); + if (ptx_ba_tbl) { + PRINTM(MCMND, + "ADDBA REQ: " MACSTR + " tid=%d ssn=%d win_size=%d,amsdu=%d\n", + MAC2STR(padd_ba_rsp->peer_mac_addr), tid, + padd_ba_rsp->ssn, + ((padd_ba_rsp-> + block_ack_param_set & + BLOCKACKPARAM_WINSIZE_MASK) >> + BLOCKACKPARAM_WINSIZE_POS), + padd_ba_rsp-> + block_ack_param_set & + BLOCKACKPARAM_AMSDU_SUPP_MASK); + ptx_ba_tbl->ba_status = BA_STREAM_SETUP_COMPLETE; + if ((padd_ba_rsp-> + block_ack_param_set & + BLOCKACKPARAM_AMSDU_SUPP_MASK) && + priv->add_ba_param.tx_amsdu && + (priv->aggr_prio_tbl[tid].amsdu != + BA_STREAM_NOT_ALLOWED)) + ptx_ba_tbl->amsdu = MTRUE; + else + ptx_ba_tbl->amsdu = MFALSE; + if (ra_list) { + ra_list->amsdu_in_ampdu = ptx_ba_tbl->amsdu; + ra_list->ba_status = BA_STREAM_SETUP_COMPLETE; + } + } else { + PRINTM(MERROR, "BA stream not created\n"); + } + } else { + if (ra_list) { + ra_list->amsdu_in_ampdu = MFALSE; + ra_list->ba_status = BA_STREAM_NOT_SETUP; + + } + mlan_11n_delete_bastream_tbl(priv, tid, + padd_ba_rsp->peer_mac_addr, + TYPE_DELBA_SENT, MTRUE, 0); + if (padd_ba_rsp->add_rsp_result != BA_RESULT_TIMEOUT) { +#ifdef UAP_SUPPORT + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) + disable_station_ampdu(priv, tid, + padd_ba_rsp-> + peer_mac_addr); +#endif /* UAP_SUPPORT */ + if (priv->bss_mode == MLAN_BSS_MODE_IBSS) + disable_station_ampdu(priv, tid, + padd_ba_rsp-> + peer_mac_addr); + if (ra_list && ra_list->is_tdls_link) + disable_station_ampdu(priv, tid, + padd_ba_rsp-> + peer_mac_addr); + priv->aggr_prio_tbl[tid].ampdu_ap = + BA_STREAM_NOT_ALLOWED; + + } else { + + if (ra_list) { + ra_list->packet_count = 0; + ra_list->ba_packet_threshold = + wlan_get_random_ba_threshold(priv-> + adapter); + } + } + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function restore tx_pause flag + * + * @param priv A pointer to mlan_private structure + * @param flag MTRUE/MFALSE; + * + * @return N/A + */ +void +wlan_set_tx_pause_flag(mlan_private *priv, t_u8 flag) +{ + mlan_private *pmpriv = MNULL; + t_u8 i; + for (i = 0; i < priv->adapter->priv_num; i++) { + pmpriv = priv->adapter->priv[i]; + if (pmpriv) + pmpriv->tx_pause = flag; + } +} + +/** + * @brief This function prepares command of reconfigure tx buf + * + * @param priv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action The action: GET or SET + * @param pdata_buf A pointer to data buffer + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_cmd_recfg_tx_buf(mlan_private *priv, + HostCmd_DS_COMMAND *cmd, int cmd_action, void *pdata_buf) +{ + HostCmd_DS_TXBUF_CFG *ptx_buf = &cmd->params.tx_buf; + t_u16 action = (t_u16)cmd_action; + t_u16 buf_size = *((t_u16 *)pdata_buf); + + ENTER(); + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_RECONFIGURE_TX_BUFF); + cmd->size = wlan_cpu_to_le16(sizeof(HostCmd_DS_TXBUF_CFG) + + S_DS_GEN); + ptx_buf->action = wlan_cpu_to_le16(action); + switch (action) { + case HostCmd_ACT_GEN_SET: + PRINTM(MCMND, "set tx_buf = %d\n", buf_size); + ptx_buf->buff_size = wlan_cpu_to_le16(buf_size); + /** stop tx traffic */ + wlan_set_tx_pause_flag(priv, MTRUE); + break; + case HostCmd_ACT_GEN_GET: + default: + ptx_buf->buff_size = 0; + break; + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of amsdu aggr control + * + * @param priv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action The action: GET or SET + * @param pdata_buf A pointer to data buffer + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_cmd_amsdu_aggr_ctrl(mlan_private *priv, + HostCmd_DS_COMMAND *cmd, + int cmd_action, void *pdata_buf) +{ + HostCmd_DS_AMSDU_AGGR_CTRL *pamsdu_ctrl = &cmd->params.amsdu_aggr_ctrl; + t_u16 action = (t_u16)cmd_action; + mlan_ds_11n_amsdu_aggr_ctrl *aa_ctrl = (mlan_ds_11n_amsdu_aggr_ctrl *) + pdata_buf; + + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_AMSDU_AGGR_CTRL); + cmd->size = wlan_cpu_to_le16(sizeof(HostCmd_DS_AMSDU_AGGR_CTRL) + + S_DS_GEN); + pamsdu_ctrl->action = wlan_cpu_to_le16(action); + switch (action) { + case HostCmd_ACT_GEN_SET: + pamsdu_ctrl->enable = wlan_cpu_to_le16(aa_ctrl->enable); + pamsdu_ctrl->curr_buf_size = 0; + break; + case HostCmd_ACT_GEN_GET: + default: + pamsdu_ctrl->curr_buf_size = 0; + break; + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of amsdu aggr ctrl + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_ret_amsdu_aggr_ctrl(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf) +{ + mlan_ds_11n_cfg *cfg = MNULL; + HostCmd_DS_AMSDU_AGGR_CTRL *amsdu_ctrl = &resp->params.amsdu_aggr_ctrl; + + ENTER(); + + if (pioctl_buf) { + cfg = (mlan_ds_11n_cfg *)pioctl_buf->pbuf; + cfg->param.amsdu_aggr_ctrl.enable = + wlan_le16_to_cpu(amsdu_ctrl->enable); + cfg->param.amsdu_aggr_ctrl.curr_buf_size = + wlan_le16_to_cpu(amsdu_ctrl->curr_buf_size); + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares 11n cfg command + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action the action: GET or SET + * @param pdata_buf A pointer to data buffer + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_cmd_11n_cfg(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, IN t_void *pdata_buf) +{ + HostCmd_DS_11N_CFG *htcfg = &cmd->params.htcfg; + mlan_ds_11n_tx_cfg *txcfg = (mlan_ds_11n_tx_cfg *)pdata_buf; + + ENTER(); + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_11N_CFG); + cmd->size = wlan_cpu_to_le16(sizeof(HostCmd_DS_11N_CFG) + S_DS_GEN); + htcfg->action = wlan_cpu_to_le16(cmd_action); + htcfg->ht_tx_cap = wlan_cpu_to_le16(txcfg->httxcap); + htcfg->ht_tx_info = wlan_cpu_to_le16(txcfg->httxinfo); + htcfg->misc_config = wlan_cpu_to_le16(txcfg->misc_cfg); + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of 11ncfg + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_ret_11n_cfg(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, IN mlan_ioctl_req *pioctl_buf) +{ + mlan_ds_11n_cfg *cfg = MNULL; + HostCmd_DS_11N_CFG *htcfg = &resp->params.htcfg; + + ENTER(); + if (pioctl_buf && + (wlan_le16_to_cpu(htcfg->action) == HostCmd_ACT_GEN_GET)) { + cfg = (mlan_ds_11n_cfg *)pioctl_buf->pbuf; + cfg->param.tx_cfg.httxcap = wlan_le16_to_cpu(htcfg->ht_tx_cap); + cfg->param.tx_cfg.httxinfo = + wlan_le16_to_cpu(htcfg->ht_tx_info); + cfg->param.tx_cfg.misc_cfg = + wlan_le16_to_cpu(htcfg->misc_config); + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares reject addba req command + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action the action: GET or SET + * @param pdata_buf A pointer to data buffer + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_cmd_reject_addba_req(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, IN t_void *pdata_buf) +{ + HostCmd_DS_REJECT_ADDBA_REQ *preject_addba_req = + &cmd->params.rejectaddbareq; + mlan_ds_reject_addba_req *prejaddbareq = + (mlan_ds_reject_addba_req *)pdata_buf; + + ENTER(); + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_REJECT_ADDBA_REQ); + cmd->size = + wlan_cpu_to_le16(sizeof(HostCmd_DS_REJECT_ADDBA_REQ) + + S_DS_GEN); + preject_addba_req->action = wlan_cpu_to_le16(cmd_action); + preject_addba_req->conditions = + wlan_cpu_to_le32(prejaddbareq->conditions); + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of reject addba req + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_ret_reject_addba_req(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf) +{ + mlan_ds_11n_cfg *cfg = MNULL; + HostCmd_DS_REJECT_ADDBA_REQ *preject_addba_req = + &resp->params.rejectaddbareq; + + ENTER(); + if (pioctl_buf && + (wlan_le16_to_cpu(preject_addba_req->action) == + HostCmd_ACT_GEN_GET)) { + cfg = (mlan_ds_11n_cfg *)pioctl_buf->pbuf; + cfg->param.reject_addba_req.conditions = + wlan_le32_to_cpu(preject_addba_req->conditions); + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares TX BF configuration command + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action The action: GET or SET + * @param pdata_buf A pointer to data buffer + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_cmd_tx_bf_cfg(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, IN t_void *pdata_buf) +{ + pmlan_adapter pmadapter = pmpriv->adapter; + HostCmd_DS_TX_BF_CFG *txbfcfg = &cmd->params.tx_bf_cfg; + mlan_ds_11n_tx_bf_cfg *txbf = (mlan_ds_11n_tx_bf_cfg *)pdata_buf; + + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_TX_BF_CFG); + cmd->size = wlan_cpu_to_le16(sizeof(HostCmd_DS_TX_BF_CFG) + S_DS_GEN); + + if (txbf->bf_action == SET_GET_BF_PERIODICITY) { + memcpy(pmadapter, txbfcfg->body.bf_periodicity.peer_mac, + txbf->body.bf_periodicity[0].peer_mac, + MLAN_MAC_ADDR_LENGTH); + } + txbfcfg->action = wlan_cpu_to_le16(txbf->action); + txbfcfg->bf_action = wlan_cpu_to_le16(txbf->bf_action); + if (cmd_action == HostCmd_ACT_GEN_SET) { + switch (txbf->bf_action) { + case BF_GLOBAL_CONFIGURATION: + txbfcfg->body.bf_global_cfg.bf_enbl = + txbf->body.bf_global_cfg.bf_enbl; + txbfcfg->body.bf_global_cfg.sounding_enbl = + txbf->body.bf_global_cfg.sounding_enbl; + txbfcfg->body.bf_global_cfg.fb_type = + txbf->body.bf_global_cfg.fb_type; + txbfcfg->body.bf_global_cfg.snr_threshold = + txbf->body.bf_global_cfg.snr_threshold; + txbfcfg->body.bf_global_cfg.sounding_interval = + wlan_cpu_to_le16(txbf->body.bf_global_cfg. + sounding_interval); + txbfcfg->body.bf_global_cfg.bf_mode = + txbf->body.bf_global_cfg.bf_mode; + break; + case TRIGGER_SOUNDING_FOR_PEER: + memcpy(pmadapter, txbfcfg->body.bf_sound_args.peer_mac, + txbf->body.bf_sound[0].peer_mac, + MLAN_MAC_ADDR_LENGTH); + break; + case SET_GET_BF_PERIODICITY: + txbfcfg->body.bf_periodicity.interval = + wlan_cpu_to_le16(txbf->body.bf_periodicity-> + interval); + break; + case TX_BF_FOR_PEER_ENBL: + memcpy(pmadapter, txbfcfg->body.tx_bf_peer.peer_mac, + txbf->body.tx_bf_peer[0].peer_mac, + MLAN_MAC_ADDR_LENGTH); + txbfcfg->body.tx_bf_peer.bf_enbl = + txbf->body.tx_bf_peer[0].bf_enbl; + txbfcfg->body.tx_bf_peer.sounding_enbl = + txbf->body.tx_bf_peer[0].sounding_enbl; + txbfcfg->body.tx_bf_peer.fb_type = + txbf->body.tx_bf_peer[0].fb_type; + break; + case SET_SNR_THR_PEER: + memcpy(pmadapter, txbfcfg->body.bf_snr.peer_mac, + txbf->body.bf_snr[0].peer_mac, + MLAN_MAC_ADDR_LENGTH); + txbfcfg->body.bf_snr.snr = txbf->body.bf_snr[0].snr; + break; + default: + LEAVE(); + return MLAN_STATUS_FAILURE; + } + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response + * of TX BF configuration + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_ret_tx_bf_cfg(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, IN mlan_ioctl_req *pioctl_buf) +{ + pmlan_adapter pmadapter = pmpriv->adapter; + HostCmd_DS_TX_BF_CFG *txbfcfg = &resp->params.tx_bf_cfg; + mlan_ds_11n_cfg *cfg_11n = MNULL; + mlan_ds_11n_tx_bf_cfg *txbf = MNULL; + bf_peer_args *tx_bf_peer; + bf_snr_thr_t *bf_snr; + int i; + + ENTER(); + + if (pioctl_buf) { + cfg_11n = (mlan_ds_11n_cfg *)pioctl_buf->pbuf; + txbf = (mlan_ds_11n_tx_bf_cfg *)&cfg_11n->param.tx_bf; + txbf->bf_action = wlan_le16_to_cpu(txbfcfg->bf_action); + switch (txbf->bf_action) { + case BF_GLOBAL_CONFIGURATION: + txbf->body.bf_global_cfg.bf_enbl = + txbfcfg->body.bf_global_cfg.bf_enbl; + txbf->body.bf_global_cfg.sounding_enbl = + txbfcfg->body.bf_global_cfg.sounding_enbl; + txbf->body.bf_global_cfg.fb_type = + txbfcfg->body.bf_global_cfg.fb_type; + txbf->body.bf_global_cfg.snr_threshold = + txbfcfg->body.bf_global_cfg.snr_threshold; + txbf->body.bf_global_cfg.sounding_interval = + wlan_le16_to_cpu(txbfcfg->body.bf_global_cfg. + sounding_interval); + txbf->body.bf_global_cfg.bf_mode = + txbfcfg->body.bf_global_cfg.bf_mode; + break; + case TRIGGER_SOUNDING_FOR_PEER: + memcpy(pmadapter, txbf->body.bf_sound[0].peer_mac, + txbfcfg->body.bf_sound_args.peer_mac, + MLAN_MAC_ADDR_LENGTH); + txbf->body.bf_sound[0].status = + txbfcfg->body.bf_sound_args.status; + break; + case SET_GET_BF_PERIODICITY: + memcpy(pmadapter, txbf->body.bf_periodicity->peer_mac, + txbfcfg->body.bf_periodicity.peer_mac, + MLAN_MAC_ADDR_LENGTH); + txbf->body.bf_periodicity->interval = + wlan_le16_to_cpu(txbfcfg->body.bf_periodicity. + interval); + break; + case TX_BF_FOR_PEER_ENBL: + txbf->no_of_peers = *(t_u8 *)&txbfcfg->body; + tx_bf_peer = + (bf_peer_args *)((t_u8 *)&txbfcfg->body + + sizeof(t_u8)); + for (i = 0; i < txbf->no_of_peers; i++) { + memcpy(pmadapter, + txbf->body.tx_bf_peer[i].peer_mac, + (t_u8 *)tx_bf_peer->peer_mac, + MLAN_MAC_ADDR_LENGTH); + txbf->body.tx_bf_peer[i].bf_enbl = + tx_bf_peer->bf_enbl; + txbf->body.tx_bf_peer[i].sounding_enbl = + tx_bf_peer->sounding_enbl; + txbf->body.tx_bf_peer[i].fb_type = + tx_bf_peer->fb_type; + tx_bf_peer++; + } + break; + case SET_SNR_THR_PEER: + txbf->no_of_peers = *(t_u8 *)&txbfcfg->body; + bf_snr = (bf_snr_thr_t *)((t_u8 *)&txbfcfg->body + + sizeof(t_u8)); + for (i = 0; i < txbf->no_of_peers; i++) { + memcpy(pmadapter, txbf->body.bf_snr[i].peer_mac, + (t_u8 *)bf_snr->peer_mac, + MLAN_MAC_ADDR_LENGTH); + txbf->body.bf_snr[i].snr = bf_snr->snr; + bf_snr++; + } + break; + default: + LEAVE(); + return MLAN_STATUS_FAILURE; + } + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Get second channel offset + * + * @param chan channel num + * @return second channel offset + */ +t_u8 +wlan_get_second_channel_offset(int chan) +{ + t_u8 chan2Offset = SEC_CHAN_NONE; + + switch (chan) { + case 36: + case 44: + case 52: + case 60: + case 100: + case 108: + case 116: + case 124: + case 132: + case 149: + case 157: + chan2Offset = SEC_CHAN_ABOVE; + break; + case 40: + case 48: + case 56: + case 64: + case 104: + case 112: + case 120: + case 128: + case 136: + case 153: + case 161: + chan2Offset = SEC_CHAN_BELOW; + break; + case 165: + /* Special Case: 20Mhz-only Channel */ + chan2Offset = SEC_CHAN_NONE; + break; + } + return chan2Offset; +} + +#ifdef STA_SUPPORT +/** + * @brief validate the channel offset for Infra/Ad-hoc band configuration + * + * @param pmpriv A pointer to mlan_private structure + * @param band band + * @param chan primary channel + * @param chan_bw channel bandwidth + * + * @return channel offset (NO_SEC_CHANNEL, SEC_CHANNEL_ABOVE, + * SEC_CHANNEL_BELOW) + */ +t_u8 +wlan_validate_chan_offset(IN mlan_private *pmpriv, + IN t_u8 band, IN t_u32 chan, IN t_u8 chan_bw) +{ + t_u8 chan_offset; + pmlan_adapter pmadapter = pmpriv->adapter; + + if (chan_bw == CHANNEL_BW_40MHZ_ABOVE) + chan_offset = SEC_CHAN_ABOVE; + else if (chan_bw == CHANNEL_BW_40MHZ_BELOW) + chan_offset = SEC_CHAN_BELOW; + else + chan_offset = SEC_CHAN_NONE; + + /* validation */ + if (chan_offset != SEC_CHAN_NONE) { + if (band & BAND_GN) { + if ((chan == 1) || (chan == 2) || (chan == 3) || + (chan == 4)) + chan_offset = SEC_CHAN_ABOVE; + else if ((chan == 10) || (chan == 11) || + (chan == 12) || (chan == 13)) + chan_offset = SEC_CHAN_BELOW; + + /* check if channel 12 is supported in the region */ + if (!wlan_find_cfp_by_band_and_channel + (pmadapter, band, 12)) + if ((chan == 8) || (chan == 9)) + chan_offset = SEC_CHAN_BELOW; + } else if (band & BAND_AN) + chan_offset = wlan_get_second_channel_offset(chan); + } + return chan_offset; +} + +/** + * @brief This function check if ht40 is allowed in current region + * + * @param pmpriv A pointer to mlan_private structure + * @param pbss_desc A pointer to BSSDescriptor_t structure + * + * @return MTRUE/MFALSE + */ +static int +wlan_check_chan_width_ht40_by_region(IN mlan_private *pmpriv, + IN BSSDescriptor_t *pbss_desc) +{ + pmlan_adapter pmadapter = pmpriv->adapter; + int i = 0; + int cover_pri_chan = MFALSE; + t_u8 pri_chan; + t_u8 chan_offset; + t_u8 num_cfp; + + ENTER(); + + if (pbss_desc->pht_info == MNULL) { + PRINTM(MERROR, "ht_info pointer NULL, force use HT20\n"); + LEAVE(); + return MFALSE; + } + if (pmpriv->curr_chan_flags & CHAN_FLAGS_NO_HT40PLUS && + pmpriv->curr_chan_flags & CHAN_FLAGS_NO_HT40MINUS) { + LEAVE(); + return MFALSE; + } + + pri_chan = pbss_desc->pht_info->ht_info.pri_chan; + chan_offset = GET_SECONDARYCHAN(pbss_desc->pht_info->ht_info.field2); + if ((chan_offset == SEC_CHAN_ABOVE) && + (pmpriv->curr_chan_flags & CHAN_FLAGS_NO_HT40PLUS)) + return MFALSE; + if ((chan_offset == SEC_CHAN_BELOW) && + (pmpriv->curr_chan_flags & CHAN_FLAGS_NO_HT40MINUS)) + return MFALSE; + + num_cfp = pmadapter->region_channel[0].num_cfp; + + if ((pbss_desc->bss_band & (BAND_B | BAND_G)) && + pmadapter->region_channel && pmadapter->region_channel[0].valid) { + for (i = 0; i < num_cfp; i++) { + if (pri_chan == + pmadapter->region_channel[0].pcfp[i].channel) { + cover_pri_chan = MTRUE; + break; + } + } + if (!cover_pri_chan) { + PRINTM(MERROR, "Invalid channel, force use HT20\n"); + LEAVE(); + return MFALSE; + } + + if (chan_offset == SEC_CHAN_ABOVE) { + if (pri_chan > num_cfp - 4) { + PRINTM(MERROR, + "Invalid second channel offset, force use HT20\n"); + LEAVE(); + return MFALSE; + } + } + } + LEAVE(); + return MTRUE; +} + +/** + * @brief This function append the 802_11N tlv + * + * @param pmpriv A pointer to mlan_private structure + * @param pbss_desc A pointer to BSSDescriptor_t structure + * @param ppbuffer A Pointer to command buffer pointer + * + * @return bytes added to the buffer + */ +int +wlan_cmd_append_11n_tlv(IN mlan_private *pmpriv, + IN BSSDescriptor_t *pbss_desc, OUT t_u8 **ppbuffer) +{ + pmlan_adapter pmadapter = pmpriv->adapter; + MrvlIETypes_HTCap_t *pht_cap; + MrvlIETypes_HTInfo_t *pht_info; + MrvlIEtypes_ChanListParamSet_t *pchan_list; + MrvlIETypes_2040BSSCo_t *p2040_bss_co; + MrvlIETypes_ExtCap_t *pext_cap; + t_u32 usr_dot_11n_dev_cap, orig_usr_dot_11n_dev_cap = 0; + int ret_len = 0; + + ENTER(); + + /* Null Checks */ + if (ppbuffer == MNULL) { + LEAVE(); + return 0; + } + if (*ppbuffer == MNULL) { + LEAVE(); + return 0; + } + + if (pbss_desc->bss_band & BAND_A) + usr_dot_11n_dev_cap = pmpriv->usr_dot_11n_dev_cap_a; + else + usr_dot_11n_dev_cap = pmpriv->usr_dot_11n_dev_cap_bg; + + if ((pbss_desc->bss_band & (BAND_B | BAND_G)) && + ISSUPP_CHANWIDTH40(usr_dot_11n_dev_cap) && + !wlan_check_chan_width_ht40_by_region(pmpriv, pbss_desc)) { + orig_usr_dot_11n_dev_cap = usr_dot_11n_dev_cap; + RESETSUPP_CHANWIDTH40(usr_dot_11n_dev_cap); + RESET_40MHZ_INTOLARENT(usr_dot_11n_dev_cap); + RESETSUPP_SHORTGI40(usr_dot_11n_dev_cap); + pmpriv->usr_dot_11n_dev_cap_bg = usr_dot_11n_dev_cap; + pbss_desc->curr_bandwidth = BW_20MHZ; + } + if (pbss_desc->pht_cap) { + pht_cap = (MrvlIETypes_HTCap_t *)*ppbuffer; + memset(pmadapter, pht_cap, 0, sizeof(MrvlIETypes_HTCap_t)); + pht_cap->header.type = wlan_cpu_to_le16(HT_CAPABILITY); + pht_cap->header.len = sizeof(HTCap_t); + memcpy(pmadapter, (t_u8 *)pht_cap + sizeof(MrvlIEtypesHeader_t), + (t_u8 *)pbss_desc->pht_cap + sizeof(IEEEtypes_Header_t), + pht_cap->header.len); + + pht_cap->ht_cap.ht_cap_info = + wlan_le16_to_cpu(pht_cap->ht_cap.ht_cap_info); + pht_cap->ht_cap.ht_ext_cap = + wlan_le16_to_cpu(pht_cap->ht_cap.ht_ext_cap); + wlan_fill_ht_cap_tlv(pmpriv, pht_cap, pbss_desc->bss_band, + MTRUE); + + HEXDUMP("HT_CAPABILITIES IE", (t_u8 *)pht_cap, + sizeof(MrvlIETypes_HTCap_t)); + *ppbuffer += sizeof(MrvlIETypes_HTCap_t); + ret_len += sizeof(MrvlIETypes_HTCap_t); + pht_cap->header.len = wlan_cpu_to_le16(pht_cap->header.len); + } else { + //AP don't support 11N + LEAVE(); + return 0; + } + + if (pbss_desc->pht_info) { + if (pmpriv->bss_mode == MLAN_BSS_MODE_IBSS) { + pht_info = (MrvlIETypes_HTInfo_t *)*ppbuffer; + memset(pmadapter, pht_info, 0, + sizeof(MrvlIETypes_HTInfo_t)); + pht_info->header.type = wlan_cpu_to_le16(HT_OPERATION); + pht_info->header.len = sizeof(HTInfo_t); + + memcpy(pmadapter, + (t_u8 *)pht_info + sizeof(MrvlIEtypesHeader_t), + (t_u8 *)pbss_desc->pht_info + + sizeof(IEEEtypes_Header_t), + pht_info->header.len); + + if (!ISSUPP_CHANWIDTH40(usr_dot_11n_dev_cap)) + RESET_CHANWIDTH40(pht_info->ht_info.field2); + + *ppbuffer += sizeof(MrvlIETypes_HTInfo_t); + ret_len += sizeof(MrvlIETypes_HTInfo_t); + pht_info->header.len = + wlan_cpu_to_le16(pht_info->header.len); + } + + pchan_list = (MrvlIEtypes_ChanListParamSet_t *)*ppbuffer; + memset(pmadapter, pchan_list, 0, + sizeof(MrvlIEtypes_ChanListParamSet_t)); + pchan_list->header.type = wlan_cpu_to_le16(TLV_TYPE_CHANLIST); + pchan_list->header.len = + sizeof(MrvlIEtypes_ChanListParamSet_t) - + sizeof(MrvlIEtypesHeader_t); + pchan_list->chan_scan_param[0].chan_number = + pbss_desc->pht_info->ht_info.pri_chan; + pchan_list->chan_scan_param[0].bandcfg.chanBand = + wlan_band_to_radio_type((t_u8)pbss_desc->bss_band); + if (ISSUPP_CHANWIDTH40(usr_dot_11n_dev_cap) && + ISALLOWED_CHANWIDTH40(pbss_desc->pht_info->ht_info.field2) + && wlan_check_chan_width_ht40_by_region(pmpriv, + pbss_desc)) { + pchan_list->chan_scan_param[0].bandcfg.chan2Offset = + GET_SECONDARYCHAN(pbss_desc->pht_info->ht_info. + field2); + pbss_desc->curr_bandwidth = BW_40MHZ; + pchan_list->chan_scan_param[0].bandcfg.chanWidth = + CHAN_BW_40MHZ; + } + pchan_list->chan_scan_param[0].bandcfg.scanMode = + SCAN_MODE_USER; + HEXDUMP("ChanList", (t_u8 *)pchan_list, + sizeof(MrvlIEtypes_ChanListParamSet_t)); + HEXDUMP("pht_info", (t_u8 *)pbss_desc->pht_info, + sizeof(MrvlIETypes_HTInfo_t) - 2); + *ppbuffer += sizeof(MrvlIEtypes_ChanListParamSet_t); + ret_len += sizeof(MrvlIEtypes_ChanListParamSet_t); + pchan_list->header.len = + wlan_cpu_to_le16(pchan_list->header.len); + } + + if (pbss_desc->pbss_co_2040) { + p2040_bss_co = (MrvlIETypes_2040BSSCo_t *)*ppbuffer; + memset(pmadapter, p2040_bss_co, 0, + sizeof(MrvlIETypes_2040BSSCo_t)); + p2040_bss_co->header.type = wlan_cpu_to_le16(BSSCO_2040); + p2040_bss_co->header.len = sizeof(BSSCo2040_t); + + memcpy(pmadapter, + (t_u8 *)p2040_bss_co + sizeof(MrvlIEtypesHeader_t), + (t_u8 *)pbss_desc->pbss_co_2040 + + sizeof(IEEEtypes_Header_t), p2040_bss_co->header.len); + + HEXDUMP("20/40 BSS Coexistence IE", (t_u8 *)p2040_bss_co, + sizeof(MrvlIETypes_2040BSSCo_t)); + *ppbuffer += sizeof(MrvlIETypes_2040BSSCo_t); + ret_len += sizeof(MrvlIETypes_2040BSSCo_t); + p2040_bss_co->header.len = + wlan_cpu_to_le16(p2040_bss_co->header.len); + } + + if (pbss_desc->pext_cap) { + pext_cap = (MrvlIETypes_ExtCap_t *)*ppbuffer; + memset(pmadapter, pext_cap, 0, sizeof(MrvlIETypes_ExtCap_t)); + pext_cap->header.type = wlan_cpu_to_le16(EXT_CAPABILITY); + pext_cap->header.len = sizeof(ExtCap_t); + + memcpy(pmadapter, + (t_u8 *)pext_cap + sizeof(MrvlIEtypesHeader_t), + (t_u8 *)&pmpriv->ext_cap, sizeof(ExtCap_t)); + if (!pmadapter->ecsa_enable) + RESET_EXTCAP_EXT_CHANNEL_SWITCH(pext_cap->ext_cap); + else + SET_EXTCAP_EXT_CHANNEL_SWITCH(pext_cap->ext_cap); + + HEXDUMP("Extended Capabilities IE", (t_u8 *)pext_cap, + sizeof(MrvlIETypes_ExtCap_t)); + *ppbuffer += sizeof(MrvlIETypes_ExtCap_t); + ret_len += sizeof(MrvlIETypes_ExtCap_t); + pext_cap->header.len = wlan_cpu_to_le16(pext_cap->header.len); + } + PRINTM(MCMND, "curr_bandwidth=%d\n", pbss_desc->curr_bandwidth); + if (orig_usr_dot_11n_dev_cap) + pmpriv->usr_dot_11n_dev_cap_bg = orig_usr_dot_11n_dev_cap; + + LEAVE(); + return ret_len; +} + +#endif /* STA_SUPPORT */ + +/** + * @brief 11n configuration handler + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +mlan_status +wlan_11n_cfg_ioctl(IN pmlan_adapter pmadapter, IN pmlan_ioctl_req pioctl_req) +{ + mlan_status status = MLAN_STATUS_SUCCESS; + mlan_ds_11n_cfg *cfg = MNULL; + + ENTER(); + + if (pioctl_req->buf_len < sizeof(mlan_ds_11n_cfg)) { + PRINTM(MINFO, "MLAN bss IOCTL length is too short.\n"); + pioctl_req->data_read_written = 0; + pioctl_req->buf_len_needed = sizeof(mlan_ds_11n_cfg); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + LEAVE(); + return MLAN_STATUS_RESOURCE; + } + cfg = (mlan_ds_11n_cfg *)pioctl_req->pbuf; + switch (cfg->sub_command) { + case MLAN_OID_11N_CFG_TX: + status = wlan_11n_ioctl_httxcfg(pmadapter, pioctl_req); + break; + case MLAN_OID_11N_HTCAP_CFG: + status = wlan_11n_ioctl_htusrcfg(pmadapter, pioctl_req); + break; + case MLAN_OID_11N_CFG_AGGR_PRIO_TBL: + status = wlan_11n_ioctl_aggr_prio_tbl(pmadapter, pioctl_req); + break; + case MLAN_OID_11N_CFG_ADDBA_REJECT: + status = wlan_11n_ioctl_addba_reject(pmadapter, pioctl_req); + break; + case MLAN_OID_11N_CFG_ADDBA_PARAM: + status = wlan_11n_ioctl_addba_param(pmadapter, pioctl_req); + break; + case MLAN_OID_11N_CFG_DELBA: + status = wlan_11n_ioctl_delba(pmadapter, pioctl_req); + break; + case MLAN_OID_11N_CFG_REJECT_ADDBA_REQ: + status = wlan_11n_ioctl_rejectaddbareq(pmadapter, pioctl_req); + break; + case MLAN_OID_11N_CFG_MAX_TX_BUF_SIZE: + status = wlan_11n_ioctl_max_tx_buf_size(pmadapter, pioctl_req); + break; + case MLAN_OID_11N_CFG_AMSDU_AGGR_CTRL: + status = wlan_11n_ioctl_amsdu_aggr_ctrl(pmadapter, pioctl_req); + break; + case MLAN_OID_11N_CFG_SUPPORTED_MCS_SET: + status = wlan_11n_ioctl_supported_mcs_set(pmadapter, + pioctl_req); + break; + case MLAN_OID_11N_CFG_TX_BF_CAP: + status = wlan_11n_ioctl_tx_bf_cap(pmadapter, pioctl_req); + break; + case MLAN_OID_11N_CFG_TX_BF_CFG: + status = wlan_11n_ioctl_tx_bf_cfg(pmadapter, pioctl_req); + break; + case MLAN_OID_11N_CFG_COEX_RX_WINSIZE: + status = wlan_11n_ioctl_coex_rx_winsize(pmadapter, pioctl_req); + break; + case MLAN_OID_11N_CFG_TX_AGGR_CTRL: + status = wlan_11n_ioctl_txaggrctrl(pmadapter, pioctl_req); + break; + case MLAN_OID_11N_CFG_IBSS_AMPDU_PARAM: + status = wlan_11n_ioctl_ibss_ampdu_param(pmadapter, pioctl_req); + break; + default: + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + status = MLAN_STATUS_FAILURE; + break; + } + LEAVE(); + return status; +} + +/** + * @brief This function will delete the given entry in Tx BA Stream table + * + * @param priv Pointer to mlan_private + * @param ptx_tbl Pointer to tx ba stream entry to delete + * + * @return N/A + */ +void +wlan_11n_delete_txbastream_tbl_entry(mlan_private *priv, TxBAStreamTbl *ptx_tbl) +{ + pmlan_adapter pmadapter = priv->adapter; + + ENTER(); + + if (!ptx_tbl || !wlan_is_txbastreamptr_valid(priv, ptx_tbl)) + goto exit; + PRINTM(MINFO, "Delete BA stream table entry: %p\n", ptx_tbl); + util_unlink_list(pmadapter->pmoal_handle, &priv->tx_ba_stream_tbl_ptr, + (pmlan_linked_list)ptx_tbl, MNULL, MNULL); + pmadapter->callbacks.moal_mfree(pmadapter->pmoal_handle, + (t_u8 *)ptx_tbl); +exit: + LEAVE(); +} + +/** + * @brief This function will delete all the entries in Tx BA Stream table + * + * @param priv A pointer to mlan_private + * + * @return N/A + */ +void +wlan_11n_deleteall_txbastream_tbl(mlan_private *priv) +{ + int i; + TxBAStreamTbl *del_tbl_ptr = MNULL; + + ENTER(); + + wlan_request_ralist_lock(priv); + while ((del_tbl_ptr = (TxBAStreamTbl *) + util_peek_list(priv->adapter->pmoal_handle, + &priv->tx_ba_stream_tbl_ptr, MNULL, MNULL))) { + wlan_11n_delete_txbastream_tbl_entry(priv, del_tbl_ptr); + } + + util_init_list((pmlan_linked_list)&priv->tx_ba_stream_tbl_ptr); + wlan_release_ralist_lock(priv); + for (i = 0; i < MAX_NUM_TID; ++i) { + priv->aggr_prio_tbl[i].ampdu_ap = + priv->aggr_prio_tbl[i].ampdu_user; + } + + LEAVE(); +} + +/** + * @brief This function will return the pointer to an entry in BA Stream + * table which matches the give RA/TID pair + * + * @param priv A pointer to mlan_private + * @param tid TID to find in reordering table + * @param ra RA to find in reordering table + * @param lock flag for request the spin_lock + * + * @return A pointer to first entry matching RA/TID in BA stream + * NULL if not found + */ +TxBAStreamTbl * +wlan_11n_get_txbastream_tbl(mlan_private *priv, int tid, t_u8 *ra, int lock) +{ + TxBAStreamTbl *ptx_tbl; + pmlan_adapter pmadapter = priv->adapter; + + ENTER(); + + if (lock) + wlan_request_ralist_lock(priv); + ptx_tbl = (TxBAStreamTbl *)util_peek_list(pmadapter->pmoal_handle, + &priv->tx_ba_stream_tbl_ptr, + MNULL, MNULL); + if (!ptx_tbl) { + if (lock) + wlan_release_ralist_lock(priv); + LEAVE(); + return MNULL; + } + + while (ptx_tbl != (TxBAStreamTbl *)&priv->tx_ba_stream_tbl_ptr) { + + PRINTM(MDAT_D, "get_txbastream_tbl TID %d\n", ptx_tbl->tid); + DBG_HEXDUMP(MDAT_D, "RA", ptx_tbl->ra, MLAN_MAC_ADDR_LENGTH); + + if ((!memcmp(pmadapter, ptx_tbl->ra, ra, MLAN_MAC_ADDR_LENGTH)) + && (ptx_tbl->tid == tid)) { + if (lock) + wlan_release_ralist_lock(priv); + LEAVE(); + return ptx_tbl; + } + + ptx_tbl = ptx_tbl->pnext; + } + if (lock) + wlan_release_ralist_lock(priv); + LEAVE(); + return MNULL; +} + +/** + * @brief This function will create a entry in tx ba stream table for the + * given RA/TID. + * + * @param priv A pointer to mlan_private + * @param ra RA to find in reordering table + * @param tid TID to find in reordering table + * @param ba_status BA stream status to create the stream with + * + * @return N/A + */ +void +wlan_11n_create_txbastream_tbl(mlan_private *priv, + t_u8 *ra, int tid, baStatus_e ba_status) +{ + TxBAStreamTbl *new_node = MNULL; + pmlan_adapter pmadapter = priv->adapter; + raListTbl *ra_list = MNULL; + int tid_down; + + ENTER(); + + PRINTM(MDAT_D, "create_txbastream_tbl TID %d\n", tid); + DBG_HEXDUMP(MDAT_D, "RA", ra, MLAN_MAC_ADDR_LENGTH); + + if (pmadapter->callbacks. + moal_malloc(pmadapter->pmoal_handle, sizeof(TxBAStreamTbl), + MLAN_MEM_DEF, (t_u8 **)&new_node)) { + PRINTM(MERROR, + "wlan_11n_create_txbastream_tbl Failed to allocate new_node\n"); + LEAVE(); + return; + } + tid_down = wlan_get_wmm_tid_down(priv, tid); + ra_list = wlan_wmm_get_ralist_node(priv, tid_down, ra); + if (ra_list) { + ra_list->amsdu_in_ampdu = MFALSE; + ra_list->ba_status = ba_status; + } + util_init_list((pmlan_linked_list)new_node); + + new_node->tid = tid; + new_node->ba_status = ba_status; + memcpy(pmadapter, new_node->ra, ra, MLAN_MAC_ADDR_LENGTH); + + util_enqueue_list_tail(pmadapter->pmoal_handle, + &priv->tx_ba_stream_tbl_ptr, + (pmlan_linked_list)new_node, MNULL, MNULL); + + LEAVE(); +} + +/** + * @brief This function will send a block ack to given tid/ra + * + * @param priv A pointer to mlan_private + * @param tid TID to send the ADDBA + * @param peer_mac MAC address to send the ADDBA + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +int +wlan_send_addba(mlan_private *priv, int tid, t_u8 *peer_mac) +{ + HostCmd_DS_11N_ADDBA_REQ add_ba_req; + static t_u8 dialog_tok; + mlan_status ret; + + ENTER(); + + PRINTM(MCMND, "Send addba: TID %d\n", tid); + DBG_HEXDUMP(MCMD_D, "Send addba RA", peer_mac, MLAN_MAC_ADDR_LENGTH); + + add_ba_req.block_ack_param_set = + (t_u16)((tid << BLOCKACKPARAM_TID_POS) | + (priv->add_ba_param. + tx_win_size << BLOCKACKPARAM_WINSIZE_POS) | + IMMEDIATE_BLOCK_ACK); + /** enable AMSDU inside AMPDU */ + if (priv->add_ba_param.tx_amsdu && + (priv->aggr_prio_tbl[tid].amsdu != BA_STREAM_NOT_ALLOWED)) + add_ba_req.block_ack_param_set |= BLOCKACKPARAM_AMSDU_SUPP_MASK; + add_ba_req.block_ack_tmo = (t_u16)priv->add_ba_param.timeout; + + ++dialog_tok; + + if (dialog_tok == 0) + dialog_tok = 1; + + add_ba_req.dialog_token = dialog_tok; + memcpy(priv->adapter, &add_ba_req.peer_mac_addr, peer_mac, + MLAN_MAC_ADDR_LENGTH); + + /* We don't wait for the response of this command */ + ret = wlan_prepare_cmd(priv, HostCmd_CMD_11N_ADDBA_REQ, + 0, 0, MNULL, &add_ba_req); + + LEAVE(); + return ret; +} + +/** + * @brief This function will delete a block ack to given tid/ra + * + * @param priv A pointer to mlan_private + * @param pioctl_req A pointer to ioctl request buffer + * @param tid TID to send the ADDBA + * @param peer_mac MAC address to send the ADDBA + * @param initiator MTRUE if we have initiated ADDBA, MFALSE otherwise + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +int +wlan_send_delba(mlan_private *priv, pmlan_ioctl_req pioctl_req, int tid, + t_u8 *peer_mac, int initiator) +{ + HostCmd_DS_11N_DELBA delba; + mlan_status ret; + + ENTER(); + + memset(priv->adapter, &delba, 0, sizeof(delba)); + delba.del_ba_param_set = (tid << DELBA_TID_POS); + + if (initiator) + DELBA_INITIATOR(delba.del_ba_param_set); + else + DELBA_RECIPIENT(delba.del_ba_param_set); + + memcpy(priv->adapter, &delba.peer_mac_addr, peer_mac, + MLAN_MAC_ADDR_LENGTH); + + ret = wlan_prepare_cmd(priv, HostCmd_CMD_11N_DELBA, + HostCmd_ACT_GEN_SET, 0, (t_void *)pioctl_req, + (t_void *)&delba); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief This function handles the command response of + * delete a block ack request + * + * @param priv A pointer to mlan_private structure + * @param del_ba A pointer to command response buffer + * + * @return N/A + */ +void +wlan_11n_delete_bastream(mlan_private *priv, t_u8 *del_ba) +{ + HostCmd_DS_11N_DELBA *pdel_ba = (HostCmd_DS_11N_DELBA *)del_ba; + int tid; + + ENTER(); + + DBG_HEXDUMP(MCMD_D, "Delba:", (t_u8 *)pdel_ba, 20); + pdel_ba->del_ba_param_set = wlan_le16_to_cpu(pdel_ba->del_ba_param_set); + pdel_ba->reason_code = wlan_le16_to_cpu(pdel_ba->reason_code); + + tid = pdel_ba->del_ba_param_set >> DELBA_TID_POS; + + mlan_11n_delete_bastream_tbl(priv, tid, pdel_ba->peer_mac_addr, + TYPE_DELBA_RECEIVE, + INITIATOR_BIT(pdel_ba->del_ba_param_set), + pdel_ba->reason_code); + + LEAVE(); +} + +/** + * @brief Get Rx reordering table + * + * @param priv A pointer to mlan_private structure + * @param buf A pointer to rx_reorder_tbl structure + * @return number of rx reorder table entry + */ +int +wlan_get_rxreorder_tbl(mlan_private *priv, rx_reorder_tbl *buf) +{ + int i; + rx_reorder_tbl *ptbl = buf; + RxReorderTbl *rx_reorder_tbl_ptr; + int count = 0; + ENTER(); + rx_reorder_tbl_ptr = + (RxReorderTbl *)util_peek_list(priv->adapter->pmoal_handle, + &priv->rx_reorder_tbl_ptr, + priv->adapter->callbacks. + moal_spin_lock, + priv->adapter->callbacks. + moal_spin_unlock); + if (!rx_reorder_tbl_ptr) { + LEAVE(); + return count; + } + while (rx_reorder_tbl_ptr != (RxReorderTbl *)&priv->rx_reorder_tbl_ptr) { + ptbl->tid = (t_u16)rx_reorder_tbl_ptr->tid; + memcpy(priv->adapter, ptbl->ta, rx_reorder_tbl_ptr->ta, + MLAN_MAC_ADDR_LENGTH); + ptbl->start_win = rx_reorder_tbl_ptr->start_win; + ptbl->win_size = rx_reorder_tbl_ptr->win_size; + ptbl->amsdu = rx_reorder_tbl_ptr->amsdu; + for (i = 0; i < rx_reorder_tbl_ptr->win_size; ++i) { + if (rx_reorder_tbl_ptr->rx_reorder_ptr[i]) + ptbl->buffer[i] = MTRUE; + else + ptbl->buffer[i] = MFALSE; + } + rx_reorder_tbl_ptr = rx_reorder_tbl_ptr->pnext; + ptbl++; + count++; + if (count >= MLAN_MAX_RX_BASTREAM_SUPPORTED) + break; + } + LEAVE(); + return count; +} + +/** + * @brief Get transmit BA stream table + * + * @param priv A pointer to mlan_private structure + * @param buf A pointer to tx_ba_stream_tbl structure + * @return number of ba stream table entry + */ +int +wlan_get_txbastream_tbl(mlan_private *priv, tx_ba_stream_tbl *buf) +{ + TxBAStreamTbl *ptxtbl; + tx_ba_stream_tbl *ptbl = buf; + int count = 0; + t_u32 bastream_max = 0; + + ENTER(); + + wlan_request_ralist_lock(priv); + ptxtbl = (TxBAStreamTbl *)util_peek_list(priv->adapter->pmoal_handle, + &priv->tx_ba_stream_tbl_ptr, + MNULL, MNULL); + if (!ptxtbl) { + wlan_release_ralist_lock(priv); + LEAVE(); + return count; + } + bastream_max = ISSUPP_GETTXBASTREAM(priv->adapter->hw_dot_11n_dev_cap); + if (bastream_max == 0) + bastream_max = MLAN_MAX_TX_BASTREAM_DEFAULT; + + while (ptxtbl != (TxBAStreamTbl *)&priv->tx_ba_stream_tbl_ptr) { + ptbl->tid = (t_u16)ptxtbl->tid; + PRINTM(MINFO, "tid=%d\n", ptbl->tid); + memcpy(priv->adapter, ptbl->ra, ptxtbl->ra, + MLAN_MAC_ADDR_LENGTH); + ptbl->amsdu = ptxtbl->amsdu; + ptxtbl = ptxtbl->pnext; + ptbl++; + count++; + if (count >= bastream_max) + break; + } + wlan_release_ralist_lock(priv); + LEAVE(); + return count; +} + +/** + * @brief This function check if 11AC is allowed in bandcfg + * + * @param pmpriv A pointer to mlan_private structure + * @param bss_band bss band + * + * @return 0--not allowed, other value allowed + */ +t_u8 +wlan_11n_bandconfig_allowed(mlan_private *pmpriv, t_u8 bss_band) +{ + if (pmpriv->bss_mode == MLAN_BSS_MODE_IBSS) { + if (bss_band & BAND_G) + return (pmpriv->adapter->adhoc_start_band & BAND_GN); + else if (bss_band & BAND_A) + return (pmpriv->adapter->adhoc_start_band & BAND_AN); + } else { + if (bss_band & BAND_G) + return (pmpriv->config_bands & BAND_GN); + else if (bss_band & BAND_A) + return (pmpriv->config_bands & BAND_AN); + } + return 0; +} + +/** + * @brief This function cleans up txbastream_tbl for specific station + * + * @param priv A pointer to mlan_private + * @param ra RA to find in txbastream_tbl + * @return N/A + */ +void +wlan_11n_cleanup_txbastream_tbl(mlan_private *priv, t_u8 *ra) +{ + TxBAStreamTbl *ptx_tbl = MNULL; + t_u8 i; + ENTER(); + + wlan_request_ralist_lock(priv); + for (i = 0; i < MAX_NUM_TID; ++i) { + ptx_tbl = wlan_11n_get_txbastream_tbl(priv, i, ra, MFALSE); + if (ptx_tbl) + wlan_11n_delete_txbastream_tbl_entry(priv, ptx_tbl); + } + wlan_release_ralist_lock(priv); + LEAVE(); + return; +} + +void +wlan_update_11n_cap(mlan_private *pmpriv) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + + pmpriv->usr_dev_mcs_support = pmadapter->hw_dev_mcs_support; + pmpriv->usr_dot_11n_dev_cap_bg = + pmadapter->hw_dot_11n_dev_cap & DEFAULT_11N_CAP_MASK_BG; + pmpriv->usr_dot_11n_dev_cap_a = + pmadapter->hw_dot_11n_dev_cap & DEFAULT_11N_CAP_MASK_A; +} diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_11n.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_11n.h new file mode 100644 index 000000000000..1f65097c5d55 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_11n.h @@ -0,0 +1,392 @@ +/** @file mlan_11n.h + * + * @brief Interface for the 802.11n mlan_11n module implemented in mlan_11n.c + * + * Driver interface functions and type declarations for the 11n module + * implemented in mlan_11n.c. + * + * Copyright (C) 2008-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +/******************************************************** +Change log: + 12/01/2008: initial version +********************************************************/ + +#ifndef _MLAN_11N_H_ +#define _MLAN_11N_H_ + +#include "mlan_11n_aggr.h" +#include "mlan_11n_rxreorder.h" +#include "mlan_wmm.h" + +/** Print the 802.11n device capability */ +void wlan_show_dot11ndevcap(pmlan_adapter pmadapter, t_u32 cap); +/** Print the 802.11n device MCS */ +void wlan_show_devmcssupport(pmlan_adapter pmadapter, t_u8 support); +/** Handle the command response of a delete block ack request */ +mlan_status wlan_ret_11n_delba(mlan_private *priv, HostCmd_DS_COMMAND *resp); +/** Handle the command response of an add block ack request */ +mlan_status wlan_ret_11n_addba_req(mlan_private *priv, + HostCmd_DS_COMMAND *resp); +/** Handle the command response of 11ncfg command */ +mlan_status wlan_ret_11n_cfg(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf); +/** Prepare 11ncfg command */ +mlan_status wlan_cmd_11n_cfg(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, IN t_u16 cmd_action, + IN t_void *pdata_buf); +/** Prepare reject addba requst command */ +mlan_status wlan_cmd_reject_addba_req(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, + IN t_void *pdata_buf); +/** Handle the command response of rejecting addba request */ +mlan_status wlan_ret_reject_addba_req(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf); +/** Prepare TX BF configuration command */ +mlan_status wlan_cmd_tx_bf_cfg(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, IN t_void *pdata_buf); +/** Handle the command response TX BF configuration */ +mlan_status wlan_ret_tx_bf_cfg(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf); +#ifdef STA_SUPPORT +t_u8 wlan_11n_bandconfig_allowed(mlan_private *pmpriv, t_u8 bss_band); +/** Append the 802_11N tlv */ +int wlan_cmd_append_11n_tlv(IN mlan_private *pmpriv, + IN BSSDescriptor_t *pbss_desc, OUT t_u8 **ppbuffer); +/** wlan fill HT cap tlv */ +void wlan_fill_ht_cap_tlv(mlan_private *priv, MrvlIETypes_HTCap_t *pht_cap, + t_u8 band, t_u8 fill); +/** wlan fill HT cap IE */ +void wlan_fill_ht_cap_ie(mlan_private *priv, IEEEtypes_HTCap_t *pht_cap, + t_u8 bands); +#endif /* STA_SUPPORT */ +/** Miscellaneous configuration handler */ +mlan_status wlan_11n_cfg_ioctl(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req); +/** Delete Tx BA stream table entry */ +void wlan_11n_delete_txbastream_tbl_entry(mlan_private *priv, + TxBAStreamTbl *ptx_tbl); +/** Delete all Tx BA stream table entries */ +void wlan_11n_deleteall_txbastream_tbl(mlan_private *priv); +/** Get Tx BA stream table */ +TxBAStreamTbl *wlan_11n_get_txbastream_tbl(mlan_private *priv, int tid, + t_u8 *ra, int lock); +/** Create Tx BA stream table */ +void wlan_11n_create_txbastream_tbl(mlan_private *priv, t_u8 *ra, int tid, + baStatus_e ba_status); +/** Send ADD BA request */ +int wlan_send_addba(mlan_private *priv, int tid, t_u8 *peer_mac); +/** Send DEL BA request */ +int wlan_send_delba(mlan_private *priv, pmlan_ioctl_req pioctl_req, int tid, + t_u8 *peer_mac, int initiator); +/** This function handles the command response of delete a block ack request*/ +void wlan_11n_delete_bastream(mlan_private *priv, t_u8 *del_ba); +/** get rx reorder table */ +int wlan_get_rxreorder_tbl(mlan_private *priv, rx_reorder_tbl *buf); +/** get tx ba stream table */ +int wlan_get_txbastream_tbl(mlan_private *priv, tx_ba_stream_tbl *buf); +/** send delba */ +void wlan_11n_delba(mlan_private *priv, int tid); +/** update amdpdu tx win size */ +void wlan_update_ampdu_txwinsize(pmlan_adapter pmadapter); +/** Minimum number of AMSDU */ +#define MIN_NUM_AMSDU 2 +/** AMSDU Aggr control cmd resp */ +mlan_status wlan_ret_amsdu_aggr_ctrl(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf); +void wlan_set_tx_pause_flag(mlan_private *priv, t_u8 flag); +/** reconfigure tx buf size */ +mlan_status wlan_cmd_recfg_tx_buf(mlan_private *priv, + HostCmd_DS_COMMAND *cmd, + int cmd_action, void *pdata_buf); +/** AMSDU aggr control cmd */ +mlan_status wlan_cmd_amsdu_aggr_ctrl(mlan_private *priv, + HostCmd_DS_COMMAND *cmd, + int cmd_action, void *pdata_buf); + +t_u8 wlan_validate_chan_offset(IN mlan_private *pmpriv, + IN t_u8 band, IN t_u32 chan, IN t_u8 chan_bw); +/** get channel offset */ +t_u8 wlan_get_second_channel_offset(int chan); + +void wlan_update_11n_cap(mlan_private *pmpriv); + +/** clean up txbastream_tbl */ +void wlan_11n_cleanup_txbastream_tbl(mlan_private *priv, t_u8 *ra); +/** + * @brief This function checks whether a station has 11N enabled or not + * + * @param priv A pointer to mlan_private + * @param mac station mac address + * @return MTRUE or MFALSE + */ +static INLINE t_u8 +is_station_11n_enabled(mlan_private *priv, t_u8 *mac) +{ + sta_node *sta_ptr = MNULL; + sta_ptr = wlan_get_station_entry(priv, mac); + if (sta_ptr) + return (sta_ptr->is_11n_enabled) ? MTRUE : MFALSE; + return MFALSE; +} + +/** + * @brief This function get station max amsdu size + * + * @param priv A pointer to mlan_private + * @param mac station mac address + * @return max amsdu size statio supported + */ +static INLINE t_u16 +get_station_max_amsdu_size(mlan_private *priv, t_u8 *mac) +{ + sta_node *sta_ptr = MNULL; + sta_ptr = wlan_get_station_entry(priv, mac); + if (sta_ptr) + return sta_ptr->max_amsdu; + return 0; +} + +/** + * @brief This function checks whether a station allows AMPDU or not + * + * @param priv A pointer to mlan_private + * @param ptr A pointer to RA list table + * @param tid TID value for ptr + * @return MTRUE or MFALSE + */ +static INLINE t_u8 +is_station_ampdu_allowed(mlan_private *priv, raListTbl *ptr, int tid) +{ + sta_node *sta_ptr = MNULL; + sta_ptr = wlan_get_station_entry(priv, ptr->ra); + if (sta_ptr) { + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) { + if (priv->sec_info.wapi_enabled && + !sta_ptr->wapi_key_on) + return MFALSE; + } + return (sta_ptr->ampdu_sta[tid] != BA_STREAM_NOT_ALLOWED) + ? MTRUE : MFALSE; + } + return MFALSE; +} + +/** + * @brief This function disable station ampdu for specific tid + * + * @param priv A pointer to mlan_private + * @param tid tid index + * @param ra station mac address + * @return N/A + */ +static INLINE void +disable_station_ampdu(mlan_private *priv, t_u8 tid, t_u8 *ra) +{ + sta_node *sta_ptr = MNULL; + sta_ptr = wlan_get_station_entry(priv, ra); + if (sta_ptr) + sta_ptr->ampdu_sta[tid] = BA_STREAM_NOT_ALLOWED; + return; +} + +/** + * @brief This function reset station ampdu for specific id to user setting. + * + * @param priv A pointer to mlan_private + * @param tid tid index + * @param ra station mac address + * @return N/A + */ +static INLINE void +reset_station_ampdu(mlan_private *priv, t_u8 tid, t_u8 *ra) +{ + sta_node *sta_ptr = MNULL; + sta_ptr = wlan_get_station_entry(priv, ra); + if (sta_ptr) + sta_ptr->ampdu_sta[tid] = priv->aggr_prio_tbl[tid].ampdu_user; + return; +} + +/** + * @brief This function checks whether AMPDU is allowed or not + * + * @param priv A pointer to mlan_private + * @param ptr A pointer to RA list table + * @param tid TID value for ptr + * + * @return MTRUE or MFALSE + */ +static INLINE t_u8 +wlan_is_ampdu_allowed(mlan_private *priv, raListTbl *ptr, int tid) +{ +#ifdef UAP_SUPPORT + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) + return is_station_ampdu_allowed(priv, ptr, tid); +#endif /* UAP_SUPPORT */ + if (priv->sec_info.wapi_enabled && !priv->sec_info.wapi_key_on) + return MFALSE; + if (ptr->is_tdls_link) + return is_station_ampdu_allowed(priv, ptr, tid); + if (priv->adapter->tdls_status != TDLS_NOT_SETUP && !priv->txaggrctrl) + return MFALSE; + if (priv->bss_mode == MLAN_BSS_MODE_IBSS) + return is_station_ampdu_allowed(priv, ptr, tid); + return (priv->aggr_prio_tbl[tid].ampdu_ap != BA_STREAM_NOT_ALLOWED) + ? MTRUE : MFALSE; +} + +/** + * @brief This function checks whether AMSDU is allowed or not + * + * @param priv A pointer to mlan_private + * @param ptr A pointer to RA list table + * @param tid TID value for ptr + * + * @return MTRUE or MFALSE + */ +static INLINE t_u8 +wlan_is_amsdu_allowed(mlan_private *priv, raListTbl *ptr, int tid) +{ +#ifdef UAP_SUPPORT + sta_node *sta_ptr = MNULL; + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) { + sta_ptr = wlan_get_station_entry(priv, ptr->ra); + if (sta_ptr) { + if (priv->sec_info.wapi_enabled && + !sta_ptr->wapi_key_on) + return MFALSE; + } + } +#endif /* UAP_SUPPORT */ +#define TXRATE_BITMAP_INDEX_MCS0_7 2 + return ((priv->aggr_prio_tbl[tid].amsdu != BA_STREAM_NOT_ALLOWED) + &&((priv->is_data_rate_auto) + || !((priv->bitmap_rates[TXRATE_BITMAP_INDEX_MCS0_7]) & + 0x03))) ? MTRUE : MFALSE; +} + +/** + * @brief This function checks whether a BA stream is available or not + * + * @param priv A pointer to mlan_private + * + * @return MTRUE or MFALSE + */ +static INLINE t_u8 +wlan_is_bastream_avail(mlan_private *priv) +{ + mlan_private *pmpriv = MNULL; + t_u8 i = 0; + t_u32 bastream_num = 0; + t_u32 bastream_max = 0; + for (i = 0; i < priv->adapter->priv_num; i++) { + pmpriv = priv->adapter->priv[i]; + if (pmpriv) + bastream_num += + wlan_wmm_list_len(priv->adapter, + (pmlan_list_head)&pmpriv-> + tx_ba_stream_tbl_ptr); + } + bastream_max = ISSUPP_GETTXBASTREAM(priv->adapter->hw_dot_11n_dev_cap); + if (bastream_max == 0) + bastream_max = MLAN_MAX_TX_BASTREAM_DEFAULT; + return (bastream_num < bastream_max) ? MTRUE : MFALSE; +} + +/** + * @brief This function finds the stream to delete + * + * @param priv A pointer to mlan_private + * @param ptr A pointer to RA list table + * @param ptr_tid TID value of ptr + * @param ptid A pointer to TID of stream to delete, if return MTRUE + * @param ra RA of stream to delete, if return MTRUE + * + * @return MTRUE or MFALSE + */ +static INLINE t_u8 +wlan_find_stream_to_delete(mlan_private *priv, + raListTbl *ptr, int ptr_tid, int *ptid, t_u8 *ra) +{ + int tid; + t_u8 ret = MFALSE; + TxBAStreamTbl *ptx_tbl; + + ENTER(); + + ptx_tbl = (TxBAStreamTbl *)util_peek_list(priv->adapter->pmoal_handle, + &priv->tx_ba_stream_tbl_ptr, + MNULL, MNULL); + if (!ptx_tbl) { + LEAVE(); + return ret; + } + + tid = priv->aggr_prio_tbl[ptr_tid].ampdu_user; + + while (ptx_tbl != (TxBAStreamTbl *)&priv->tx_ba_stream_tbl_ptr) { + if (tid > priv->aggr_prio_tbl[ptx_tbl->tid].ampdu_user) { + tid = priv->aggr_prio_tbl[ptx_tbl->tid].ampdu_user; + *ptid = ptx_tbl->tid; + memcpy(priv->adapter, ra, ptx_tbl->ra, + MLAN_MAC_ADDR_LENGTH); + ret = MTRUE; + } + + ptx_tbl = ptx_tbl->pnext; + } + LEAVE(); + return ret; +} + +/** + * @brief This function checks whether 11n is supported + * + * @param priv A pointer to mlan_private + * @param ra Address of the receiver STA + * + * @return MTRUE or MFALSE + */ +static int INLINE +wlan_is_11n_enabled(mlan_private *priv, t_u8 *ra) +{ + int ret = MFALSE; + ENTER(); +#ifdef UAP_SUPPORT + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) { + if ((!(ra[0] & 0x01)) && (priv->is_11n_enabled)) + ret = is_station_11n_enabled(priv, ra); + } +#endif /* UAP_SUPPORT */ +#ifdef STA_SUPPORT + if (priv->bss_mode == MLAN_BSS_MODE_IBSS) { + if ((!(ra[0] & 0x01)) && (priv->adapter->adhoc_11n_enabled)) + ret = is_station_11n_enabled(priv, ra); + } +#endif + LEAVE(); + return ret; +} +#endif /* !_MLAN_11N_H_ */ diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_11n_aggr.c b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_11n_aggr.c new file mode 100644 index 000000000000..f346da2eb6f4 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_11n_aggr.c @@ -0,0 +1,592 @@ +/** @file mlan_11n_aggr.c + * + * @brief This file contains functions for 11n Aggregation. + * + * Copyright (C) 2008-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +/******************************************************** +Change log: + 11/10/2008: initial version +********************************************************/ + +#include "mlan.h" +#include "mlan_join.h" +#include "mlan_util.h" +#include "mlan_fw.h" +#include "mlan_main.h" +#include "mlan_wmm.h" +#include "mlan_11n.h" +#include "mlan_11n_aggr.h" +#include "mlan_sdio.h" + +/******************************************************** + Local Variables +********************************************************/ + +/******************************************************** + Global Variables +********************************************************/ + +/******************************************************** + Local Functions +********************************************************/ +/** + * @brief Aggregate individual packets into one AMSDU packet + * + * @param pmadapter A pointer to mlan_adapter structure + * @param amsdu_buf A pointer to packet buffer + * @param data A pointer to aggregated data packet being formed + * @param pkt_len Length of current packet to aggregate + * @param pad Pad + * + * @return Final packet size + */ +static int +wlan_11n_form_amsdu_pkt(pmlan_adapter pmadapter, t_u8 *amsdu_buf, t_u8 *data, + int pkt_len, int *pad) +{ + int dt_offset, amsdu_buf_offset; + Rfc1042Hdr_t snap = { + 0xaa, /* LLC DSAP */ + 0xaa, /* LLC SSAP */ + 0x03, /* LLC CTRL */ + {0x00, 0x00, 0x00}, /* SNAP OUI */ + 0x0000 /* SNAP type */ + /* + * This field will be overwritten + * later with ethertype + */ + }; + + ENTER(); + + memcpy(pmadapter, amsdu_buf, data, (MLAN_MAC_ADDR_LENGTH) * 2); + dt_offset = amsdu_buf_offset = (MLAN_MAC_ADDR_LENGTH) * 2; + + snap.snap_type = *(t_u16 *)(data + dt_offset); + dt_offset += sizeof(t_u16); + *(t_u16 *)(amsdu_buf + amsdu_buf_offset) = mlan_htons(pkt_len + + LLC_SNAP_LEN - + ((2 * + MLAN_MAC_ADDR_LENGTH) + + + sizeof(t_u16))); + amsdu_buf_offset += sizeof(t_u16); + memcpy(pmadapter, amsdu_buf + amsdu_buf_offset, &snap, LLC_SNAP_LEN); + amsdu_buf_offset += LLC_SNAP_LEN; + + memcpy(pmadapter, amsdu_buf + amsdu_buf_offset, data + dt_offset, + pkt_len - dt_offset); + *pad = (((pkt_len + LLC_SNAP_LEN) & 3)) ? (4 - + (((pkt_len + + LLC_SNAP_LEN)) & 3)) : 0; + + LEAVE(); + return pkt_len + LLC_SNAP_LEN + *pad; +} + +/** + * @brief Add TxPD to AMSDU header + * + * @param priv A pointer to mlan_private structure + * @param mbuf Pointer to buffer where the TxPD will be formed + * + * @return N/A + */ +static void +wlan_11n_form_amsdu_txpd(mlan_private *priv, mlan_buffer *mbuf) +{ + TxPD *ptx_pd; + mlan_adapter *pmadapter = priv->adapter; + + ENTER(); + + ptx_pd = (TxPD *)mbuf->pbuf; + memset(pmadapter, ptx_pd, 0, sizeof(TxPD)); + + /* + * Original priority has been overwritten + */ + ptx_pd->priority = (t_u8)mbuf->priority; + ptx_pd->pkt_delay_2ms = + wlan_wmm_compute_driver_packet_delay(priv, mbuf); + ptx_pd->bss_num = GET_BSS_NUM(priv); + ptx_pd->bss_type = priv->bss_type; + /* Always zero as the data is followed by TxPD */ + ptx_pd->tx_pkt_offset = sizeof(TxPD); + ptx_pd->tx_pkt_type = PKT_TYPE_AMSDU; + if (mbuf->flags & MLAN_BUF_FLAG_TDLS) + ptx_pd->flags = MRVDRV_TxPD_FLAGS_TDLS_PACKET; + if (ptx_pd->tx_control == 0) + /* TxCtrl set by user or default */ + ptx_pd->tx_control = priv->pkt_tx_ctrl; + + endian_convert_TxPD(ptx_pd); + + LEAVE(); +} + +/** + * @brief Update the TxPktLength field in TxPD after the complete AMSDU + * packet is formed + * + * @param priv A pointer to mlan_private structure + * @param mbuf TxPD buffer + * + * @return N/A + */ +static INLINE void +wlan_11n_update_pktlen_amsdu_txpd(mlan_private *priv, pmlan_buffer mbuf) +{ + TxPD *ptx_pd; + ENTER(); + + ptx_pd = (TxPD *)mbuf->pbuf; + ptx_pd->tx_pkt_length = + (t_u16)wlan_cpu_to_le16(mbuf->data_len - sizeof(TxPD)); +#ifdef STA_SUPPORT + if ((GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) && + (priv->adapter->pps_uapsd_mode)) { + if (MTRUE == wlan_check_last_packet_indication(priv)) { + priv->adapter->tx_lock_flag = MTRUE; + ptx_pd->flags |= MRVDRV_TxPD_POWER_MGMT_LAST_PACKET; + } + } +#endif /* STA_SUPPORT */ + LEAVE(); +} + +/** + * @brief Get number of aggregated packets + * + * @param data A pointer to packet data + * @param total_pkt_len Total packet length + * + * @return Number of packets + */ +static int +wlan_11n_get_num_aggrpkts(t_u8 *data, int total_pkt_len) +{ + int pkt_count = 0, pkt_len, pad; + t_u8 hdr_len = sizeof(Eth803Hdr_t); + + ENTER(); + while (total_pkt_len >= hdr_len) { + /* Length will be in network format, change it to host */ + pkt_len = mlan_ntohs((*(t_u16 *)(data + (2 * + MLAN_MAC_ADDR_LENGTH)))); + if (pkt_len > total_pkt_len) { + PRINTM(MERROR, "Error in packet length.\n"); + break; + } + + pad = (((pkt_len + sizeof(Eth803Hdr_t)) & 3)) ? + (4 - ((pkt_len + sizeof(Eth803Hdr_t)) & 3)) : 0; + data += pkt_len + pad + sizeof(Eth803Hdr_t); + total_pkt_len -= pkt_len + pad + sizeof(Eth803Hdr_t); + ++pkt_count; + } + LEAVE(); + return pkt_count; +} + +/******************************************************** + Global Functions +********************************************************/ + +/** + * @brief Deaggregate the received AMSDU packet + * + * @param priv A pointer to mlan_private structure + * @param pmbuf A pointer to aggregated data packet + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +mlan_status +wlan_11n_deaggregate_pkt(mlan_private *priv, pmlan_buffer pmbuf) +{ + t_u16 pkt_len; + int total_pkt_len; + t_u8 *data; + mlan_adapter *pmadapter = priv->adapter; + t_u32 max_rx_data_size = MLAN_RX_DATA_BUF_SIZE; + int pad; + mlan_status ret = MLAN_STATUS_FAILURE; + RxPacketHdr_t *prx_pkt; + mlan_buffer *daggr_mbuf = MNULL; + t_u8 rfc1042_eth_hdr[MLAN_MAC_ADDR_LENGTH] = { 0xaa, 0xaa, 0x03, + 0x00, 0x00, 0x00 + }; + t_u8 hdr_len = sizeof(Eth803Hdr_t); + t_u8 eapol_type[2] = { 0x88, 0x8e }; + t_u8 tdls_action_type[2] = { 0x89, 0x0d }; + + ENTER(); + + data = (t_u8 *)(pmbuf->pbuf + pmbuf->data_offset); + total_pkt_len = pmbuf->data_len; + + /* Sanity test */ + if (total_pkt_len > max_rx_data_size) { + PRINTM(MERROR, "Total packet length greater than tx buffer" + " size %d\n", total_pkt_len); + goto done; + } + + pmbuf->use_count = wlan_11n_get_num_aggrpkts(data, total_pkt_len); + + while (total_pkt_len >= hdr_len) { + prx_pkt = (RxPacketHdr_t *)data; + /* Length will be in network format, change it to host */ + pkt_len = mlan_ntohs((*(t_u16 *)(data + (2 * + MLAN_MAC_ADDR_LENGTH)))); + if (pkt_len > total_pkt_len) { + PRINTM(MERROR, + "Error in packet length: total_pkt_len = %d, pkt_len = %d\n", + total_pkt_len, pkt_len); + ret = MLAN_STATUS_FAILURE; + break; + } + + pad = (((pkt_len + sizeof(Eth803Hdr_t)) & 3)) ? + (4 - ((pkt_len + sizeof(Eth803Hdr_t)) & 3)) : 0; + + total_pkt_len -= pkt_len + pad + sizeof(Eth803Hdr_t); + + if (memcmp(pmadapter, &prx_pkt->rfc1042_hdr, + rfc1042_eth_hdr, sizeof(rfc1042_eth_hdr)) == 0) { + memmove(pmadapter, data + LLC_SNAP_LEN, data, (2 * + MLAN_MAC_ADDR_LENGTH)); + data += LLC_SNAP_LEN; + pkt_len += sizeof(Eth803Hdr_t) - LLC_SNAP_LEN; + } else { + *(t_u16 *)(data + (2 * MLAN_MAC_ADDR_LENGTH)) + = (t_u16)0; + pkt_len += sizeof(Eth803Hdr_t); + } + daggr_mbuf = + wlan_alloc_mlan_buffer(pmadapter, + pkt_len + MLAN_NET_IP_ALIGN, 0, + MOAL_ALLOC_MLAN_BUFFER); + daggr_mbuf->data_offset += MLAN_NET_IP_ALIGN; + if (daggr_mbuf == MNULL) { + PRINTM(MERROR, "Error allocating daggr mlan_buffer\n"); + ret = MLAN_STATUS_FAILURE; + break; + } + daggr_mbuf->bss_index = pmbuf->bss_index; + daggr_mbuf->buf_type = pmbuf->buf_type; + daggr_mbuf->data_len = pkt_len; + daggr_mbuf->in_ts_sec = pmbuf->in_ts_sec; + daggr_mbuf->in_ts_usec = pmbuf->in_ts_usec; + daggr_mbuf->pparent = pmbuf; + daggr_mbuf->priority = pmbuf->priority; + memcpy(pmadapter, daggr_mbuf->pbuf + daggr_mbuf->data_offset, + data, pkt_len); + +#ifdef UAP_SUPPORT + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) { + ret = wlan_uap_recv_packet(priv, daggr_mbuf); + } else { +#endif /* UAP_SUPPORT */ + /** send EAPOL from AMSDU pkt to firmware */ + if (priv->sec_info.ewpa_enabled && + (!memcmp + (pmadapter, + daggr_mbuf->pbuf + daggr_mbuf->data_offset + + MLAN_ETHER_PKT_TYPE_OFFSET, eapol_type, + sizeof(eapol_type)))) { + ret = wlan_prepare_cmd(priv, + HostCmd_CMD_802_11_EAPOL_PKT, + 0, 0, MNULL, daggr_mbuf); + if (ret == MLAN_STATUS_SUCCESS) + wlan_recv_event(priv, + MLAN_EVENT_ID_DRV_DEFER_HANDLING, + MNULL); + wlan_free_mlan_buffer(pmadapter, daggr_mbuf); + data += pkt_len + pad; + continue; + } + /**process tdls packet*/ + if (!memcmp + (pmadapter, + daggr_mbuf->pbuf + daggr_mbuf->data_offset + + MLAN_ETHER_PKT_TYPE_OFFSET, tdls_action_type, + sizeof(tdls_action_type))) { + PRINTM(MEVENT, + "Recevie AMSDU TDLS action frame\n"); + wlan_process_tdls_action_frame(priv, + daggr_mbuf-> + pbuf + + daggr_mbuf-> + data_offset, + daggr_mbuf-> + data_len); + } + + ret = pmadapter->callbacks.moal_recv_packet(pmadapter-> + pmoal_handle, + daggr_mbuf); +#ifdef UAP_SUPPORT + } +#endif /* UAP_SUPPORT */ + switch (ret) { + case MLAN_STATUS_PENDING: + break; + case MLAN_STATUS_FAILURE: + PRINTM(MERROR, "Deaggr, send to moal failed\n"); + daggr_mbuf->status_code = MLAN_ERROR_PKT_INVALID; + case MLAN_STATUS_SUCCESS: + wlan_recv_packet_complete(pmadapter, daggr_mbuf, ret); + break; + default: + break; + } + + data += pkt_len + pad; + } + +done: + priv->msdu_in_rx_amsdu_cnt += pmbuf->use_count; + priv->amsdu_rx_cnt++; + /** we should free the aggr buffer after deaggr */ + wlan_free_mlan_buffer(pmadapter, pmbuf); + LEAVE(); + return ret; +} + +/** + * @brief Aggregate multiple packets into one single AMSDU packet + * + * @param priv A pointer to mlan_private structure + * @param pra_list Pointer to the RA List table containing the pointers + * to packets. + * @param headroom Any interface specific headroom that may be need. TxPD + * will be formed leaving this headroom. + * @param ptrindex Pointer index + * + * @return Final packet size or MLAN_STATUS_FAILURE + */ +int +wlan_11n_aggregate_pkt(mlan_private *priv, raListTbl *pra_list, + int headroom, int ptrindex) +{ + int pkt_size = 0; + pmlan_adapter pmadapter = priv->adapter; + mlan_buffer *pmbuf_aggr, *pmbuf_src; + t_u8 *data; + int pad = 0; + mlan_status ret = MLAN_STATUS_SUCCESS; +#ifdef DEBUG_LEVEL1 + t_u32 sec = 0, usec = 0; +#endif + mlan_tx_param tx_param; +#ifdef STA_SUPPORT + TxPD *ptx_pd = MNULL; +#endif + t_u32 max_amsdu_size = MIN(pra_list->max_amsdu, pmadapter->tx_buf_size); + ENTER(); + + PRINTM(MDAT_D, "Handling Aggr packet\n"); + + pmbuf_src = + (pmlan_buffer)util_peek_list(pmadapter->pmoal_handle, + &pra_list->buf_head, MNULL, MNULL); + if (pmbuf_src) { + pmbuf_aggr = wlan_alloc_mlan_buffer(pmadapter, + pmadapter->tx_buf_size, 0, + MOAL_MALLOC_BUFFER); + if (!pmbuf_aggr) { + PRINTM(MERROR, "Error allocating mlan_buffer\n"); + pmadapter->callbacks.moal_spin_unlock(pmadapter-> + pmoal_handle, + priv->wmm. + ra_list_spinlock); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + data = pmbuf_aggr->pbuf + headroom; + pmbuf_aggr->bss_index = pmbuf_src->bss_index; + pmbuf_aggr->buf_type = pmbuf_src->buf_type; + pmbuf_aggr->priority = pmbuf_src->priority; + pmbuf_aggr->pbuf = data; + pmbuf_aggr->data_offset = 0; + pmbuf_aggr->in_ts_sec = pmbuf_src->in_ts_sec; + pmbuf_aggr->in_ts_usec = pmbuf_src->in_ts_usec; + if (pmbuf_src->flags & MLAN_BUF_FLAG_TDLS) + pmbuf_aggr->flags |= MLAN_BUF_FLAG_TDLS; + if (pmbuf_src->flags & MLAN_BUF_FLAG_TCP_ACK) + pmbuf_aggr->flags |= MLAN_BUF_FLAG_TCP_ACK; + + /* Form AMSDU */ + wlan_11n_form_amsdu_txpd(priv, pmbuf_aggr); + pkt_size = sizeof(TxPD); +#ifdef STA_SUPPORT + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) + ptx_pd = (TxPD *)pmbuf_aggr->pbuf; +#endif + priv->msdu_in_tx_amsdu_cnt++; + } else { + pmadapter->callbacks.moal_spin_unlock(pmadapter->pmoal_handle, + priv->wmm. + ra_list_spinlock); + goto exit; + } + + while (pmbuf_src && ((pkt_size + (pmbuf_src->data_len + LLC_SNAP_LEN) + + headroom) <= max_amsdu_size)) { + + pmbuf_src = (pmlan_buffer) + util_dequeue_list(pmadapter->pmoal_handle, + &pra_list->buf_head, MNULL, MNULL); + + pra_list->total_pkts--; + + /* decrement for every PDU taken from the list */ + priv->wmm.pkts_queued[ptrindex]--; + util_scalar_decrement(pmadapter->pmoal_handle, + &priv->wmm.tx_pkts_queued, MNULL, MNULL); + + pmadapter->callbacks.moal_spin_unlock(pmadapter->pmoal_handle, + priv->wmm. + ra_list_spinlock); + + pkt_size += wlan_11n_form_amsdu_pkt(pmadapter, + (data + pkt_size), + pmbuf_src->pbuf + + pmbuf_src->data_offset, + pmbuf_src->data_len, &pad); + + DBG_HEXDUMP(MDAT_D, "pmbuf_src", pmbuf_src, + sizeof(mlan_buffer)); + wlan_write_data_complete(pmadapter, pmbuf_src, + MLAN_STATUS_SUCCESS); + + pmadapter->callbacks.moal_spin_lock(pmadapter->pmoal_handle, + priv->wmm.ra_list_spinlock); + + if (!wlan_is_ralist_valid(priv, pra_list, ptrindex)) { + pmadapter->callbacks.moal_spin_unlock(pmadapter-> + pmoal_handle, + priv->wmm. + ra_list_spinlock); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + pmbuf_src = + (pmlan_buffer)util_peek_list(pmadapter->pmoal_handle, + &pra_list->buf_head, MNULL, + MNULL); + priv->msdu_in_tx_amsdu_cnt++; + } + + pmadapter->callbacks.moal_spin_unlock(pmadapter->pmoal_handle, + priv->wmm.ra_list_spinlock); + + /* Last AMSDU packet does not need padding */ + pkt_size -= pad; + pmbuf_aggr->data_len = pkt_size; + wlan_11n_update_pktlen_amsdu_txpd(priv, pmbuf_aggr); + pmbuf_aggr->data_len += headroom; + pmbuf_aggr->pbuf = data - headroom; + tx_param.next_pkt_len = ((pmbuf_src) ? + pmbuf_src->data_len + sizeof(TxPD) : 0); + ret = wlan_sdio_host_to_card(pmadapter, MLAN_TYPE_DATA, + pmbuf_aggr, &tx_param); + switch (ret) { + case MLAN_STATUS_RESOURCE: + pmadapter->callbacks.moal_spin_lock(pmadapter->pmoal_handle, + priv->wmm.ra_list_spinlock); + + if (!wlan_is_ralist_valid(priv, pra_list, ptrindex)) { + pmadapter->callbacks.moal_spin_unlock(pmadapter-> + pmoal_handle, + priv->wmm. + ra_list_spinlock); + pmbuf_aggr->status_code = MLAN_ERROR_PKT_INVALID; + wlan_write_data_complete(pmadapter, pmbuf_aggr, + MLAN_STATUS_FAILURE); + LEAVE(); + return MLAN_STATUS_FAILURE; + } +#ifdef STA_SUPPORT + /* reset tx_lock_flag */ + if ((GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) && + pmadapter->pps_uapsd_mode && + (pmadapter->tx_lock_flag == MTRUE)) { + pmadapter->tx_lock_flag = MFALSE; + ptx_pd->flags = 0; + } +#endif + util_enqueue_list_head(pmadapter->pmoal_handle, + &pra_list->buf_head, + (pmlan_linked_list)pmbuf_aggr, MNULL, + MNULL); + + pra_list->total_pkts++; + + /* add back only one: aggregated packet is requeued as one */ + priv->wmm.pkts_queued[ptrindex]++; + util_scalar_increment(pmadapter->pmoal_handle, + &priv->wmm.tx_pkts_queued, MNULL, MNULL); + pmbuf_aggr->flags |= MLAN_BUF_FLAG_REQUEUED_PKT; + pmadapter->callbacks.moal_spin_unlock(pmadapter->pmoal_handle, + priv->wmm. + ra_list_spinlock); + PRINTM(MINFO, "MLAN_STATUS_RESOURCE is returned\n"); + pmbuf_aggr->status_code = MLAN_ERROR_PKT_INVALID; + break; + case MLAN_STATUS_FAILURE: + pmadapter->data_sent = MFALSE; + PRINTM(MERROR, "Error: host_to_card failed: 0x%X\n", ret); + pmbuf_aggr->status_code = MLAN_ERROR_DATA_TX_FAIL; + pmadapter->dbg.num_tx_host_to_card_failure++; + wlan_write_data_complete(pmadapter, pmbuf_aggr, ret); + goto exit; + case MLAN_STATUS_PENDING: + break; + case MLAN_STATUS_SUCCESS: + wlan_write_data_complete(pmadapter, pmbuf_aggr, ret); + break; + default: + break; + } + if (ret != MLAN_STATUS_RESOURCE) { + pmadapter->callbacks.moal_spin_lock(pmadapter->pmoal_handle, + priv->wmm.ra_list_spinlock); + if (wlan_is_ralist_valid(priv, pra_list, ptrindex)) { + priv->wmm.packets_out[ptrindex]++; + priv->wmm.tid_tbl_ptr[ptrindex].ra_list_curr = pra_list; + } + pmadapter->bssprio_tbl[priv->bss_priority].bssprio_cur = + pmadapter->bssprio_tbl[priv->bss_priority].bssprio_cur-> + pnext; + pmadapter->callbacks.moal_spin_unlock(pmadapter->pmoal_handle, + priv->wmm. + ra_list_spinlock); + } + PRINTM_GET_SYS_TIME(MDATA, &sec, &usec); + PRINTM_NETINTF(MDATA, priv); + PRINTM(MDATA, "%lu.%06lu : Data => FW\n", sec, usec); + priv->amsdu_tx_cnt++; +exit: + LEAVE(); + return pkt_size + headroom; +} diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_11n_aggr.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_11n_aggr.h new file mode 100644 index 000000000000..84a568efb0c0 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_11n_aggr.h @@ -0,0 +1,37 @@ +/** @file mlan_11n_aggr.h + * + * @brief This file contains related macros, enum, and struct + * of 11n aggregation functionalities + * + * Copyright (C) 2008-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +/******************************************************** +Change log: + 11/10/2008: initial version +********************************************************/ + +#ifndef _MLAN_11N_AGGR_H_ +#define _MLAN_11N_AGGR_H_ + +/** Aggregate 11N packets */ +mlan_status wlan_11n_deaggregate_pkt(pmlan_private priv, pmlan_buffer pmbuf); +/** Deaggregate 11N packets */ +int wlan_11n_aggregate_pkt(mlan_private *priv, raListTbl *ptr, + int headroom, int ptrindex); + +#endif /* !_MLAN_11N_AGGR_H_ */ diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_11n_rxreorder.c b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_11n_rxreorder.c new file mode 100644 index 000000000000..40afbfd1e96f --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_11n_rxreorder.c @@ -0,0 +1,1570 @@ +/** @file mlan_11n_rxreorder.c + * + * @brief This file contains the handling of RxReordering in wlan + * driver. + * + * Copyright (C) 2008-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/******************************************************** +Change log: + 11/10/2008: initial version +********************************************************/ + +#include "mlan.h" +#include "mlan_join.h" +#include "mlan_util.h" +#include "mlan_fw.h" +#include "mlan_main.h" +#include "mlan_wmm.h" +#include "mlan_11n.h" +#include "mlan_11n_rxreorder.h" + +/******************************************************** + Local Variables +********************************************************/ + +/******************************************************** + Global Variables +********************************************************/ + +/******************************************************** + Local Functions +********************************************************/ +/** + * @brief This function will dispatch amsdu packet and + * forward it to kernel/upper layer + * + * @param priv A pointer to mlan_private + * @param pmbuf A pointer to the received buffer + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status +wlan_11n_dispatch_amsdu_pkt(mlan_private *priv, pmlan_buffer pmbuf) +{ + RxPD *prx_pd; + prx_pd = (RxPD *)(pmbuf->pbuf + pmbuf->data_offset); + + ENTER(); + if (prx_pd->rx_pkt_type == PKT_TYPE_AMSDU) { + pmbuf->data_len = prx_pd->rx_pkt_length; + pmbuf->data_offset += prx_pd->rx_pkt_offset; + wlan_11n_deaggregate_pkt(priv, pmbuf); + LEAVE(); + return MLAN_STATUS_SUCCESS; + } + LEAVE(); + return MLAN_STATUS_FAILURE; +} + +/** + * @brief This function will process the rx packet and + * forward it to kernel/upper layer + * + * @param priv A pointer to mlan_private + * @param payload A pointer to rx packet payload + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status +wlan_11n_dispatch_pkt(t_void *priv, t_void *payload) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; +#ifdef STA_SUPPORT + pmlan_adapter pmadapter = ((pmlan_private)priv)->adapter; +#endif + ENTER(); + if (payload == (t_void *)RX_PKT_DROPPED_IN_FW) { + LEAVE(); + return ret; + } +#ifdef UAP_SUPPORT + if (GET_BSS_ROLE((mlan_private *)priv) == MLAN_BSS_ROLE_UAP) { + if (MLAN_STATUS_SUCCESS == + wlan_11n_dispatch_amsdu_pkt((mlan_private *)priv, + (pmlan_buffer)payload)) { + LEAVE(); + return ret; + } + ret = wlan_process_uap_rx_packet(priv, (pmlan_buffer)payload); + LEAVE(); + return ret; + } +#endif /* UAP_SUPPORT */ + +#ifdef STA_SUPPORT + if (MLAN_STATUS_SUCCESS == + wlan_11n_dispatch_amsdu_pkt((mlan_private *)priv, + (pmlan_buffer)payload)) { + LEAVE(); + return ret; + } + ret = wlan_process_rx_packet(pmadapter, (pmlan_buffer)payload); +#endif /* STA_SUPPORT */ + LEAVE(); + return ret; +} + +/** + * @brief This function restarts the reordering timeout timer + * + * @param pmadapter A pointer to mlan_adapter + * @param rx_reor_tbl_ptr A pointer to structure RxReorderTbl + * + * @return N/A + */ +static void +mlan_11n_rxreorder_timer_restart(pmlan_adapter pmadapter, + RxReorderTbl *rx_reor_tbl_ptr) +{ + t_u16 min_flush_time = 0; + ENTER(); + + if (rx_reor_tbl_ptr->win_size >= 32) + min_flush_time = MIN_FLUSH_TIMER_15_MS; + else + min_flush_time = MIN_FLUSH_TIMER_MS; + + if (rx_reor_tbl_ptr->timer_context.timer_is_set) + pmadapter->callbacks.moal_stop_timer(pmadapter->pmoal_handle, + rx_reor_tbl_ptr-> + timer_context.timer); + + pmadapter->callbacks.moal_start_timer(pmadapter->pmoal_handle, + rx_reor_tbl_ptr->timer_context. + timer, MFALSE, + (rx_reor_tbl_ptr->win_size * + min_flush_time)); + + rx_reor_tbl_ptr->timer_context.timer_is_set = MTRUE; + LEAVE(); +} + +/** + * @brief This function dispatches all the packets in the buffer. + * There could be holes in the buffer. + * + * @param priv A pointer to mlan_private + * @param rx_reor_tbl_ptr A pointer to structure RxReorderTbl + * @param start_win Start window + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_11n_dispatch_pkt_until_start_win(t_void *priv, + RxReorderTbl *rx_reor_tbl_ptr, + int start_win) +{ + int no_pkt_to_send, i, xchg; + mlan_status ret = MLAN_STATUS_SUCCESS; + void *rx_tmp_ptr = MNULL; + mlan_private *pmpriv = (mlan_private *)priv; + + ENTER(); + + no_pkt_to_send = (start_win > rx_reor_tbl_ptr->start_win) ? + MIN((start_win - rx_reor_tbl_ptr->start_win), + rx_reor_tbl_ptr->win_size) : rx_reor_tbl_ptr->win_size; + + for (i = 0; i < no_pkt_to_send; ++i) { + pmpriv->adapter->callbacks.moal_spin_lock(pmpriv->adapter-> + pmoal_handle, + pmpriv->rx_pkt_lock); + rx_tmp_ptr = MNULL; + if (rx_reor_tbl_ptr->rx_reorder_ptr[i]) { + rx_tmp_ptr = rx_reor_tbl_ptr->rx_reorder_ptr[i]; + rx_reor_tbl_ptr->rx_reorder_ptr[i] = MNULL; + } + pmpriv->adapter->callbacks.moal_spin_unlock(pmpriv->adapter-> + pmoal_handle, + pmpriv-> + rx_pkt_lock); + if (rx_tmp_ptr) + wlan_11n_dispatch_pkt(priv, rx_tmp_ptr); + } + + pmpriv->adapter->callbacks.moal_spin_lock(pmpriv->adapter->pmoal_handle, + pmpriv->rx_pkt_lock); + /* + * We don't have a circular buffer, hence use rotation to simulate + * circular buffer + */ + xchg = rx_reor_tbl_ptr->win_size - no_pkt_to_send; + for (i = 0; i < xchg; ++i) { + rx_reor_tbl_ptr->rx_reorder_ptr[i] = + rx_reor_tbl_ptr->rx_reorder_ptr[no_pkt_to_send + i]; + rx_reor_tbl_ptr->rx_reorder_ptr[no_pkt_to_send + i] = MNULL; + } + + rx_reor_tbl_ptr->start_win = start_win; + pmpriv->adapter->callbacks.moal_spin_unlock(pmpriv->adapter-> + pmoal_handle, + pmpriv->rx_pkt_lock); + + LEAVE(); + return ret; +} + +/** + * @brief This function will display the rxReorder table + * + * @param pmadapter A pointer to mlan_adapter structure + * @param rx_reor_tbl_ptr A pointer to structure RxReorderTbl + * + * @return N/A + */ +static t_void +wlan_11n_display_tbl_ptr(pmlan_adapter pmadapter, RxReorderTbl *rx_reor_tbl_ptr) +{ + ENTER(); + + DBG_HEXDUMP(MDAT_D, "Reorder ptr", rx_reor_tbl_ptr->rx_reorder_ptr, + sizeof(t_void *) * rx_reor_tbl_ptr->win_size); + + LEAVE(); +} + +/** + * @brief This function will dispatch all packets sequentially + * from start_win until a hole is found and adjust the + * start_win appropriately + * + * @param priv A pointer to mlan_private + * @param rx_reor_tbl_ptr A pointer to structure RxReorderTbl + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_11n_scan_and_dispatch(t_void *priv, RxReorderTbl *rx_reor_tbl_ptr) +{ + int i, j, xchg; + mlan_status ret = MLAN_STATUS_SUCCESS; + void *rx_tmp_ptr = MNULL; + mlan_private *pmpriv = (mlan_private *)priv; + + ENTER(); + + for (i = 0; i < rx_reor_tbl_ptr->win_size; ++i) { + pmpriv->adapter->callbacks.moal_spin_lock(pmpriv->adapter-> + pmoal_handle, + pmpriv->rx_pkt_lock); + if (!rx_reor_tbl_ptr->rx_reorder_ptr[i]) { + pmpriv->adapter->callbacks.moal_spin_unlock(pmpriv-> + adapter-> + pmoal_handle, + pmpriv-> + rx_pkt_lock); + break; + } + rx_tmp_ptr = rx_reor_tbl_ptr->rx_reorder_ptr[i]; + rx_reor_tbl_ptr->rx_reorder_ptr[i] = MNULL; + pmpriv->adapter->callbacks.moal_spin_unlock(pmpriv->adapter-> + pmoal_handle, + pmpriv-> + rx_pkt_lock); + wlan_11n_dispatch_pkt(priv, rx_tmp_ptr); + } + + pmpriv->adapter->callbacks.moal_spin_lock(pmpriv->adapter->pmoal_handle, + pmpriv->rx_pkt_lock); + /* + * We don't have a circular buffer, hence use rotation to simulate + * circular buffer + */ + if (i > 0) { + xchg = rx_reor_tbl_ptr->win_size - i; + for (j = 0; j < xchg; ++j) { + rx_reor_tbl_ptr->rx_reorder_ptr[j] = + rx_reor_tbl_ptr->rx_reorder_ptr[i + j]; + rx_reor_tbl_ptr->rx_reorder_ptr[i + j] = MNULL; + } + } + + rx_reor_tbl_ptr->start_win = (rx_reor_tbl_ptr->start_win + i) + & (MAX_TID_VALUE - 1); + + pmpriv->adapter->callbacks.moal_spin_unlock(pmpriv->adapter-> + pmoal_handle, + pmpriv->rx_pkt_lock); + LEAVE(); + return ret; +} + +/** + * @brief This function delete rxreorder table's entry + * and free the memory + * + * @param priv A pointer to mlan_private + * @param rx_reor_tbl_ptr A pointer to structure RxReorderTbl + * + * @return N/A + */ +static t_void +wlan_11n_delete_rxreorder_tbl_entry(mlan_private *priv, + RxReorderTbl *rx_reor_tbl_ptr) +{ + pmlan_adapter pmadapter = priv->adapter; + + ENTER(); + + if (!rx_reor_tbl_ptr) { + LEAVE(); + return; + } + mlan_block_rx_process(pmadapter, MTRUE); + + wlan_11n_dispatch_pkt_until_start_win(priv, rx_reor_tbl_ptr, + (rx_reor_tbl_ptr->start_win + + rx_reor_tbl_ptr->win_size) + & (MAX_TID_VALUE - 1)); + + if (rx_reor_tbl_ptr->timer_context.timer) { + if (rx_reor_tbl_ptr->timer_context.timer_is_set) { + priv->adapter->callbacks.moal_stop_timer(pmadapter-> + pmoal_handle, + rx_reor_tbl_ptr->timer_context. + timer); + rx_reor_tbl_ptr->timer_context.timer_is_set = MFALSE; + } + priv->adapter->callbacks.moal_free_timer(pmadapter-> + pmoal_handle, + rx_reor_tbl_ptr->timer_context. + timer); + rx_reor_tbl_ptr->timer_context.timer = MNULL; + } + + PRINTM(MDAT_D, "Delete rx_reor_tbl_ptr: %p\n", rx_reor_tbl_ptr); + util_unlink_list(pmadapter->pmoal_handle, + &priv->rx_reorder_tbl_ptr, + (pmlan_linked_list)rx_reor_tbl_ptr, + pmadapter->callbacks.moal_spin_lock, + pmadapter->callbacks.moal_spin_unlock); + + pmadapter->callbacks.moal_mfree(pmadapter->pmoal_handle, + (t_u8 *)rx_reor_tbl_ptr-> + rx_reorder_ptr); + pmadapter->callbacks.moal_mfree(pmadapter->pmoal_handle, + (t_u8 *)rx_reor_tbl_ptr); + mlan_block_rx_process(pmadapter, MFALSE); + + LEAVE(); +} + +/** + * @brief This function returns the last used sequence number + * + * @param rx_reorder_tbl_ptr A pointer to structure RxReorderTbl + * + * @return Last used sequence number + */ +static int +wlan_11n_find_last_seqnum(RxReorderTbl *rx_reorder_tbl_ptr) +{ + int i; + + ENTER(); + for (i = (rx_reorder_tbl_ptr->win_size - 1); i >= 0; --i) { + if (rx_reorder_tbl_ptr->rx_reorder_ptr[i]) { + LEAVE(); + return i; + } + } + LEAVE(); + return -1; +} + +/** + * @brief This function flushes all data + * + * @param priv A pointer to mlan_private structure + * @param rx_reor_tbl_ptr A pointer to RxReorderTbl + * + * @return N/A + */ +static t_void +wlan_start_flush_data(mlan_private *priv, RxReorderTbl *rx_reor_tbl_ptr) +{ + + int startWin; + + ENTER(); + wlan_11n_display_tbl_ptr(priv->adapter, rx_reor_tbl_ptr); + + startWin = wlan_11n_find_last_seqnum(rx_reor_tbl_ptr); + if (startWin >= 0) { + PRINTM(MINFO, "Flush data %d\n", startWin); + wlan_11n_dispatch_pkt_until_start_win(priv, rx_reor_tbl_ptr, + ((rx_reor_tbl_ptr-> + start_win + startWin + + 1) & (MAX_TID_VALUE - + 1))); + } + wlan_11n_display_tbl_ptr(priv->adapter, rx_reor_tbl_ptr); + LEAVE(); +} + +/** + * @brief This function set the flag to flushes data + * + * @param context Reorder context pointer + * + * @return N/A + */ +static t_void +wlan_flush_data(t_void *context) +{ + reorder_tmr_cnxt_t *reorder_cnxt = (reorder_tmr_cnxt_t *)context; + ENTER(); + /* Set the flag to flush data */ + reorder_cnxt->priv->adapter->flush_data = MTRUE; + reorder_cnxt->ptr->flush_data = MTRUE; + reorder_cnxt->timer_is_set = MFALSE; + wlan_recv_event(reorder_cnxt->priv, MLAN_EVENT_ID_DRV_DEFER_RX_WORK, + MNULL); + LEAVE(); +} + +/** + * @brief This function will create a entry in rx reordering table for the + * given ta/tid and will initialize it with seq_num, win_size + * + * @param priv A pointer to mlan_private + * @param ta ta to find in reordering table + * @param tid tid to find in reordering table + * @param win_size win_size for the give ta/tid pair. + * @param seq_num Starting sequence number for current entry. + * + * @return N/A + */ +static t_void +wlan_11n_create_rxreorder_tbl(mlan_private *priv, t_u8 *ta, int tid, + int win_size, int seq_num) +{ + int i; + pmlan_adapter pmadapter = priv->adapter; + RxReorderTbl *rx_reor_tbl_ptr, *new_node; + sta_node *sta_ptr = MNULL; + t_u16 last_seq = 0; + + ENTER(); + + /* + * If we get a TID, ta pair which is already present dispatch all the + * the packets and move the window size until the ssn + */ + rx_reor_tbl_ptr = wlan_11n_get_rxreorder_tbl(priv, tid, ta); + if (rx_reor_tbl_ptr) { + wlan_11n_dispatch_pkt_until_start_win(priv, + rx_reor_tbl_ptr, seq_num); + } else { + PRINTM(MDAT_D, "%s: seq_num %d, tid %d, ta " MACSTR + ", win_size %d\n", __func__, + seq_num, tid, MAC2STR(ta), win_size); + if (pmadapter->callbacks. + moal_malloc(pmadapter->pmoal_handle, sizeof(RxReorderTbl), + MLAN_MEM_DEF, (t_u8 **)&new_node)) { + PRINTM(MERROR, "Rx reorder memory allocation failed\n"); + LEAVE(); + return; + } + + util_init_list((pmlan_linked_list)new_node); + new_node->tid = tid; + memcpy(pmadapter, new_node->ta, ta, MLAN_MAC_ADDR_LENGTH); + new_node->start_win = seq_num; + new_node->pkt_count = 0; + if (queuing_ra_based(priv)) { + /* TODO for adhoc */ + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) { + sta_ptr = wlan_get_station_entry(priv, ta); + if (sta_ptr) + last_seq = sta_ptr->rx_seq[tid]; + } + if (priv->bss_mode == MLAN_BSS_MODE_IBSS) { + sta_ptr = wlan_get_station_entry(priv, ta); + if (sta_ptr) + last_seq = sta_ptr->rx_seq[tid]; + } + PRINTM(MINFO, "UAP/ADHOC:last_seq=%d start_win=%d\n", + last_seq, new_node->start_win); + } else { + sta_ptr = wlan_get_station_entry(priv, ta); + if (sta_ptr) + last_seq = sta_ptr->rx_seq[tid]; + else + last_seq = priv->rx_seq[tid]; + } + new_node->last_seq = last_seq; + new_node->win_size = win_size; + new_node->force_no_drop = MFALSE; + new_node->check_start_win = MTRUE; + + if (pmadapter->callbacks. + moal_malloc(pmadapter->pmoal_handle, + sizeof(t_void *) * win_size, MLAN_MEM_DEF, + (t_u8 **)&new_node->rx_reorder_ptr)) { + PRINTM(MERROR, + "Rx reorder table memory allocation" "failed\n"); + pmadapter->callbacks.moal_mfree(pmadapter->pmoal_handle, + (t_u8 *)new_node); + LEAVE(); + return; + } + + PRINTM(MDAT_D, + "Create ReorderPtr: %p start_win=%d last_seq=%d\n", + new_node, new_node->start_win, last_seq); + new_node->timer_context.ptr = new_node; + new_node->timer_context.priv = priv; + new_node->timer_context.timer_is_set = MFALSE; + new_node->ba_status = BA_STREAM_SETUP_INPROGRESS; + + pmadapter->callbacks.moal_init_timer(pmadapter->pmoal_handle, + &new_node-> + timer_context.timer, + wlan_flush_data, + &new_node->timer_context); + + for (i = 0; i < win_size; ++i) + new_node->rx_reorder_ptr[i] = MNULL; + + util_enqueue_list_tail(pmadapter->pmoal_handle, + &priv->rx_reorder_tbl_ptr, + (pmlan_linked_list)new_node, + pmadapter->callbacks.moal_spin_lock, + pmadapter->callbacks.moal_spin_unlock); + } + + LEAVE(); +} + +/******************************************************** + Global Functions +********************************************************/ + +/** + * @brief This function will return the pointer to a entry in rx reordering + * table which matches the give TA/TID pair + * + * @param priv A pointer to mlan_private + * @param ta ta to find in reordering table + * @param tid tid to find in reordering table + * + * @return A pointer to structure RxReorderTbl + */ +RxReorderTbl * +wlan_11n_get_rxreorder_tbl(mlan_private *priv, int tid, t_u8 *ta) +{ + RxReorderTbl *rx_reor_tbl_ptr; + + ENTER(); + + rx_reor_tbl_ptr = + (RxReorderTbl *)util_peek_list(priv->adapter->pmoal_handle, + &priv->rx_reorder_tbl_ptr, MNULL, + MNULL); + if (!rx_reor_tbl_ptr) { + LEAVE(); + return MNULL; + } + + while (rx_reor_tbl_ptr != (RxReorderTbl *)&priv->rx_reorder_tbl_ptr) { + if ((!memcmp + (priv->adapter, rx_reor_tbl_ptr->ta, ta, + MLAN_MAC_ADDR_LENGTH)) && (rx_reor_tbl_ptr->tid == tid)) { + LEAVE(); + return rx_reor_tbl_ptr; + } + + rx_reor_tbl_ptr = rx_reor_tbl_ptr->pnext; + } + + LEAVE(); + return MNULL; +} + +/** + * @brief This function prepares command for adding a block ack + * request. + * + * @param priv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param pdata_buf A pointer to data buffer + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_cmd_11n_addba_req(mlan_private *priv, + HostCmd_DS_COMMAND *cmd, t_void *pdata_buf) +{ + HostCmd_DS_11N_ADDBA_REQ *padd_ba_req = (HostCmd_DS_11N_ADDBA_REQ *) + &cmd->params.add_ba_req; + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_11N_ADDBA_REQ); + cmd->size = wlan_cpu_to_le16(sizeof(HostCmd_DS_11N_ADDBA_REQ) + + S_DS_GEN); + + memcpy(priv->adapter, padd_ba_req, pdata_buf, + sizeof(HostCmd_DS_11N_ADDBA_REQ)); + padd_ba_req->block_ack_param_set = + wlan_cpu_to_le16(padd_ba_req->block_ack_param_set); + padd_ba_req->block_ack_tmo = + wlan_cpu_to_le16(padd_ba_req->block_ack_tmo); + padd_ba_req->ssn = wlan_cpu_to_le16(padd_ba_req->ssn); + padd_ba_req->add_req_result = 0; + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function check if AMPDU Rx allowed + * + * @param priv A pointer to mlan_private structure + * @param tid TID + * + * @return MTRUE/MFALSE + */ +t_u8 +wlan_is_addba_reject(mlan_private *priv, t_u8 tid) +{ +#ifdef STA_SUPPORT + if (priv->bss_mode == MLAN_BSS_MODE_IBSS) + return priv->ibss_addba_reject[tid]; +#endif + return priv->addba_reject[tid]; +} + +/** + * @brief This function prepares command for adding a block ack + * response. + * + * @param priv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param pdata_buf A pointer to data buffer + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_cmd_11n_addba_rspgen(mlan_private *priv, + HostCmd_DS_COMMAND *cmd, void *pdata_buf) +{ + HostCmd_DS_11N_ADDBA_RSP *padd_ba_rsp = (HostCmd_DS_11N_ADDBA_RSP *) + &cmd->params.add_ba_rsp; + HostCmd_DS_11N_ADDBA_REQ *pevt_addba_req = + (HostCmd_DS_11N_ADDBA_REQ *)pdata_buf; + t_u8 tid = 0; + int win_size = 0; + + ENTER(); + + pevt_addba_req->block_ack_param_set = + wlan_le16_to_cpu(pevt_addba_req->block_ack_param_set); + pevt_addba_req->block_ack_tmo = + wlan_le16_to_cpu(pevt_addba_req->block_ack_tmo); + pevt_addba_req->ssn = wlan_le16_to_cpu(pevt_addba_req->ssn); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_11N_ADDBA_RSP); + cmd->size = + wlan_cpu_to_le16(sizeof(HostCmd_DS_11N_ADDBA_RSP) + S_DS_GEN); + + memcpy(priv->adapter, padd_ba_rsp->peer_mac_addr, + pevt_addba_req->peer_mac_addr, MLAN_MAC_ADDR_LENGTH); + padd_ba_rsp->dialog_token = pevt_addba_req->dialog_token; + padd_ba_rsp->block_ack_tmo = + wlan_cpu_to_le16(pevt_addba_req->block_ack_tmo); + padd_ba_rsp->ssn = wlan_cpu_to_le16(pevt_addba_req->ssn); + padd_ba_rsp->add_rsp_result = 0; + + padd_ba_rsp->block_ack_param_set = pevt_addba_req->block_ack_param_set; + tid = (padd_ba_rsp->block_ack_param_set & BLOCKACKPARAM_TID_MASK) + >> BLOCKACKPARAM_TID_POS; + if (wlan_is_addba_reject(priv, tid) +#ifdef STA_SUPPORT + || ((priv->bss_mode == MLAN_BSS_MODE_IBSS) + && !wlan_is_11n_enabled(priv, pevt_addba_req->peer_mac_addr)) + || ((GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) + && priv->wps.session_enable) +#endif +#ifdef UAP_SUPPORT + || ((GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) + && (util_scalar_read(priv->adapter->pmoal_handle, + &priv->adapter->pending_bridge_pkts, + priv->adapter->callbacks.moal_spin_lock, + priv->adapter->callbacks. + moal_spin_unlock) > RX_LOW_THRESHOLD)) +#endif + ) + padd_ba_rsp->status_code = + wlan_cpu_to_le16(ADDBA_RSP_STATUS_DECLINED); + else + padd_ba_rsp->status_code = + wlan_cpu_to_le16(ADDBA_RSP_STATUS_ACCEPT); + padd_ba_rsp->block_ack_param_set &= ~BLOCKACKPARAM_WINSIZE_MASK; + if (!priv->add_ba_param.rx_amsdu) + /* We do not support AMSDU inside AMPDU, hence reset the bit */ + padd_ba_rsp->block_ack_param_set &= + ~BLOCKACKPARAM_AMSDU_SUPP_MASK; + + padd_ba_rsp->block_ack_param_set |= (priv->add_ba_param.rx_win_size << + BLOCKACKPARAM_WINSIZE_POS); + win_size = + (padd_ba_rsp->block_ack_param_set & BLOCKACKPARAM_WINSIZE_MASK) + >> BLOCKACKPARAM_WINSIZE_POS; + + if (win_size == 0) + padd_ba_rsp->status_code = + wlan_cpu_to_le16(ADDBA_RSP_STATUS_DECLINED); + + padd_ba_rsp->block_ack_param_set = + wlan_cpu_to_le16(padd_ba_rsp->block_ack_param_set); + + if (padd_ba_rsp->status_code == + wlan_cpu_to_le16(ADDBA_RSP_STATUS_ACCEPT)) + wlan_11n_create_rxreorder_tbl(priv, + pevt_addba_req->peer_mac_addr, + tid, win_size, + pevt_addba_req->ssn); + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command for deleting a block ack + * request. + * + * @param priv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param pdata_buf A pointer to data buffer + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_cmd_11n_delba(mlan_private *priv, HostCmd_DS_COMMAND *cmd, void *pdata_buf) +{ + HostCmd_DS_11N_DELBA *pdel_ba = (HostCmd_DS_11N_DELBA *) + &cmd->params.del_ba; + + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_11N_DELBA); + cmd->size = wlan_cpu_to_le16(sizeof(HostCmd_DS_11N_DELBA) + S_DS_GEN); + + memcpy(priv->adapter, pdel_ba, pdata_buf, sizeof(HostCmd_DS_11N_DELBA)); + pdel_ba->del_ba_param_set = wlan_cpu_to_le16(pdel_ba->del_ba_param_set); + pdel_ba->reason_code = wlan_cpu_to_le16(pdel_ba->reason_code); + pdel_ba->del_result = 0; + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function will identify if RxReodering is needed for the packet + * and will do the reordering if required before sending it to kernel + * + * @param priv A pointer to mlan_private + * @param seq_num Seqence number of the current packet + * @param tid Tid of the current packet + * @param ta Transmiter address of the current packet + * @param pkt_type Packetype for the current packet (to identify if its a BAR) + * @param payload Pointer to the payload + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +mlan_11n_rxreorder_pkt(void *priv, t_u16 seq_num, t_u16 tid, + t_u8 *ta, t_u8 pkt_type, void *payload) +{ + RxReorderTbl *rx_reor_tbl_ptr; + int prev_start_win, start_win, end_win, win_size; + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_adapter pmadapter = ((mlan_private *)priv)->adapter; + + ENTER(); + + rx_reor_tbl_ptr = + wlan_11n_get_rxreorder_tbl((mlan_private *)priv, tid, ta); + if (!rx_reor_tbl_ptr || rx_reor_tbl_ptr->win_size <= 1) { + if (pkt_type != PKT_TYPE_BAR) + wlan_11n_dispatch_pkt(priv, payload); + + LEAVE(); + return ret; + + } else { + if (rx_reor_tbl_ptr->flush_data) { + rx_reor_tbl_ptr->flush_data = MFALSE; + wlan_start_flush_data(priv, rx_reor_tbl_ptr); + } + if ((pkt_type == PKT_TYPE_AMSDU) && !rx_reor_tbl_ptr->amsdu) { + wlan_11n_dispatch_pkt(priv, payload); + LEAVE(); + return ret; + } + if (pkt_type == PKT_TYPE_BAR) + PRINTM(MDAT_D, "BAR "); + if (pkt_type == PKT_TYPE_AMSDU) + PRINTM(MDAT_D, "AMSDU "); + + if (rx_reor_tbl_ptr->check_start_win) { + if (seq_num == rx_reor_tbl_ptr->start_win) + rx_reor_tbl_ptr->check_start_win = MFALSE; + else { + rx_reor_tbl_ptr->pkt_count++; + if (rx_reor_tbl_ptr->pkt_count < + (rx_reor_tbl_ptr->win_size / 2)) { + if (rx_reor_tbl_ptr->last_seq == + seq_num) { + /** drop duplicate packet */ + ret = MLAN_STATUS_FAILURE; + } else { + /** forward the packet to kernel */ + rx_reor_tbl_ptr->last_seq = + seq_num; + if (pkt_type != PKT_TYPE_BAR) + wlan_11n_dispatch_pkt + (priv, payload); + } + LEAVE(); + return ret; + } + rx_reor_tbl_ptr->check_start_win = MFALSE; + if ((seq_num != rx_reor_tbl_ptr->start_win) && + (rx_reor_tbl_ptr->last_seq != + DEFAULT_SEQ_NUM)) { + end_win = + (rx_reor_tbl_ptr->start_win + + rx_reor_tbl_ptr->win_size - + 1) & (MAX_TID_VALUE - 1); + if (((end_win > + rx_reor_tbl_ptr->start_win) + && (rx_reor_tbl_ptr->last_seq >= + rx_reor_tbl_ptr->start_win) + && (rx_reor_tbl_ptr->last_seq < + end_win)) || + ((end_win < + rx_reor_tbl_ptr->start_win) && + ((rx_reor_tbl_ptr->last_seq >= + rx_reor_tbl_ptr->start_win) || + (rx_reor_tbl_ptr->last_seq < + end_win)))) { + PRINTM(MDAT_D, + "Update start_win: last_seq=%d, start_win=%d seq_num=%d\n", + rx_reor_tbl_ptr-> + last_seq, + rx_reor_tbl_ptr-> + start_win, seq_num); + rx_reor_tbl_ptr->start_win = + rx_reor_tbl_ptr-> + last_seq + 1; + } else if ((seq_num < + rx_reor_tbl_ptr->start_win) + && (seq_num > + rx_reor_tbl_ptr-> + last_seq)) { + PRINTM(MDAT_D, + "Update start_win: last_seq=%d, start_win=%d seq_num=%d\n", + rx_reor_tbl_ptr-> + last_seq, + rx_reor_tbl_ptr-> + start_win, seq_num); + rx_reor_tbl_ptr->start_win = + rx_reor_tbl_ptr-> + last_seq + 1; + } + } + } + } + if (rx_reor_tbl_ptr->force_no_drop) { + wlan_11n_dispatch_pkt_until_start_win(priv, + rx_reor_tbl_ptr, + (rx_reor_tbl_ptr-> + start_win + + rx_reor_tbl_ptr-> + win_size) + & (MAX_TID_VALUE - + 1)); + if (pkt_type != PKT_TYPE_BAR) + rx_reor_tbl_ptr->start_win = seq_num; + mlan_11n_rxreorder_timer_restart(pmadapter, + rx_reor_tbl_ptr); + } + + prev_start_win = start_win = rx_reor_tbl_ptr->start_win; + win_size = rx_reor_tbl_ptr->win_size; + end_win = ((start_win + win_size) - 1) & (MAX_TID_VALUE - 1); + + PRINTM(MDAT_D, "TID %d, TA " MACSTR "\n", tid, MAC2STR(ta)); + PRINTM(MDAT_D, + "1:seq_num %d start_win %d win_size %d end_win %d\n", + seq_num, start_win, win_size, end_win); + /* + * If seq_num is less then starting win then ignore and drop + * the packet + */ + if (rx_reor_tbl_ptr->force_no_drop) { + PRINTM(MDAT_D, "No drop packet\n"); + rx_reor_tbl_ptr->force_no_drop = MFALSE; + } else { + /* Wrap */ + if ((start_win + TWOPOW11) > (MAX_TID_VALUE - 1)) { + if (seq_num >= ((start_win + (TWOPOW11)) & + (MAX_TID_VALUE - 1)) && + (seq_num < start_win)) { + if (pkt_type == PKT_TYPE_BAR) + PRINTM(MDAT_D, + "BAR: start_win=%d, end_win=%d, seq_num=%d\n", + start_win, end_win, + seq_num); + ret = MLAN_STATUS_FAILURE; + goto done; + } + } else if ((seq_num < start_win) || + (seq_num >= (start_win + (TWOPOW11)))) { + if (pkt_type == PKT_TYPE_BAR) + PRINTM(MDAT_D, + "BAR: start_win=%d, end_win=%d, seq_num=%d\n", + start_win, end_win, seq_num); + ret = MLAN_STATUS_FAILURE; + goto done; + } + } + + /* + * If this packet is a BAR we adjust seq_num as + * WinStart = seq_num + */ + if (pkt_type == PKT_TYPE_BAR) + seq_num = + ((seq_num + win_size) - 1) & (MAX_TID_VALUE - + 1); + + PRINTM(MDAT_D, + "2:seq_num %d start_win %d win_size %d end_win %d\n", + seq_num, start_win, win_size, end_win); + + if (((end_win < start_win) && + (seq_num < start_win) && (seq_num > end_win)) + || ((end_win > start_win) && + ((seq_num > end_win) || (seq_num < start_win)))) { + + end_win = seq_num; + if (((seq_num - win_size) + 1) >= 0) + start_win = (end_win - win_size) + 1; + else + start_win = (MAX_TID_VALUE - + (win_size - seq_num)) + 1; + ret = wlan_11n_dispatch_pkt_until_start_win(priv, + rx_reor_tbl_ptr, + start_win); + if (ret) + goto done; + } + + PRINTM(MDAT_D, "3:seq_num %d start_win %d win_size %d" + " end_win %d\n", seq_num, start_win, win_size, end_win); + if (pkt_type != PKT_TYPE_BAR) { + if (seq_num >= start_win) { + if (rx_reor_tbl_ptr->rx_reorder_ptr[seq_num + - + start_win]) + { + PRINTM(MDAT_D, "Drop Duplicate Pkt\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + rx_reor_tbl_ptr->rx_reorder_ptr[seq_num + - start_win] = + payload; + } else { /* Wrap condition */ + if (rx_reor_tbl_ptr->rx_reorder_ptr[(seq_num + + + (MAX_TID_VALUE)) + - + start_win]) + { + PRINTM(MDAT_D, "Drop Duplicate Pkt\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + rx_reor_tbl_ptr->rx_reorder_ptr[(seq_num + + + (MAX_TID_VALUE)) + - start_win] = + payload; + } + } + + wlan_11n_display_tbl_ptr(pmadapter, rx_reor_tbl_ptr); + + /* + * Dispatch all packets sequentially from start_win until a + * hole is found and adjust the start_win appropriately + */ + ret = wlan_11n_scan_and_dispatch(priv, rx_reor_tbl_ptr); + + wlan_11n_display_tbl_ptr(pmadapter, rx_reor_tbl_ptr); + } + +done: + if (!rx_reor_tbl_ptr->timer_context.timer_is_set || + (prev_start_win != rx_reor_tbl_ptr->start_win)) { + + mlan_11n_rxreorder_timer_restart(pmadapter, rx_reor_tbl_ptr); + } + + LEAVE(); + return ret; +} + +/** + * @brief This function will delete an entry for a given tid/ta pair. tid/ta + * are taken from delba_event body + * + * @param priv A pointer to mlan_private + * @param tid tid to send delba + * @param peer_mac MAC address to send delba + * @param type TYPE_DELBA_SENT or TYPE_DELBA_RECEIVE + * @param initiator MTRUE if we are initiator of ADDBA, MFALSE otherwise + * @param reason_code delete ba reason + * + * @return N/A + */ +void +mlan_11n_delete_bastream_tbl(mlan_private *priv, int tid, + t_u8 *peer_mac, t_u8 type, int initiator, + t_u16 reason_code) +{ + RxReorderTbl *rx_reor_tbl_ptr; + TxBAStreamTbl *ptxtbl; + t_u8 cleanup_rx_reorder_tbl; + raListTbl *ra_list = MNULL; + int tid_down; + + ENTER(); + + if (type == TYPE_DELBA_RECEIVE) + cleanup_rx_reorder_tbl = (initiator) ? MTRUE : MFALSE; + else + cleanup_rx_reorder_tbl = (initiator) ? MFALSE : MTRUE; + + PRINTM(MEVENT, "delete_bastream_tbl: " MACSTR " tid=%d, type=%d" + "initiator=%d reason=%d\n", MAC2STR(peer_mac), tid, type, + initiator, reason_code); + + if (cleanup_rx_reorder_tbl) { + rx_reor_tbl_ptr = + wlan_11n_get_rxreorder_tbl(priv, tid, peer_mac); + if (!rx_reor_tbl_ptr) { + PRINTM(MWARN, "TID, TA not found in table!\n"); + LEAVE(); + return; + } + wlan_11n_delete_rxreorder_tbl_entry(priv, rx_reor_tbl_ptr); + } else { + wlan_request_ralist_lock(priv); + ptxtbl = wlan_11n_get_txbastream_tbl(priv, tid, peer_mac, + MFALSE); + if (!ptxtbl) { + PRINTM(MWARN, "TID, RA not found in table!\n"); + wlan_release_ralist_lock(priv); + LEAVE(); + return; + } + wlan_11n_delete_txbastream_tbl_entry(priv, ptxtbl); + wlan_release_ralist_lock(priv); + tid_down = wlan_get_wmm_tid_down(priv, tid); + ra_list = wlan_wmm_get_ralist_node(priv, tid_down, peer_mac); + if (ra_list) { + ra_list->amsdu_in_ampdu = MFALSE; + ra_list->ba_status = BA_STREAM_NOT_SETUP; + if (type == TYPE_DELBA_RECEIVE) { + if (reason_code == REASON_CODE_STA_TIMEOUT) + ra_list->del_ba_count = 0; + else + ra_list->del_ba_count++; + ra_list->packet_count = 0; + ra_list->ba_packet_threshold = + wlan_get_random_ba_threshold(priv-> + adapter); + } + } + } + + LEAVE(); +} + +/** + * @brief This function handles the command response of + * a block ack response + * + * @param priv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_ret_11n_addba_resp(mlan_private *priv, HostCmd_DS_COMMAND *resp) +{ + HostCmd_DS_11N_ADDBA_RSP *padd_ba_rsp = (HostCmd_DS_11N_ADDBA_RSP *) + &resp->params.add_ba_rsp; + int tid; + RxReorderTbl *rx_reor_tbl_ptr = MNULL; + + ENTER(); + + padd_ba_rsp->status_code = wlan_le16_to_cpu(padd_ba_rsp->status_code); + padd_ba_rsp->block_ack_param_set = + wlan_le16_to_cpu(padd_ba_rsp->block_ack_param_set); + padd_ba_rsp->block_ack_tmo = + wlan_le16_to_cpu(padd_ba_rsp->block_ack_tmo); + padd_ba_rsp->ssn = wlan_le16_to_cpu(padd_ba_rsp->ssn); + + tid = (padd_ba_rsp->block_ack_param_set & BLOCKACKPARAM_TID_MASK) + >> BLOCKACKPARAM_TID_POS; + /* Check if we had rejected the ADDBA, if yes then do not create the + * stream + */ + if (padd_ba_rsp->status_code == BA_RESULT_SUCCESS) { + PRINTM(MCMND, + "ADDBA RSP: " MACSTR + " tid=%d ssn=%d win_size=%d,amsdu=%d\n", + MAC2STR(padd_ba_rsp->peer_mac_addr), tid, + padd_ba_rsp->ssn, + ((padd_ba_rsp-> + block_ack_param_set & BLOCKACKPARAM_WINSIZE_MASK) + >> BLOCKACKPARAM_WINSIZE_POS), + padd_ba_rsp-> + block_ack_param_set & BLOCKACKPARAM_AMSDU_SUPP_MASK); + + rx_reor_tbl_ptr = + wlan_11n_get_rxreorder_tbl(priv, tid, + padd_ba_rsp->peer_mac_addr); + if (rx_reor_tbl_ptr) { + rx_reor_tbl_ptr->ba_status = BA_STREAM_SETUP_COMPLETE; + if ((padd_ba_rsp-> + block_ack_param_set & + BLOCKACKPARAM_AMSDU_SUPP_MASK) && + priv->add_ba_param.rx_amsdu) + rx_reor_tbl_ptr->amsdu = MTRUE; + else + rx_reor_tbl_ptr->amsdu = MFALSE; + } + } else { + PRINTM(MERROR, "ADDBA RSP: Failed(" MACSTR " tid=%d)\n", + MAC2STR(padd_ba_rsp->peer_mac_addr), tid); + rx_reor_tbl_ptr = wlan_11n_get_rxreorder_tbl(priv, tid, + padd_ba_rsp-> + peer_mac_addr); + if (rx_reor_tbl_ptr) + wlan_11n_delete_rxreorder_tbl_entry(priv, + rx_reor_tbl_ptr); + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles ba_stream_timeout event + * + * @param priv A pointer to mlan_private + * @param event A pointer to structure HostCmd_DS_11N_BATIMEOUT + * + * @return N/A + */ +void +wlan_11n_ba_stream_timeout(mlan_private *priv, HostCmd_DS_11N_BATIMEOUT *event) +{ + HostCmd_DS_11N_DELBA delba; + + ENTER(); + + DBG_HEXDUMP(MCMD_D, "Event:", (t_u8 *)event, 20); + + memset(priv->adapter, &delba, 0, sizeof(HostCmd_DS_11N_DELBA)); + memcpy(priv->adapter, delba.peer_mac_addr, event->peer_mac_addr, + MLAN_MAC_ADDR_LENGTH); + + delba.del_ba_param_set |= (t_u16)event->tid << DELBA_TID_POS; + delba.del_ba_param_set |= + (t_u16)event->origninator << DELBA_INITIATOR_POS; + delba.reason_code = REASON_CODE_STA_TIMEOUT; + wlan_prepare_cmd(priv, HostCmd_CMD_11N_DELBA, 0, 0, MNULL, &delba); + + LEAVE(); + return; +} + +/** + * @brief This function cleans up reorder tbl + * + * @param priv A pointer to mlan_private + * + * @return N/A + */ +void +wlan_11n_cleanup_reorder_tbl(mlan_private *priv) +{ + RxReorderTbl *del_tbl_ptr; + + ENTER(); + + while ((del_tbl_ptr = (RxReorderTbl *) + util_peek_list(priv->adapter->pmoal_handle, + &priv->rx_reorder_tbl_ptr, + priv->adapter->callbacks.moal_spin_lock, + priv->adapter->callbacks.moal_spin_unlock))) { + wlan_11n_delete_rxreorder_tbl_entry(priv, del_tbl_ptr); + } + + util_init_list((pmlan_linked_list)&priv->rx_reorder_tbl_ptr); + + memset(priv->adapter, priv->rx_seq, 0xff, sizeof(priv->rx_seq)); + LEAVE(); +} + +/** + * @brief This function handle the rxba_sync event + * + * @param priv A pointer to mlan_private + * @param event_buf A pointer to event buf + * @param len event_buf length + * @return N/A + */ +void +wlan_11n_rxba_sync_event(mlan_private *priv, t_u8 *event_buf, t_u16 len) +{ + MrvlIEtypes_RxBaSync_t *tlv_rxba = (MrvlIEtypes_RxBaSync_t *)event_buf; + t_u16 tlv_type, tlv_len; + RxReorderTbl *rx_reor_tbl_ptr = MNULL; + t_u8 i, j; + t_u16 seq_num = 0; + int tlv_buf_left = len; + ENTER(); + + DBG_HEXDUMP(MEVT_D, "RXBA_SYNC_EVT", event_buf, len); + while (tlv_buf_left >= sizeof(MrvlIEtypes_RxBaSync_t)) { + tlv_type = wlan_le16_to_cpu(tlv_rxba->header.type); + tlv_len = wlan_le16_to_cpu(tlv_rxba->header.len); + if (tlv_type != TLV_TYPE_RXBA_SYNC) { + PRINTM(MERROR, "Wrong TLV id=0x%x\n", tlv_type); + goto done; + } + tlv_rxba->seq_num = wlan_le16_to_cpu(tlv_rxba->seq_num); + tlv_rxba->bitmap_len = wlan_le16_to_cpu(tlv_rxba->bitmap_len); + PRINTM(MEVENT, MACSTR " tid=%d seq_num=%d bitmap_len=%d\n", + MAC2STR(tlv_rxba->mac), tlv_rxba->tid, tlv_rxba->seq_num, + tlv_rxba->bitmap_len); + rx_reor_tbl_ptr = + wlan_11n_get_rxreorder_tbl(priv, tlv_rxba->tid, + tlv_rxba->mac); + if (!rx_reor_tbl_ptr) { + PRINTM(MEVENT, "Can not find rx_reorder_tbl\n"); + goto done; + } + if (rx_reor_tbl_ptr->force_no_drop) { + PRINTM(MEVENT, "Ignore RXBA_SYNC_EVT in resume\n"); + goto done; + } + for (i = 0; i < tlv_rxba->bitmap_len; i++) { + for (j = 0; j < 8; j++) { + if (tlv_rxba->bitmap[i] & (1 << j)) { + seq_num = + (tlv_rxba->seq_num + i * 8 + + j) & (MAX_TID_VALUE - 1); + PRINTM(MEVENT, + "Fw dropped packet, seq=%d start_win=%d, win_size=%d\n", + seq_num, + rx_reor_tbl_ptr->start_win, + rx_reor_tbl_ptr->win_size); + if (MLAN_STATUS_SUCCESS != + mlan_11n_rxreorder_pkt(priv, + seq_num, + tlv_rxba-> + tid, + tlv_rxba-> + mac, 0, + (t_void *) + RX_PKT_DROPPED_IN_FW)) + { + PRINTM(MERROR, + "Fail to handle dropped packet, seq=%d\n", + seq_num); + } + } + } + } + tlv_buf_left -= (sizeof(MrvlIEtypesHeader_t) + tlv_len); + tlv_rxba = + (MrvlIEtypes_RxBaSync_t *)((t_u8 *)tlv_rxba + tlv_len + + sizeof(MrvlIEtypesHeader_t)); + } +done: + LEAVE(); + return; +} + +/** + * @brief This function cleans up reorder tbl for specific station + * + * @param priv A pointer to mlan_private + * @param ta ta to find in reordering table + * @return N/A + */ +void +wlan_cleanup_reorder_tbl(mlan_private *priv, t_u8 *ta) +{ + RxReorderTbl *rx_reor_tbl_ptr = MNULL; + t_u8 i; + ENTER(); + for (i = 0; i < MAX_NUM_TID; ++i) { + rx_reor_tbl_ptr = wlan_11n_get_rxreorder_tbl(priv, i, ta); + if (rx_reor_tbl_ptr) + wlan_11n_delete_rxreorder_tbl_entry(priv, + rx_reor_tbl_ptr); + } + LEAVE(); + return; +} + +/** + * @brief This function will set force_no_drop flag in rxreorder_tbl. + * + * @param priv A pointer to mlan_private + * @param flag MTRUE/MFALSE + * + * @return N/A + */ +void +wlan_set_rxreorder_tbl_no_drop_flag(mlan_private *priv, t_u8 flag) +{ + RxReorderTbl *rx_reor_tbl_ptr; + + ENTER(); + + rx_reor_tbl_ptr = + (RxReorderTbl *)util_peek_list(priv->adapter->pmoal_handle, + &priv->rx_reorder_tbl_ptr, + priv->adapter->callbacks. + moal_spin_lock, + priv->adapter->callbacks. + moal_spin_unlock); + if (!rx_reor_tbl_ptr) { + LEAVE(); + return; + } + + while (rx_reor_tbl_ptr != (RxReorderTbl *)&priv->rx_reorder_tbl_ptr) { + rx_reor_tbl_ptr->force_no_drop = flag; + rx_reor_tbl_ptr = rx_reor_tbl_ptr->pnext; + } + + LEAVE(); + return; +} + +/** + * @brief This function update all the rx_reorder_tbl's force_no_drop flag + * + * @param pmadapter A pointer to mlan_adapter + * @param flag MTRUE/MFALSE + * @return N/A + */ +void +wlan_update_rxreorder_tbl(pmlan_adapter pmadapter, t_u8 flag) +{ + t_u8 i; + pmlan_private priv = MNULL; + for (i = 0; i < pmadapter->priv_num; i++) { + if (pmadapter->priv[i]) { + priv = pmadapter->priv[i]; + wlan_set_rxreorder_tbl_no_drop_flag(priv, flag); + } + } + return; +} + +/** + * @brief This function will flush the data in rxreorder_tbl. + * which has flush_data flag on. + * + * @param priv A pointer to mlan_private + * + * @return N/A + */ +void +wlan_flush_priv_rxreorder_tbl(mlan_private *priv) +{ + RxReorderTbl *rx_reor_tbl_ptr; + + ENTER(); + + rx_reor_tbl_ptr = + (RxReorderTbl *)util_peek_list(priv->adapter->pmoal_handle, + &priv->rx_reorder_tbl_ptr, + priv->adapter->callbacks. + moal_spin_lock, + priv->adapter->callbacks. + moal_spin_unlock); + if (!rx_reor_tbl_ptr) { + LEAVE(); + return; + } + + while (rx_reor_tbl_ptr != (RxReorderTbl *)&priv->rx_reorder_tbl_ptr) { + if (rx_reor_tbl_ptr->flush_data) { + rx_reor_tbl_ptr->flush_data = MFALSE; + wlan_start_flush_data(priv, rx_reor_tbl_ptr); + } + rx_reor_tbl_ptr = rx_reor_tbl_ptr->pnext; + } + + LEAVE(); + return; +} + +/** + * @brief This function update all the rx_reorder_tbl's force_no_drop flag + * + * @param pmadapter A pointer to mlan_adapter + * @return N/A + */ +void +wlan_flush_rxreorder_tbl(pmlan_adapter pmadapter) +{ + t_u8 i; + pmlan_private priv = MNULL; + for (i = 0; i < pmadapter->priv_num; i++) { + if (pmadapter->priv[i]) { + priv = pmadapter->priv[i]; + wlan_flush_priv_rxreorder_tbl(priv); + } + } + return; +} + +/** + * @brief This function update all the rx_win_size based on coex flag + * + * @param pmadapter A pointer to mlan_adapter + * @param coex_flag coex flag + * + * @return N/A + */ +void +wlan_update_ampdu_rxwinsize(pmlan_adapter pmadapter, t_u8 coex_flag) +{ + t_u8 i; + t_u32 rx_win_size = 0; + pmlan_private priv = MNULL; + + ENTER(); + if (!pmadapter->coex_rx_winsize) { + LEAVE(); + return; + } + PRINTM(MEVENT, "Update rxwinsize %d\n", coex_flag); + for (i = 0; i < pmadapter->priv_num; i++) { + if (pmadapter->priv[i]) { + priv = pmadapter->priv[i]; + rx_win_size = priv->add_ba_param.rx_win_size; + if (coex_flag == MTRUE) { +#ifdef STA_SUPPORT + if (priv->bss_type == MLAN_BSS_TYPE_STA) + priv->add_ba_param.rx_win_size = + MLAN_STA_COEX_AMPDU_DEF_RXWINSIZE; +#endif +#ifdef WIFI_DIRECT_SUPPORT + if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT) + priv->add_ba_param.rx_win_size = + MLAN_WFD_COEX_AMPDU_DEF_RXWINSIZE; +#endif + if (priv->bss_type == MLAN_BSS_TYPE_NAN) + priv->add_ba_param.rx_win_size = + MLAN_NAN_COEX_AMPDU_DEF_RXWINSIZE; +#ifdef UAP_SUPPORT + if (priv->bss_type == MLAN_BSS_TYPE_UAP) + priv->add_ba_param.rx_win_size = + MLAN_UAP_COEX_AMPDU_DEF_RXWINSIZE; +#endif + + } else { + priv->add_ba_param.rx_win_size = + priv->user_rxwinsize; + } + if (pmadapter->coex_win_size && + pmadapter->coex_rx_win_size) + priv->add_ba_param.rx_win_size = + pmadapter->coex_rx_win_size; + if (rx_win_size != priv->add_ba_param.rx_win_size) { + if (priv->media_connected == MTRUE) { + for (i = 0; i < MAX_NUM_TID; i++) + wlan_11n_delba(priv, i); + wlan_recv_event(priv, + MLAN_EVENT_ID_DRV_DEFER_HANDLING, + MNULL); + } + } + } + } + LEAVE(); + return; +} + +/** + * @brief This function updates ampdu rx_win_size + * + * @param pmadapter A pointer to mlan_adapter + * + * @return N/A + */ +void +wlan_coex_ampdu_rxwinsize(pmlan_adapter pmadapter) +{ + t_u8 i; + pmlan_private priv = MNULL; + t_u8 count = 0; + for (i = 0; i < pmadapter->priv_num; i++) { + if (pmadapter->priv[i]) { + priv = pmadapter->priv[i]; +#ifdef STA_SUPPORT + if (GET_BSS_ROLE((mlan_private *)priv) == + MLAN_BSS_ROLE_STA) { + if (priv->media_connected == MTRUE) + count++; + } +#endif +#ifdef UAP_SUPPORT + if (GET_BSS_ROLE((mlan_private *)priv) == + MLAN_BSS_ROLE_UAP) { + if (priv->uap_bss_started) + count++; + } +#endif + } + if (count >= 2) + break; + } + if (count >= 2) + wlan_update_ampdu_rxwinsize(pmadapter, MTRUE); + else + wlan_update_ampdu_rxwinsize(pmadapter, MFALSE); + return; +} diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_11n_rxreorder.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_11n_rxreorder.h new file mode 100644 index 000000000000..b6865de46ae0 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_11n_rxreorder.h @@ -0,0 +1,102 @@ +/** @file mlan_11n_rxreorder.h + * + * @brief This file contains related macros, enum, and struct + * of 11n RxReordering functionalities + * + * Copyright (C) 2008-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/******************************************************** +Change log: + 11/10/2008: initial version +********************************************************/ + +#ifndef _MLAN_11N_RXREORDER_H_ +#define _MLAN_11N_RXREORDER_H_ + +/** Max value a TID can take = 2^12 = 4096 */ +#define MAX_TID_VALUE (2 << 11) +/** 2^11 = 2048 */ +#define TWOPOW11 (2 << 10) + +/** Tid Mask used for extracting TID from BlockAckParamSet */ +#define BLOCKACKPARAM_TID_MASK 0x3C +/** Tid position in BlockAckParamSet */ +#define BLOCKACKPARAM_TID_POS 2 +/** WinSize Mask used for extracting WinSize from BlockAckParamSet */ +#define BLOCKACKPARAM_WINSIZE_MASK 0xffc0 +/** WinSize Mask used for extracting WinSize from BlockAckParamSet */ +#define BLOCKACKPARAM_AMSDU_SUPP_MASK 0x1 +/** WinSize position in BlockAckParamSet */ +#define BLOCKACKPARAM_WINSIZE_POS 6 +/** Position of TID in DelBA Param set */ +#define DELBA_TID_POS 12 +/** Position of INITIATOR in DelBA Param set */ +#define DELBA_INITIATOR_POS 11 +/** Reason code: Requested from peer STA as it does not want to + * use the mechanism */ +#define REASON_CODE_STA_DONT_WANT 37 +/** Reason code: Requested from peer STA due to timeout*/ +#define REASON_CODE_STA_TIMEOUT 39 +/** Type: send delba command */ +#define TYPE_DELBA_SENT 1 +/** Type: recieve delba command */ +#define TYPE_DELBA_RECEIVE 2 +/** Set Initiator Bit */ +#define DELBA_INITIATOR(paramset) (paramset = (paramset | (1 << 11))) +/** Reset Initiator Bit for recipient */ +#define DELBA_RECIPIENT(paramset) (paramset = (paramset & ~(1 << 11))) +/** Immediate block ack */ +#define IMMEDIATE_BLOCK_ACK 0x2 + +/** The request has been declined */ +#define ADDBA_RSP_STATUS_DECLINED 37 +/** ADDBA response status : Reject */ +#define ADDBA_RSP_STATUS_REJECT 1 +/** ADDBA response status : Accept */ +#define ADDBA_RSP_STATUS_ACCEPT 0 + +/** DEFAULT SEQ NUM */ +#define DEFAULT_SEQ_NUM 0xffff + +/** Indicate packet has been dropped in FW */ +#define RX_PKT_DROPPED_IN_FW 0xffffffff + +mlan_status mlan_11n_rxreorder_pkt(void *priv, t_u16 seqNum, t_u16 tid, + t_u8 *ta, t_u8 pkttype, void *payload); +void mlan_11n_delete_bastream_tbl(mlan_private *priv, int tid, + t_u8 *PeerMACAddr, t_u8 type, int initiator, + t_u16 reason_code); +void wlan_11n_ba_stream_timeout(mlan_private *priv, + HostCmd_DS_11N_BATIMEOUT *event); +mlan_status wlan_ret_11n_addba_resp(mlan_private *priv, + HostCmd_DS_COMMAND *resp); +mlan_status wlan_cmd_11n_delba(mlan_private *priv, HostCmd_DS_COMMAND *cmd, + void *pdata_buf); +mlan_status wlan_cmd_11n_addba_rspgen(mlan_private *priv, + HostCmd_DS_COMMAND *cmd, void *pdata_buf); +mlan_status wlan_cmd_11n_addba_req(mlan_private *priv, HostCmd_DS_COMMAND *cmd, + void *pdata_buf); +void wlan_11n_cleanup_reorder_tbl(mlan_private *priv); +RxReorderTbl *wlan_11n_get_rxreorder_tbl(mlan_private *priv, int tid, t_u8 *ta); +void wlan_11n_rxba_sync_event(mlan_private *priv, t_u8 *event_buf, t_u16 len); +void wlan_update_rxreorder_tbl(pmlan_adapter pmadapter, t_u8 flag); +void wlan_flush_rxreorder_tbl(pmlan_adapter pmadapter); +void wlan_coex_ampdu_rxwinsize(pmlan_adapter pmadapter); + +/** clean up reorder_tbl */ +void wlan_cleanup_reorder_tbl(mlan_private *priv, t_u8 *ta); +#endif /* _MLAN_11N_RXREORDER_H_ */ diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_cfp.c b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_cfp.c new file mode 100644 index 000000000000..be308042e735 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_cfp.c @@ -0,0 +1,2776 @@ +/** + * @file mlan_cfp.c + * + * @brief This file contains WLAN client mode channel, frequency and power + * related code + * + * Copyright (C) 2009-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/************************************************************* +Change Log: + 04/16/2009: initial version +************************************************************/ + +#include "mlan.h" +#include "mlan_util.h" +#include "mlan_fw.h" +#include "mlan_join.h" +#include "mlan_main.h" + +/******************************************************** + Local Variables +********************************************************/ + +/** 100mW */ +#define WLAN_TX_PWR_DEFAULT 20 +/** 100mW */ +#define WLAN_TX_PWR_00_DEFAULT 20 +/** 100mW */ +#define WLAN_TX_PWR_US_DEFAULT 20 +/** 100mW */ +#define WLAN_TX_PWR_JP_BG_DEFAULT 20 +/** 200mW */ +#define WLAN_TX_PWR_JP_A_DEFAULT 23 +/** 100mW */ +#define WLAN_TX_PWR_FR_100MW 20 +/** 10mW */ +#define WLAN_TX_PWR_FR_10MW 10 +/** 100mW */ +#define WLAN_TX_PWR_EMEA_DEFAULT 20 +/** 2000mW */ +#define WLAN_TX_PWR_CN_2000MW 33 +/** 200mW */ +#define WLAN_TX_PWR_200MW 23 +/** 1000mW */ +#define WLAN_TX_PWR_1000MW 30 +/** 30mW */ +#define WLAN_TX_PWR_SP_30MW 14 +/** 60mW */ +#define WLAN_TX_PWR_SP_60MW 17 +/** 25mW */ +#define WLAN_TX_PWR_25MW 14 +/** 250mW */ +#define WLAN_TX_PWR_250MW 24 + +/** Region code mapping */ +typedef struct _country_code_mapping { + /** Region */ + t_u8 country_code[COUNTRY_CODE_LEN]; + /** Code for B/G CFP table */ + t_u8 cfp_code_bg; + /** Code for A CFP table */ + t_u8 cfp_code_a; +} country_code_mapping_t; + +#define EU_CFP_CODE_BG 0x30 +#define EU_CFP_CODE_A 0x30 + +/** Region code mapping table */ +static country_code_mapping_t country_code_mapping[] = { + {"US", 0x10, 0x10}, /* US FCC */ + {"CA", 0x10, 0x20}, /* IC Canada */ + {"SG", 0x10, 0x10}, /* Singapore */ + {"EU", 0x30, 0x30}, /* ETSI */ + {"AU", 0x30, 0x30}, /* Australia */ + {"KR", 0x30, 0x30}, /* Republic Of Korea */ + {"JP", 0xFF, 0x40}, /* Japan */ + {"CN", 0x30, 0x50}, /* China */ + {"BR", 0x01, 0x09}, /* Brazil */ + {"RU", 0x30, 0x0f}, /* Russia */ + {"IN", 0x10, 0x06}, /* India */ + {"MY", 0x30, 0x06}, /* Malaysia */ +}; + +/** Country code for ETSI */ +static t_u8 eu_country_code_table[][COUNTRY_CODE_LEN] = { + "AL", "AD", "AT", "AU", "BY", "BE", "BA", "BG", "HR", "CY", + "CZ", "DK", "EE", "FI", "FR", "MK", "DE", "GR", "HU", "IS", + "IE", "IT", "KR", "LV", "LI", "LT", "LU", "MT", "MD", "MC", + "ME", "NL", "NO", "PL", "RO", "RU", "SM", "RS", "SI", "SK", + "ES", "SE", "CH", "TR", "UA", "UK", "GB" +}; + +/** + * The structure for Channel-Frequency-Power table + */ +typedef struct _cfp_table { + /** Region or Code */ + t_u8 code; + /** Frequency/Power */ + chan_freq_power_t *cfp; + /** No of CFP flag */ + int cfp_no; +} cfp_table_t; + +/* Format { Channel, Frequency (MHz), MaxTxPower } */ +/** Band: 'B/G', Region: USA FCC/Canada IC */ +static chan_freq_power_t channel_freq_power_US_BG[] = { + {1, 2412, WLAN_TX_PWR_US_DEFAULT, MFALSE}, + {2, 2417, WLAN_TX_PWR_US_DEFAULT, MFALSE}, + {3, 2422, WLAN_TX_PWR_US_DEFAULT, MFALSE}, + {4, 2427, WLAN_TX_PWR_US_DEFAULT, MFALSE}, + {5, 2432, WLAN_TX_PWR_US_DEFAULT, MFALSE}, + {6, 2437, WLAN_TX_PWR_US_DEFAULT, MFALSE}, + {7, 2442, WLAN_TX_PWR_US_DEFAULT, MFALSE}, + {8, 2447, WLAN_TX_PWR_US_DEFAULT, MFALSE}, + {9, 2452, WLAN_TX_PWR_US_DEFAULT, MFALSE}, + {10, 2457, WLAN_TX_PWR_US_DEFAULT, MFALSE}, + {11, 2462, WLAN_TX_PWR_US_DEFAULT, MFALSE} +}; + +/** Band: 'B/G', Region: Europe ETSI/China */ +static chan_freq_power_t channel_freq_power_EU_BG[] = { + {1, 2412, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE}, + {2, 2417, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE}, + {3, 2422, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE}, + {4, 2427, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE}, + {5, 2432, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE}, + {6, 2437, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE}, + {7, 2442, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE}, + {8, 2447, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE}, + {9, 2452, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE}, + {10, 2457, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE}, + {11, 2462, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE}, + {12, 2467, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE}, + {13, 2472, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE} +}; + +/** Band: 'B/G', Region: Japan */ +static chan_freq_power_t channel_freq_power_JPN41_BG[] = { + {1, 2412, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {2, 2417, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {3, 2422, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {4, 2427, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {5, 2432, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {6, 2437, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {7, 2442, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {8, 2447, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {9, 2452, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {10, 2457, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {11, 2462, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {12, 2467, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {13, 2472, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE} +}; + +/** Band: 'B/G', Region: Japan */ +static chan_freq_power_t channel_freq_power_JPN40_BG[] = { + {14, 2484, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE} +}; + +/** Band: 'B/G', Region: Japan */ +static chan_freq_power_t channel_freq_power_JPNFE_BG[] = { + {1, 2412, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {2, 2417, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {3, 2422, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {4, 2427, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {5, 2432, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {6, 2437, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {7, 2442, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {8, 2447, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {9, 2452, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {10, 2457, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {11, 2462, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {12, 2467, WLAN_TX_PWR_JP_BG_DEFAULT, MTRUE}, + {13, 2472, WLAN_TX_PWR_JP_BG_DEFAULT, MTRUE} +}; + +/** Band : 'B/G', Region: Brazil */ +static chan_freq_power_t channel_freq_power_BR_BG[] = { + {1, 2412, WLAN_TX_PWR_1000MW, MFALSE}, + {2, 2417, WLAN_TX_PWR_1000MW, MFALSE}, + {3, 2422, WLAN_TX_PWR_1000MW, MFALSE}, + {4, 2427, WLAN_TX_PWR_1000MW, MFALSE}, + {5, 2432, WLAN_TX_PWR_1000MW, MFALSE}, + {6, 2437, WLAN_TX_PWR_1000MW, MFALSE}, + {7, 2442, WLAN_TX_PWR_1000MW, MFALSE}, + {8, 2447, WLAN_TX_PWR_1000MW, MFALSE}, + {9, 2452, WLAN_TX_PWR_1000MW, MFALSE}, + {10, 2457, WLAN_TX_PWR_1000MW, MFALSE}, + {11, 2462, WLAN_TX_PWR_1000MW, MFALSE}, + {12, 2467, WLAN_TX_PWR_1000MW, MFALSE}, + {13, 2472, WLAN_TX_PWR_1000MW, MFALSE}, +}; + +/** Band : 'B/G', Region: Special */ +static chan_freq_power_t channel_freq_power_SPECIAL_BG[] = { + {1, 2412, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {2, 2417, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {3, 2422, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {4, 2427, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {5, 2432, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {6, 2437, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {7, 2442, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {8, 2447, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {9, 2452, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {10, 2457, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {11, 2462, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {12, 2467, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {13, 2472, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {14, 2484, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE} +}; + +/** + * The 2.4GHz CFP tables + */ +static cfp_table_t cfp_table_BG[] = { + { + 0x01, /* Brazil */ + channel_freq_power_BR_BG, + NELEMENTS(channel_freq_power_BR_BG), + }, + {0x10, /* US FCC */ + channel_freq_power_US_BG, + NELEMENTS(channel_freq_power_US_BG), + }, + {0x20, /* CANADA IC */ + channel_freq_power_US_BG, + NELEMENTS(channel_freq_power_US_BG), + }, + {0x30, /* EU */ + channel_freq_power_EU_BG, + NELEMENTS(channel_freq_power_EU_BG), + }, + {0x40, /* JAPAN */ + channel_freq_power_JPN40_BG, + NELEMENTS(channel_freq_power_JPN40_BG), + }, + {0x41, /* JAPAN */ + channel_freq_power_JPN41_BG, + NELEMENTS(channel_freq_power_JPN41_BG), + }, + {0x50, /* China */ + channel_freq_power_EU_BG, + NELEMENTS(channel_freq_power_EU_BG), + }, + { + 0xfe, /* JAPAN */ + channel_freq_power_JPNFE_BG, + NELEMENTS(channel_freq_power_JPNFE_BG), + }, + {0xff, /* Special */ + channel_freq_power_SPECIAL_BG, + NELEMENTS(channel_freq_power_SPECIAL_BG), + }, +/* Add new region here */ +}; + +/** Number of the CFP tables for 2.4GHz */ +#define MLAN_CFP_TABLE_SIZE_BG (NELEMENTS(cfp_table_BG)) + +/* Format { Channel, Frequency (MHz), MaxTxPower, DFS } */ +/** Band: 'A', Region: World FCC */ + +/* Format { Channel, Frequency (MHz), MaxTxPower, DFS } */ +/** Band: 'A', Region: USA FCC */ +static chan_freq_power_t channel_freq_power_A[] = { + {36, 5180, WLAN_TX_PWR_US_DEFAULT, MFALSE}, + {40, 5200, WLAN_TX_PWR_US_DEFAULT, MFALSE}, + {44, 5220, WLAN_TX_PWR_US_DEFAULT, MFALSE}, + {48, 5240, WLAN_TX_PWR_US_DEFAULT, MFALSE}, + {52, 5260, WLAN_TX_PWR_US_DEFAULT, MTRUE}, + {56, 5280, WLAN_TX_PWR_US_DEFAULT, MTRUE}, + {60, 5300, WLAN_TX_PWR_US_DEFAULT, MTRUE}, + {64, 5320, WLAN_TX_PWR_US_DEFAULT, MTRUE}, + {100, 5500, WLAN_TX_PWR_US_DEFAULT, MTRUE}, + {104, 5520, WLAN_TX_PWR_US_DEFAULT, MTRUE}, + {108, 5540, WLAN_TX_PWR_US_DEFAULT, MTRUE}, + {112, 5560, WLAN_TX_PWR_US_DEFAULT, MTRUE}, + {116, 5580, WLAN_TX_PWR_US_DEFAULT, MTRUE}, + {120, 5600, WLAN_TX_PWR_US_DEFAULT, MTRUE}, + {124, 5620, WLAN_TX_PWR_US_DEFAULT, MTRUE}, + {128, 5640, WLAN_TX_PWR_US_DEFAULT, MTRUE}, + {132, 5660, WLAN_TX_PWR_US_DEFAULT, MTRUE}, + {136, 5680, WLAN_TX_PWR_US_DEFAULT, MTRUE}, + {140, 5700, WLAN_TX_PWR_US_DEFAULT, MTRUE}, + {149, 5745, WLAN_TX_PWR_US_DEFAULT, MFALSE}, + {153, 5765, WLAN_TX_PWR_US_DEFAULT, MFALSE}, + {157, 5785, WLAN_TX_PWR_US_DEFAULT, MFALSE}, + {161, 5805, WLAN_TX_PWR_US_DEFAULT, MFALSE}, + {165, 5825, WLAN_TX_PWR_US_DEFAULT, MFALSE} +}; + +/** Band: 'A', Region: Canada IC */ +static chan_freq_power_t channel_freq_power_CAN_A[] = { + {36, 5180, WLAN_TX_PWR_US_DEFAULT, MFALSE}, + {40, 5200, WLAN_TX_PWR_US_DEFAULT, MFALSE}, + {44, 5220, WLAN_TX_PWR_US_DEFAULT, MFALSE}, + {48, 5240, WLAN_TX_PWR_US_DEFAULT, MFALSE}, + {52, 5260, WLAN_TX_PWR_US_DEFAULT, MTRUE}, + {56, 5280, WLAN_TX_PWR_US_DEFAULT, MTRUE}, + {60, 5300, WLAN_TX_PWR_US_DEFAULT, MTRUE}, + {64, 5320, WLAN_TX_PWR_US_DEFAULT, MTRUE}, + {100, 5500, WLAN_TX_PWR_US_DEFAULT, MTRUE}, + {104, 5520, WLAN_TX_PWR_US_DEFAULT, MTRUE}, + {108, 5540, WLAN_TX_PWR_US_DEFAULT, MTRUE}, + {112, 5560, WLAN_TX_PWR_US_DEFAULT, MTRUE}, + {116, 5580, WLAN_TX_PWR_US_DEFAULT, MTRUE}, + {132, 5660, WLAN_TX_PWR_US_DEFAULT, MTRUE}, + {136, 5680, WLAN_TX_PWR_US_DEFAULT, MTRUE}, + {140, 5700, WLAN_TX_PWR_US_DEFAULT, MTRUE}, + {149, 5745, WLAN_TX_PWR_US_DEFAULT, MFALSE}, + {153, 5765, WLAN_TX_PWR_US_DEFAULT, MFALSE}, + {157, 5785, WLAN_TX_PWR_US_DEFAULT, MFALSE}, + {161, 5805, WLAN_TX_PWR_US_DEFAULT, MFALSE}, + {165, 5825, WLAN_TX_PWR_US_DEFAULT, MFALSE} +}; + +/** Band: 'A', Region: Europe ETSI */ +static chan_freq_power_t channel_freq_power_EU_A[] = { + {36, 5180, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE}, + {40, 5200, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE}, + {44, 5220, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE}, + {48, 5240, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE}, + {52, 5260, WLAN_TX_PWR_EMEA_DEFAULT, MTRUE}, + {56, 5280, WLAN_TX_PWR_EMEA_DEFAULT, MTRUE}, + {60, 5300, WLAN_TX_PWR_EMEA_DEFAULT, MTRUE}, + {64, 5320, WLAN_TX_PWR_EMEA_DEFAULT, MTRUE}, + {100, 5500, WLAN_TX_PWR_EMEA_DEFAULT, MTRUE}, + {104, 5520, WLAN_TX_PWR_EMEA_DEFAULT, MTRUE}, + {108, 5540, WLAN_TX_PWR_EMEA_DEFAULT, MTRUE}, + {112, 5560, WLAN_TX_PWR_EMEA_DEFAULT, MTRUE}, + {116, 5580, WLAN_TX_PWR_EMEA_DEFAULT, MTRUE}, + {120, 5600, WLAN_TX_PWR_EMEA_DEFAULT, MTRUE}, + {124, 5620, WLAN_TX_PWR_EMEA_DEFAULT, MTRUE}, + {128, 5640, WLAN_TX_PWR_EMEA_DEFAULT, MTRUE}, + {132, 5660, WLAN_TX_PWR_EMEA_DEFAULT, MTRUE}, + {136, 5680, WLAN_TX_PWR_EMEA_DEFAULT, MTRUE}, + {140, 5700, WLAN_TX_PWR_EMEA_DEFAULT, MTRUE}, + {149, 5745, WLAN_TX_PWR_EMEA_DEFAULT, MTRUE}, + {153, 5765, WLAN_TX_PWR_EMEA_DEFAULT, MTRUE}, + {157, 5785, WLAN_TX_PWR_EMEA_DEFAULT, MTRUE}, + {161, 5805, WLAN_TX_PWR_EMEA_DEFAULT, MTRUE}, + {165, 5825, WLAN_TX_PWR_EMEA_DEFAULT, MTRUE} +}; + +/** Band: 'A', Region: Japan */ +static chan_freq_power_t channel_freq_power_JPN_A[] = { + {36, 5180, WLAN_TX_PWR_JP_A_DEFAULT, MFALSE}, + {40, 5200, WLAN_TX_PWR_JP_A_DEFAULT, MFALSE}, + {44, 5220, WLAN_TX_PWR_JP_A_DEFAULT, MFALSE}, + {48, 5240, WLAN_TX_PWR_JP_A_DEFAULT, MFALSE}, + {52, 5260, WLAN_TX_PWR_JP_A_DEFAULT, MTRUE}, + {56, 5280, WLAN_TX_PWR_JP_A_DEFAULT, MTRUE}, + {60, 5300, WLAN_TX_PWR_JP_A_DEFAULT, MTRUE}, + {64, 5320, WLAN_TX_PWR_JP_A_DEFAULT, MTRUE}, + {100, 5500, WLAN_TX_PWR_JP_A_DEFAULT, MTRUE}, + {104, 5520, WLAN_TX_PWR_JP_A_DEFAULT, MTRUE}, + {108, 5540, WLAN_TX_PWR_JP_A_DEFAULT, MTRUE}, + {112, 5560, WLAN_TX_PWR_JP_A_DEFAULT, MTRUE}, + {116, 5580, WLAN_TX_PWR_JP_A_DEFAULT, MTRUE}, + {120, 5600, WLAN_TX_PWR_JP_A_DEFAULT, MTRUE}, + {124, 5620, WLAN_TX_PWR_JP_A_DEFAULT, MTRUE}, + {128, 5640, WLAN_TX_PWR_JP_A_DEFAULT, MTRUE}, + {132, 5660, WLAN_TX_PWR_JP_A_DEFAULT, MTRUE}, + {136, 5680, WLAN_TX_PWR_JP_A_DEFAULT, MTRUE}, + {140, 5700, WLAN_TX_PWR_JP_A_DEFAULT, MTRUE} +}; + +/** Band: 'A', Region: China */ +static chan_freq_power_t channel_freq_power_CN_A[] = { + {36, 5180, WLAN_TX_PWR_200MW, MFALSE}, + {40, 5200, WLAN_TX_PWR_200MW, MFALSE}, + {44, 5220, WLAN_TX_PWR_200MW, MFALSE}, + {48, 5240, WLAN_TX_PWR_200MW, MFALSE}, + {52, 5260, WLAN_TX_PWR_200MW, MTRUE}, + {56, 5280, WLAN_TX_PWR_200MW, MTRUE}, + {60, 5300, WLAN_TX_PWR_200MW, MTRUE}, + {64, 5320, WLAN_TX_PWR_200MW, MTRUE}, + {149, 5745, WLAN_TX_PWR_CN_2000MW, MFALSE}, + {153, 5765, WLAN_TX_PWR_CN_2000MW, MFALSE}, + {157, 5785, WLAN_TX_PWR_CN_2000MW, MFALSE}, + {161, 5805, WLAN_TX_PWR_CN_2000MW, MFALSE}, + {165, 5825, WLAN_TX_PWR_CN_2000MW, MFALSE} +}; + +/** Band: 'A', NULL */ +static chan_freq_power_t channel_freq_power_NULL_A[] = { +}; + +/** Band: 'A', Region: Spain/Austria/Brazil */ +static chan_freq_power_t channel_freq_power_SPN2_A[] = { + {36, 5180, WLAN_TX_PWR_200MW, MFALSE}, + {40, 5200, WLAN_TX_PWR_200MW, MFALSE}, + {44, 5220, WLAN_TX_PWR_200MW, MFALSE}, + {48, 5240, WLAN_TX_PWR_200MW, MFALSE}, + {52, 5260, WLAN_TX_PWR_200MW, MTRUE}, + {56, 5280, WLAN_TX_PWR_200MW, MTRUE}, + {60, 5300, WLAN_TX_PWR_200MW, MTRUE}, + {64, 5320, WLAN_TX_PWR_200MW, MTRUE}, +}; + +/** Band: 'A', Region: Brazil */ +static chan_freq_power_t channel_freq_power_BR1_A[] = { + {100, 5500, WLAN_TX_PWR_250MW, MTRUE}, + {104, 5520, WLAN_TX_PWR_250MW, MTRUE}, + {108, 5540, WLAN_TX_PWR_250MW, MTRUE}, + {112, 5560, WLAN_TX_PWR_250MW, MTRUE}, + {116, 5580, WLAN_TX_PWR_250MW, MTRUE}, + {120, 5600, WLAN_TX_PWR_250MW, MTRUE}, + {124, 5620, WLAN_TX_PWR_250MW, MTRUE}, + {128, 5640, WLAN_TX_PWR_250MW, MTRUE}, + {132, 5660, WLAN_TX_PWR_250MW, MTRUE}, + {136, 5680, WLAN_TX_PWR_250MW, MTRUE}, + {140, 5700, WLAN_TX_PWR_250MW, MTRUE}, +}; + +/** Band: 'A', Region: Brazil */ +static chan_freq_power_t channel_freq_power_BR2_A[] = { + {149, 5745, WLAN_TX_PWR_1000MW, MFALSE}, + {153, 5765, WLAN_TX_PWR_1000MW, MFALSE}, + {157, 5785, WLAN_TX_PWR_1000MW, MFALSE}, + {161, 5805, WLAN_TX_PWR_1000MW, MFALSE}, + {165, 5825, WLAN_TX_PWR_1000MW, MFALSE} +}; + +/** Band: 'A', Region: Russia */ +static chan_freq_power_t channel_freq_power_RU_A[] = { + {36, 5180, WLAN_TX_PWR_DEFAULT, MFALSE}, + {40, 5200, WLAN_TX_PWR_DEFAULT, MFALSE}, + {44, 5220, WLAN_TX_PWR_DEFAULT, MFALSE}, + {48, 5240, WLAN_TX_PWR_DEFAULT, MFALSE}, + {52, 5260, WLAN_TX_PWR_DEFAULT, MFALSE}, + {56, 5280, WLAN_TX_PWR_DEFAULT, MFALSE}, + {60, 5300, WLAN_TX_PWR_DEFAULT, MFALSE}, + {64, 5320, WLAN_TX_PWR_DEFAULT, MFALSE}, + {132, 5660, WLAN_TX_PWR_DEFAULT, MFALSE}, + {136, 5680, WLAN_TX_PWR_DEFAULT, MFALSE}, + {140, 5700, WLAN_TX_PWR_DEFAULT, MFALSE}, + {149, 5745, WLAN_TX_PWR_DEFAULT, MFALSE}, + {153, 5765, WLAN_TX_PWR_DEFAULT, MFALSE}, + {157, 5785, WLAN_TX_PWR_DEFAULT, MFALSE}, + {161, 5805, WLAN_TX_PWR_DEFAULT, MFALSE}, +}; + +/** Band: 'A', Code: 1, Low band (5150-5250 MHz) channels */ +static chan_freq_power_t channel_freq_power_low_band[] = { + {36, 5180, WLAN_TX_PWR_DEFAULT, MFALSE}, + {40, 5200, WLAN_TX_PWR_DEFAULT, MFALSE}, + {44, 5220, WLAN_TX_PWR_DEFAULT, MFALSE}, + {48, 5240, WLAN_TX_PWR_DEFAULT, MFALSE}, +}; + +/** Band: 'A', Code: 2, Lower middle band (5250-5350 MHz) channels */ +static chan_freq_power_t channel_freq_power_lower_middle_band[] = { + {52, 5260, WLAN_TX_PWR_DEFAULT, MTRUE}, + {56, 5280, WLAN_TX_PWR_DEFAULT, MTRUE}, + {60, 5300, WLAN_TX_PWR_DEFAULT, MTRUE}, + {64, 5320, WLAN_TX_PWR_DEFAULT, MTRUE}, +}; + +/** Band: 'A', Code: 3, Upper middle band (5470-5725 MHz) channels */ +static chan_freq_power_t channel_freq_power_upper_middle_band[] = { + {100, 5500, WLAN_TX_PWR_DEFAULT, MTRUE}, + {104, 5520, WLAN_TX_PWR_DEFAULT, MTRUE}, + {108, 5540, WLAN_TX_PWR_DEFAULT, MTRUE}, + {112, 5560, WLAN_TX_PWR_DEFAULT, MTRUE}, + {116, 5580, WLAN_TX_PWR_DEFAULT, MTRUE}, + {120, 5600, WLAN_TX_PWR_DEFAULT, MTRUE}, + {124, 5620, WLAN_TX_PWR_DEFAULT, MTRUE}, + {128, 5640, WLAN_TX_PWR_DEFAULT, MTRUE}, + {132, 5660, WLAN_TX_PWR_DEFAULT, MTRUE}, + {136, 5680, WLAN_TX_PWR_DEFAULT, MTRUE}, + {140, 5700, WLAN_TX_PWR_DEFAULT, MTRUE}, +}; + +/** Band: 'A', Code: 4, High band (5725-5850 MHz) channels */ +static chan_freq_power_t channel_freq_power_high_band[] = { + {149, 5745, WLAN_TX_PWR_DEFAULT, MFALSE}, + {153, 5765, WLAN_TX_PWR_DEFAULT, MFALSE}, + {157, 5785, WLAN_TX_PWR_DEFAULT, MFALSE}, + {161, 5805, WLAN_TX_PWR_DEFAULT, MFALSE}, + {165, 5825, WLAN_TX_PWR_DEFAULT, MFALSE} +}; + +/** Band: 'A', Code: 5, Low band (5150-5250 MHz) and + * High band (5725-5850 MHz) channels */ +static chan_freq_power_t channel_freq_power_low_high_band[] = { + {36, 5180, WLAN_TX_PWR_DEFAULT, MFALSE}, + {40, 5200, WLAN_TX_PWR_DEFAULT, MFALSE}, + {44, 5220, WLAN_TX_PWR_DEFAULT, MFALSE}, + {48, 5240, WLAN_TX_PWR_DEFAULT, MFALSE}, + {149, 5745, WLAN_TX_PWR_DEFAULT, MFALSE}, + {153, 5765, WLAN_TX_PWR_DEFAULT, MFALSE}, + {157, 5785, WLAN_TX_PWR_DEFAULT, MFALSE}, + {161, 5805, WLAN_TX_PWR_DEFAULT, MFALSE}, + {165, 5825, WLAN_TX_PWR_DEFAULT, MFALSE} +}; + +/** Band: 'A', Code: 6, Low band (5150-5250 MHz) and + * mid low (5260-5320) and High band (5725-5850 MHz) channels */ +static chan_freq_power_t channel_freq_power_low_middle_high_band[] = { + {36, 5180, WLAN_TX_PWR_DEFAULT, MFALSE}, + {40, 5200, WLAN_TX_PWR_DEFAULT, MFALSE}, + {44, 5220, WLAN_TX_PWR_DEFAULT, MFALSE}, + {48, 5240, WLAN_TX_PWR_DEFAULT, MFALSE}, + {52, 5260, WLAN_TX_PWR_DEFAULT, MFALSE}, + {56, 5280, WLAN_TX_PWR_DEFAULT, MFALSE}, + {60, 5300, WLAN_TX_PWR_DEFAULT, MFALSE}, + {64, 5320, WLAN_TX_PWR_DEFAULT, MFALSE}, + {149, 5745, WLAN_TX_PWR_DEFAULT, MFALSE}, + {153, 5765, WLAN_TX_PWR_DEFAULT, MFALSE}, + {157, 5785, WLAN_TX_PWR_DEFAULT, MFALSE}, + {161, 5805, WLAN_TX_PWR_DEFAULT, MFALSE}, + {165, 5825, WLAN_TX_PWR_DEFAULT, MFALSE} +}; + +/** + * The 5GHz CFP tables + */ +static cfp_table_t cfp_table_A[] = { + {0x1, /* Low band (5150-5250 MHz) channels */ + channel_freq_power_low_band, + NELEMENTS(channel_freq_power_low_band) + }, + {0x2, /* Lower middle band (5250-5350 MHz) channels */ + channel_freq_power_lower_middle_band, + NELEMENTS(channel_freq_power_lower_middle_band) + }, + {0x3, /* Upper middle band (5470-5725 MHz) channels */ + channel_freq_power_upper_middle_band, + NELEMENTS(channel_freq_power_upper_middle_band) + }, + {0x4, /* High band (5725-5850 MHz) channels */ + channel_freq_power_high_band, + NELEMENTS(channel_freq_power_high_band) + }, + {0x5, /* Low band (5150-5250 MHz) and + High band (5725-5850 MHz) channels */ + channel_freq_power_low_high_band, + NELEMENTS(channel_freq_power_low_high_band) + }, + {0x6, /* Low band (5150-5250 MHz) + Mid band (5260-5320) and + High band (5725-5850 MHz) channels */ + channel_freq_power_low_middle_high_band, + NELEMENTS(channel_freq_power_low_middle_high_band) + }, + {0x09, /* SPAIN/Austria/Brazil */ + channel_freq_power_SPN2_A, + NELEMENTS(channel_freq_power_SPN2_A), + }, + {0x0c, /* Brazil */ + channel_freq_power_BR1_A, + NELEMENTS(channel_freq_power_BR1_A), + }, + {0x0e, /* Brazil */ + channel_freq_power_BR2_A, + NELEMENTS(channel_freq_power_BR2_A), + }, + {0x0f, /* Russia */ + channel_freq_power_RU_A, + NELEMENTS(channel_freq_power_RU_A), + }, + {0x10, /* US FCC */ + channel_freq_power_A, + NELEMENTS(channel_freq_power_A), + }, + {0x20, /* CANADA IC */ + channel_freq_power_CAN_A, + NELEMENTS(channel_freq_power_CAN_A), + }, + {0x30, /* EU */ + channel_freq_power_EU_A, + NELEMENTS(channel_freq_power_EU_A), + }, + {0x40, /* JAPAN */ + channel_freq_power_JPN_A, + NELEMENTS(channel_freq_power_JPN_A), + }, + {0x41, /* JAPAN */ + channel_freq_power_JPN_A, + NELEMENTS(channel_freq_power_JPN_A), + }, + {0x50, /* China */ + channel_freq_power_CN_A, + NELEMENTS(channel_freq_power_CN_A), + }, + {0xfe, /* JAPAN */ + channel_freq_power_NULL_A, + NELEMENTS(channel_freq_power_NULL_A), + }, + {0xff, /* Special */ + channel_freq_power_JPN_A, + NELEMENTS(channel_freq_power_JPN_A), + }, +/* Add new region here */ +}; + +/** Number of the CFP tables for 5GHz */ +#define MLAN_CFP_TABLE_SIZE_A (NELEMENTS(cfp_table_A)) + +enum { + RATEID_DBPSK1Mbps, //(0) + RATEID_DQPSK2Mbps, //(1) + RATEID_CCK5_5Mbps, //(2) + RATEID_CCK11Mbps, //(3) + RATEID_CCK22Mbps, //(4) + RATEID_OFDM6Mbps, //(5) + RATEID_OFDM9Mbps, //(6) + RATEID_OFDM12Mbps, //(7) + RATEID_OFDM18Mbps, //(8) + RATEID_OFDM24Mbps, //(9) + RATEID_OFDM36Mbps, //(10) + RATEID_OFDM48Mbps, //(11) + RATEID_OFDM54Mbps, //(12) + RATEID_OFDM72Mbps, //(13) +}; + +static const t_u8 rateUnit_500Kbps[] = { + (10 / 5), /* 1Mbps */ + (20 / 5), /* 2Mbps */ + + (55 / 5), /* 5.5Mbps */ + (110 / 5), /* 11Mbps */ + (10 / 5), /* 22Mbps, intentionally set to 1Mbps because it's not available */ + + (60 / 5), /* 6Mbps */ + (90 / 5), /* 9Mbps */ + (120 / 5), /* 12Mbps */ + (180 / 5), /* 18Mbps */ + (240 / 5), /* 24Mbps */ + (360 / 5), /* 36Mbps */ + (480 / 5), /* 48Mbps */ + (540 / 5), /* 54Mbps */ + (60 / 5), /* 72Mbps intentionally set to 6Mbps because it's not available */ +}; + +typedef struct _rate_map { + /** Rate, in 0.5Mbps */ + t_u32 rate; + /** Mrvl rate id, refer to RATEID_XXX in FW */ + t_u32 id; + /** nss: 0-nss1, 1-nss2 */ + t_u8 nss; +} rate_map; + +/** rate_map_table_1x1 is based on rate_map_table_2x2 and remove nss2 part. + * For the chip who only support 1x1, Mrvl rate idx define is different with 2x2 in FW + * We need redefine a bitrate to Mrvl rate idx table for 1x1 chip. + */ +const rate_map rate_map_table_1x1[] = { + /* LG <--> Mrvl rate idx */ + {2, 0, 0}, //RATEID_DBPSK1Mbps + {4, 1, 0}, //RATEID_DQPSK2Mbps + {11, 2, 0}, //RATEID_CCK5_5Mbps + {22, 3, 0}, //RATEID_CCK11Mbps + {44, 4, 0}, //RATEID_CCK22Mbps + {12, 5, 0}, //RATEID_OFDM6Mbps + {18, 6, 0}, //RATEID_OFDM9Mbps + {24, 7, 0}, //RATEID_OFDM12Mbps + {36, 8, 0}, //RATEID_OFDM18Mbps + {48, 9, 0}, //RATEID_OFDM24Mbps + {72, 10, 0}, //RATEID_OFDM36Mbps + {96, 11, 0}, //RATEID_OFDM48Mbps + {108, 12, 0}, //RATEID_OFDM54Mbps + {144, 13, 0}, //RATEID_OFDM72Mbps + + /* HT bw20 <--> Mrvl rate idx */ + {13, 14, 0}, //RATEID_MCS0_6d5Mbps + {26, 15, 0}, //RATEID_MCS1_13Mbps + {39, 16, 0}, //RATEID_MCS2_19d5Mbps + {52, 17, 0}, //RATEID_MCS3_26Mbps + {78, 18, 0}, //RATEID_MCS4_39Mbps + {104, 19, 0}, //RATEID_MCS5_52Mbps + {117, 20, 0}, //RATEID_MCS6_58d5Mbps + {130, 21, 0}, //RATEID_MCS7_65Mbps + + /* HT bw40<--> Mrvl rate idx */ + {12, 22, 0}, //RATEID_MCS32BW40_6Mbps, for 1x1 start from 22 + {27, 23, 0}, //RATEID_MCS0BW40_13d5Mbps + {54, 24, 0}, //RATEID_MCS1BW40_27Mbps + {81, 25, 0}, //RATEID_MCS2BW40_40d5Mbps + {108, 26, 0}, //RATEID_MCS3BW40_54Mbps + {162, 27, 0}, //RATEID_MCS4BW40_81Mbps + {216, 28, 0}, //RATEID_MCS5BW40_108Mbps + {243, 29, 0}, //RATEID_MCS6BW40_121d5Mbps + {270, 30, 0}, //RATEID_MCS7BW40_135Mbps + +}; + +/******************************************************** + Global Variables +********************************************************/ +/** + * The table to keep region code + */ +t_u16 region_code_index[MRVDRV_MAX_REGION_CODE] = { + 0x10, 0x20, 0x30, 0x40, 0x41, 0x50, 0xfe, 0xff +}; + +/** The table to keep CFP code for BG */ +t_u16 cfp_code_index_bg[MRVDRV_MAX_CFP_CODE_BG] = { }; + +/** The table to keep CFP code for A */ +t_u16 cfp_code_index_a[MRVDRV_MAX_CFP_CODE_A] = { 0x1, 0x2, 0x3, 0x4, 0x5 }; + +/** + * The rates supported for ad-hoc B mode + */ +t_u8 AdhocRates_B[B_SUPPORTED_RATES] = { 0x82, 0x84, 0x8b, 0x96, 0 }; + +/** + * The rates supported for ad-hoc G mode + */ +t_u8 AdhocRates_G[G_SUPPORTED_RATES] = { + 0x8c, 0x12, 0x98, 0x24, 0xb0, 0x48, 0x60, 0x6c, 0x00 +}; + +/** + * The rates supported for ad-hoc BG mode + */ +t_u8 AdhocRates_BG[BG_SUPPORTED_RATES] = { + 0x82, 0x84, 0x8b, 0x96, 0x0c, 0x12, 0x18, 0x24, 0x30, 0x48, + 0x60, 0x6c, 0x00 +}; + +/** + * The rates supported in A mode for ad-hoc + */ +t_u8 AdhocRates_A[A_SUPPORTED_RATES] = { + 0x8c, 0x12, 0x98, 0x24, 0xb0, 0x48, 0x60, 0x6c, 0x00 +}; + +/** + * The rates supported in A mode (used for BAND_A) + */ +t_u8 SupportedRates_A[A_SUPPORTED_RATES] = { + 0x0c, 0x12, 0x18, 0x24, 0xb0, 0x48, 0x60, 0x6c, 0x00 +}; + +/** + * The rates supported by the card + */ +t_u16 WlanDataRates[WLAN_SUPPORTED_RATES_EXT] = { + 0x02, 0x04, 0x0B, 0x16, 0x00, 0x0C, 0x12, + 0x18, 0x24, 0x30, 0x48, 0x60, 0x6C, 0x90, + 0x0D, 0x1A, 0x27, 0x34, 0x4E, 0x68, 0x75, + 0x82, 0x0C, 0x1B, 0x36, 0x51, 0x6C, 0xA2, + 0xD8, 0xF3, 0x10E, 0x00 +}; + +/** + * The rates supported in B mode + */ +t_u8 SupportedRates_B[B_SUPPORTED_RATES] = { + 0x02, 0x04, 0x0b, 0x16, 0x00 +}; + +/** + * The rates supported in G mode (BAND_G, BAND_G|BAND_GN) + */ +t_u8 SupportedRates_G[G_SUPPORTED_RATES] = { + 0x0c, 0x12, 0x18, 0x24, 0x30, 0x48, 0x60, 0x6c, 0x00 +}; + +/** + * The rates supported in BG mode (BAND_B|BAND_G, BAND_B|BAND_G|BAND_GN) + */ +t_u8 SupportedRates_BG[BG_SUPPORTED_RATES] = { + 0x02, 0x04, 0x0b, 0x0c, 0x12, 0x16, 0x18, 0x24, 0x30, 0x48, + 0x60, 0x6c, 0x00 +}; + +/** + * The rates supported in N mode + */ +t_u8 SupportedRates_N[N_SUPPORTED_RATES] = { 0x02, 0x04, 0 }; + +/******************************************************** + Local Functions +********************************************************/ +/** + * @brief Find a character in a string. + * + * @param pmadapter A pointer to mlan_adapter structure + * @param s A pointer to string + * @param c Character to be located + * @param n The length of string + * + * @return A pointer to the first occurrence of c in string, or MNULL if c is not found. + */ +static void * +wlan_memchr(pmlan_adapter pmadapter, void *s, int c, int n) +{ + const t_u8 *p = (t_u8 *)s; + + ENTER(); + + while (n--) { + if ((t_u8)c == *p++) { + LEAVE(); + return (void *)(p - 1); + } + } + + LEAVE(); + return MNULL; +} + +/** + * @brief This function finds the CFP in + * cfp_table_BG/A based on region/code and band parameter. + * + * @param pmadapter A pointer to mlan_adapter structure + * @param region The region code + * @param band The band + * @param cfp_no A pointer to CFP number + * + * @return A pointer to CFP + */ +static chan_freq_power_t * +wlan_get_region_cfp_table(pmlan_adapter pmadapter, t_u8 region, t_u8 band, + int *cfp_no) +{ + t_u32 i; + t_u8 cfp_bg, cfp_a; + + ENTER(); + + cfp_bg = cfp_a = region; + if (!region) { + /* Invalid region code, use CFP code */ + cfp_bg = pmadapter->cfp_code_bg; + cfp_a = pmadapter->cfp_code_a; + } + if (band & (BAND_B | BAND_G | BAND_GN)) { + /* Return the FW cfp table for requested region code, if available. + * If region is not forced and the requested region code is different, + * simply return the corresponding pre-defined table. + */ + if (pmadapter->otp_region && pmadapter->cfp_otp_bg) { + if (pmadapter->otp_region->force_reg || + (cfp_bg == + (t_u8)pmadapter->otp_region->region_code)) { + *cfp_no = FW_CFP_TABLE_MAX_ROWS_BG; + LEAVE(); + return pmadapter->cfp_otp_bg; + } + } + for (i = 0; i < MLAN_CFP_TABLE_SIZE_BG; i++) { + PRINTM(MINFO, "cfp_table_BG[%d].code=%d\n", i, + cfp_table_BG[i].code); + /* Check if region/code matches for BG bands */ + if (cfp_table_BG[i].code == cfp_bg) { + /* Select by band */ + *cfp_no = cfp_table_BG[i].cfp_no; + LEAVE(); + return cfp_table_BG[i].cfp; + } + } + } + if (band & (BAND_A | BAND_AN)) { + /* Return the FW cfp table for requested region code */ + if (pmadapter->otp_region && pmadapter->cfp_otp_a) { + if (pmadapter->otp_region->force_reg || + (cfp_a == + (t_u8)pmadapter->otp_region->region_code)) { + *cfp_no = FW_CFP_TABLE_MAX_ROWS_A; + LEAVE(); + return pmadapter->cfp_otp_a; + } + } + for (i = 0; i < MLAN_CFP_TABLE_SIZE_A; i++) { + PRINTM(MINFO, "cfp_table_A[%d].code=%d\n", i, + cfp_table_A[i].code); + /* Check if region/code matches for A bands */ + if (cfp_table_A[i].code == cfp_a) { + /* Select by band */ + *cfp_no = cfp_table_A[i].cfp_no; + LEAVE(); + return cfp_table_A[i].cfp; + } + } + } + + if (!region) + PRINTM(MERROR, "Error Band[0x%x] or code[BG:%#x, A:%#x]\n", + band, cfp_bg, cfp_a); + else + PRINTM(MERROR, "Error Band[0x%x] or region[%#x]\n", band, + region); + + LEAVE(); + return MNULL; +} + +/** + * @brief This function copies dynamic CFP elements from one table to another. + * Only copy elements where channel numbers match. + * + * @param pmadapter A pointer to mlan_adapter structure + * @param cfp Destination table + * @param num_cfp Number of elements in dest table + * @param cfp_src Source table + * @param num_cfp_src Number of elements in source table + */ +static t_void +wlan_cfp_copy_dynamic(pmlan_adapter pmadapter, + chan_freq_power_t *cfp, t_u8 num_cfp, + chan_freq_power_t *cfp_src, t_u8 num_cfp_src) +{ + int i, j; + ENTER(); + + /* first clear dest dynamic blacklisted entries */ + for (i = 0; i < num_cfp; i++) + cfp[i].dynamic.blacklist = MFALSE; + + /* copy dynamic blacklisted entries from source where channels match */ + if (cfp_src) { + for (i = 0; i < num_cfp; i++) + for (j = 0; j < num_cfp_src; j++) + if (cfp[i].channel == cfp_src[j].channel) { + cfp[i].dynamic.blacklist = + cfp_src[j].dynamic.blacklist; + break; + } + } + + LEAVE(); +} + +/******************************************************** + Global Functions +********************************************************/ +/** + * @brief This function converts region string to integer code + * + * @param pmadapter A pointer to mlan_adapter structure + * @param country_code Country string + * @param cfp_bg Pointer to buffer + * @param cfp_a Pointer to buffer + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_misc_country_2_cfp_table_code(pmlan_adapter pmadapter, t_u8 *country_code, + t_u8 *cfp_bg, t_u8 *cfp_a) +{ + t_u8 i; + + ENTER(); + + /* Look for code in mapping table */ + for (i = 0; i < NELEMENTS(country_code_mapping); i++) { + if (!memcmp(pmadapter, country_code_mapping[i].country_code, + country_code, COUNTRY_CODE_LEN - 1)) { + *cfp_bg = country_code_mapping[i].cfp_code_bg; + *cfp_a = country_code_mapping[i].cfp_code_a; + LEAVE(); + return MLAN_STATUS_SUCCESS; + } + } + + /* If still not found, look for code in EU country code table */ + for (i = 0; i < NELEMENTS(eu_country_code_table); i++) { + if (!memcmp(pmadapter, eu_country_code_table[i], + country_code, COUNTRY_CODE_LEN - 1)) { + *cfp_bg = EU_CFP_CODE_BG; + *cfp_a = EU_CFP_CODE_A; + LEAVE(); + return MLAN_STATUS_SUCCESS; + } + } + + LEAVE(); + return MLAN_STATUS_FAILURE; +} + +/** + * @brief This function finds if given country code is in EU table + * + * @param pmadapter A pointer to mlan_adapter structure + * @param country_code Country string + * + * @return MTRUE or MFALSE + */ +t_bool +wlan_is_etsi_country(pmlan_adapter pmadapter, t_u8 *country_code) +{ + + t_u8 i; + + ENTER(); + + /* Look for code in EU country code table */ + for (i = 0; i < NELEMENTS(eu_country_code_table); i++) { + if (!memcmp(pmadapter, eu_country_code_table[i], + country_code, COUNTRY_CODE_LEN - 1)) { + LEAVE(); + return MTRUE; + } + } + + LEAVE(); + return MFALSE; +} + +#define BAND_MASK_5G 0x03 +#define ANTENNA_OFFSET 2 +/** + * @brief This function adjust the antenna index + * + * V16_FW_API: Bit0: ant A, Bit 1:ant B, Bit0 & Bit 1: A+B + * 8887: case1: 0 - 2.4G ant A, 1- 2.4G antB, 2-- 5G ant C + * case2: 0 - 2.4G ant A, 1- 2.4G antB, 0x80- 5G antA, 0x81-5G ant B + * @param priv A pointer to mlan_private structure + * @param prx_pd A pointer to the RxPD structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +t_u8 +wlan_adjust_antenna(pmlan_private priv, RxPD *prx_pd) +{ + t_u8 antenna = prx_pd->antenna; + if (prx_pd->antenna == 0xff) + return 0; + if ((antenna & MBIT(0)) && (antenna & MBIT(1))) + antenna = 2; + else if (antenna & MBIT(1)) + antenna = 1; + else if (antenna & MBIT(0)) + antenna = 0; + + return antenna; +} + +/** + * @brief This function adjust the rate index + * + * @param priv A pointer to mlan_private structure + * @param rx_rate rx rate + * @param rate_info rate info + * @return rate index + */ +t_u8 +wlan_adjust_data_rate(mlan_private *priv, t_u8 rx_rate, t_u8 rate_info) +{ + t_u8 rate_index = 0; + t_u8 bw = 0; + t_bool sgi_enable = 0; + +#define MAX_MCS_NUM_SUPP 16 + +#define MAX_MCS_NUM_AC 10 +#define RATE_INDEX_MCS0 12 + bw = (rate_info & 0xC) >> 2; + sgi_enable = (rate_info & 0x10) >> 4; + if ((rate_info & 0x3) == 0) { + rate_index = + (rx_rate > + MLAN_RATE_INDEX_OFDM0) ? rx_rate - 1 : rx_rate; + } else if ((rate_info & 0x03) == 1) { + rate_index = RATE_INDEX_MCS0 + + MAX_MCS_NUM_SUPP * 2 * sgi_enable + + MAX_MCS_NUM_SUPP * bw + rx_rate; + } + return rate_index; +} + +#ifdef STA_SUPPORT +#endif /* STA_SUPPORT */ + +/** + * @brief convert ht_info to rate_info + * + * @param ht_info ht info + * + * @return rate info + */ +t_u8 +wlan_convert_v14_rate_ht_info(t_u8 ht_info) +{ + t_u8 rate_info = 0; + rate_info = ht_info & 0x01; + /* band */ + rate_info |= (ht_info & MBIT(1)) << 1; + /* short GI */ + rate_info |= (ht_info & MBIT(2)) << 2; + return rate_info; +} + +/** + * @brief Use index to get the data rate + * + * @param pmadapter A pointer to mlan_adapter structure + * @param index The index of data rate + * @param tx_rate_info Tx rate info + * + * @return Data rate or 0 + */ +t_u32 +wlan_index_to_data_rate(pmlan_adapter pmadapter, t_u8 index, t_u8 tx_rate_info) +{ +#define MCS_NUM_SUPP 8 + t_u16 mcs_rate[4][MCS_NUM_SUPP] = { {0x1b, 0x36, 0x51, 0x6c, 0xa2, 0xd8, 0xf3, 0x10e}, /*LG 40M */ + {0x1e, 0x3c, 0x5a, 0x78, 0xb4, 0xf0, 0x10e, 0x12c}, /*SG 40M */ + {0x0d, 0x1a, 0x27, 0x34, 0x4e, 0x68, 0x75, 0x82}, /*LG 20M */ + {0x0e, 0x1c, 0x2b, 0x39, 0x56, 0x73, 0x82, 0x90} + }; /*SG 20M */ + + t_u32 rate = 0; + t_u8 bw = 0; + t_u8 gi = 0; + ENTER(); + + if ((tx_rate_info & 0x3) == MLAN_RATE_FORMAT_HT) { + /* HT rate */ + /* 20M: bw=0, 40M: bw=1 */ + bw = (tx_rate_info & 0xC) >> 2; + /* LGI: gi =0, SGI: gi = 1 */ + gi = (tx_rate_info & 0x10) >> 4; + if (index == MLAN_RATE_BITMAP_MCS0) { + if (gi == 1) + rate = 0x0D; /* MCS 32 SGI rate */ + else + rate = 0x0C; /* MCS 32 LGI rate */ + } else if (index < MCS_NUM_SUPP) { + if (bw <= 1) + rate = mcs_rate[2 * (1 - bw) + gi][index]; + else + rate = WlanDataRates[0]; + } else + rate = WlanDataRates[0]; + } else { + /* 11n non HT rates */ + if (index >= WLAN_SUPPORTED_RATES_EXT) + index = 0; + rate = WlanDataRates[index]; + } + LEAVE(); + return rate; +} + +/** + * @brief Use rate to get the index + * + * @param pmadapter A pointer to mlan_adapter structure + * @param rate Data rate + * + * @return Index or 0 + */ +t_u8 +wlan_data_rate_to_index(pmlan_adapter pmadapter, t_u32 rate) +{ + t_u16 *ptr; + + ENTER(); + if (rate) { + ptr = wlan_memchr(pmadapter, WlanDataRates, (t_u8)rate, + sizeof(WlanDataRates)); + if (ptr) { + LEAVE(); + return (t_u8)(ptr - WlanDataRates); + } + } + LEAVE(); + return 0; +} + +/** + * @brief Get active data rates + * + * @param pmpriv A pointer to mlan_private structure + * @param bss_mode The specified BSS mode (Infra/IBSS) + * @param config_bands The specified band configuration + * @param rates The buf to return the active rates + * + * @return The number of Rates + */ +t_u32 +wlan_get_active_data_rates(mlan_private *pmpriv, t_u32 bss_mode, + t_u8 config_bands, WLAN_802_11_RATES rates) +{ + t_u32 k; + + ENTER(); + + if (pmpriv->media_connected != MTRUE) { + k = wlan_get_supported_rates(pmpriv, bss_mode, config_bands, + rates); + } else { + k = wlan_copy_rates(rates, 0, + pmpriv->curr_bss_params.data_rates, + pmpriv->curr_bss_params.num_of_rates); + } + + LEAVE(); + return k; +} + +#ifdef STA_SUPPORT +/** + * @brief This function search through all the regions cfp table to find the channel, + * if the channel is found then gets the MIN txpower of the channel + * present in all the regions. + * + * @param pmpriv A pointer to mlan_private structure + * @param channel Channel number. + * + * @return The Tx power + */ +t_u8 +wlan_get_txpwr_of_chan_from_cfp(mlan_private *pmpriv, t_u8 channel) +{ + t_u8 i = 0; + t_u8 j = 0; + t_u8 tx_power = 0; + t_u32 cfp_no; + chan_freq_power_t *cfp = MNULL; + chan_freq_power_t *cfp_a = MNULL; + t_u32 cfp_no_a; + + ENTER(); + + for (i = 0; i < MLAN_CFP_TABLE_SIZE_BG; i++) { + /* Get CFP */ + cfp = cfp_table_BG[i].cfp; + cfp_no = cfp_table_BG[i].cfp_no; + /* Find matching channel and get Tx power */ + for (j = 0; j < cfp_no; j++) { + if ((cfp + j)->channel == channel) { + if (tx_power != 0) + tx_power = + MIN(tx_power, + (cfp + j)->max_tx_power); + else + tx_power = + (t_u8)(cfp + j)->max_tx_power; + break; + } + } + } + + for (i = 0; i < MLAN_CFP_TABLE_SIZE_A; i++) { + /* Get CFP */ + cfp_a = cfp_table_A[i].cfp; + cfp_no_a = cfp_table_A[i].cfp_no; + for (j = 0; j < cfp_no_a; j++) { + if ((cfp_a + j)->channel == channel) { + if (tx_power != 0) + tx_power = + MIN(tx_power, + (cfp_a + j)->max_tx_power); + else + tx_power = + (t_u8)((cfp_a + + j)->max_tx_power); + break; + } + } + } + + LEAVE(); + return tx_power; +} + +/** + * @brief Get the channel frequency power info for a specific channel + * + * @param pmadapter A pointer to mlan_adapter structure + * @param band It can be BAND_A, BAND_G or BAND_B + * @param channel The channel to search for + * @param region_channel A pointer to region_chan_t structure + * + * @return A pointer to chan_freq_power_t structure or MNULL if not found. + */ + +chan_freq_power_t * +wlan_get_cfp_by_band_and_channel(pmlan_adapter pmadapter, + t_u8 band, + t_u16 channel, region_chan_t *region_channel) +{ + region_chan_t *rc; + chan_freq_power_t *cfp = MNULL; + int i, j; + + ENTER(); + + for (j = 0; !cfp && (j < MAX_REGION_CHANNEL_NUM); j++) { + rc = ®ion_channel[j]; + + if (!rc->valid || !rc->pcfp) + continue; + switch (rc->band) { + case BAND_A: + switch (band) { + case BAND_AN: + case BAND_A | BAND_AN: + case BAND_A: /* Matching BAND_A */ + break; + + default: + continue; + } + break; + case BAND_B: + case BAND_G: + switch (band) { + case BAND_GN: + case BAND_B | BAND_G | BAND_GN: + case BAND_G | BAND_GN: + case BAND_B | BAND_G: + case BAND_B: /* Matching BAND_B/G */ + case BAND_G: + case 0: + break; + default: + continue; + } + break; + default: + continue; + } + if (channel == FIRST_VALID_CHANNEL) + cfp = &rc->pcfp[0]; + else { + for (i = 0; i < rc->num_cfp; i++) { + if (rc->pcfp[i].channel == channel) { + cfp = &rc->pcfp[i]; + break; + } + } + } + } + + if (!cfp && channel) + PRINTM(MCMND, "wlan_get_cfp_by_band_and_channel(): cannot find " + "cfp by band %d & channel %d\n", band, channel); + + LEAVE(); + return cfp; +} + +/** + * @brief Find the channel frequency power info for a specific channel + * + * @param pmadapter A pointer to mlan_adapter structure + * @param band It can be BAND_A, BAND_G or BAND_B + * @param channel The channel to search for + * + * @return A pointer to chan_freq_power_t structure or MNULL if not found. + */ +chan_freq_power_t * +wlan_find_cfp_by_band_and_channel(mlan_adapter *pmadapter, + t_u8 band, t_u16 channel) +{ + chan_freq_power_t *cfp = MNULL; + + ENTER(); + + /* Any station(s) with 11D enabled */ + if (wlan_count_priv_cond(pmadapter, wlan_11d_is_enabled, + wlan_is_station) > 0) + cfp = wlan_get_cfp_by_band_and_channel(pmadapter, band, channel, + pmadapter-> + universal_channel); + else + cfp = wlan_get_cfp_by_band_and_channel(pmadapter, band, channel, + pmadapter-> + region_channel); + + LEAVE(); + return cfp; +} + +/** + * @brief Find the channel frequency power info for a specific frequency + * + * @param pmadapter A pointer to mlan_adapter structure + * @param band It can be BAND_A, BAND_G or BAND_B + * @param freq The frequency to search for + * + * @return Pointer to chan_freq_power_t structure; MNULL if not found + */ +chan_freq_power_t * +wlan_find_cfp_by_band_and_freq(mlan_adapter *pmadapter, t_u8 band, t_u32 freq) +{ + chan_freq_power_t *cfp = MNULL; + region_chan_t *rc; + int i, j; + + ENTER(); + + for (j = 0; !cfp && (j < MAX_REGION_CHANNEL_NUM); j++) { + rc = &pmadapter->region_channel[j]; + + /* Any station(s) with 11D enabled */ + if (wlan_count_priv_cond(pmadapter, wlan_11d_is_enabled, + wlan_is_station) > 0) + rc = &pmadapter->universal_channel[j]; + + if (!rc->valid || !rc->pcfp) + continue; + switch (rc->band) { + case BAND_A: + switch (band) { + case BAND_AN: + case BAND_A | BAND_AN: + case BAND_A: /* Matching BAND_A */ + break; + default: + continue; + } + break; + case BAND_B: + case BAND_G: + switch (band) { + case BAND_GN: + case BAND_B | BAND_G | BAND_GN: + case BAND_G | BAND_GN: + case BAND_B | BAND_G: + case BAND_B: + case BAND_G: + case 0: + break; + default: + continue; + } + break; + default: + continue; + } + for (i = 0; i < rc->num_cfp; i++) { + if (rc->pcfp[i].freq == freq) { + cfp = &rc->pcfp[i]; + break; + } + } + } + + if (!cfp && freq) + PRINTM(MERROR, + "wlan_find_cfp_by_band_and_freq(): cannot find cfp by " + "band %d & freq %d\n", band, freq); + + LEAVE(); + return cfp; +} +#endif /* STA_SUPPORT */ + +/** + * @brief Check if Rate Auto + * + * @param pmpriv A pointer to mlan_private structure + * + * @return MTRUE or MFALSE + */ +t_u8 +wlan_is_rate_auto(mlan_private *pmpriv) +{ + t_u32 i; + int rate_num = 0; + + ENTER(); + + for (i = 0; i < NELEMENTS(pmpriv->bitmap_rates); i++) + if (pmpriv->bitmap_rates[i]) + rate_num++; + + LEAVE(); + if (rate_num > 1) + return MTRUE; + else + return MFALSE; +} + +/** + * @brief Covert Rate Bitmap to Rate index + * + * @param pmadapter Pointer to mlan_adapter structure + * @param rate_bitmap Pointer to rate bitmap + * @param size Size of the bitmap array + * + * @return Rate index + */ +int +wlan_get_rate_index(pmlan_adapter pmadapter, t_u16 *rate_bitmap, int size) +{ + int i; + + ENTER(); + + for (i = 0; i < size * 8; i++) { + if (rate_bitmap[i / 16] & (1 << (i % 16))) { + LEAVE(); + return i; + } + } + + LEAVE(); + return -1; +} + +/** + * @brief Get supported data rates + * + * @param pmpriv A pointer to mlan_private structure + * @param bss_mode The specified BSS mode (Infra/IBSS) + * @param config_bands The specified band configuration + * @param rates The buf to return the supported rates + * + * @return The number of Rates + */ +t_u32 +wlan_get_supported_rates(mlan_private *pmpriv, t_u32 bss_mode, + t_u8 config_bands, WLAN_802_11_RATES rates) +{ + t_u32 k = 0; + + ENTER(); + + if (bss_mode == MLAN_BSS_MODE_INFRA) { + /* Infra. mode */ + switch (config_bands) { + case (t_u8)BAND_B: + PRINTM(MINFO, "Infra Band=%d SupportedRates_B\n", + config_bands); + k = wlan_copy_rates(rates, k, SupportedRates_B, + sizeof(SupportedRates_B)); + break; + case (t_u8)BAND_G: + case BAND_G | BAND_GN: + PRINTM(MINFO, "Infra band=%d SupportedRates_G\n", + config_bands); + k = wlan_copy_rates(rates, k, SupportedRates_G, + sizeof(SupportedRates_G)); + break; + case BAND_B | BAND_G: + case BAND_A | BAND_B | BAND_G: + case BAND_A | BAND_B: + case BAND_A | BAND_B | BAND_G | BAND_GN | BAND_AN: + case BAND_B | BAND_G | BAND_GN: + PRINTM(MINFO, "Infra band=%d SupportedRates_BG\n", + config_bands); +#ifdef WIFI_DIRECT_SUPPORT + if (pmpriv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT) + k = wlan_copy_rates(rates, k, SupportedRates_G, + sizeof(SupportedRates_G)); + else + k = wlan_copy_rates(rates, k, SupportedRates_BG, + sizeof(SupportedRates_BG)); +#else + k = wlan_copy_rates(rates, k, SupportedRates_BG, + sizeof(SupportedRates_BG)); +#endif + break; + case BAND_A: + case BAND_A | BAND_G: + PRINTM(MINFO, "Infra band=%d SupportedRates_A\n", + config_bands); + k = wlan_copy_rates(rates, k, SupportedRates_A, + sizeof(SupportedRates_A)); + break; + case BAND_AN: + case BAND_A | BAND_AN: + case BAND_A | BAND_G | BAND_AN | BAND_GN: + PRINTM(MINFO, "Infra band=%d SupportedRates_A\n", + config_bands); + k = wlan_copy_rates(rates, k, SupportedRates_A, + sizeof(SupportedRates_A)); + break; + case BAND_GN: + PRINTM(MINFO, "Infra band=%d SupportedRates_N\n", + config_bands); + k = wlan_copy_rates(rates, k, SupportedRates_N, + sizeof(SupportedRates_N)); + break; + } + } else { + /* Ad-hoc mode */ + switch (config_bands) { + case (t_u8)BAND_B: + PRINTM(MINFO, "Band: Adhoc B\n"); + k = wlan_copy_rates(rates, k, AdhocRates_B, + sizeof(AdhocRates_B)); + break; + case (t_u8)BAND_G: + case BAND_G | BAND_GN: + PRINTM(MINFO, "Band: Adhoc G only\n"); + k = wlan_copy_rates(rates, k, AdhocRates_G, + sizeof(AdhocRates_G)); + break; + case BAND_B | BAND_G: + case BAND_B | BAND_G | BAND_GN: + PRINTM(MINFO, "Band: Adhoc BG\n"); + k = wlan_copy_rates(rates, k, AdhocRates_BG, + sizeof(AdhocRates_BG)); + break; + case BAND_A: + case BAND_AN: + case BAND_A | BAND_AN: + PRINTM(MINFO, "Band: Adhoc A\n"); + k = wlan_copy_rates(rates, k, AdhocRates_A, + sizeof(AdhocRates_A)); + break; + } + } + + LEAVE(); + return k; +} + +#define COUNTRY_ID_US 0 +#define COUNTRY_ID_JP 1 +#define COUNTRY_ID_CN 2 +#define COUNTRY_ID_EU 3 +typedef struct _oper_bw_chan { + /*non-global operating class */ + t_u8 oper_class; + /*global operating class */ + t_u8 global_oper_class; + /*bandwidth 0-20M 1-40M 2-80M 3-160M */ + t_u8 bandwidth; + /*channel list */ + t_u8 channel_list[13]; +} oper_bw_chan; + +/** oper class table for US*/ +static oper_bw_chan oper_bw_chan_us[] = { + /** non-Global oper class, global oper class, bandwidth, channel list*/ + {1, 115, 0, {36, 40, 44, 48}}, + {2, 118, 0, {52, 56, 60, 64}}, + {3, 124, 0, {149, 153, 157, 161}}, + {4, 121, 0, {100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140}}, + {5, 125, 0, {149, 153, 157, 161, 165}}, + {12, 81, 0, {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}}, + {22, 116, 1, {36, 44}}, + {23, 119, 1, {52, 60}}, + {24, 122, 1, {100, 108, 116, 124, 132}}, + {25, 126, 1, {149, 157}}, + {26, 126, 1, {149, 157}}, + {27, 117, 1, {40, 48}}, + {28, 120, 1, {56, 64}}, + {29, 123, 1, {104, 112, 120, 128, 136}}, + {30, 127, 1, {153, 161}}, + {31, 127, 1, {153, 161}}, + {32, 83, 1, {1, 2, 3, 4, 5, 6, 7}}, + {33, 84, 1, {5, 6, 7, 8, 9, 10, 11}}, +}; + +/** oper class table for EU*/ +static oper_bw_chan oper_bw_chan_eu[] = { + /** non-global oper class,global oper class, bandwidth, channel list*/ + {1, 115, 0, {36, 40, 44, 48}}, + {2, 118, 0, {52, 56, 60, 64}}, + {3, 121, 0, {100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140}}, + {4, 81, 0, {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}}, + {5, 116, 1, {36, 44}}, + {6, 119, 1, {52, 60}}, + {7, 122, 1, {100, 108, 116, 124, 132}}, + {8, 117, 1, {40, 48}}, + {9, 120, 1, {56, 64}}, + {10, 123, 1, {104, 112, 120, 128, 136}}, + {11, 83, 1, {1, 2, 3, 4, 5, 6, 7, 8, 9}}, + {12, 84, 1, {5, 6, 7, 8, 9, 10, 11, 12, 13}}, + {17, 125, 0, {149, 153, 157, 161, 165, 169}}, +}; + +/** oper class table for Japan*/ +static oper_bw_chan oper_bw_chan_jp[] = { + /** non-Global oper class,global oper class, bandwidth, channel list*/ + {1, 115, 0, {34, 38, 42, 46, 36, 40, 44, 48}}, + {30, 81, 0, {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}}, + {31, 82, 0, {14}}, + {32, 118, 0, {52, 56, 60, 64}}, + {33, 118, 0, {52, 56, 60, 64}}, + {34, 121, 0, {100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140}}, + {35, 121, 0, {100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140}}, + {36, 116, 1, {36, 44}}, + {37, 119, 1, {52, 60}}, + {38, 119, 1, {52, 60}}, + {39, 122, 1, {100, 108, 116, 124, 132}}, + {40, 122, 1, {100, 108, 116, 124, 132}}, + {41, 117, 1, {40, 48}}, + {42, 120, 1, {56, 64}}, + {43, 120, 1, {56, 64}}, + {44, 123, 1, {104, 112, 120, 128, 136}}, + {45, 123, 1, {104, 112, 120, 128, 136}}, + {56, 83, 1, {1, 2, 3, 4, 5, 6, 7, 8, 9}}, + {57, 84, 1, {5, 6, 7, 8, 9, 10, 11, 12, 13}}, + {58, 121, 0, {100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140}}, +}; + +/** oper class table for China*/ +static oper_bw_chan oper_bw_chan_cn[] = { + /** non-Global oper class,global oper class, bandwidth, channel list*/ + {1, 115, 0, {36, 40, 44, 48}}, + {2, 118, 0, {52, 56, 60, 64}}, + {3, 125, 0, {149, 153, 157, 161, 165}}, + {4, 116, 1, {36, 44}}, + {5, 119, 1, {52, 60}}, + {6, 126, 1, {149, 157}}, + {7, 81, 0, {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}}, + {8, 83, 0, {1, 2, 3, 4, 5, 6, 7, 8, 9}}, + {9, 84, 1, {5, 6, 7, 8, 9, 10, 11, 12, 13}}, +}; + +/** + * @brief Get non-global operaing class table according to country + * + * @param pmpriv A pointer to mlan_private structure + * @param arraysize A pointer to table size + * + * @return A pointer to oper_bw_chan + */ +oper_bw_chan * +wlan_get_nonglobal_operclass_table(mlan_private *pmpriv, int *arraysize) +{ + t_u8 country_code[][COUNTRY_CODE_LEN] = { "US", "JP", "CN" }; + int country_id = 0; + oper_bw_chan *poper_bw_chan = MNULL; + + ENTER(); + + for (country_id = 0; country_id < 3; country_id++) + if (!memcmp + (pmpriv->adapter, pmpriv->adapter->country_code, + country_code[country_id], COUNTRY_CODE_LEN - 1)) + break; + if (country_id >= 3) + country_id = COUNTRY_ID_US; /*Set default to US */ + if (wlan_is_etsi_country + (pmpriv->adapter, pmpriv->adapter->country_code)) + country_id = COUNTRY_ID_EU; + /** Country in EU */ + + switch (country_id) { + case COUNTRY_ID_US: + poper_bw_chan = oper_bw_chan_us; + *arraysize = sizeof(oper_bw_chan_us); + break; + case COUNTRY_ID_JP: + poper_bw_chan = oper_bw_chan_jp; + *arraysize = sizeof(oper_bw_chan_jp); + break; + case COUNTRY_ID_CN: + poper_bw_chan = oper_bw_chan_cn; + *arraysize = sizeof(oper_bw_chan_cn); + break; + case COUNTRY_ID_EU: + poper_bw_chan = oper_bw_chan_eu; + *arraysize = sizeof(oper_bw_chan_eu); + break; + default: + PRINTM(MERROR, "Country not support!\n"); + break; + } + + LEAVE(); + return poper_bw_chan; +} + +/** + * @brief Check validation of given channel and oper class + * + * @param pmpriv A pointer to mlan_private structure + * @param channel Channel number + * @param oper_class operating class + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +mlan_status +wlan_check_operclass_validation(mlan_private *pmpriv, t_u8 channel, + t_u8 oper_class) +{ + int arraysize = 0, i = 0, channum = 0; + oper_bw_chan *poper_bw_chan = MNULL; + t_u8 center_freqs[] = { 42, 50, 58, 106, 114, 122, 138, 155 }; + + ENTER(); + + for (i = 0; i < sizeof(center_freqs); i++) { + if (channel == center_freqs[i]) { + PRINTM(MERROR, "Invalid channel number %d!\n", channel); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + } + if (oper_class <= 0 || oper_class > 130) { + PRINTM(MERROR, "Invalid operating class!\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + poper_bw_chan = wlan_get_nonglobal_operclass_table(pmpriv, &arraysize); + + if (!poper_bw_chan) { + PRINTM(MCMND, "Operating class table do not find!\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + for (i = 0; i < arraysize / sizeof(oper_bw_chan); i++) { + if (poper_bw_chan[i].oper_class == oper_class || + poper_bw_chan[i].global_oper_class == oper_class) { + for (channum = 0; + channum < sizeof(poper_bw_chan[i].channel_list); + channum++) { + if (poper_bw_chan[i].channel_list[channum] && + poper_bw_chan[i].channel_list[channum] == + channel) { + LEAVE(); + return MLAN_STATUS_SUCCESS; + } + } + } + } + + PRINTM(MCMND, "Operating class %d do not match channel %d!\n", + oper_class, channel); + LEAVE(); + return MLAN_STATUS_FAILURE; +} + +/** + * @brief Get current operating class from channel and bandwidth + * + * @param pmpriv A pointer to mlan_private structure + * @param channel Channel number + * @param bw Bandwidth + * @param oper_class A pointer to current operating class + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +mlan_status +wlan_get_curr_oper_class(mlan_private *pmpriv, t_u8 channel, t_u8 bw, + t_u8 *oper_class) +{ + oper_bw_chan *poper_bw_chan = MNULL; + t_u8 center_freqs[] = { 42, 50, 58, 106, 114, 122, 138, 155 }; + int i = 0, arraysize = 0, channum = 0; + + ENTER(); + + poper_bw_chan = wlan_get_nonglobal_operclass_table(pmpriv, &arraysize); + + if (!poper_bw_chan) { + PRINTM(MCMND, "Operating class table do not find!\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + for (i = 0; i < sizeof(center_freqs); i++) { + if (channel == center_freqs[i]) { + PRINTM(MERROR, "Invalid channel number %d!\n", channel); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + } + + for (i = 0; i < arraysize / sizeof(oper_bw_chan); i++) { + if (poper_bw_chan[i].bandwidth == bw) { + for (channum = 0; + channum < sizeof(poper_bw_chan[i].channel_list); + channum++) { + if (poper_bw_chan[i].channel_list[channum] && + poper_bw_chan[i].channel_list[channum] == + channel) { + *oper_class = + poper_bw_chan[i].oper_class; + return MLAN_STATUS_SUCCESS; + } + } + } + } + + PRINTM(MCMND, "Operating class not find!\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; +} + +/** + * @brief Add Supported operating classes IE + * + * @param pmpriv A pointer to mlan_private structure + * @param pptlv_out A pointer to TLV to fill in + * @param curr_oper_class Current operating class + * + * @return Length + */ +int +wlan_add_supported_oper_class_ie(IN mlan_private *pmpriv, OUT t_u8 **pptlv_out, + t_u8 curr_oper_class) +{ + + t_u8 oper_class_us[] = + { 1, 2, 3, 4, 5, 12, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, + 33 + }; + t_u8 oper_class_eu[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 17 + }; + t_u8 oper_class_jp[] = + { 1, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 56, 57, 58 + }; + t_u8 oper_class_cn[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 + }; + t_u8 country_code[][COUNTRY_CODE_LEN] = { "US", "JP", "CN" }; + int country_id = 0, ret = 0; + MrvlIETypes_SuppOperClass_t *poper_class = MNULL; + + ENTER(); + + for (country_id = 0; country_id < 3; country_id++) + if (!memcmp + (pmpriv->adapter, pmpriv->adapter->country_code, + country_code[country_id], COUNTRY_CODE_LEN - 1)) + break; + if (country_id >= 3) + country_id = COUNTRY_ID_US; /*Set default to US */ + if (wlan_is_etsi_country + (pmpriv->adapter, pmpriv->adapter->country_code)) + country_id = COUNTRY_ID_EU; + /** Country in EU */ + poper_class = (MrvlIETypes_SuppOperClass_t *) * pptlv_out; + memset(pmpriv->adapter, poper_class, 0, + sizeof(MrvlIETypes_SuppOperClass_t)); + poper_class->header.type = wlan_cpu_to_le16(REGULATORY_CLASS); + if (country_id == COUNTRY_ID_US) { + poper_class->header.len = sizeof(oper_class_us); + memcpy(pmpriv->adapter, &poper_class->oper_class, oper_class_us, + sizeof(oper_class_us)); + } else if (country_id == COUNTRY_ID_JP) { + poper_class->header.len = sizeof(oper_class_jp); + memcpy(pmpriv->adapter, &poper_class->oper_class, oper_class_jp, + sizeof(oper_class_jp)); + } else if (country_id == COUNTRY_ID_CN) { + poper_class->header.len = sizeof(oper_class_cn); + memcpy(pmpriv->adapter, &poper_class->oper_class, oper_class_cn, + sizeof(oper_class_cn)); + } else if (country_id == COUNTRY_ID_EU) { + poper_class->header.len = sizeof(oper_class_eu); + memcpy(pmpriv->adapter, &poper_class->oper_class, oper_class_eu, + sizeof(oper_class_eu)); + } + poper_class->current_oper_class = curr_oper_class; + poper_class->header.len += sizeof(poper_class->current_oper_class); + DBG_HEXDUMP(MCMD_D, "Operating class", (t_u8 *)poper_class, + sizeof(MrvlIEtypesHeader_t) + poper_class->header.len); + ret = sizeof(MrvlIEtypesHeader_t) + poper_class->header.len; + *pptlv_out += ret; + poper_class->header.len = wlan_cpu_to_le16(poper_class->header.len); + + LEAVE(); + return ret; +} + +/** + * @brief This function sets region table. + * + * @param pmpriv A pointer to mlan_private structure + * @param region The region code + * @param band The band + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_set_regiontable(mlan_private *pmpriv, t_u8 region, t_u8 band) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + int i = 0, j; + chan_freq_power_t *cfp; + int cfp_no; + region_chan_t region_chan_old[MAX_REGION_CHANNEL_NUM]; + + ENTER(); + + memcpy(pmadapter, region_chan_old, pmadapter->region_channel, + sizeof(pmadapter->region_channel)); + memset(pmadapter, pmadapter->region_channel, 0, + sizeof(pmadapter->region_channel)); + + if (band & (BAND_B | BAND_G | BAND_GN)) { + cfp = wlan_get_region_cfp_table(pmadapter, region, + BAND_G | BAND_B | BAND_GN, + &cfp_no); + if (cfp) { + pmadapter->region_channel[i].num_cfp = (t_u8)cfp_no; + pmadapter->region_channel[i].pcfp = cfp; + } else { + PRINTM(MERROR, "wrong region code %#x in Band B-G\n", + region); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + pmadapter->region_channel[i].valid = MTRUE; + pmadapter->region_channel[i].region = region; + if (band & BAND_GN) + pmadapter->region_channel[i].band = BAND_G; + else + pmadapter->region_channel[i].band = + (band & BAND_G) ? BAND_G : BAND_B; + + for (j = 0; j < MAX_REGION_CHANNEL_NUM; j++) { + if (region_chan_old[j].band & (BAND_B | BAND_G)) + break; + } + if ((j < MAX_REGION_CHANNEL_NUM) && region_chan_old[j].valid) + wlan_cfp_copy_dynamic(pmadapter, cfp, cfp_no, + region_chan_old[j].pcfp, + region_chan_old[j].num_cfp); + else + wlan_cfp_copy_dynamic(pmadapter, cfp, cfp_no, MNULL, 0); + i++; + } + if (band & (BAND_A | BAND_AN)) { + cfp = wlan_get_region_cfp_table(pmadapter, region, BAND_A, + &cfp_no); + if (cfp) { + pmadapter->region_channel[i].num_cfp = (t_u8)cfp_no; + pmadapter->region_channel[i].pcfp = cfp; + } else { + PRINTM(MERROR, "wrong region code %#x in Band A\n", + region); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + pmadapter->region_channel[i].valid = MTRUE; + pmadapter->region_channel[i].region = region; + pmadapter->region_channel[i].band = BAND_A; + + for (j = 0; j < MAX_REGION_CHANNEL_NUM; j++) { + if (region_chan_old[j].band & BAND_A) + break; + } + if ((j < MAX_REGION_CHANNEL_NUM) && region_chan_old[j].valid) + wlan_cfp_copy_dynamic(pmadapter, cfp, cfp_no, + region_chan_old[j].pcfp, + region_chan_old[j].num_cfp); + else + wlan_cfp_copy_dynamic(pmadapter, cfp, cfp_no, MNULL, 0); + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Get if radar detection is enabled or not on a certain channel + * + * @param priv Private driver information structure + * @param chnl Channel to determine radar detection requirements + * + * @return + * - MTRUE if radar detection is required + * - MFALSE otherwise + */ +t_bool +wlan_get_cfp_radar_detect(mlan_private *priv, t_u8 chnl) +{ + int i, j; + t_bool required = MFALSE; + chan_freq_power_t *pcfp = MNULL; + + ENTER(); + + /*get the cfp table first */ + for (i = 0; i < MAX_REGION_CHANNEL_NUM; i++) { + if (priv->adapter->region_channel[i].band == BAND_A) { + pcfp = priv->adapter->region_channel[i].pcfp; + break; + } + } + + if (!pcfp) { + /*This means operation in BAND-A is not support, we can + just return false here, it's harmless */ + goto done; + } + + /*get the radar detection requirements according to chan num */ + for (j = 0; j < priv->adapter->region_channel[i].num_cfp; j++) { + if (pcfp[j].channel == chnl) { + required = pcfp[j].passive_scan_or_radar_detect; + break; + } + } + +done: + LEAVE(); + return required; +} + +/** + * @brief Get if scan type is passive or not on a certain channel for b/g band + * + * @param priv Private driver information structure + * @param chnl Channel to determine scan type + * + * @return + * - MTRUE if scan type is passive + * - MFALSE otherwise + */ + +t_bool +wlan_bg_scan_type_is_passive(mlan_private *priv, t_u8 chnl) +{ + int i, j; + t_bool passive = MFALSE; + chan_freq_power_t *pcfp = MNULL; + + ENTER(); + + /*get the cfp table first */ + for (i = 0; i < MAX_REGION_CHANNEL_NUM; i++) { + if (priv->adapter->region_channel[i].band & (BAND_B | BAND_G)) { + pcfp = priv->adapter->region_channel[i].pcfp; + break; + } + } + + if (!pcfp) { + /*This means operation in BAND-B or BAND_G is not support, we can + * just return false here*/ + goto done; + } + + /*get the bg scan type according to chan num */ + for (j = 0; j < priv->adapter->region_channel[i].num_cfp; j++) { + if (pcfp[j].channel == chnl) { + passive = pcfp[j].passive_scan_or_radar_detect; + break; + } + } + +done: + LEAVE(); + return passive; +} + +/** + * @brief Get if a channel is disabled or not + * + * @param priv Private driver information structure + * @param band Band to check + * @param chan Channel to check + * + * @return + * - MTRUE if channel is disabled + * - MFALSE otherwise + */ + +t_bool +wlan_is_chan_disabled(mlan_private *priv, t_u8 band, t_u8 chan) +{ + int i, j; + t_bool disabled = MFALSE; + chan_freq_power_t *pcfp = MNULL; + + ENTER(); + + /* get the cfp table first */ + for (i = 0; i < MAX_REGION_CHANNEL_NUM; i++) { + if (priv->adapter->region_channel[i].band & band) { + pcfp = priv->adapter->region_channel[i].pcfp; + break; + } + } + + if (pcfp) { + /* check table according to chan num */ + for (j = 0; j < priv->adapter->region_channel[i].num_cfp; j++) { + if (pcfp[j].channel == chan) { + if (pcfp[j].dynamic. + flags & MARVELL_CHANNEL_DISABLED) + disabled = MTRUE; + break; + } + } + } + + LEAVE(); + return disabled; +} + +/** + * @brief Get if a channel is blacklisted or not + * + * @param priv Private driver information structure + * @param band Band to check + * @param chan Channel to check + * + * @return + * - MTRUE if channel is blacklisted + * - MFALSE otherwise + */ + +t_bool +wlan_is_chan_blacklisted(mlan_private *priv, t_u8 band, t_u8 chan) +{ + int i, j; + t_bool blacklist = MFALSE; + chan_freq_power_t *pcfp = MNULL; + + ENTER(); + + /*get the cfp table first */ + for (i = 0; i < MAX_REGION_CHANNEL_NUM; i++) { + if (priv->adapter->region_channel[i].band & band) { + pcfp = priv->adapter->region_channel[i].pcfp; + break; + } + } + + if (pcfp) { + /*check table according to chan num */ + for (j = 0; j < priv->adapter->region_channel[i].num_cfp; j++) { + if (pcfp[j].channel == chan) { + blacklist = pcfp[j].dynamic.blacklist; + break; + } + } + } + + LEAVE(); + return blacklist; +} + +/** + * @brief Set a channel as blacklisted or not + * + * @param priv Private driver information structure + * @param band Band to check + * @param chan Channel to check + * @param bl Blacklist if MTRUE + * + * @return + * - MTRUE if channel setting is updated + * - MFALSE otherwise + */ + +t_bool +wlan_set_chan_blacklist(mlan_private *priv, t_u8 band, t_u8 chan, t_bool bl) +{ + int i, j; + t_bool set_bl = MFALSE; + chan_freq_power_t *pcfp = MNULL; + + ENTER(); + + /*get the cfp table first */ + for (i = 0; i < MAX_REGION_CHANNEL_NUM; i++) { + if (priv->adapter->region_channel[i].band & band) { + pcfp = priv->adapter->region_channel[i].pcfp; + break; + } + } + + if (pcfp) { + /*check table according to chan num */ + for (j = 0; j < priv->adapter->region_channel[i].num_cfp; j++) { + if (pcfp[j].channel == chan) { + pcfp[j].dynamic.blacklist = bl; + set_bl = MTRUE; + break; + } + } + } + + LEAVE(); + return set_bl; +} + +/** + * @brief Convert rateid in IEEE format to MRVL format + * + * @param priv Private driver information structure + * @param IeeeMacRate Rate in terms of IEEE format + * @param pmbuf A pointer to packet buffer + * + * @return + * Rate ID in terms of MRVL format + */ +t_u8 +wlan_ieee_rateid_to_mrvl_rateid(mlan_private *priv, t_u16 IeeeMacRate, + t_u8 *dst_mac) +{ + /* Set default rate ID to RATEID_DBPSK1Mbps */ + t_u8 mrvlRATEID = 0; + const rate_map *rate_tbl = rate_map_table_1x1; + t_u32 cnt = sizeof(rate_map_table_1x1) / sizeof(rate_map); + t_u8 skip_nss2 = MTRUE; + t_u32 i = 0; + + ENTER(); + + for (i = 0; i < cnt; i++) { + if (rate_tbl[i].nss && skip_nss2) + continue; + if (rate_tbl[i].rate == IeeeMacRate) { + mrvlRATEID = rate_tbl[i].id; + break; + } + } + + return mrvlRATEID; +} + +/** + * @brief Convert rateid in MRVL format to IEEE format + * + * @param IeeeMacRate Rate in terms of MRVL format + * + * @return + * Rate ID in terms of IEEE format + */ +t_u8 +wlan_mrvl_rateid_to_ieee_rateid(t_u8 rate) +{ + return rateUnit_500Kbps[rate]; +} + +/** + * @brief Update CFP tables and power tables from FW + * + * @param priv Private driver information structure + * @param buf Pointer to the buffer holding TLV data + * from 0x242 command response. + * @param buf_left bufsize + * + * @return + * None + */ +void +wlan_add_fw_cfp_tables(pmlan_private pmpriv, t_u8 *buf, t_u16 buf_left) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + mlan_callbacks *pcb = (mlan_callbacks *)&pmadapter->callbacks; + MrvlIEtypesHeader_t *head; + t_u16 tlv; + t_u16 tlv_buf_len; + t_u16 tlv_buf_left; + t_u16 i; + t_u16 max_tx_pwr_bg = WLAN_TX_PWR_DEFAULT; + t_u16 max_tx_pwr_a = WLAN_TX_PWR_DEFAULT; + t_u8 *tlv_buf; + t_u8 *data; + t_u8 *tmp; + mlan_status ret; + + ENTER(); + + if (!buf) { + PRINTM(MERROR, "CFP table update failed!\n"); + goto out; + } + tlv_buf = (t_u8 *)buf; + tlv_buf_left = buf_left; + + while (tlv_buf_left >= sizeof(*head)) { + head = (MrvlIEtypesHeader_t *)tlv_buf; + tlv = wlan_le16_to_cpu(head->type); + tlv_buf_len = wlan_le16_to_cpu(head->len); + + if (tlv_buf_left < (sizeof(*head) + tlv_buf_len)) + break; + data = (t_u8 *)head + sizeof(*head); + + switch (tlv) { + case TLV_TYPE_REGION_INFO: + /* Skip adding fw region info if it already exists or + * if this TLV has no set data + */ + if (*data == 0) + break; + if (pmadapter->otp_region) + break; + + ret = pcb->moal_malloc(pmadapter->pmoal_handle, + sizeof(otp_region_info_t), + MLAN_MEM_DEF, + (t_u8 **)&pmadapter->otp_region); + if (ret != MLAN_STATUS_SUCCESS || + !pmadapter->otp_region) { + PRINTM(MERROR, + "Memory allocation for the otp region" + " info struct failed!\n"); + break; + } + /* Save region info values from OTP in the otp_region + * structure + */ + memcpy(pmadapter, pmadapter->otp_region, data, + sizeof(otp_region_info_t)); + data += sizeof(otp_region_info_t); + /* Get pre-defined cfp tables corresponding to the region code + * in OTP + */ + for (i = 0; i < MLAN_CFP_TABLE_SIZE_BG; i++) { + if (cfp_table_BG[i].code == + pmadapter->otp_region->region_code) { + max_tx_pwr_bg = + (cfp_table_BG[i].cfp)-> + max_tx_power; + break; + } + } + for (i = 0; i < MLAN_CFP_TABLE_SIZE_A; i++) { + if (cfp_table_A[i].code == + pmadapter->otp_region->region_code) { + max_tx_pwr_a = + (cfp_table_A[i].cfp)-> + max_tx_power; + break; + } + } + /* Update the region code and the country code in pmadapter */ + pmadapter->region_code = + pmadapter->otp_region->region_code; + pmadapter->country_code[0] = + pmadapter->otp_region->country_code[0]; + pmadapter->country_code[1] = + pmadapter->otp_region->country_code[1]; + pmadapter->country_code[2] = '\0'; + pmadapter->domain_reg.country_code[0] = + pmadapter->otp_region->country_code[0]; + pmadapter->domain_reg.country_code[1] = + pmadapter->otp_region->country_code[1]; + pmadapter->domain_reg.country_code[2] = '\0'; + pmadapter->cfp_code_bg = + pmadapter->otp_region->region_code; + pmadapter->cfp_code_a = + pmadapter->otp_region->region_code; + break; + case TLV_TYPE_CHAN_ATTR_CFG: + /* Skip adding fw cfp tables if they already exist or + * if this TLV has no set data + */ + if (*data == 0) + break; + if (pmadapter->cfp_otp_bg || pmadapter->cfp_otp_a) { + break; + } + + ret = pcb->moal_malloc(pmadapter->pmoal_handle, + FW_CFP_TABLE_MAX_ROWS_BG * + sizeof(chan_freq_power_t), + MLAN_MEM_DEF, + (t_u8 **)&pmadapter->cfp_otp_bg); + if (ret != MLAN_STATUS_SUCCESS || + !pmadapter->cfp_otp_bg) { + PRINTM(MERROR, + "Memory allocation for storing otp bg" + " table data failed!\n"); + break; + } + /* Save channel usability flags from OTP data in the fw cfp bg + * table and set frequency and max_tx_power values + */ + for (i = 0; i < FW_CFP_TABLE_MAX_ROWS_BG; i++) { + (pmadapter->cfp_otp_bg + i)->channel = *data; + if (*data == 14) + (pmadapter->cfp_otp_bg + i)->freq = + 2484; + else + (pmadapter->cfp_otp_bg + i)->freq = + 2412 + 5 * (*data - 1); + (pmadapter->cfp_otp_bg + i)->max_tx_power = + max_tx_pwr_bg; + data++; + (pmadapter->cfp_otp_bg + i)->dynamic.flags = + *data; + if (*data & MARVELL_CHANNEL_DFS) + (pmadapter->cfp_otp_bg + + i)->passive_scan_or_radar_detect = + MTRUE; + data++; + } + ret = pcb->moal_malloc(pmadapter->pmoal_handle, + FW_CFP_TABLE_MAX_ROWS_A * + sizeof(chan_freq_power_t), + MLAN_MEM_DEF, + (t_u8 **)&pmadapter->cfp_otp_a); + if (ret != MLAN_STATUS_SUCCESS || !pmadapter->cfp_otp_a) { + PRINTM(MERROR, + "Memory allocation for storing otp a" + " table data failed!\n"); + break; + } + /* Save channel usability flags from OTP data in the fw cfp a + * table and set frequency and max_tx_power values + */ + for (i = 0; i < FW_CFP_TABLE_MAX_ROWS_A; i++) { + (pmadapter->cfp_otp_a + i)->channel = *data; + if (*data < 183) + /* 5GHz channels */ + (pmadapter->cfp_otp_a + i)->freq = + 5035 + 5 * (*data - 7); + else + /* 4GHz channels */ + (pmadapter->cfp_otp_a + i)->freq = + 4915 + 5 * (*data - 183); + (pmadapter->cfp_otp_a + i)->max_tx_power = + max_tx_pwr_a; + data++; + (pmadapter->cfp_otp_a + i)->dynamic.flags = + *data; + if (*data & MARVELL_CHANNEL_DFS) + (pmadapter->cfp_otp_a + + i)->passive_scan_or_radar_detect = + MTRUE; + data++; + } + break; + case TLV_TYPE_POWER_TABLE: + /* Skip adding fw power tables if this TLV has no data or + * if they already exists but force reg rule is set in the otp + */ + if (*data == 0) + break; + if (pmadapter->otp_region && + pmadapter->otp_region->force_reg) + break; + + /* Save the tlv data in power tables for band BG and A */ + tmp = data; + i = 0; + while ((i < FW_CFP_TABLE_MAX_ROWS_BG * + FW_CFP_TABLE_MAX_COLS_BG) + && (i < tlv_buf_len) && (*tmp != 36)) { + i++; + tmp++; + } + if (!pmadapter->tx_power_table_bg) { + ret = pcb->moal_malloc(pmadapter->pmoal_handle, + i, MLAN_MEM_DEF, + (t_u8 **)&pmadapter-> + tx_power_table_bg); + if (ret != MLAN_STATUS_SUCCESS || + !pmadapter->tx_power_table_bg) { + PRINTM(MERROR, + "Memory allocation for the BG-band" + " power table falied!\n"); + break; + } + } + memcpy(pmadapter, pmadapter->tx_power_table_bg, data, + i); + pmadapter->tx_power_table_bg_size = i; + data += i; + i = 0; + while ((i < + FW_CFP_TABLE_MAX_ROWS_A * + FW_CFP_TABLE_MAX_COLS_A) + && (i < + (tlv_buf_len - + pmadapter->tx_power_table_bg_size))) { + i++; + } + if (!pmadapter->tx_power_table_a) { + ret = pcb->moal_malloc(pmadapter->pmoal_handle, + i, MLAN_MEM_DEF, + (t_u8 **)&pmadapter-> + tx_power_table_a); + if (ret != MLAN_STATUS_SUCCESS || + !pmadapter->tx_power_table_a) { + PRINTM(MERROR, + "Memory allocation for the A-band" + " power table failed!\n"); + break; + } + } + memcpy(pmadapter, pmadapter->tx_power_table_a, data, i); + pmadapter->tx_power_table_a_size = i; + break; + default: + break; + } + tlv_buf += (sizeof(*head) + tlv_buf_len); + tlv_buf_left -= (sizeof(*head) + tlv_buf_len); + } +out: + LEAVE(); +} + +/** + * @brief This function deallocates otp cfp and power tables memory. + * + * @param pmadapter A pointer to mlan_adapter structure + */ +void +wlan_free_fw_cfp_tables(mlan_adapter *pmadapter) +{ + pmlan_callbacks pcb; + + ENTER(); + + pcb = &pmadapter->callbacks; + if (pmadapter->otp_region) + pcb->moal_mfree(pmadapter->pmoal_handle, + (t_u8 *)pmadapter->otp_region); + if (pmadapter->cfp_otp_bg) + pcb->moal_mfree(pmadapter->pmoal_handle, + (t_u8 *)pmadapter->cfp_otp_bg); + if (pmadapter->tx_power_table_bg) + pcb->moal_mfree(pmadapter->pmoal_handle, + (t_u8 *)pmadapter->tx_power_table_bg); + pmadapter->tx_power_table_bg_size = 0; + if (pmadapter->cfp_otp_a) + pcb->moal_mfree(pmadapter->pmoal_handle, + (t_u8 *)pmadapter->cfp_otp_a); + if (pmadapter->tx_power_table_a) + pcb->moal_mfree(pmadapter->pmoal_handle, + (t_u8 *)pmadapter->tx_power_table_a); + pmadapter->tx_power_table_a_size = 0; + LEAVE(); +} + +/** + * @brief Get power tables and cfp tables for set region code + * into the IOCTL request buffer + * + * @param pmadapter Private mlan adapter structure + * @param pioctl_req Pointer to the IOCTL request structure + * + * @return success, otherwise fail + * + */ +mlan_status +wlan_get_cfpinfo(IN pmlan_adapter pmadapter, IN pmlan_ioctl_req pioctl_req) +{ + chan_freq_power_t *cfp_bg = MNULL; + t_u32 cfp_no_bg = 0; + chan_freq_power_t *cfp_a = MNULL; + t_u32 cfp_no_a = 0; + t_u32 len = 0, size = 0; + t_u8 *req_buf, *tmp; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (!pioctl_req || !pioctl_req->pbuf) { + PRINTM(MERROR, "MLAN IOCTL information is not present!\n"); + ret = MLAN_STATUS_FAILURE; + goto out; + } + /* Calculate the total response size required to return region, + * country codes, cfp tables and power tables + */ + size = sizeof(pmadapter->country_code) + sizeof(pmadapter->region_code); + /* Add size to store region, country and environment codes */ + size += sizeof(t_u32); + + /* Get cfp table and its size corresponding to the region code */ + cfp_bg = wlan_get_region_cfp_table(pmadapter, pmadapter->region_code, + BAND_G | BAND_B, &cfp_no_bg); + size += cfp_no_bg * sizeof(chan_freq_power_t); + cfp_a = wlan_get_region_cfp_table(pmadapter, pmadapter->region_code, + BAND_A, &cfp_no_a); + size += cfp_no_a * sizeof(chan_freq_power_t); + if (pmadapter->otp_region) + size += sizeof(pmadapter->otp_region->environment); + + /* Get power table size */ + if (pmadapter->tx_power_table_bg) { + size += pmadapter->tx_power_table_bg_size; + /* Add size to store table size, rows and cols */ + size += 3 * sizeof(t_u32); + } + if (pmadapter->tx_power_table_a) { + size += pmadapter->tx_power_table_a_size; + size += 3 * sizeof(t_u32); + } + /* Check information buffer length of MLAN IOCTL */ + if (pioctl_req->buf_len < size) { + PRINTM(MWARN, + "MLAN IOCTL information buffer length is too short.\n"); + pioctl_req->buf_len_needed = size; + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_RESOURCE; + goto out; + } + /* Copy the total size of region code, country code and environment + * in first four bytes of the IOCTL request buffer and then copy + * codes respectively in following bytes + */ + req_buf = (t_u8 *)pioctl_req->pbuf; + size = sizeof(pmadapter->country_code) + sizeof(pmadapter->region_code); + if (pmadapter->otp_region) + size += sizeof(pmadapter->otp_region->environment); + tmp = (t_u8 *)&size; + memcpy(pmadapter, req_buf, tmp, sizeof(size)); + len += sizeof(size); + memcpy(pmadapter, req_buf + len, &pmadapter->region_code, + sizeof(pmadapter->region_code)); + len += sizeof(pmadapter->region_code); + memcpy(pmadapter, req_buf + len, &pmadapter->country_code, + sizeof(pmadapter->country_code)); + len += sizeof(pmadapter->country_code); + if (pmadapter->otp_region) { + memcpy(pmadapter, req_buf + len, + &pmadapter->otp_region->environment, + sizeof(pmadapter->otp_region->environment)); + len += sizeof(pmadapter->otp_region->environment); + } + /* copy the cfp table size followed by the entire table */ + if (!cfp_bg) + goto out; + size = cfp_no_bg * sizeof(chan_freq_power_t); + memcpy(pmadapter, req_buf + len, tmp, sizeof(size)); + len += sizeof(size); + memcpy(pmadapter, req_buf + len, cfp_bg, size); + len += size; + if (!cfp_a) + goto out; + size = cfp_no_a * sizeof(chan_freq_power_t); + memcpy(pmadapter, req_buf + len, tmp, sizeof(size)); + len += sizeof(size); + memcpy(pmadapter, req_buf + len, cfp_a, size); + len += size; + /* Copy the size of the power table, number of rows, number of cols + * and the entire power table + */ + if (!pmadapter->tx_power_table_bg) + goto out; + size = pmadapter->tx_power_table_bg_size; + memcpy(pmadapter, req_buf + len, tmp, sizeof(size)); + len += sizeof(size); + + /* No. of rows */ + size = FW_CFP_TABLE_MAX_ROWS_BG; + memcpy(pmadapter, req_buf + len, tmp, sizeof(size)); + len += sizeof(size); + + /* No. of cols */ + size = pmadapter->tx_power_table_bg_size / FW_CFP_TABLE_MAX_ROWS_BG; + memcpy(pmadapter, req_buf + len, tmp, sizeof(size)); + len += sizeof(size); + memcpy(pmadapter, req_buf + len, pmadapter->tx_power_table_bg, + pmadapter->tx_power_table_bg_size); + len += pmadapter->tx_power_table_bg_size; + if (!pmadapter->tx_power_table_a) + goto out; + size = pmadapter->tx_power_table_a_size; + memcpy(pmadapter, req_buf + len, tmp, sizeof(size)); + len += sizeof(size); + + /* No. of rows */ + size = FW_CFP_TABLE_MAX_ROWS_A; + memcpy(pmadapter, req_buf + len, tmp, sizeof(size)); + len += sizeof(size); + + /* No. of cols */ + size = pmadapter->tx_power_table_a_size / FW_CFP_TABLE_MAX_ROWS_A; + memcpy(pmadapter, req_buf + len, tmp, sizeof(size)); + len += sizeof(size); + memcpy(pmadapter, req_buf + len, pmadapter->tx_power_table_a, + pmadapter->tx_power_table_a_size); + len += pmadapter->tx_power_table_a_size; +out: + pioctl_req->data_read_written = len; + + LEAVE(); + return ret; +} diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_cmdevt.c b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_cmdevt.c new file mode 100644 index 000000000000..bc91214c34f2 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_cmdevt.c @@ -0,0 +1,6443 @@ +/** + * @file mlan_cmdevt.c + * + * @brief This file contains the handling of CMD/EVENT in MLAN + * + * Copyright (C) 2009-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/************************************************************* +Change Log: + 05/12/2009: initial version +************************************************************/ +#include "mlan.h" +#ifdef STA_SUPPORT +#include "mlan_join.h" +#endif +#include "mlan_util.h" +#include "mlan_fw.h" +#include "mlan_main.h" +#include "mlan_wmm.h" +#include "mlan_11n.h" +#include "mlan_11h.h" +#include "mlan_sdio.h" + +/******************************************************** + Local Variables +********************************************************/ + +/******************************************************* + Global Variables +********************************************************/ + +/******************************************************** + Local Functions +********************************************************/ +#ifdef STA_SUPPORT +/** + * @brief This function inserts scan command node to scan_pending_q. + * + * @param pmpriv A pointer to mlan_private structure + * @param pcmd_node A pointer to cmd_ctrl_node structure + * @return N/A + */ +static t_void +wlan_queue_scan_cmd(IN mlan_private *pmpriv, IN cmd_ctrl_node *pcmd_node) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + + ENTER(); + + if (pcmd_node == MNULL) + goto done; + pcmd_node->cmd_flag |= CMD_F_SCAN; + + util_enqueue_list_tail(pmadapter->pmoal_handle, + &pmadapter->scan_pending_q, + (pmlan_linked_list)pcmd_node, MNULL, MNULL); + +done: + LEAVE(); +} + +/** + * @brief Internal function used to flush the scan pending queue + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return N/A + */ +void +wlan_check_scan_queue(IN pmlan_adapter pmadapter) +{ + cmd_ctrl_node *pcmd_node = MNULL; + t_u16 num = 0; + + pcmd_node = + (cmd_ctrl_node *)util_peek_list(pmadapter->pmoal_handle, + &pmadapter->scan_pending_q, + MNULL, MNULL); + if (!pcmd_node) { + PRINTM(MERROR, "No pending scan command\n"); + return; + } + while (pcmd_node != (cmd_ctrl_node *)&pmadapter->scan_pending_q) { + num++; + pcmd_node = pcmd_node->pnext; + } + PRINTM(MERROR, "num_pending_scan=%d\n", num); +} +#endif + +/** + * @brief This function will dump the pending commands id + * + * @param pmadapter A pointer to mlan_adapter + * + * @return N/A + */ +static void +wlan_dump_pending_commands(pmlan_adapter pmadapter) +{ + cmd_ctrl_node *pcmd_node = MNULL; + HostCmd_DS_COMMAND *pcmd; + + ENTER(); + wlan_request_cmd_lock(pmadapter); + pcmd_node = + (cmd_ctrl_node *)util_peek_list(pmadapter->pmoal_handle, + &pmadapter->cmd_pending_q, + MNULL, MNULL); + if (!pcmd_node) { + wlan_release_cmd_lock(pmadapter); + LEAVE(); + return; + } + while (pcmd_node != (cmd_ctrl_node *)&pmadapter->cmd_pending_q) { + pcmd = (HostCmd_DS_COMMAND *)(pcmd_node->cmdbuf->pbuf + + pcmd_node->cmdbuf->data_offset); + PRINTM(MERROR, "pending command id: 0x%x ioctl_buf=%p\n", + wlan_le16_to_cpu(pcmd->command), pcmd_node->pioctl_buf); + pcmd_node = pcmd_node->pnext; + } +#ifdef STA_SUPPORT + wlan_check_scan_queue(pmadapter); +#endif + wlan_release_cmd_lock(pmadapter); + LEAVE(); + return; +} + +#define REASON_CODE_NO_CMD_NODE 1 +#define REASON_CODE_CMD_TIMEOUT 2 +#define REASON_CODE_CMD_TO_CARD_FAILURE 3 +#define REASON_CODE_EXT_SCAN_TIMEOUT 4 +/** + * @brief This function dump debug info + * + * @param pmadapter A pointer to mlan_adapter + * @param reason Reason code + * + * @return N/A + */ +t_void +wlan_dump_info(mlan_adapter *pmadapter, t_u8 reason) +{ + cmd_ctrl_node *pcmd_node = MNULL; +#ifdef DEBUG_LEVEL1 + t_u32 sec = 0, usec = 0; +#endif + t_u8 i; +#ifdef SDIO_MULTI_PORT_TX_AGGR + t_u8 j; + t_u8 mp_aggr_pkt_limit = SDIO_MP_AGGR_DEF_PKT_LIMIT; +#endif + t_u16 cmd_id, cmd_act; + mlan_private *pmpriv = MNULL; + + ENTER(); + + PRINTM(MERROR, "------------Dump info-----------\n", reason); + switch (reason) { + case REASON_CODE_NO_CMD_NODE: + pmadapter->dbg.num_no_cmd_node++; + PRINTM(MERROR, "No Free command node\n"); + break; + case REASON_CODE_CMD_TIMEOUT: + PRINTM(MERROR, "Commmand Timeout\n"); + break; + case REASON_CODE_CMD_TO_CARD_FAILURE: + PRINTM(MERROR, "Command to card failure\n"); + break; + case REASON_CODE_EXT_SCAN_TIMEOUT: + PRINTM(MERROR, "EXT_SCAN_STATUS event Timeout\n"); + break; + default: + break; + } + if ((reason == REASON_CODE_NO_CMD_NODE) && + (pmadapter->dbg.num_no_cmd_node > 1)) { + if (pmadapter->dbg.num_no_cmd_node >= 5) + wlan_recv_event(wlan_get_priv + (pmadapter, MLAN_BSS_ROLE_ANY), + MLAN_EVENT_ID_DRV_DBG_DUMP, MNULL); + LEAVE(); + return; + } + wlan_dump_pending_commands(pmadapter); + if (reason != REASON_CODE_CMD_TIMEOUT) { + if (!pmadapter->curr_cmd) { + PRINTM(MERROR, "CurCmd Empty\n"); + } else { + pcmd_node = pmadapter->curr_cmd; + cmd_id = pmadapter->dbg.last_cmd_id[pmadapter->dbg. + last_cmd_index]; + cmd_act = + pmadapter->dbg.last_cmd_act[pmadapter->dbg. + last_cmd_index]; + PRINTM_GET_SYS_TIME(MERROR, &sec, &usec); + PRINTM(MERROR, + "Current cmd id (%lu.%06lu) = 0x%x, act = 0x%x\n", + sec, usec, cmd_id, cmd_act); + if (pcmd_node->cmdbuf) { + t_u8 *pcmd_buf; + pcmd_buf = + pcmd_node->cmdbuf->pbuf + + pcmd_node->cmdbuf->data_offset + + INTF_HEADER_LEN; + for (i = 0; i < 16; i++) + PRINTM(MERROR, "%02x ", *pcmd_buf++); + PRINTM(MERROR, "\n"); + } + pmpriv = pcmd_node->priv; + if (pmpriv) + PRINTM(MERROR, "BSS type = %d BSS role= %d\n", + pmpriv->bss_type, pmpriv->bss_role); + } + } + PRINTM(MERROR, "mlan_processing =%d\n", pmadapter->mlan_processing); + PRINTM(MERROR, "main_lock_flag =%d\n", pmadapter->main_lock_flag); + PRINTM(MERROR, "main_process_cnt =%d\n", pmadapter->main_process_cnt); + PRINTM(MERROR, "delay_task_flag =%d\n", pmadapter->delay_task_flag); + PRINTM(MERROR, "mlan_rx_processing =%d\n", + pmadapter->mlan_rx_processing); + PRINTM(MERROR, "rx_pkts_queued=%d\n", pmadapter->rx_pkts_queued); + PRINTM(MERROR, "more_task_flag = %d\n", pmadapter->more_task_flag); + PRINTM(MERROR, "num_cmd_timeout = %d\n", pmadapter->num_cmd_timeout); + PRINTM(MERROR, "dbg.num_cmd_timeout = %d\n", + pmadapter->dbg.num_cmd_timeout); + PRINTM(MERROR, "last_cmd_index = %d\n", pmadapter->dbg.last_cmd_index); + PRINTM(MERROR, "last_cmd_id = "); + for (i = 0; i < DBG_CMD_NUM; i++) + PRINTM(MERROR, "0x%x ", pmadapter->dbg.last_cmd_id[i]); + PRINTM(MERROR, "\n"); + PRINTM(MERROR, "last_cmd_act = "); + for (i = 0; i < DBG_CMD_NUM; i++) + PRINTM(MERROR, "0x%x ", pmadapter->dbg.last_cmd_act[i]); + PRINTM(MERROR, "\n"); + PRINTM(MERROR, "last_cmd_resp_index = %d\n", + pmadapter->dbg.last_cmd_resp_index); + PRINTM(MERROR, "last_cmd_resp_id = "); + for (i = 0; i < DBG_CMD_NUM; i++) + PRINTM(MERROR, "0x%x ", pmadapter->dbg.last_cmd_resp_id[i]); + PRINTM(MERROR, "\n"); + PRINTM(MERROR, "last_event_index = %d\n", + pmadapter->dbg.last_event_index); + PRINTM(MERROR, "last_event = "); + for (i = 0; i < DBG_CMD_NUM; i++) + PRINTM(MERROR, "0x%x ", pmadapter->dbg.last_event[i]); + PRINTM(MERROR, "\n"); + + PRINTM(MERROR, "num_data_h2c_failure = %d\n", + pmadapter->dbg.num_tx_host_to_card_failure); + PRINTM(MERROR, "num_cmd_h2c_failure = %d\n", + pmadapter->dbg.num_cmd_host_to_card_failure); + PRINTM(MERROR, "num_data_c2h_failure = %d\n", + pmadapter->dbg.num_rx_card_to_host_failure); + PRINTM(MERROR, "num_cmdevt_c2h_failure = %d\n", + pmadapter->dbg.num_cmdevt_card_to_host_failure); + PRINTM(MERROR, "num_int_read_failure = %d\n", + pmadapter->dbg.num_int_read_failure); + PRINTM(MERROR, "last_int_status = %d\n", + pmadapter->dbg.last_int_status); + PRINTM(MERROR, "num_alloc_buffer_failure = %d\n", + pmadapter->dbg.num_alloc_buffer_failure); + PRINTM(MERROR, "num_pkt_dropped = %d\n", + pmadapter->dbg.num_pkt_dropped); + PRINTM(MERROR, "num_no_cmd_node = %d\n", + pmadapter->dbg.num_no_cmd_node); + PRINTM(MERROR, "num_event_deauth = %d\n", + pmadapter->dbg.num_event_deauth); + PRINTM(MERROR, "num_event_disassoc = %d\n", + pmadapter->dbg.num_event_disassoc); + PRINTM(MERROR, "num_event_link_lost = %d\n", + pmadapter->dbg.num_event_link_lost); + PRINTM(MERROR, "num_cmd_deauth = %d\n", pmadapter->dbg.num_cmd_deauth); + PRINTM(MERROR, "num_cmd_assoc_success = %d\n", + pmadapter->dbg.num_cmd_assoc_success); + PRINTM(MERROR, "num_cmd_assoc_failure = %d\n", + pmadapter->dbg.num_cmd_assoc_failure); + PRINTM(MERROR, "cmd_resp_received=%d\n", pmadapter->cmd_resp_received); + PRINTM(MERROR, "event_received=%d\n", pmadapter->event_received); + + PRINTM(MERROR, "max_tx_buf_size=%d\n", pmadapter->max_tx_buf_size); + PRINTM(MERROR, "tx_buf_size=%d\n", pmadapter->tx_buf_size); + PRINTM(MERROR, "curr_tx_buf_size=%d\n", pmadapter->curr_tx_buf_size); + + PRINTM(MERROR, "data_sent=%d cmd_sent=%d\n", pmadapter->data_sent, + pmadapter->cmd_sent); + + PRINTM(MERROR, "ps_mode=%d ps_state=%d\n", pmadapter->ps_mode, + pmadapter->ps_state); + PRINTM(MERROR, "wakeup_dev_req=%d wakeup_tries=%d\n", + pmadapter->pm_wakeup_card_req, pmadapter->pm_wakeup_fw_try); + PRINTM(MERROR, "hs_configured=%d hs_activated=%d\n", + pmadapter->is_hs_configured, pmadapter->hs_activated); + PRINTM(MERROR, "pps_uapsd_mode=%d sleep_pd=%d\n", + pmadapter->pps_uapsd_mode, pmadapter->sleep_period.period); + PRINTM(MERROR, "tx_lock_flag = %d\n", pmadapter->tx_lock_flag); + PRINTM(MERROR, "scan_processing = %d\n", pmadapter->scan_processing); + PRINTM(MERROR, "mp_rd_bitmap=0x%x curr_rd_port=0x%x\n", + pmadapter->mp_rd_bitmap, pmadapter->curr_rd_port); + PRINTM(MERROR, "mp_wr_bitmap=0x%x curr_wr_port=0x%x\n", + pmadapter->mp_wr_bitmap, pmadapter->curr_wr_port); + PRINTM(MERROR, "mp_invalid_update=%d\n", pmadapter->mp_invalid_update); +#ifdef SDIO_MULTI_PORT_TX_AGGR + PRINTM(MERROR, "last_recv_wr_bitmap=0x%x last_mp_index=%d\n", + pmadapter->last_recv_wr_bitmap, pmadapter->last_mp_index); + for (i = 0; i < SDIO_MP_DBG_NUM; i++) { + PRINTM(MERROR, + "mp_wr_bitmap: 0x%x mp_wr_ports=0x%x len=%d curr_wr_port=0x%x\n", + pmadapter->last_mp_wr_bitmap[i], + pmadapter->last_mp_wr_ports[i], + pmadapter->last_mp_wr_len[i], + pmadapter->last_curr_wr_port[i]); + for (j = 0; j < mp_aggr_pkt_limit; j++) { + PRINTM(MERROR, "0x%02x ", + pmadapter->last_mp_wr_info[i * + mp_aggr_pkt_limit + + j]); + } + PRINTM(MERROR, "\n"); + } +#endif + for (i = 0; i < pmadapter->priv_num; ++i) { + if (pmadapter->priv[i]) + wlan_dump_ralist(pmadapter->priv[i]); + } + if (reason != REASON_CODE_CMD_TIMEOUT) { + if ((pmadapter->dbg.num_no_cmd_node >= 5) + || (pmadapter->pm_wakeup_card_req && + pmadapter->pm_wakeup_fw_try) + || (reason == REASON_CODE_EXT_SCAN_TIMEOUT) + ) { + if (pmpriv) + wlan_recv_event(pmpriv, + MLAN_EVENT_ID_DRV_DBG_DUMP, + MNULL); + else + wlan_recv_event(wlan_get_priv + (pmadapter, MLAN_BSS_ROLE_ANY), + MLAN_EVENT_ID_DRV_DBG_DUMP, + MNULL); + } + } + PRINTM(MERROR, "-------- Dump info End---------\n", reason); + LEAVE(); + return; +} + +/** + * @brief This function convert a given character to hex + * + * @param chr Character to be converted + * + * @return The converted hex if chr is a valid hex, else 0 + */ +static t_u32 +wlan_hexval(t_u8 chr) +{ + if (chr >= '0' && chr <= '9') + return chr - '0'; + if (chr >= 'A' && chr <= 'F') + return chr - 'A' + 10; + if (chr >= 'a' && chr <= 'f') + return chr - 'a' + 10; + + return 0; +} + +/** + * @brief This function convert a given string to hex + * + * @param a A pointer to string to be converted + * + * @return The converted hex value if param a is a valid hex, else 0 + */ +int +wlan_atox(t_u8 *a) +{ + int i = 0; + + ENTER(); + + while (wlan_isxdigit(*a)) + i = i * 16 + wlan_hexval(*a++); + + LEAVE(); + return i; +} + +/** + * @brief This function parse cal data from ASCII to hex + * + * @param src A pointer to source data + * @param len Source data length + * @param dst A pointer to a buf to store the parsed data + * + * @return The parsed hex data length + */ +static t_u32 +wlan_parse_cal_cfg(t_u8 *src, t_size len, t_u8 *dst) +{ + t_u8 *ptr; + t_u8 *dptr; + + ENTER(); + ptr = src; + dptr = dst; + + while (ptr - src < len) { + if (*ptr && (wlan_isspace(*ptr) || *ptr == '\t')) { + ptr++; + continue; + } + + if (wlan_isxdigit(*ptr)) { + *dptr++ = wlan_atox(ptr); + ptr += 2; + } else { + ptr++; + } + } + LEAVE(); + return dptr - dst; +} + +/** + * @brief This function finds first occurrence of a char in a string + * + * @param s A pointer to the string to be searched + * @param c The character to search for + * + * @return Location of the first occurrence of the char + * if found, else NULL + */ +t_u8 * +wlan_strchr(t_u8 *s, int c) +{ + t_u8 *pos = s; + while (*pos != '\0') { + if (*pos == (t_u8)c) + return pos; + pos++; + } + return MNULL; +} + +/** + * @brief WOAL parse ASCII format raw data to hex format + * + * @param pmpriv MOAL handle + * @param data Source data + * @param size data length + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +static t_u32 +wlan_process_hostcmd_cfg(IN pmlan_private pmpriv, t_u8 *data, t_size size) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u8 *pos = data; + t_u8 *intf_s, *intf_e; + t_u8 *buf = MNULL; + t_u8 *ptr = MNULL; + t_u32 cmd_len = 0; + t_u8 start_raw = MFALSE; + mlan_ds_misc_cmd *hostcmd; + mlan_adapter *pmadapter = pmpriv->adapter; + mlan_callbacks *pcb = (mlan_callbacks *)&pmadapter->callbacks; + + ENTER(); + ret = pcb->moal_malloc(pmadapter->pmoal_handle, + sizeof(mlan_ds_misc_cmd), MLAN_MEM_DEF, + (t_u8 **)&hostcmd); + if (ret || !hostcmd) { + PRINTM(MERROR, "Could not allocate buffer space!\n"); + LEAVE(); + return ret; + } + buf = hostcmd->cmd; + ptr = buf; + while ((pos - data) < size) { + while (*pos == ' ' || *pos == '\t') + pos++; + if (*pos == '#') { /* Line comment */ + while (*pos != '\n') + pos++; + pos++; + } + if ((*pos == '\r' && *(pos + 1) == '\n') || + *pos == '\n' || *pos == '\0') { + pos++; + continue; /* Needn't process this line */ + } + + if (*pos == '}') { + cmd_len = *((t_u16 *)(buf + sizeof(t_u16))); + hostcmd->len = cmd_len; + ret = wlan_prepare_cmd(pmpriv, 0, 0, 0, MNULL, + (t_void *)hostcmd); + memset(pmadapter, buf, 0, MLAN_SIZE_OF_CMD_BUFFER); + ptr = buf; + start_raw = MFALSE; + pos++; + continue; + } + + if (start_raw == MFALSE) { + intf_s = wlan_strchr(pos, '='); + if (intf_s) + intf_e = wlan_strchr(intf_s, '{'); + else + intf_e = MNULL; + + if (intf_s && intf_e) { + start_raw = MTRUE; + pos = intf_e + 1; + continue; + } + } + + if (start_raw) { + /* Raw data block exists */ + while (*pos != '\n') { + if ((*pos <= 'f' && *pos >= 'a') || + (*pos <= 'F' && *pos >= 'A') || + (*pos <= '9' && *pos >= '0')) { + *ptr++ = wlan_atox(pos); + pos += 2; + } else + pos++; + } + } + } + pcb->moal_mfree(pmadapter->pmoal_handle, (t_u8 *)hostcmd); + LEAVE(); + return ret; +} + +/** + * @brief This function initializes the command node. + * + * @param pmpriv A pointer to mlan_private structure + * @param pcmd_node A pointer to cmd_ctrl_node structure + * @param cmd_oid Cmd oid: treated as sub command + * @param pioctl_buf A pointer to MLAN IOCTL Request buffer + * @param pdata_buf A pointer to information buffer + * + * @return N/A + */ +static void +wlan_init_cmd_node(IN pmlan_private pmpriv, + IN cmd_ctrl_node *pcmd_node, + IN t_u32 cmd_oid, + IN t_void *pioctl_buf, IN t_void *pdata_buf) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + + ENTER(); + + if (pcmd_node == MNULL) { + LEAVE(); + return; + } + pcmd_node->priv = pmpriv; + pcmd_node->cmd_oid = cmd_oid; + pcmd_node->pioctl_buf = pioctl_buf; + pcmd_node->pdata_buf = pdata_buf; + + pcmd_node->cmdbuf = pcmd_node->pmbuf; + + /* Make sure head_ptr for cmd buf is Align */ + pcmd_node->cmdbuf->data_offset = 0; + memset(pmadapter, pcmd_node->cmdbuf->pbuf, 0, + MRVDRV_SIZE_OF_CMD_BUFFER); + + /* Prepare mlan_buffer for command sending */ + pcmd_node->cmdbuf->buf_type = MLAN_BUF_TYPE_CMD; + pcmd_node->cmdbuf->data_offset += INTF_HEADER_LEN; + + LEAVE(); +} + +/** + * @brief This function gets a free command node if available in + * command free queue. + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return cmd_ctrl_node A pointer to cmd_ctrl_node structure or MNULL + */ +static cmd_ctrl_node * +wlan_get_cmd_node(mlan_adapter *pmadapter) +{ + cmd_ctrl_node *pcmd_node; + + ENTER(); + + if (pmadapter == MNULL) { + LEAVE(); + return MNULL; + } + wlan_request_cmd_lock(pmadapter); + if (util_peek_list(pmadapter->pmoal_handle, &pmadapter->cmd_free_q, + MNULL, MNULL)) { + pcmd_node = + (cmd_ctrl_node *)util_dequeue_list(pmadapter-> + pmoal_handle, + &pmadapter-> + cmd_free_q, MNULL, + MNULL); + } else { + PRINTM(MERROR, + "GET_CMD_NODE: cmd_ctrl_node is not available\n"); + pcmd_node = MNULL; + } + wlan_release_cmd_lock(pmadapter); + LEAVE(); + return pcmd_node; +} + +/** + * @brief This function cleans command node. + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pcmd_node A pointer to cmd_ctrl_node structure + * + * @return N/A + */ +static t_void +wlan_clean_cmd_node(pmlan_adapter pmadapter, cmd_ctrl_node *pcmd_node) +{ + ENTER(); + + if (pcmd_node == MNULL) { + LEAVE(); + return; + } + pcmd_node->cmd_oid = 0; + pcmd_node->cmd_flag = 0; + pcmd_node->pioctl_buf = MNULL; + pcmd_node->pdata_buf = MNULL; + + if (pcmd_node->respbuf) { + wlan_free_mlan_buffer(pmadapter, pcmd_node->respbuf); + pcmd_node->respbuf = MNULL; + } + + LEAVE(); + return; +} + +#ifdef STA_SUPPORT +/** + * @brief This function will return the pointer to the first entry in + * pending cmd which is scan command + * + * @param pmadapter A pointer to mlan_adapter + * + * @return A pointer to first entry match pioctl_req + */ +static cmd_ctrl_node * +wlan_get_pending_scan_cmd(pmlan_adapter pmadapter) +{ + cmd_ctrl_node *pcmd_node = MNULL; + + ENTER(); + + pcmd_node = + (cmd_ctrl_node *)util_peek_list(pmadapter->pmoal_handle, + &pmadapter->cmd_pending_q, + MNULL, MNULL); + if (!pcmd_node) { + LEAVE(); + return MNULL; + } + while (pcmd_node != (cmd_ctrl_node *)&pmadapter->cmd_pending_q) { + if (pcmd_node->cmd_flag & CMD_F_SCAN) { + LEAVE(); + return pcmd_node; + } + pcmd_node = pcmd_node->pnext; + } + LEAVE(); + return MNULL; +} +#endif + +/** + * @brief This function will return the pointer to the first entry in + * pending cmd which matches the given pioctl_req + * + * @param pmadapter A pointer to mlan_adapter + * @param pioctl_req A pointer to mlan_ioctl_req buf + * + * @return A pointer to first entry match pioctl_req + */ +static cmd_ctrl_node * +wlan_get_pending_ioctl_cmd(pmlan_adapter pmadapter, pmlan_ioctl_req pioctl_req) +{ + cmd_ctrl_node *pcmd_node = MNULL; + + ENTER(); + + pcmd_node = + (cmd_ctrl_node *)util_peek_list(pmadapter->pmoal_handle, + &pmadapter->cmd_pending_q, + MNULL, MNULL); + if (!pcmd_node) { + LEAVE(); + return MNULL; + } + while (pcmd_node != (cmd_ctrl_node *)&pmadapter->cmd_pending_q) { + if (pcmd_node->pioctl_buf && + (pcmd_node->pioctl_buf == pioctl_req)) { + LEAVE(); + return pcmd_node; + } + pcmd_node = pcmd_node->pnext; + } + LEAVE(); + return MNULL; +} + +/** + * @brief This function will return the pointer to the first entry in + * pending cmd which matches the given bss_index + * + * @param pmadapter A pointer to mlan_adapter + * @param bss_index bss_index + * + * @return A pointer to first entry match pioctl_req + */ +static cmd_ctrl_node * +wlan_get_bss_pending_ioctl_cmd(pmlan_adapter pmadapter, t_u32 bss_index) +{ + cmd_ctrl_node *pcmd_node = MNULL; + mlan_ioctl_req *pioctl_buf = MNULL; + ENTER(); + + pcmd_node = + (cmd_ctrl_node *)util_peek_list(pmadapter->pmoal_handle, + &pmadapter->cmd_pending_q, + MNULL, MNULL); + if (!pcmd_node) { + LEAVE(); + return MNULL; + } + while (pcmd_node != (cmd_ctrl_node *)&pmadapter->cmd_pending_q) { + if (pcmd_node->pioctl_buf) { + pioctl_buf = (mlan_ioctl_req *)pcmd_node->pioctl_buf; + if (pioctl_buf->bss_index == bss_index) { + LEAVE(); + return pcmd_node; + } + } + pcmd_node = pcmd_node->pnext; + } + LEAVE(); + return MNULL; +} + +/** + * @brief This function handles the command response of host_cmd + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_ret_host_cmd(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, IN mlan_ioctl_req *pioctl_buf) +{ + mlan_ds_misc_cfg *misc; + t_u16 size = wlan_le16_to_cpu(resp->size); + + ENTER(); + + PRINTM(MINFO, "host command response size = %d\n", size); + size = MIN(size, MRVDRV_SIZE_OF_CMD_BUFFER); + if (pioctl_buf) { + misc = (mlan_ds_misc_cfg *)pioctl_buf->pbuf; + misc->param.hostcmd.len = size; + memcpy(pmpriv->adapter, misc->param.hostcmd.cmd, (void *)resp, + size); + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function sends host command to firmware. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param pdata_buf A pointer to data buffer + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_cmd_host_cmd(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, IN t_void *pdata_buf) +{ + mlan_ds_misc_cmd *pcmd_ptr = (mlan_ds_misc_cmd *)pdata_buf; + + ENTER(); + + /* Copy the HOST command to command buffer */ + memcpy(pmpriv->adapter, (void *)cmd, pcmd_ptr->cmd, + MIN(MRVDRV_SIZE_OF_CMD_BUFFER, pcmd_ptr->len)); + PRINTM(MINFO, "Host command size = %d\n", pcmd_ptr->len); + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function downloads a command to firmware. + * + * @param pmpriv A pointer to mlan_private structure + * @param pcmd_node A pointer to cmd_ctrl_node structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status +wlan_dnld_cmd_to_fw(IN mlan_private *pmpriv, IN cmd_ctrl_node *pcmd_node) +{ + + mlan_adapter *pmadapter = pmpriv->adapter; + mlan_callbacks *pcb = (mlan_callbacks *)&pmadapter->callbacks; + mlan_status ret = MLAN_STATUS_SUCCESS; + HostCmd_DS_COMMAND *pcmd; + mlan_ioctl_req *pioctl_buf = MNULL; + t_u16 cmd_code; + t_u16 cmd_size; + t_u32 age_ts_usec; +#ifdef DEBUG_LEVEL1 + t_u32 sec = 0, usec = 0; +#endif + + ENTER(); + + if (pcmd_node) + if (pcmd_node->pioctl_buf != MNULL) + pioctl_buf = (mlan_ioctl_req *)pcmd_node->pioctl_buf; + if (!pmadapter || !pcmd_node) { + if (pioctl_buf) + pioctl_buf->status_code = MLAN_ERROR_CMD_DNLD_FAIL; + ret = MLAN_STATUS_FAILURE; + goto done; + } + + pcmd = (HostCmd_DS_COMMAND *)(pcmd_node->cmdbuf->pbuf + + pcmd_node->cmdbuf->data_offset); + + /* Sanity test */ + if (pcmd == MNULL || pcmd->size == 0) { + PRINTM(MERROR, + "DNLD_CMD: pcmd is null or command size is zero, " + "Not sending\n"); + if (pioctl_buf) + pioctl_buf->status_code = MLAN_ERROR_CMD_DNLD_FAIL; + wlan_request_cmd_lock(pmadapter); + wlan_insert_cmd_to_free_q(pmadapter, pcmd_node); + wlan_release_cmd_lock(pmadapter); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* Set command sequence number */ + pmadapter->seq_num++; + pcmd->seq_num = + wlan_cpu_to_le16(HostCmd_SET_SEQ_NO_BSS_INFO + (pmadapter->seq_num, pcmd_node->priv->bss_num, + pcmd_node->priv->bss_type)); + cmd_code = wlan_le16_to_cpu(pcmd->command); + cmd_size = wlan_le16_to_cpu(pcmd->size); + + pcmd_node->cmdbuf->data_len = cmd_size; + + wlan_request_cmd_lock(pmadapter); + pmadapter->curr_cmd = pcmd_node; + wlan_release_cmd_lock(pmadapter); + + /* Save the last command id and action to debug log */ + pmadapter->dbg.last_cmd_index = + (pmadapter->dbg.last_cmd_index + 1) % DBG_CMD_NUM; + pmadapter->dbg.last_cmd_id[pmadapter->dbg.last_cmd_index] = cmd_code; + pmadapter->dbg.last_cmd_act[pmadapter->dbg.last_cmd_index] = + wlan_le16_to_cpu(*(t_u16 *)((t_u8 *)pcmd + S_DS_GEN)); + pmadapter->callbacks.moal_get_system_time(pmadapter->pmoal_handle, + &pmadapter->dnld_cmd_in_secs, + &age_ts_usec); + + if (pcmd->command == HostCmd_CMD_HOST_CLOCK_CFG) { + HostCmd_DS_HOST_CLOCK_CFG *host_clock = + (HostCmd_DS_HOST_CLOCK_CFG *) & pcmd->params. + host_clock_cfg; + pmadapter->callbacks.moal_get_host_time_ns(&pmadapter->d1); + PRINTM(MINFO, "WIFI_TS: d1: %llu\n", pmadapter->d1); + /* Overwrite the time to be given to FW */ + host_clock->time = wlan_cpu_to_le64(pmadapter->d1); + } + + PRINTM_GET_SYS_TIME(MCMND, &sec, &usec); + PRINTM_NETINTF(MCMND, pmpriv); + PRINTM(MCMND, + "DNLD_CMD (%lu.%06lu): 0x%x, act 0x%x, len %d, seqno 0x%x\n", + sec, usec, cmd_code, + wlan_le16_to_cpu(*(t_u16 *)((t_u8 *)pcmd + S_DS_GEN)), cmd_size, + wlan_le16_to_cpu(pcmd->seq_num)); + DBG_HEXDUMP(MCMD_D, "DNLD_CMD", (t_u8 *)pcmd, cmd_size); + + /* Send the command to lower layer */ + + pcmd_node->cmdbuf->data_offset -= INTF_HEADER_LEN; + pcmd_node->cmdbuf->data_len += INTF_HEADER_LEN; + /* Extra header for SDIO is added here */ + ret = wlan_sdio_host_to_card(pmadapter, MLAN_TYPE_CMD, + pcmd_node->cmdbuf, MNULL); + + if (ret == MLAN_STATUS_FAILURE) { + PRINTM(MERROR, "DNLD_CMD: Host to Card Failed\n"); + if (pcmd_node->pioctl_buf) { + pioctl_buf = (mlan_ioctl_req *)pcmd_node->pioctl_buf; + pioctl_buf->status_code = MLAN_ERROR_CMD_DNLD_FAIL; + } + + wlan_request_cmd_lock(pmadapter); + wlan_insert_cmd_to_free_q(pmadapter, pmadapter->curr_cmd); + pmadapter->curr_cmd = MNULL; + wlan_release_cmd_lock(pmadapter); + if (pmadapter->dbg.last_cmd_index) + pmadapter->dbg.last_cmd_index--; + else + pmadapter->dbg.last_cmd_index = DBG_CMD_NUM - 1; + + pmadapter->dbg.num_cmd_host_to_card_failure++; + wlan_dump_info(pmadapter, REASON_CODE_CMD_TO_CARD_FAILURE); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* Clear BSS_NO_BITS from HostCmd */ + cmd_code &= HostCmd_CMD_ID_MASK; + + /* For the command who has no command response, we should return here */ + if (cmd_code == HostCmd_CMD_FW_DUMP_EVENT + || cmd_code == HostCmd_CMD_SOFT_RESET) { + if (pcmd_node->pioctl_buf) { + PRINTM(MMSG, + "CMD(0x%x) has no cmd resp: free curr_cmd and do ioctl_complete\n", + cmd_code); + pioctl_buf = (mlan_ioctl_req *)pcmd_node->pioctl_buf; + wlan_request_cmd_lock(pmadapter); + wlan_insert_cmd_to_free_q(pmadapter, + pmadapter->curr_cmd); + pmadapter->curr_cmd = MNULL; + wlan_release_cmd_lock(pmadapter); + } + goto done; + } + + /* Setup the timer after transmit command */ + pcb->moal_start_timer(pmadapter->pmoal_handle, + pmadapter->pmlan_cmd_timer, MFALSE, + MRVDRV_TIMER_10S * 2); + + pmadapter->cmd_timer_is_set = MTRUE; + + ret = MLAN_STATUS_SUCCESS; + +done: + LEAVE(); + return ret; +} + +/** + * @brief This function sends sleep confirm command to firmware. + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status +wlan_dnld_sleep_confirm_cmd(mlan_adapter *pmadapter) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + static t_u32 i; + t_u16 cmd_len = 0; + opt_sleep_confirm_buffer *sleep_cfm_buf = + (opt_sleep_confirm_buffer *)(pmadapter->psleep_cfm->pbuf + + pmadapter->psleep_cfm-> + data_offset); + mlan_private *pmpriv = MNULL; + + ENTER(); + + pmpriv = wlan_get_priv(pmadapter, MLAN_BSS_ROLE_ANY); + if (!pmpriv) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + cmd_len = sizeof(OPT_Confirm_Sleep); + pmadapter->seq_num++; + sleep_cfm_buf->ps_cfm_sleep.seq_num = + wlan_cpu_to_le16(HostCmd_SET_SEQ_NO_BSS_INFO + (pmadapter->seq_num, pmpriv->bss_num, + pmpriv->bss_type)); + DBG_HEXDUMP(MCMD_D, "SLEEP_CFM", &sleep_cfm_buf->ps_cfm_sleep, + sizeof(OPT_Confirm_Sleep)); + + /* Send sleep confirm command to firmware */ + + pmadapter->psleep_cfm->data_len = cmd_len + INTF_HEADER_LEN; + ret = wlan_sdio_host_to_card(pmadapter, MLAN_TYPE_CMD, + pmadapter->psleep_cfm, MNULL); + + if (ret == MLAN_STATUS_FAILURE) { + PRINTM(MERROR, "SLEEP_CFM: failed\n"); + pmadapter->dbg.num_cmd_sleep_cfm_host_to_card_failure++; + goto done; + } else { + if (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_UAP) + pmadapter->ps_state = PS_STATE_SLEEP_CFM; +#ifdef STA_SUPPORT + if (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_STA) { + if (!sleep_cfm_buf->ps_cfm_sleep.sleep_cfm.resp_ctrl) { + /* Response is not needed for sleep confirm command */ + pmadapter->ps_state = PS_STATE_SLEEP; + } else { + pmadapter->ps_state = PS_STATE_SLEEP_CFM; + } + + if (!sleep_cfm_buf->ps_cfm_sleep.sleep_cfm.resp_ctrl + && (pmadapter->is_hs_configured && + !pmadapter->sleep_period.period)) { + pmadapter->pm_wakeup_card_req = MTRUE; + wlan_host_sleep_activated_event(wlan_get_priv + (pmadapter, + MLAN_BSS_ROLE_STA), + MTRUE); + } + } +#endif /* STA_SUPPORT */ + +#define NUM_SC_PER_LINE 16 + if (++i % NUM_SC_PER_LINE == 0) + PRINTM(MEVENT, "+\n"); + else + PRINTM(MEVENT, "+"); + } + +done: + LEAVE(); + return ret; +} + +/******************************************************** + Global Functions +********************************************************/ + +/** + * @brief Event handler + * + * @param priv A pointer to mlan_private structure + * @param event_id Event ID + * @param pmevent Event buffer + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_recv_event(pmlan_private priv, mlan_event_id event_id, t_void *pmevent) +{ + pmlan_callbacks pcb = MNULL; + + ENTER(); + + if (!priv) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + pcb = &priv->adapter->callbacks; + + if (pmevent) + /* The caller has provided the event. */ + pcb->moal_recv_event(priv->adapter->pmoal_handle, + (pmlan_event)pmevent); + else { + mlan_event mevent; + + memset(priv->adapter, &mevent, 0, sizeof(mlan_event)); + mevent.bss_index = priv->bss_index; + mevent.event_id = event_id; + mevent.event_len = 0; + + pcb->moal_recv_event(priv->adapter->pmoal_handle, &mevent); + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function allocates the command buffer and links + * it to command free queue. + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_alloc_cmd_buffer(IN mlan_adapter *pmadapter) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_callbacks *pcb = (mlan_callbacks *)&pmadapter->callbacks; + cmd_ctrl_node *pcmd_array = MNULL; + t_u32 buf_size; + t_u32 i; + + ENTER(); + + /* Allocate and initialize cmd_ctrl_node */ + buf_size = sizeof(cmd_ctrl_node) * MRVDRV_NUM_OF_CMD_BUFFER; + ret = pcb->moal_malloc(pmadapter->pmoal_handle, buf_size, + MLAN_MEM_DEF | MLAN_MEM_DMA, + (t_u8 **)&pcmd_array); + if (ret != MLAN_STATUS_SUCCESS || !pcmd_array) { + PRINTM(MERROR, + "ALLOC_CMD_BUF: Failed to allocate pcmd_array\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + pmadapter->cmd_pool = pcmd_array; + memset(pmadapter, pmadapter->cmd_pool, 0, buf_size); + + /* Allocate and initialize command buffers */ + for (i = 0; i < MRVDRV_NUM_OF_CMD_BUFFER; i++) { + pcmd_array[i].pmbuf = wlan_alloc_mlan_buffer(pmadapter, + MRVDRV_SIZE_OF_CMD_BUFFER, + 0, + MOAL_MALLOC_BUFFER); + if (!pcmd_array[i].pmbuf) { + PRINTM(MERROR, + "ALLOC_CMD_BUF: Failed to allocate command buffer\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + } + wlan_request_cmd_lock(pmadapter); + for (i = 0; i < MRVDRV_NUM_OF_CMD_BUFFER; i++) + wlan_insert_cmd_to_free_q(pmadapter, &pcmd_array[i]); + wlan_release_cmd_lock(pmadapter); + ret = MLAN_STATUS_SUCCESS; +done: + LEAVE(); + return ret; +} + +/** + * @brief This function frees the command buffer. + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_free_cmd_buffer(IN mlan_adapter *pmadapter) +{ + mlan_callbacks *pcb = (mlan_callbacks *)&pmadapter->callbacks; + cmd_ctrl_node *pcmd_array; + t_u32 i; + + ENTER(); + + /* Need to check if cmd pool is allocated or not */ + if (pmadapter->cmd_pool == MNULL) { + PRINTM(MINFO, "FREE_CMD_BUF: cmd_pool is Null\n"); + goto done; + } + + pcmd_array = pmadapter->cmd_pool; + + /* Release shared memory buffers */ + for (i = 0; i < MRVDRV_NUM_OF_CMD_BUFFER; i++) { + if (pcmd_array[i].pmbuf) { + PRINTM(MINFO, "Free all the command buffer.\n"); + wlan_free_mlan_buffer(pmadapter, pcmd_array[i].pmbuf); + pcmd_array[i].pmbuf = MNULL; + } + if (pcmd_array[i].respbuf) { + wlan_free_mlan_buffer(pmadapter, pcmd_array[i].respbuf); + pcmd_array[i].respbuf = MNULL; + } + } + /* Release cmd_ctrl_node */ + if (pmadapter->cmd_pool) { + PRINTM(MINFO, "Free command pool.\n"); + pcb->moal_mfree(pmadapter->pmoal_handle, + (t_u8 *)pmadapter->cmd_pool); + pmadapter->cmd_pool = MNULL; + } + +done: + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles events generated by firmware + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_process_event(pmlan_adapter pmadapter) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_private priv = wlan_get_priv(pmadapter, MLAN_BSS_ROLE_ANY); + pmlan_buffer pmbuf = pmadapter->pmlan_buffer_event; + t_u32 eventcause = pmadapter->event_cause; +#ifdef DEBUG_LEVEL1 + t_u32 in_ts_sec = 0, in_ts_usec = 0; +#endif + ENTER(); + + /* Save the last event to debug log */ + pmadapter->dbg.last_event_index = + (pmadapter->dbg.last_event_index + 1) % DBG_CMD_NUM; + pmadapter->dbg.last_event[pmadapter->dbg.last_event_index] = + (t_u16)eventcause; + + if ((eventcause & EVENT_ID_MASK) == EVENT_RADAR_DETECTED) { + if (wlan_11h_dfs_event_preprocessing(pmadapter) == + MLAN_STATUS_SUCCESS) { + memcpy(pmadapter, (t_u8 *)&eventcause, + pmbuf->pbuf + pmbuf->data_offset, + sizeof(eventcause)); + } else { + PRINTM(MERROR, "Error processing DFS Event: 0x%x\n", + eventcause); + goto done; + } + } + /* Get BSS number and corresponding priv */ + priv = wlan_get_priv_by_id(pmadapter, EVENT_GET_BSS_NUM(eventcause), + EVENT_GET_BSS_TYPE(eventcause)); + if (!priv) + priv = wlan_get_priv(pmadapter, MLAN_BSS_ROLE_ANY); + if (!priv) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* Clear BSS_NO_BITS from event */ + eventcause &= EVENT_ID_MASK; + pmadapter->event_cause = eventcause; + + if (pmbuf) { + pmbuf->bss_index = priv->bss_index; + memcpy(pmadapter, + pmbuf->pbuf + pmbuf->data_offset, + (t_u8 *)&eventcause, sizeof(eventcause)); + } + + if (MTRUE + && (eventcause != EVENT_PS_SLEEP && eventcause != EVENT_PS_AWAKE) + ) { + PRINTM_GET_SYS_TIME(MEVENT, &in_ts_sec, &in_ts_usec); + PRINTM_NETINTF(MEVENT, priv); + PRINTM(MEVENT, "%lu.%06lu : Event: 0x%x\n", in_ts_sec, + in_ts_usec, eventcause); + } + + ret = priv->ops.process_event(priv); +done: + pmadapter->event_cause = 0; + pmadapter->pmlan_buffer_event = MNULL; + if (pmbuf) + wlan_free_mlan_buffer(pmadapter, pmbuf); + + LEAVE(); + return ret; +} + +/** + * @brief This function requests a lock on command queue. + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return N/A + */ +t_void +wlan_request_cmd_lock(IN mlan_adapter *pmadapter) +{ + mlan_callbacks *pcb = (mlan_callbacks *)&pmadapter->callbacks; + + ENTER(); + + /* Call MOAL spin lock callback function */ + pcb->moal_spin_lock(pmadapter->pmoal_handle, pmadapter->pmlan_cmd_lock); + + LEAVE(); + return; +} + +/** + * @brief This function releases a lock on command queue. + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return N/A + */ +t_void +wlan_release_cmd_lock(IN mlan_adapter *pmadapter) +{ + mlan_callbacks *pcb = (mlan_callbacks *)&pmadapter->callbacks; + + ENTER(); + + /* Call MOAL spin unlock callback function */ + pcb->moal_spin_unlock(pmadapter->pmoal_handle, + pmadapter->pmlan_cmd_lock); + + LEAVE(); + return; +} + +/** + * @brief This function prepare the command before sending to firmware. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd_no Command number + * @param cmd_action Command action: GET or SET + * @param cmd_oid Cmd oid: treated as sub command + * @param pioctl_buf A pointer to MLAN IOCTL Request buffer + * @param pdata_buf A pointer to information buffer + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_prepare_cmd(IN mlan_private *pmpriv, + IN t_u16 cmd_no, + IN t_u16 cmd_action, + IN t_u32 cmd_oid, IN t_void *pioctl_buf, IN t_void *pdata_buf) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_adapter *pmadapter = MNULL; + cmd_ctrl_node *pcmd_node = MNULL; + HostCmd_DS_COMMAND *cmd_ptr = MNULL; + pmlan_ioctl_req pioctl_req = (mlan_ioctl_req *)pioctl_buf; + + ENTER(); + + if (!pmpriv) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + pmadapter = pmpriv->adapter; + + /* Sanity test */ + if (!pmadapter || pmadapter->surprise_removed) { + PRINTM(MERROR, "PREP_CMD: Card is Removed\n"); + if (pioctl_req) + pioctl_req->status_code = MLAN_ERROR_FW_NOT_READY; + ret = MLAN_STATUS_FAILURE; + goto done; + } + + if (pmadapter->hw_status == WlanHardwareStatusReset) { + if ((cmd_no != HostCmd_CMD_FUNC_INIT) + ) { + PRINTM(MERROR, "PREP_CMD: FW is in reset state\n"); + if (pioctl_req) + pioctl_req->status_code = + MLAN_ERROR_FW_NOT_READY; + ret = MLAN_STATUS_FAILURE; + goto done; + } + } + + /* Get a new command node */ + pcmd_node = wlan_get_cmd_node(pmadapter); + + if (pcmd_node == MNULL) { + PRINTM(MERROR, "PREP_CMD: No free cmd node\n"); + wlan_dump_info(pmadapter, REASON_CODE_NO_CMD_NODE); + if (pioctl_req) + pioctl_req->status_code = MLAN_ERROR_NO_MEM; + ret = MLAN_STATUS_FAILURE; + goto done; + } + /** reset num no cmd node */ + pmadapter->dbg.num_no_cmd_node = 0; + + /* Initialize the command node */ + wlan_init_cmd_node(pmpriv, pcmd_node, cmd_oid, pioctl_buf, pdata_buf); + + if (pcmd_node->cmdbuf == MNULL) { + PRINTM(MERROR, "PREP_CMD: No free cmd buf\n"); + if (pioctl_req) + pioctl_req->status_code = MLAN_ERROR_NO_MEM; + ret = MLAN_STATUS_FAILURE; + goto done; + } + + cmd_ptr = + (HostCmd_DS_COMMAND *)(pcmd_node->cmdbuf->pbuf + + pcmd_node->cmdbuf->data_offset); + cmd_ptr->command = cmd_no; + cmd_ptr->result = 0; + + /* Prepare command */ + if (cmd_no) + ret = pmpriv->ops.prepare_cmd(pmpriv, cmd_no, cmd_action, + cmd_oid, pioctl_buf, pdata_buf, + cmd_ptr); + else { + ret = wlan_cmd_host_cmd(pmpriv, cmd_ptr, pdata_buf); + pcmd_node->cmd_flag |= CMD_F_HOSTCMD; + } + + /* Return error, since the command preparation failed */ + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "PREP_CMD: Command 0x%x preparation failed\n", + cmd_no); + pcmd_node->pioctl_buf = MNULL; + if (pioctl_req) + pioctl_req->status_code = MLAN_ERROR_CMD_DNLD_FAIL; + wlan_request_cmd_lock(pmadapter); + wlan_insert_cmd_to_free_q(pmadapter, pcmd_node); + wlan_release_cmd_lock(pmadapter); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + wlan_request_cmd_lock(pmadapter); + /* Send command */ +#ifdef STA_SUPPORT + if (cmd_no == HostCmd_CMD_802_11_SCAN + || cmd_no == HostCmd_CMD_802_11_SCAN_EXT) { + if (cmd_no == HostCmd_CMD_802_11_SCAN_EXT && + pmadapter->ext_scan && pmadapter->ext_scan_enh + && pmadapter->ext_scan_type == EXT_SCAN_ENHANCE) { + wlan_insert_cmd_to_pending_q(pmadapter, pcmd_node, + MFALSE); + } else + wlan_queue_scan_cmd(pmpriv, pcmd_node); + } else { +#endif + if ((cmd_no == HostCmd_CMD_802_11_HS_CFG_ENH) && + (cmd_action == HostCmd_ACT_GEN_SET) && + (pmadapter->hs_cfg.conditions == HOST_SLEEP_CFG_CANCEL)) + wlan_insert_cmd_to_pending_q(pmadapter, pcmd_node, + MFALSE); + else + wlan_insert_cmd_to_pending_q(pmadapter, pcmd_node, + MTRUE); +#ifdef STA_SUPPORT + } +#endif + wlan_release_cmd_lock(pmadapter); +done: + LEAVE(); + return ret; +} + +/** + * @brief This function inserts command node to cmd_free_q + * after cleaning it. + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pcmd_node A pointer to cmd_ctrl_node structure + * + * @return N/A + */ +t_void +wlan_insert_cmd_to_free_q(IN mlan_adapter *pmadapter, + IN cmd_ctrl_node *pcmd_node) +{ + mlan_callbacks *pcb = (mlan_callbacks *)&pmadapter->callbacks; + mlan_ioctl_req *pioctl_req = MNULL; + ENTER(); + + if (pcmd_node == MNULL) + goto done; + if (pcmd_node->pioctl_buf) { + pioctl_req = (mlan_ioctl_req *)pcmd_node->pioctl_buf; + if (pioctl_req->status_code != MLAN_ERROR_NO_ERROR) + pcb->moal_ioctl_complete(pmadapter->pmoal_handle, + pioctl_req, + MLAN_STATUS_FAILURE); + else + pcb->moal_ioctl_complete(pmadapter->pmoal_handle, + pioctl_req, + MLAN_STATUS_SUCCESS); + } + /* Clean the node */ + wlan_clean_cmd_node(pmadapter, pcmd_node); + + /* Insert node into cmd_free_q */ + util_enqueue_list_tail(pmadapter->pmoal_handle, &pmadapter->cmd_free_q, + (pmlan_linked_list)pcmd_node, MNULL, MNULL); +done: + LEAVE(); +} + +/** + * @brief This function queues the command to cmd list. + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pcmd_node A pointer to cmd_ctrl_node structure + * @param add_tail Specify if the cmd needs to be queued in the header or tail + * + * @return N/A + */ +t_void +wlan_insert_cmd_to_pending_q(IN mlan_adapter *pmadapter, + IN cmd_ctrl_node *pcmd_node, IN t_u32 add_tail) +{ + HostCmd_DS_COMMAND *pcmd = MNULL; + t_u16 command; + + ENTER(); + + if (pcmd_node == MNULL) { + PRINTM(MERROR, "QUEUE_CMD: pcmd_node is MNULL\n"); + goto done; + } + + pcmd = (HostCmd_DS_COMMAND *)(pcmd_node->cmdbuf->pbuf + + pcmd_node->cmdbuf->data_offset); + + command = wlan_le16_to_cpu(pcmd->command); + + /* Exit_PS command needs to be queued in the header always. */ + if (command == HostCmd_CMD_802_11_PS_MODE_ENH) { + HostCmd_DS_802_11_PS_MODE_ENH *pm = &pcmd->params.psmode_enh; + if (wlan_le16_to_cpu(pm->action) == DIS_AUTO_PS) { + if (pmadapter->ps_state != PS_STATE_AWAKE) + add_tail = MFALSE; + } + } + + if (add_tail) { + util_enqueue_list_tail(pmadapter->pmoal_handle, + &pmadapter->cmd_pending_q, + (pmlan_linked_list)pcmd_node, + MNULL, MNULL); + } else { + util_enqueue_list_head(pmadapter->pmoal_handle, + &pmadapter->cmd_pending_q, + (pmlan_linked_list)pcmd_node, + MNULL, MNULL); + } + + PRINTM_NETINTF(MCMND, pcmd_node->priv); + PRINTM(MCMND, "QUEUE_CMD: cmd=0x%x is queued\n", command); + +done: + LEAVE(); + return; +} + +/** + * @brief This function executes next command in command + * pending queue. It will put firmware back to PS mode + * if applicable. + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_exec_next_cmd(mlan_adapter *pmadapter) +{ + mlan_private *priv = MNULL; + cmd_ctrl_node *pcmd_node = MNULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + HostCmd_DS_COMMAND *pcmd; + + ENTER(); + + /* Sanity test */ + if (pmadapter == MNULL) { + PRINTM(MERROR, "EXEC_NEXT_CMD: pmadapter is MNULL\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + /* Check if already in processing */ + if (pmadapter->curr_cmd) { + PRINTM(MERROR, + "EXEC_NEXT_CMD: there is command in processing!\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + wlan_request_cmd_lock(pmadapter); + /* Check if any command is pending */ + pcmd_node = + (cmd_ctrl_node *)util_peek_list(pmadapter->pmoal_handle, + &pmadapter->cmd_pending_q, + MNULL, MNULL); + + if (pcmd_node) { + pcmd = (HostCmd_DS_COMMAND *)(pcmd_node->cmdbuf->pbuf + + pcmd_node->cmdbuf->data_offset); + priv = pcmd_node->priv; + + if (pmadapter->ps_state != PS_STATE_AWAKE) { + PRINTM(MERROR, + "Cannot send command in sleep state, this should not happen\n"); + wlan_release_cmd_lock(pmadapter); + goto done; + } + + util_unlink_list(pmadapter->pmoal_handle, + &pmadapter->cmd_pending_q, + (pmlan_linked_list)pcmd_node, MNULL, MNULL); + wlan_release_cmd_lock(pmadapter); + ret = wlan_dnld_cmd_to_fw(priv, pcmd_node); + priv = wlan_get_priv(pmadapter, MLAN_BSS_ROLE_ANY); + /* Any command sent to the firmware when host is in sleep mode, should de-configure host sleep */ + /* We should skip the host sleep configuration command itself though */ + if (priv && + (pcmd->command != + wlan_cpu_to_le16(HostCmd_CMD_802_11_HS_CFG_ENH))) { + if (pmadapter->hs_activated == MTRUE) { + pmadapter->is_hs_configured = MFALSE; + wlan_host_sleep_activated_event(priv, MFALSE); + } + } + goto done; + } else { + wlan_release_cmd_lock(pmadapter); + } + ret = MLAN_STATUS_SUCCESS; +done: + LEAVE(); + return ret; +} + +/** + * @brief This function handles the command response + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_process_cmdresp(mlan_adapter *pmadapter) +{ + HostCmd_DS_COMMAND *resp = MNULL; + mlan_private *pmpriv = wlan_get_priv(pmadapter, MLAN_BSS_ROLE_ANY); + mlan_private *pmpriv_next = MNULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u16 orig_cmdresp_no; + t_u16 cmdresp_no; + t_u16 cmdresp_result; + mlan_ioctl_req *pioctl_buf = MNULL; + mlan_callbacks *pcb = (mlan_callbacks *)&pmadapter->callbacks; +#ifdef DEBUG_LEVEL1 + t_u32 sec = 0, usec = 0; +#endif + t_u32 i; + + ENTER(); + + /* Now we got response from FW, cancel the command timer */ + if (pmadapter->cmd_timer_is_set) { + /* Cancel command timeout timer */ + pcb->moal_stop_timer(pmadapter->pmoal_handle, + pmadapter->pmlan_cmd_timer); + /* Cancel command timeout timer */ + pmadapter->cmd_timer_is_set = MFALSE; + } + + if (pmadapter->curr_cmd) + if (pmadapter->curr_cmd->pioctl_buf != MNULL) { + pioctl_buf = + (mlan_ioctl_req *)pmadapter->curr_cmd-> + pioctl_buf; + } + + if (!pmadapter->curr_cmd || !pmadapter->curr_cmd->respbuf) { + resp = (HostCmd_DS_COMMAND *)pmadapter->upld_buf; + resp->command = wlan_le16_to_cpu(resp->command); + PRINTM(MERROR, "CMD_RESP: No curr_cmd, 0x%x\n", resp->command); + if (pioctl_buf) + pioctl_buf->status_code = MLAN_ERROR_CMD_RESP_FAIL; + ret = MLAN_STATUS_FAILURE; + goto done; + } + + pmadapter->num_cmd_timeout = 0; + + DBG_HEXDUMP(MCMD_D, "CMD_RESP", + pmadapter->curr_cmd->respbuf->pbuf + + pmadapter->curr_cmd->respbuf->data_offset, + pmadapter->curr_cmd->respbuf->data_len); + + resp = (HostCmd_DS_COMMAND *)(pmadapter->curr_cmd->respbuf->pbuf + + pmadapter->curr_cmd->respbuf-> + data_offset); + wlan_request_cmd_lock(pmadapter); + if (pmadapter->curr_cmd->cmd_flag & CMD_F_CANCELED) { + cmd_ctrl_node *free_cmd = pmadapter->curr_cmd; + pmadapter->curr_cmd = MNULL; + PRINTM(MCMND, "CMD_RESP: 0x%x been canceled!\n", + wlan_le16_to_cpu(resp->command)); + if (pioctl_buf) + pioctl_buf->status_code = MLAN_ERROR_CMD_CANCEL; + wlan_insert_cmd_to_free_q(pmadapter, free_cmd); + wlan_release_cmd_lock(pmadapter); + ret = MLAN_STATUS_FAILURE; + goto done; + } else { + wlan_release_cmd_lock(pmadapter); + } + if (pmadapter->curr_cmd->cmd_flag & CMD_F_HOSTCMD) { + /* Copy original response back to response buffer */ + wlan_ret_host_cmd(pmpriv, resp, pioctl_buf); + } + orig_cmdresp_no = wlan_le16_to_cpu(resp->command); + resp->size = wlan_le16_to_cpu(resp->size); + resp->seq_num = wlan_le16_to_cpu(resp->seq_num); + resp->result = wlan_le16_to_cpu(resp->result); + + /* Get BSS number and corresponding priv */ + pmpriv = wlan_get_priv_by_id(pmadapter, + HostCmd_GET_BSS_NO(resp->seq_num), + HostCmd_GET_BSS_TYPE(resp->seq_num)); + if (!pmpriv) + pmpriv = wlan_get_priv(pmadapter, MLAN_BSS_ROLE_ANY); + /* Clear RET_BIT from HostCmd */ + resp->command = (orig_cmdresp_no & HostCmd_CMD_ID_MASK); + cmdresp_no = resp->command; + + cmdresp_result = resp->result; + + if (resp->command == HostCmd_CMD_HOST_CLOCK_CFG) { + /* d2 needs to be fast - is there a way to avoid callback? */ + if (pmpriv) { + pcb->moal_get_host_time_ns(&pmadapter->d2); + PRINTM(MINFO, + "WIFI_TS: RTT for Host_CLOCK_CFG= %d ns\n", + pmadapter->d2 - pmadapter->d1); + } + } + + /* Save the last command response to debug log */ + pmadapter->dbg.last_cmd_resp_index = + (pmadapter->dbg.last_cmd_resp_index + 1) % DBG_CMD_NUM; + pmadapter->dbg.last_cmd_resp_id[pmadapter->dbg.last_cmd_resp_index] = + orig_cmdresp_no; + + PRINTM_GET_SYS_TIME(MCMND, &sec, &usec); + PRINTM_NETINTF(MCMND, pmadapter->curr_cmd->priv); + PRINTM(MCMND, + "CMD_RESP (%lu.%06lu): 0x%x, result %d, len %d, seqno 0x%x\n", + sec, usec, orig_cmdresp_no, cmdresp_result, resp->size, + resp->seq_num); + + if (!(orig_cmdresp_no & HostCmd_RET_BIT)) { + PRINTM(MERROR, "CMD_RESP: Invalid response to command!\n"); + if (pioctl_buf) + pioctl_buf->status_code = MLAN_ERROR_FW_CMDRESP; + wlan_request_cmd_lock(pmadapter); + wlan_insert_cmd_to_free_q(pmadapter, pmadapter->curr_cmd); + pmadapter->curr_cmd = MNULL; + wlan_release_cmd_lock(pmadapter); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + if (pmadapter->curr_cmd->cmd_flag & CMD_F_HOSTCMD) { + pmadapter->curr_cmd->cmd_flag &= ~CMD_F_HOSTCMD; + if ((cmdresp_result == HostCmd_RESULT_OK) + && (cmdresp_no == HostCmd_CMD_802_11_HS_CFG_ENH)) + ret = wlan_ret_802_11_hs_cfg(pmpriv, resp, pioctl_buf); + } else { + /* handle response */ + ret = pmpriv->ops.process_cmdresp(pmpriv, cmdresp_no, resp, + pioctl_buf); + } + + /* Check init command response */ + if (pmadapter->hw_status == WlanHardwareStatusInitializing || + pmadapter->hw_status == WlanHardwareStatusGetHwSpec) { + if (ret == MLAN_STATUS_FAILURE) { +#if defined(STA_SUPPORT) + if (pmadapter->pwarm_reset_ioctl_req) { + /* warm reset failure */ + pmadapter->pwarm_reset_ioctl_req->status_code = + MLAN_ERROR_CMD_RESP_FAIL; + pcb->moal_ioctl_complete(pmadapter-> + pmoal_handle, + pmadapter-> + pwarm_reset_ioctl_req, + MLAN_STATUS_FAILURE); + pmadapter->pwarm_reset_ioctl_req = MNULL; + goto done; + } +#endif + PRINTM(MERROR, + "cmd 0x%02x failed during initialization\n", + cmdresp_no); + wlan_init_fw_complete(pmadapter); + goto done; + } + } + + wlan_request_cmd_lock(pmadapter); + if (pmadapter->curr_cmd) { + cmd_ctrl_node *free_cmd = pmadapter->curr_cmd; + pioctl_buf = (mlan_ioctl_req *)pmadapter->curr_cmd->pioctl_buf; + pmadapter->curr_cmd = MNULL; + if (pioctl_buf && (ret == MLAN_STATUS_SUCCESS)) + pioctl_buf->status_code = MLAN_ERROR_NO_ERROR; + else if (pioctl_buf && (ret == MLAN_STATUS_FAILURE) && + !pioctl_buf->status_code) + pioctl_buf->status_code = MLAN_ERROR_CMD_RESP_FAIL; + + /* Clean up and put current command back to cmd_free_q */ + wlan_insert_cmd_to_free_q(pmadapter, free_cmd); + } + wlan_release_cmd_lock(pmadapter); + + if ((pmadapter->hw_status == WlanHardwareStatusInitializing) && + (pmadapter->last_init_cmd == cmdresp_no)) { + i = pmpriv->bss_index + 1; + while (i < pmadapter->priv_num && + (!(pmpriv_next = pmadapter->priv[i]) + || pmpriv_next->bss_virtual)) + i++; + if (!pmpriv_next || i >= pmadapter->priv_num) { + +#if defined(STA_SUPPORT) + if (pmadapter->pwarm_reset_ioctl_req) { + /* warm reset complete */ + pmadapter->hw_status = WlanHardwareStatusReady; + pcb->moal_ioctl_complete(pmadapter-> + pmoal_handle, + pmadapter-> + pwarm_reset_ioctl_req, + MLAN_STATUS_SUCCESS); + pmadapter->pwarm_reset_ioctl_req = MNULL; + goto done; + } +#endif + pmadapter->hw_status = WlanHardwareStatusInitdone; + } else { + /* Issue init commands for the next interface */ + ret = pmpriv_next->ops.init_cmd(pmpriv_next, MFALSE); + } + } else if ((pmadapter->hw_status == WlanHardwareStatusGetHwSpec) && + (HostCmd_CMD_GET_HW_SPEC == cmdresp_no)) { + pmadapter->hw_status = WlanHardwareStatusGetHwSpecdone; + } +done: + LEAVE(); + return ret; +} + +/** + * @brief This function handles the timeout of command sending. + * It will re-send the same command again. + * + * @param function_context A pointer to function_context + * @return N/A + */ +t_void +wlan_cmd_timeout_func(t_void *function_context) +{ + mlan_adapter *pmadapter = (mlan_adapter *)function_context; + cmd_ctrl_node *pcmd_node = MNULL; + mlan_ioctl_req *pioctl_buf = MNULL; +#ifdef DEBUG_LEVEL1 + t_u32 sec = 0, usec = 0; +#endif + t_u8 i; + mlan_private *pmpriv = MNULL; + + ENTER(); + + pmadapter->cmd_timer_is_set = MFALSE; + if (!pmadapter->curr_cmd) { + if (pmadapter->ext_scan && pmadapter->ext_scan_enh && + pmadapter->scan_processing) { + PRINTM(MMSG, "Ext scan enh timeout\n"); + pmadapter->ext_scan_timeout = MTRUE; + wlan_dump_info(pmadapter, REASON_CODE_EXT_SCAN_TIMEOUT); + goto exit; + } + PRINTM(MWARN, "CurCmd Empty\n"); + goto exit; + } + pmadapter->num_cmd_timeout++; + pmadapter->dbg.num_cmd_timeout++; + pcmd_node = pmadapter->curr_cmd; + if (pcmd_node->pioctl_buf != MNULL) { + pioctl_buf = (mlan_ioctl_req *)pcmd_node->pioctl_buf; + pioctl_buf->status_code = MLAN_ERROR_CMD_TIMEOUT; + } + + pmadapter->dbg.timeout_cmd_id = + pmadapter->dbg.last_cmd_id[pmadapter->dbg.last_cmd_index]; + pmadapter->dbg.timeout_cmd_act = + pmadapter->dbg.last_cmd_act[pmadapter->dbg.last_cmd_index]; + PRINTM_GET_SYS_TIME(MERROR, &sec, &usec); + PRINTM(MERROR, "Timeout cmd id (%lu.%06lu) = 0x%x, act = 0x%x\n", sec, + usec, pmadapter->dbg.timeout_cmd_id, + pmadapter->dbg.timeout_cmd_act); + if (pcmd_node->cmdbuf) { + t_u8 *pcmd_buf; + pcmd_buf = + pcmd_node->cmdbuf->pbuf + + pcmd_node->cmdbuf->data_offset + INTF_HEADER_LEN; + for (i = 0; i < 16; i++) + PRINTM(MERROR, "%02x ", *pcmd_buf++); + PRINTM(MERROR, "\n"); + } + + pmpriv = pcmd_node->priv; + if (pmpriv) + PRINTM(MERROR, "BSS type = %d BSS role= %d\n", pmpriv->bss_type, + pmpriv->bss_role); + wlan_dump_info(pmadapter, REASON_CODE_CMD_TIMEOUT); + + if (pmadapter->hw_status == WlanHardwareStatusInitializing || + pmadapter->hw_status == WlanHardwareStatusGetHwSpec) + wlan_init_fw_complete(pmadapter); + else { + /* Signal MOAL to perform extra handling for debugging */ + if (pmpriv) { + wlan_recv_event(pmpriv, MLAN_EVENT_ID_DRV_DBG_DUMP, + MNULL); + } else { + wlan_recv_event(wlan_get_priv + (pmadapter, MLAN_BSS_ROLE_ANY), + MLAN_EVENT_ID_DRV_DBG_DUMP, MNULL); + } + } + +exit: + LEAVE(); + return; +} + +#ifdef STA_SUPPORT +/** + * @brief Internal function used to flush the scan pending queue + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return N/A + */ +t_void +wlan_flush_scan_queue(IN pmlan_adapter pmadapter) +{ + + cmd_ctrl_node *pcmd_node = MNULL; + + ENTER(); + + wlan_request_cmd_lock(pmadapter); + while ((pcmd_node = + (cmd_ctrl_node *)util_peek_list(pmadapter->pmoal_handle, + &pmadapter->scan_pending_q, + MNULL, MNULL))) { + util_unlink_list(pmadapter->pmoal_handle, + &pmadapter->scan_pending_q, + (pmlan_linked_list)pcmd_node, MNULL, MNULL); + pcmd_node->pioctl_buf = MNULL; + wlan_insert_cmd_to_free_q(pmadapter, pcmd_node); + } + + pmadapter->scan_processing = MFALSE; + wlan_release_cmd_lock(pmadapter); + + LEAVE(); +} + +/** + * @brief Cancel pending SCAN ioctl cmd. + * + * @param pmadapter A pointer to mlan_adapter + * @param pioctl_req A pointer to pmlan_ioctl_req + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING + */ +mlan_status +wlan_cancel_pending_scan_cmd(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + pmlan_callbacks pcb = &pmadapter->callbacks; + cmd_ctrl_node *pcmd_node = MNULL; + mlan_ioctl_req *pioctl_buf = MNULL; + pmlan_private priv = MNULL; + mlan_status status = MLAN_STATUS_SUCCESS; + ENTER(); + + PRINTM(MIOCTL, "Cancel scan command\n"); + wlan_request_cmd_lock(pmadapter); + /* IOCTL will be completed, avoid calling IOCTL complete again from EVENT/CMDRESP */ + if (pmadapter->pscan_ioctl_req) { + pioctl_buf = pmadapter->pscan_ioctl_req; + priv = pmadapter->priv[pioctl_buf->bss_index]; + pmadapter->pscan_ioctl_req = MNULL; + pioctl_buf->status_code = MLAN_ERROR_CMD_CANCEL; + pcb->moal_ioctl_complete(pmadapter->pmoal_handle, pioctl_buf, + MLAN_STATUS_FAILURE); + } + + if (pmadapter->curr_cmd && pmadapter->curr_cmd->pioctl_buf) { + pioctl_buf = (mlan_ioctl_req *)pmadapter->curr_cmd->pioctl_buf; + if (pioctl_buf->req_id == MLAN_IOCTL_SCAN) { + PRINTM(MIOCTL, "wlan_cancel_scan: current command\n"); + pcmd_node = pmadapter->curr_cmd; + pcmd_node->pioctl_buf = MNULL; + pcmd_node->cmd_flag |= CMD_F_CANCELED; + pioctl_buf->status_code = MLAN_ERROR_CMD_CANCEL; + pcb->moal_ioctl_complete(pmadapter->pmoal_handle, + pioctl_buf, + MLAN_STATUS_FAILURE); + } + } + while ((pcmd_node = wlan_get_pending_scan_cmd(pmadapter)) != MNULL) { + PRINTM(MIOCTL, + "wlan_cancel_scan: find scan command in cmd_pending_q\n"); + util_unlink_list(pmadapter->pmoal_handle, + &pmadapter->cmd_pending_q, + (pmlan_linked_list)pcmd_node, MNULL, MNULL); + wlan_insert_cmd_to_free_q(pmadapter, pcmd_node); + } + wlan_release_cmd_lock(pmadapter); + if (pmadapter->scan_processing && + pmadapter->ext_scan_type == EXT_SCAN_ENHANCE) { + if (priv) { + wlan_prepare_cmd(priv, HostCmd_CMD_802_11_SCAN_EXT, + HostCmd_ACT_GEN_SET, 0, pioctl_req, + MNULL); + wlan_recv_event(priv, MLAN_EVENT_ID_DRV_DEFER_HANDLING, + MNULL); + status = MLAN_STATUS_PENDING; + } + } else + /* Cancel all pending scan command */ + wlan_flush_scan_queue(pmadapter); + LEAVE(); + return status; +} +#endif + +/** + * @brief Cancel all pending cmd. + * + * @param pmadapter A pointer to mlan_adapter + * + * @return N/A + */ +t_void +wlan_cancel_all_pending_cmd(pmlan_adapter pmadapter) +{ + cmd_ctrl_node *pcmd_node = MNULL; + pmlan_callbacks pcb = &pmadapter->callbacks; + mlan_ioctl_req *pioctl_buf = MNULL; +#ifdef STA_SUPPORT + pmlan_private priv = MNULL; +#endif + ENTER(); + /* Cancel current cmd */ + wlan_request_cmd_lock(pmadapter); +#ifdef STA_SUPPORT + /* IOCTL will be completed, avoid calling IOCTL complete again from EVENT/CMDRESP */ + if (pmadapter->pscan_ioctl_req) { + pioctl_buf = pmadapter->pscan_ioctl_req; + priv = pmadapter->priv[pioctl_buf->bss_index]; + pmadapter->pscan_ioctl_req = MNULL; + pioctl_buf->status_code = MLAN_ERROR_CMD_CANCEL; + pcb->moal_ioctl_complete(pmadapter->pmoal_handle, pioctl_buf, + MLAN_STATUS_FAILURE); + } +#endif + if (pmadapter->curr_cmd) { + pcmd_node = pmadapter->curr_cmd; + pmadapter->curr_cmd = MNULL; + if (pcmd_node->pioctl_buf) { + pioctl_buf = (mlan_ioctl_req *)pcmd_node->pioctl_buf; + pioctl_buf->status_code = MLAN_ERROR_CMD_CANCEL; + pcb->moal_ioctl_complete(pmadapter->pmoal_handle, + pioctl_buf, + MLAN_STATUS_FAILURE); + pcmd_node->pioctl_buf = MNULL; + } + wlan_insert_cmd_to_free_q(pmadapter, pcmd_node); + } + + /* Cancel all pending command */ + while ((pcmd_node = + (cmd_ctrl_node *)util_peek_list(pmadapter->pmoal_handle, + &pmadapter->cmd_pending_q, + MNULL, MNULL))) { + util_unlink_list(pmadapter->pmoal_handle, + &pmadapter->cmd_pending_q, + (pmlan_linked_list)pcmd_node, MNULL, MNULL); + if (pcmd_node->pioctl_buf) { + pioctl_buf = (mlan_ioctl_req *)pcmd_node->pioctl_buf; + pioctl_buf->status_code = MLAN_ERROR_CMD_CANCEL; + pcb->moal_ioctl_complete(pmadapter->pmoal_handle, + pioctl_buf, + MLAN_STATUS_FAILURE); + pcmd_node->pioctl_buf = MNULL; + } + wlan_insert_cmd_to_free_q(pmadapter, pcmd_node); + } + wlan_release_cmd_lock(pmadapter); +#ifdef STA_SUPPORT + if (pmadapter->scan_processing && + pmadapter->ext_scan_type == EXT_SCAN_ENHANCE) { + if (priv) { + wlan_prepare_cmd(priv, HostCmd_CMD_802_11_SCAN_EXT, + HostCmd_ACT_GEN_SET, 0, MNULL, MNULL); + wlan_recv_event(priv, MLAN_EVENT_ID_DRV_DEFER_HANDLING, + MNULL); + } + } else + /* Cancel all pending scan command */ + wlan_flush_scan_queue(pmadapter); +#endif + LEAVE(); +} + +/** + * @brief Cancel specific bss's pending ioctl cmd. + * + * @param pmadapter A pointer to mlan_adapter + * @param bss_index BSS index + * + * @return N/A + */ +t_void +wlan_cancel_bss_pending_cmd(pmlan_adapter pmadapter, t_u32 bss_index) +{ + pmlan_callbacks pcb = &pmadapter->callbacks; + cmd_ctrl_node *pcmd_node = MNULL; + mlan_ioctl_req *pioctl_buf = MNULL; +#ifdef STA_SUPPORT + t_u8 flash_scan = MFALSE; +#endif +#ifdef STA_SUPPORT + pmlan_private priv = MNULL; +#endif + ENTER(); + + PRINTM(MIOCTL, "MOAL Cancel BSS IOCTL: bss_index=%d\n", (int)bss_index); + wlan_request_cmd_lock(pmadapter); +#ifdef STA_SUPPORT + if (pmadapter->pscan_ioctl_req && + (pmadapter->pscan_ioctl_req->bss_index == bss_index)) { + /* IOCTL will be completed, avoid calling IOCTL complete again from EVENT/CMDRESP */ + flash_scan = MTRUE; + pioctl_buf = pmadapter->pscan_ioctl_req; + priv = pmadapter->priv[pioctl_buf->bss_index]; + pmadapter->pscan_ioctl_req = MNULL; + pioctl_buf->status_code = MLAN_ERROR_CMD_CANCEL; + pcb->moal_ioctl_complete(pmadapter->pmoal_handle, pioctl_buf, + MLAN_STATUS_FAILURE); + } +#endif + if (pmadapter->curr_cmd && pmadapter->curr_cmd->pioctl_buf) { + pioctl_buf = (mlan_ioctl_req *)pmadapter->curr_cmd->pioctl_buf; + if (pioctl_buf->bss_index == bss_index) { + pcmd_node = pmadapter->curr_cmd; + pcmd_node->pioctl_buf = MNULL; + pcmd_node->cmd_flag |= CMD_F_CANCELED; +#ifdef STA_SUPPORT + if (pioctl_buf->req_id == MLAN_IOCTL_SCAN) + flash_scan = MTRUE; +#endif + pioctl_buf->status_code = MLAN_ERROR_CMD_CANCEL; + pcb->moal_ioctl_complete(pmadapter->pmoal_handle, + pioctl_buf, + MLAN_STATUS_FAILURE); + } + } + while ((pcmd_node = + wlan_get_bss_pending_ioctl_cmd(pmadapter, + bss_index)) != MNULL) { + util_unlink_list(pmadapter->pmoal_handle, + &pmadapter->cmd_pending_q, + (pmlan_linked_list)pcmd_node, MNULL, MNULL); + pioctl_buf = (mlan_ioctl_req *)pcmd_node->pioctl_buf; + pcmd_node->pioctl_buf = MNULL; +#ifdef STA_SUPPORT + if (pioctl_buf->req_id == MLAN_IOCTL_SCAN) + flash_scan = MTRUE; +#endif + pioctl_buf->status_code = MLAN_ERROR_CMD_CANCEL; + pcb->moal_ioctl_complete(pmadapter->pmoal_handle, pioctl_buf, + MLAN_STATUS_FAILURE); + wlan_insert_cmd_to_free_q(pmadapter, pcmd_node); + } + wlan_release_cmd_lock(pmadapter); +#ifdef STA_SUPPORT + if (flash_scan) { + if (pmadapter->scan_processing && + pmadapter->ext_scan_type == EXT_SCAN_ENHANCE) { + if (priv) { + wlan_prepare_cmd(priv, + HostCmd_CMD_802_11_SCAN_EXT, + HostCmd_ACT_GEN_SET, 0, MNULL, + MNULL); + wlan_recv_event(priv, + MLAN_EVENT_ID_DRV_DEFER_HANDLING, + MNULL); + } + } else + /* Cancel all pending scan command */ + wlan_flush_scan_queue(pmadapter); + } +#endif + LEAVE(); + return; +} + +/** + * @brief Cancel pending ioctl cmd. + * + * @param pmadapter A pointer to mlan_adapter + * @param pioctl_req A pointer to mlan_ioctl_req buf + * + * @return N/A + */ +t_void +wlan_cancel_pending_ioctl(pmlan_adapter pmadapter, pmlan_ioctl_req pioctl_req) +{ + pmlan_callbacks pcb = &pmadapter->callbacks; + cmd_ctrl_node *pcmd_node = MNULL; + t_u8 find = MFALSE; +#ifdef STA_SUPPORT + pmlan_private priv = MNULL; +#endif + + ENTER(); + + PRINTM(MIOCTL, "MOAL Cancel IOCTL: 0x%x sub_id=0x%x action=%d\n", + pioctl_req->req_id, *((t_u32 *)pioctl_req->pbuf), + (int)pioctl_req->action); + + wlan_request_cmd_lock(pmadapter); +#ifdef STA_SUPPORT + /* IOCTL will be completed, avoid calling IOCTL complete again from EVENT/CMDRESP */ + if (pmadapter->pscan_ioctl_req == pioctl_req) { + priv = pmadapter->priv[pioctl_req->bss_index]; + pmadapter->pscan_ioctl_req = MNULL; + find = MTRUE; + } +#endif + if ((pmadapter->curr_cmd) && + (pmadapter->curr_cmd->pioctl_buf == pioctl_req)) { + pcmd_node = pmadapter->curr_cmd; + pcmd_node->pioctl_buf = MNULL; + pcmd_node->cmd_flag |= CMD_F_CANCELED; + find = MTRUE; + } + + while ((pcmd_node = + wlan_get_pending_ioctl_cmd(pmadapter, pioctl_req)) != MNULL) { + util_unlink_list(pmadapter->pmoal_handle, + &pmadapter->cmd_pending_q, + (pmlan_linked_list)pcmd_node, MNULL, MNULL); + pcmd_node->pioctl_buf = MNULL; + find = MTRUE; + wlan_insert_cmd_to_free_q(pmadapter, pcmd_node); + } + wlan_release_cmd_lock(pmadapter); +#ifdef STA_SUPPORT + if (pioctl_req->req_id == MLAN_IOCTL_SCAN) { + if (pmadapter->scan_processing && + pmadapter->ext_scan_type == EXT_SCAN_ENHANCE) { + if (priv) { + wlan_prepare_cmd(priv, + HostCmd_CMD_802_11_SCAN_EXT, + HostCmd_ACT_GEN_SET, 0, MNULL, + MNULL); + wlan_recv_event(priv, + MLAN_EVENT_ID_DRV_DEFER_HANDLING, + MNULL); + } + } else + /* Cancel all pending scan command */ + wlan_flush_scan_queue(pmadapter); + } +#endif + if (find) { + pioctl_req->status_code = MLAN_ERROR_CMD_CANCEL; + pcb->moal_ioctl_complete(pmadapter->pmoal_handle, pioctl_req, + MLAN_STATUS_FAILURE); + } + + LEAVE(); + return; +} + +/** + * @brief Handle the version_ext resp + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_ret_ver_ext(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, IN mlan_ioctl_req *pioctl_buf) +{ + HostCmd_DS_VERSION_EXT *ver_ext = &resp->params.verext; + mlan_ds_get_info *info; + ENTER(); + if (pioctl_buf) { + info = (mlan_ds_get_info *)pioctl_buf->pbuf; + info->param.ver_ext.version_str_sel = ver_ext->version_str_sel; + memcpy(pmpriv->adapter, info->param.ver_ext.version_str, + ver_ext->version_str, sizeof(char) * 128); + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Handle the rx mgmt forward registration resp + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_ret_rx_mgmt_ind(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, IN mlan_ioctl_req *pioctl_buf) +{ + mlan_ds_misc_cfg *misc = MNULL; + ENTER(); + + if (pioctl_buf) { + misc = (mlan_ds_misc_cfg *)pioctl_buf->pbuf; + misc->param.mgmt_subtype_mask = + wlan_le32_to_cpu(resp->params.rx_mgmt_ind. + mgmt_subtype_mask); + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function checks conditions and prepares to + * send sleep confirm command to firmware if OK. + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return N/A + */ +t_void +wlan_check_ps_cond(mlan_adapter *pmadapter) +{ + ENTER(); + + if (!pmadapter->cmd_sent && + !pmadapter->curr_cmd && !IS_CARD_RX_RCVD(pmadapter)) { + wlan_dnld_sleep_confirm_cmd(pmadapter); + } else { + PRINTM(MCMND, "Delay Sleep Confirm (%s%s%s)\n", + (pmadapter->cmd_sent) ? "D" : "", + (pmadapter->curr_cmd) ? "C" : "", + (IS_CARD_RX_RCVD(pmadapter)) ? "R" : ""); + } + + LEAVE(); +} + +/** + * @brief This function sends the HS_ACTIVATED event to the application + * + * @param priv A pointer to mlan_private structure + * @param activated MTRUE if activated, MFALSE if de-activated + * + * @return N/A + */ +t_void +wlan_host_sleep_activated_event(pmlan_private priv, t_u8 activated) +{ + ENTER(); + + if (!priv) { + LEAVE(); + return; + } + + if (activated) { + if (priv->adapter->is_hs_configured) { + priv->adapter->hs_activated = MTRUE; + wlan_update_rxreorder_tbl(priv->adapter, MTRUE); + PRINTM(MEVENT, "hs_activated\n"); + wlan_recv_event(priv, MLAN_EVENT_ID_DRV_HS_ACTIVATED, + MNULL); + } else + PRINTM(MWARN, "hs_activated: HS not configured !!!\n"); + } else { + PRINTM(MEVENT, "hs_deactived\n"); + priv->adapter->hs_activated = MFALSE; + wlan_recv_event(priv, MLAN_EVENT_ID_DRV_HS_DEACTIVATED, MNULL); + } + + LEAVE(); + return; +} + +/** + * @brief This function sends the HS_WAKEUP event to the application + * + * @param priv A pointer to mlan_private structure + * + * @return N/A + */ +t_void +wlan_host_sleep_wakeup_event(pmlan_private priv) +{ + ENTER(); + + if (priv->adapter->is_hs_configured) + wlan_recv_event(priv, MLAN_EVENT_ID_FW_HS_WAKEUP, MNULL); + else + PRINTM(MWARN, "hs_wakeup: Host Sleep not configured !!!\n"); + + LEAVE(); +} + +/** + * @brief This function handles the command response of hs_cfg + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_ret_802_11_hs_cfg(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf) +{ + pmlan_adapter pmadapter = pmpriv->adapter; + HostCmd_DS_802_11_HS_CFG_ENH *phs_cfg = &resp->params.opt_hs_cfg; + + ENTER(); + + if (wlan_le16_to_cpu(phs_cfg->action) == HS_ACTIVATE) { + /* clean up curr_cmd to allow suspend */ + if (pioctl_buf) + pioctl_buf->status_code = MLAN_ERROR_NO_ERROR; + /* Clean up and put current command back to cmd_free_q */ + wlan_request_cmd_lock(pmadapter); + wlan_insert_cmd_to_free_q(pmadapter, pmadapter->curr_cmd); + pmadapter->curr_cmd = MNULL; + wlan_release_cmd_lock(pmadapter); + wlan_host_sleep_activated_event(pmpriv, MTRUE); + goto done; + } else { + phs_cfg->params.hs_config.conditions = + wlan_le32_to_cpu(phs_cfg->params.hs_config.conditions); + PRINTM(MCMND, + "CMD_RESP: HS_CFG cmd reply result=%#x," + " conditions=0x%x gpio=0x%x gap=0x%x\n", resp->result, + phs_cfg->params.hs_config.conditions, + phs_cfg->params.hs_config.gpio, + phs_cfg->params.hs_config.gap); + } + if (phs_cfg->params.hs_config.conditions != HOST_SLEEP_CFG_CANCEL) { + pmadapter->is_hs_configured = MTRUE; + } else { + pmadapter->is_hs_configured = MFALSE; + if (pmadapter->hs_activated) + wlan_host_sleep_activated_event(pmpriv, MFALSE); + } + +done: + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Perform hs related activities on receiving the power up interrupt + * + * @param pmadapter A pointer to the adapter structure + * @return N/A + */ +t_void +wlan_process_hs_config(pmlan_adapter pmadapter) +{ + ENTER(); + PRINTM(MINFO, "Recevie interrupt/data in HS mode\n"); + if (pmadapter->hs_cfg.gap == HOST_SLEEP_CFG_GAP_FF) + wlan_pm_wakeup_card(pmadapter); + LEAVE(); + return; +} + +/** + * @brief Check sleep confirm command response and set the state to ASLEEP + * + * @param pmadapter A pointer to the adapter structure + * @param pbuf A pointer to the command response buffer + * @param upld_len Command response buffer length + * @return N/A + */ +void +wlan_process_sleep_confirm_resp(pmlan_adapter pmadapter, t_u8 *pbuf, + t_u32 upld_len) +{ + HostCmd_DS_COMMAND *cmd; + + ENTER(); + + if (!upld_len) { + PRINTM(MERROR, "Command size is 0\n"); + LEAVE(); + return; + } + cmd = (HostCmd_DS_COMMAND *)pbuf; + cmd->result = wlan_le16_to_cpu(cmd->result); + cmd->command = wlan_le16_to_cpu(cmd->command); + cmd->seq_num = wlan_le16_to_cpu(cmd->seq_num); + + /* Update sequence number */ + cmd->seq_num = HostCmd_GET_SEQ_NO(cmd->seq_num); + /* Clear RET_BIT from HostCmd */ + cmd->command &= HostCmd_CMD_ID_MASK; + + if (cmd->command != HostCmd_CMD_802_11_PS_MODE_ENH) { + PRINTM(MERROR, + "Received unexpected response for command %x, result = %x\n", + cmd->command, cmd->result); + LEAVE(); + return; + } + PRINTM(MEVENT, "#\n"); + if (cmd->result != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "Sleep confirm command failed\n"); + pmadapter->pm_wakeup_card_req = MFALSE; + pmadapter->ps_state = PS_STATE_AWAKE; + LEAVE(); + return; + } + pmadapter->pm_wakeup_card_req = MTRUE; + + if (pmadapter->is_hs_configured) { + wlan_host_sleep_activated_event(wlan_get_priv + (pmadapter, MLAN_BSS_ROLE_ANY), + MTRUE); + } + pmadapter->ps_state = PS_STATE_SLEEP; + LEAVE(); +} + +/** + * @brief This function prepares command of power mode + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action the action: GET or SET + * @param ps_bitmap PS bitmap + * @param pdata_buf A pointer to data buffer + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_cmd_enh_power_mode(pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, + IN t_u16 ps_bitmap, IN t_void *pdata_buf) +{ + HostCmd_DS_802_11_PS_MODE_ENH *psmode_enh = &cmd->params.psmode_enh; + t_u8 *tlv = MNULL; + t_u16 cmd_size = 0; + + ENTER(); + + PRINTM(MCMND, "PS Command: action = 0x%x, bitmap = 0x%x\n", cmd_action, + ps_bitmap); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_802_11_PS_MODE_ENH); + if (cmd_action == DIS_AUTO_PS) { + psmode_enh->action = wlan_cpu_to_le16(DIS_AUTO_PS); + psmode_enh->params.ps_bitmap = wlan_cpu_to_le16(ps_bitmap); + cmd->size = wlan_cpu_to_le16(S_DS_GEN + AUTO_PS_FIX_SIZE); + } else if (cmd_action == GET_PS) { + psmode_enh->action = wlan_cpu_to_le16(GET_PS); + psmode_enh->params.ps_bitmap = wlan_cpu_to_le16(ps_bitmap); + cmd->size = wlan_cpu_to_le16(S_DS_GEN + AUTO_PS_FIX_SIZE); + } else if (cmd_action == EN_AUTO_PS) { + psmode_enh->action = wlan_cpu_to_le16(EN_AUTO_PS); + psmode_enh->params.auto_ps.ps_bitmap = + wlan_cpu_to_le16(ps_bitmap); + cmd_size = S_DS_GEN + AUTO_PS_FIX_SIZE; + tlv = (t_u8 *)cmd + cmd_size; + if (ps_bitmap & BITMAP_STA_PS) { + pmlan_adapter pmadapter = pmpriv->adapter; + MrvlIEtypes_ps_param_t *ps_tlv = + (MrvlIEtypes_ps_param_t *)tlv; + ps_param *ps_mode = &ps_tlv->param; + ps_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_PS_PARAM); + ps_tlv->header.len = + wlan_cpu_to_le16(sizeof(MrvlIEtypes_ps_param_t) + - sizeof(MrvlIEtypesHeader_t)); + cmd_size += sizeof(MrvlIEtypes_ps_param_t); + tlv += sizeof(MrvlIEtypes_ps_param_t); + ps_mode->null_pkt_interval = + wlan_cpu_to_le16(pmadapter->null_pkt_interval); + ps_mode->multiple_dtims = + wlan_cpu_to_le16(pmadapter->multiple_dtim); + ps_mode->bcn_miss_timeout = + wlan_cpu_to_le16(pmadapter->bcn_miss_time_out); + ps_mode->local_listen_interval = + wlan_cpu_to_le16(pmadapter-> + local_listen_interval); + ps_mode->adhoc_wake_period = + wlan_cpu_to_le16(pmadapter->adhoc_awake_period); + ps_mode->delay_to_ps = + wlan_cpu_to_le16(pmadapter->delay_to_ps); + ps_mode->mode = + wlan_cpu_to_le16(pmadapter->enhanced_ps_mode); + } + if (ps_bitmap & BITMAP_BCN_TMO) { + MrvlIEtypes_bcn_timeout_t *bcn_tmo_tlv = + (MrvlIEtypes_bcn_timeout_t *) tlv; + mlan_ds_bcn_timeout *bcn_tmo = + (mlan_ds_bcn_timeout *) pdata_buf; + bcn_tmo_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_BCN_TIMEOUT); + bcn_tmo_tlv->header.len = + wlan_cpu_to_le16(sizeof + (MrvlIEtypes_bcn_timeout_t) - + sizeof(MrvlIEtypesHeader_t)); + bcn_tmo_tlv->bcn_miss_tmo_window = + wlan_cpu_to_le16(bcn_tmo->bcn_miss_tmo_window); + bcn_tmo_tlv->bcn_miss_tmo_period = + wlan_cpu_to_le16(bcn_tmo->bcn_miss_tmo_period); + bcn_tmo_tlv->bcn_rq_tmo_window = + wlan_cpu_to_le16(bcn_tmo->bcn_rq_tmo_window); + bcn_tmo_tlv->bcn_rq_tmo_period = + wlan_cpu_to_le16(bcn_tmo->bcn_rq_tmo_period); + cmd_size += sizeof(MrvlIEtypes_bcn_timeout_t); + tlv += sizeof(MrvlIEtypes_bcn_timeout_t); + + psmode_enh->params.auto_ps.ps_bitmap = + wlan_cpu_to_le16((ps_bitmap & (~BITMAP_BCN_TMO)) + | BITMAP_STA_PS); + } + if (ps_bitmap & BITMAP_AUTO_DS) { + MrvlIEtypes_auto_ds_param_t *auto_ps_tlv = + (MrvlIEtypes_auto_ds_param_t *)tlv; + auto_ds_param *auto_ds = &auto_ps_tlv->param; + t_u16 idletime = 0; + auto_ps_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_AUTO_DS_PARAM); + auto_ps_tlv->header.len = + wlan_cpu_to_le16(sizeof + (MrvlIEtypes_auto_ds_param_t) - + sizeof(MrvlIEtypesHeader_t)); + cmd_size += sizeof(MrvlIEtypes_auto_ds_param_t); + tlv += sizeof(MrvlIEtypes_auto_ds_param_t); + if (pdata_buf) + idletime = + ((mlan_ds_auto_ds *)pdata_buf)-> + idletime; + auto_ds->deep_sleep_timeout = + wlan_cpu_to_le16(idletime); + } +#if defined(UAP_SUPPORT) + if (pdata_buf && + (ps_bitmap & (BITMAP_UAP_INACT_PS | BITMAP_UAP_DTIM_PS))) { + mlan_ds_ps_mgmt *ps_mgmt = (mlan_ds_ps_mgmt *)pdata_buf; + MrvlIEtypes_sleep_param_t *sleep_tlv = MNULL; + MrvlIEtypes_inact_sleep_param_t *inact_tlv = MNULL; + if (ps_mgmt->flags & PS_FLAG_SLEEP_PARAM) { + sleep_tlv = (MrvlIEtypes_sleep_param_t *)tlv; + sleep_tlv->header.type = + wlan_cpu_to_le16 + (TLV_TYPE_AP_SLEEP_PARAM); + sleep_tlv->header.len = + wlan_cpu_to_le16(sizeof + (MrvlIEtypes_sleep_param_t) + - + sizeof + (MrvlIEtypesHeader_t)); + sleep_tlv->ctrl_bitmap = + wlan_cpu_to_le32(ps_mgmt->sleep_param. + ctrl_bitmap); + sleep_tlv->min_sleep = + wlan_cpu_to_le32(ps_mgmt->sleep_param. + min_sleep); + sleep_tlv->max_sleep = + wlan_cpu_to_le32(ps_mgmt->sleep_param. + max_sleep); + cmd_size += sizeof(MrvlIEtypes_sleep_param_t); + tlv += sizeof(MrvlIEtypes_sleep_param_t); + } + if (ps_mgmt->flags & PS_FLAG_INACT_SLEEP_PARAM) { + inact_tlv = + (MrvlIEtypes_inact_sleep_param_t *)tlv; + inact_tlv->header.type = + wlan_cpu_to_le16 + (TLV_TYPE_AP_INACT_SLEEP_PARAM); + inact_tlv->header.len = + wlan_cpu_to_le16(sizeof + (MrvlIEtypes_inact_sleep_param_t) + - + sizeof + (MrvlIEtypesHeader_t)); + inact_tlv->inactivity_to = + wlan_cpu_to_le32(ps_mgmt->inact_param. + inactivity_to); + inact_tlv->min_awake = + wlan_cpu_to_le32(ps_mgmt->inact_param. + min_awake); + inact_tlv->max_awake = + wlan_cpu_to_le32(ps_mgmt->inact_param. + max_awake); + cmd_size += + sizeof(MrvlIEtypes_inact_sleep_param_t); + tlv += sizeof(MrvlIEtypes_inact_sleep_param_t); + } + } +#endif + cmd->size = wlan_cpu_to_le16(cmd_size); + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of ps_mode_enh + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_ret_enh_power_mode(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf) +{ + pmlan_adapter pmadapter = pmpriv->adapter; + MrvlIEtypesHeader_t *mrvl_tlv = MNULL; + MrvlIEtypes_auto_ds_param_t *auto_ds_tlv = MNULL; + HostCmd_DS_802_11_PS_MODE_ENH *ps_mode = &resp->params.psmode_enh; + + ENTER(); + + ps_mode->action = wlan_le16_to_cpu(ps_mode->action); + PRINTM(MINFO, "CMD_RESP: PS_MODE cmd reply result=%#x action=0x%X\n", + resp->result, ps_mode->action); + if (ps_mode->action == EN_AUTO_PS) { + ps_mode->params.auto_ps.ps_bitmap = + wlan_le16_to_cpu(ps_mode->params.auto_ps.ps_bitmap); + if (ps_mode->params.auto_ps.ps_bitmap & BITMAP_AUTO_DS) { + PRINTM(MCMND, "Enabled auto deep sleep\n"); + pmpriv->adapter->is_deep_sleep = MTRUE; + mrvl_tlv = + (MrvlIEtypesHeader_t *)((t_u8 *)ps_mode + + AUTO_PS_FIX_SIZE); + while (wlan_le16_to_cpu(mrvl_tlv->type) != + TLV_TYPE_AUTO_DS_PARAM) { + mrvl_tlv = + (MrvlIEtypesHeader_t *)((t_u8 *)mrvl_tlv + + + wlan_le16_to_cpu + (mrvl_tlv->len) + + + sizeof + (MrvlIEtypesHeader_t)); + } + auto_ds_tlv = (MrvlIEtypes_auto_ds_param_t *)mrvl_tlv; + pmpriv->adapter->idle_time = + wlan_le16_to_cpu(auto_ds_tlv->param. + deep_sleep_timeout); + } + if (ps_mode->params.auto_ps.ps_bitmap & BITMAP_STA_PS) { + PRINTM(MCMND, "Enabled STA power save\n"); + if (pmadapter->sleep_period.period) { + PRINTM(MCMND, + "Setting uapsd/pps mode to TRUE\n"); + } + } +#if defined(UAP_SUPPORT) + if (ps_mode->params.auto_ps. + ps_bitmap & (BITMAP_UAP_INACT_PS | BITMAP_UAP_DTIM_PS)) { + pmadapter->ps_mode = Wlan802_11PowerModePSP; + PRINTM(MCMND, "Enabled uAP power save\n"); + } +#endif + } else if (ps_mode->action == DIS_AUTO_PS) { + ps_mode->params.ps_bitmap = + wlan_cpu_to_le16(ps_mode->params.ps_bitmap); + if (ps_mode->params.ps_bitmap & BITMAP_AUTO_DS) { + pmpriv->adapter->is_deep_sleep = MFALSE; + PRINTM(MCMND, "Disabled auto deep sleep\n"); + } + if (ps_mode->params.ps_bitmap & BITMAP_STA_PS) { + PRINTM(MCMND, "Disabled STA power save\n"); + if (pmadapter->sleep_period.period) { + pmadapter->delay_null_pkt = MFALSE; + pmadapter->tx_lock_flag = MFALSE; + pmadapter->pps_uapsd_mode = MFALSE; + } + } +#if defined(UAP_SUPPORT) + if (ps_mode->params. + ps_bitmap & (BITMAP_UAP_INACT_PS | BITMAP_UAP_DTIM_PS)) { + pmadapter->ps_mode = Wlan802_11PowerModeCAM; + PRINTM(MCMND, "Disabled uAP power save\n"); + } +#endif + } else if (ps_mode->action == GET_PS) { + ps_mode->params.ps_bitmap = + wlan_le16_to_cpu(ps_mode->params.ps_bitmap); + if (ps_mode->params.auto_ps. + ps_bitmap & (BITMAP_STA_PS | BITMAP_UAP_INACT_PS | + BITMAP_UAP_DTIM_PS)) + pmadapter->ps_mode = Wlan802_11PowerModePSP; + else + pmadapter->ps_mode = Wlan802_11PowerModeCAM; + PRINTM(MCMND, "ps_bitmap=0x%x\n", ps_mode->params.ps_bitmap); + if (pioctl_buf) { + mlan_ds_pm_cfg *pm_cfg = + (mlan_ds_pm_cfg *)pioctl_buf->pbuf; + if (pm_cfg->sub_command == MLAN_OID_PM_CFG_IEEE_PS) { + if (ps_mode->params.auto_ps. + ps_bitmap & BITMAP_STA_PS) + pm_cfg->param.ps_mode = 1; + else + pm_cfg->param.ps_mode = 0; + } +#if defined(UAP_SUPPORT) + if (pm_cfg->sub_command == MLAN_OID_PM_CFG_PS_MODE) { + MrvlIEtypes_sleep_param_t *sleep_tlv = MNULL; + MrvlIEtypes_inact_sleep_param_t *inact_tlv = + MNULL; + MrvlIEtypesHeader_t *tlv = MNULL; + t_u16 tlv_type = 0; + t_u16 tlv_len = 0; + t_u16 tlv_buf_left = 0; + pm_cfg->param.ps_mgmt.flags = PS_FLAG_PS_MODE; + if (ps_mode->params. + ps_bitmap & BITMAP_UAP_INACT_PS) + pm_cfg->param.ps_mgmt.ps_mode = + PS_MODE_INACTIVITY; + else if (ps_mode->params. + ps_bitmap & BITMAP_UAP_DTIM_PS) + pm_cfg->param.ps_mgmt.ps_mode = + PS_MODE_PERIODIC_DTIM; + else + pm_cfg->param.ps_mgmt.ps_mode = + PS_MODE_DISABLE; + tlv_buf_left = + resp->size - (S_DS_GEN + + AUTO_PS_FIX_SIZE); + tlv = (MrvlIEtypesHeader_t *)((t_u8 *)ps_mode + + AUTO_PS_FIX_SIZE); + while (tlv_buf_left >= + sizeof(MrvlIEtypesHeader_t)) { + tlv_type = wlan_le16_to_cpu(tlv->type); + tlv_len = wlan_le16_to_cpu(tlv->len); + switch (tlv_type) { + case TLV_TYPE_AP_SLEEP_PARAM: + sleep_tlv = + (MrvlIEtypes_sleep_param_t + *)tlv; + pm_cfg->param.ps_mgmt.flags |= + PS_FLAG_SLEEP_PARAM; + pm_cfg->param.ps_mgmt. + sleep_param. + ctrl_bitmap = + wlan_le32_to_cpu + (sleep_tlv-> + ctrl_bitmap); + pm_cfg->param.ps_mgmt. + sleep_param.min_sleep = + wlan_le32_to_cpu + (sleep_tlv->min_sleep); + pm_cfg->param.ps_mgmt. + sleep_param.max_sleep = + wlan_le32_to_cpu + (sleep_tlv->max_sleep); + break; + case TLV_TYPE_AP_INACT_SLEEP_PARAM: + inact_tlv = + (MrvlIEtypes_inact_sleep_param_t + *)tlv; + pm_cfg->param.ps_mgmt.flags |= + PS_FLAG_INACT_SLEEP_PARAM; + pm_cfg->param.ps_mgmt. + inact_param. + inactivity_to = + wlan_le32_to_cpu + (inact_tlv-> + inactivity_to); + pm_cfg->param.ps_mgmt. + inact_param.min_awake = + wlan_le32_to_cpu + (inact_tlv->min_awake); + pm_cfg->param.ps_mgmt. + inact_param.max_awake = + wlan_le32_to_cpu + (inact_tlv->max_awake); + break; + } + tlv_buf_left -= + tlv_len + + sizeof(MrvlIEtypesHeader_t); + tlv = (MrvlIEtypesHeader_t *)((t_u8 *) + tlv + + tlv_len + + sizeof + (MrvlIEtypesHeader_t)); + } + } +#endif + } + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of tx rate query + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_ret_802_11_tx_rate_query(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + mlan_ds_rate *rate = MNULL; + ENTER(); + + pmpriv->tx_rate = resp->params.tx_rate.tx_rate; + pmpriv->tx_rate_info = resp->params.tx_rate.tx_rate_info; + + if (!pmpriv->is_data_rate_auto) { + pmpriv->data_rate = wlan_index_to_data_rate(pmadapter, + pmpriv->tx_rate, + pmpriv-> + tx_rate_info); + } + + if (pioctl_buf) { + rate = (mlan_ds_rate *)pioctl_buf->pbuf; + if (rate->sub_command == MLAN_OID_RATE_CFG) { + if (rate->param.rate_cfg.rate_type == MLAN_RATE_INDEX) { + if ((pmpriv->tx_rate_info & 0x3) == + MLAN_RATE_FORMAT_HT) + /* HT rate */ + rate->param.rate_cfg.rate = + pmpriv->tx_rate + + MLAN_RATE_INDEX_MCS0; + else + /* LG rate */ + /* For HostCmd_CMD_802_11_TX_RATE_QUERY, + * there is a hole (0x4) in rate table + * between HR/DSSS and OFDM rates, + * so minus 1 for OFDM rate index */ + rate->param.rate_cfg.rate = + (pmpriv->tx_rate > + MLAN_RATE_INDEX_OFDM0) ? + pmpriv->tx_rate - + 1 : pmpriv->tx_rate; + } else { + /* rate_type = MLAN_RATE_VALUE */ + rate->param.rate_cfg.rate = + wlan_index_to_data_rate(pmadapter, + pmpriv->tx_rate, + pmpriv-> + tx_rate_info); + } + } else if (rate->sub_command == MLAN_OID_GET_DATA_RATE) { + /* Tx rate info */ + if ((pmpriv->tx_rate_info & 0x3) == MLAN_RATE_FORMAT_HT) { + /* HT rate */ + rate->param.data_rate.tx_rate_format = + MLAN_RATE_FORMAT_HT; + rate->param.data_rate.tx_ht_bw = + (pmpriv->tx_rate_info & 0xC) >> 2; + rate->param.data_rate.tx_ht_gi = + (pmpriv->tx_rate_info & 0x10) >> 4; + rate->param.data_rate.tx_mcs_index = + pmpriv->tx_rate; + rate->param.data_rate.tx_data_rate = + wlan_index_to_data_rate(pmadapter, + pmpriv->tx_rate, + pmpriv-> + tx_rate_info); + } else { + /* LG rate */ + rate->param.data_rate.tx_rate_format = + MLAN_RATE_FORMAT_LG; + /* For HostCmd_CMD_802_11_TX_RATE_QUERY, + * there is a hole in rate table + * between HR/DSSS and OFDM rates, + * so minus 1 for OFDM rate index */ + rate->param.data_rate.tx_data_rate = + (pmpriv->tx_rate > + MLAN_RATE_INDEX_OFDM0) ? pmpriv-> + tx_rate - 1 : pmpriv->tx_rate; + } + + /* Rx rate info */ + if ((pmpriv->rxpd_rate_info & 0x3) == + MLAN_RATE_FORMAT_HT) { + /* HT rate */ + rate->param.data_rate.rx_rate_format = + MLAN_RATE_FORMAT_HT; + rate->param.data_rate.rx_ht_bw = + (pmpriv->rxpd_rate_info & 0xC) >> 2; + rate->param.data_rate.rx_ht_gi = + (pmpriv->rxpd_rate_info & 0x10) >> 4; + rate->param.data_rate.rx_mcs_index = + pmpriv->rxpd_rate; + rate->param.data_rate.rx_data_rate = + wlan_index_to_data_rate(pmadapter, + pmpriv-> + rxpd_rate, + pmpriv-> + rxpd_rate_info); + } else { + /* LG rate */ + rate->param.data_rate.rx_rate_format = + MLAN_RATE_FORMAT_LG; + /* For rate index in RxPD, + * there is a hole in rate table + * between HR/DSSS and OFDM rates, + * so minus 1 for OFDM rate index */ + rate->param.data_rate.rx_data_rate = + (pmpriv->rxpd_rate > + MLAN_RATE_INDEX_OFDM0) ? pmpriv-> + rxpd_rate - 1 : pmpriv->rxpd_rate; + } + } + pioctl_buf->data_read_written = sizeof(mlan_data_rate) + + MLAN_SUB_COMMAND_SIZE; + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of fw_wakeup_method. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action The action: GET or SET + * @param pdata_buf A pointer to data buffer + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_cmd_802_11_fw_wakeup_method(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, IN t_u16 *pdata_buf) +{ + HostCmd_DS_802_11_FW_WAKEUP_METHOD *fwwm = &cmd->params.fwwakeupmethod; + mlan_fw_wakeup_params *fw_wakeup_params = MNULL; + MrvlIEtypes_WakeupSourceGPIO_t *tlv = + (MrvlIEtypes_WakeupSourceGPIO_t *) ((t_u8 *)fwwm + + sizeof + (HostCmd_DS_802_11_FW_WAKEUP_METHOD)); + + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_802_11_FW_WAKE_METHOD); + cmd->size = sizeof(HostCmd_DS_802_11_FW_WAKEUP_METHOD) + S_DS_GEN; + fwwm->action = wlan_cpu_to_le16(cmd_action); + switch (cmd_action) { + case HostCmd_ACT_GEN_SET: + fw_wakeup_params = (mlan_fw_wakeup_params *) pdata_buf; + fwwm->method = wlan_cpu_to_le16(fw_wakeup_params->method); + + if (fw_wakeup_params->method == WAKEUP_FW_THRU_GPIO) { + cmd->size += sizeof(MrvlIEtypes_WakeupSourceGPIO_t); + tlv->header.type = + wlan_cpu_to_le16 + (TLV_TYPE_HS_WAKEUP_SOURCE_GPIO); + tlv->header.len = + wlan_cpu_to_le16(sizeof + (MrvlIEtypes_WakeupSourceGPIO_t) + - sizeof(MrvlIEtypesHeader_t)); + tlv->ind_gpio = (t_u8)fw_wakeup_params->gpio_pin; + } + + break; + case HostCmd_ACT_GEN_GET: + default: + fwwm->method = wlan_cpu_to_le16(WAKEUP_FW_UNCHANGED); + break; + } + cmd->size = wlan_cpu_to_le16(cmd->size); + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of fw_wakeup_method + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_ret_fw_wakeup_method(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf) +{ + HostCmd_DS_802_11_FW_WAKEUP_METHOD *fwwm = &resp->params.fwwakeupmethod; + t_u16 action; + MrvlIEtypes_WakeupSourceGPIO_t *gpio_tlv = + (MrvlIEtypes_WakeupSourceGPIO_t *) ((t_u8 *)fwwm + + sizeof + (HostCmd_DS_802_11_FW_WAKEUP_METHOD)); + mlan_ds_pm_cfg *pmcfg = MNULL; + + ENTER(); + + action = wlan_le16_to_cpu(fwwm->action); + + pmpriv->adapter->fw_wakeup_method = wlan_le16_to_cpu(fwwm->method); + pmpriv->adapter->fw_wakeup_gpio_pin = 0; + + if ((resp->size - + (sizeof(HostCmd_DS_802_11_FW_WAKEUP_METHOD) + S_DS_GEN)) + == sizeof(MrvlIEtypes_WakeupSourceGPIO_t)) { + pmpriv->adapter->fw_wakeup_gpio_pin = gpio_tlv->ind_gpio; + } + PRINTM(MCMND, "FW wakeup method=%d, gpio=%d\n", + pmpriv->adapter->fw_wakeup_method, + pmpriv->adapter->fw_wakeup_gpio_pin); + + if (pioctl_buf) { + pmcfg = (mlan_ds_pm_cfg *)pioctl_buf->pbuf; + pmcfg->param.fw_wakeup_params.method = + pmpriv->adapter->fw_wakeup_method; + pmcfg->param.fw_wakeup_params.gpio_pin = + pmpriv->adapter->fw_wakeup_gpio_pin; + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of robustcoex. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action The action: GET or SET + * @param pdata_buf A pointer to data buffer + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_cmd_robustcoex(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, IN t_u16 *pdata_buf) +{ + HostCmd_DS_802_11_ROBUSTCOEX *rbstcx = &cmd->params.robustcoexparams; + mlan_ds_misc_robustcoex_params *robustcoex_params = MNULL; + MrvlIEtypes_RobustcoexSourceGPIO_t *tlv = + (MrvlIEtypes_RobustcoexSourceGPIO_t *) ((t_u8 *)rbstcx + + sizeof + (HostCmd_DS_802_11_ROBUSTCOEX)); + + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_802_11_ROBUSTCOEX); + cmd->size = sizeof(HostCmd_DS_802_11_ROBUSTCOEX) + S_DS_GEN; + rbstcx->action = wlan_cpu_to_le16(cmd_action); + switch (cmd_action) { + case HostCmd_ACT_GEN_SET: + robustcoex_params = + (mlan_ds_misc_robustcoex_params *) pdata_buf; + if (robustcoex_params->method == ROBUSTCOEX_GPIO_CFG) { + cmd->size += sizeof(MrvlIEtypes_RobustcoexSourceGPIO_t); + tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_ROBUSTCOEX); + tlv->header.len = + wlan_cpu_to_le16(sizeof + (MrvlIEtypes_RobustcoexSourceGPIO_t) + - sizeof(MrvlIEtypesHeader_t)); + tlv->enable = (t_u8)robustcoex_params->enable; + tlv->gpio_num = (t_u8)robustcoex_params->gpio_num; + tlv->gpio_polarity = + (t_u8)robustcoex_params->gpio_polarity; + } + break; + case HostCmd_ACT_GEN_GET: + default: + break; + } + cmd->size = wlan_cpu_to_le16(cmd->size); + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of tx_rate_cfg. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action The action: GET or SET + * @param pdata_buf A pointer to data buffer + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_cmd_tx_rate_cfg(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, IN t_void *pdata_buf) +{ + HostCmd_DS_TX_RATE_CFG *rate_cfg = + (HostCmd_DS_TX_RATE_CFG *)&(cmd->params.tx_rate_cfg); + MrvlRateScope_t *rate_scope; + MrvlRateDropPattern_t *rate_drop; + t_u16 *pbitmap_rates = (t_u16 *)pdata_buf; + + t_u32 i; + + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_TX_RATE_CFG); + + rate_cfg->action = wlan_cpu_to_le16(cmd_action); + + rate_scope = (MrvlRateScope_t *)((t_u8 *)rate_cfg + + sizeof(HostCmd_DS_TX_RATE_CFG)); + rate_scope->type = wlan_cpu_to_le16(TLV_TYPE_RATE_SCOPE); + rate_scope->length = wlan_cpu_to_le16(sizeof(MrvlRateScope_t) - + sizeof(MrvlIEtypesHeader_t)); + if (pbitmap_rates != MNULL) { + rate_scope->hr_dsss_rate_bitmap = + wlan_cpu_to_le16(pbitmap_rates[0]); + rate_scope->ofdm_rate_bitmap = + wlan_cpu_to_le16(pbitmap_rates[1]); + for (i = 0; i < NELEMENTS(rate_scope->ht_mcs_rate_bitmap); i++) + rate_scope->ht_mcs_rate_bitmap[i] = + wlan_cpu_to_le16(pbitmap_rates[2 + i]); + } else { + rate_scope->hr_dsss_rate_bitmap = + wlan_cpu_to_le16(pmpriv->bitmap_rates[0]); + rate_scope->ofdm_rate_bitmap = + wlan_cpu_to_le16(pmpriv->bitmap_rates[1]); + for (i = 0; i < NELEMENTS(rate_scope->ht_mcs_rate_bitmap); i++) + rate_scope->ht_mcs_rate_bitmap[i] = + wlan_cpu_to_le16(pmpriv->bitmap_rates[2 + i]); + } + + rate_drop = (MrvlRateDropPattern_t *)((t_u8 *)rate_scope + + sizeof(MrvlRateScope_t)); + rate_drop->type = wlan_cpu_to_le16(TLV_TYPE_RATE_DROP_PATTERN); + rate_drop->length = wlan_cpu_to_le16(sizeof(rate_drop->rate_drop_mode)); + rate_drop->rate_drop_mode = 0; + + cmd->size = wlan_cpu_to_le16(S_DS_GEN + sizeof(HostCmd_DS_TX_RATE_CFG) + + sizeof(MrvlRateScope_t) + + sizeof(MrvlRateDropPattern_t)); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of tx_rate_cfg + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_ret_tx_rate_cfg(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, IN mlan_ioctl_req *pioctl_buf) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + mlan_ds_rate *ds_rate = MNULL; + HostCmd_DS_TX_RATE_CFG *prate_cfg = MNULL; + MrvlRateScope_t *prate_scope; + MrvlIEtypesHeader_t *head = MNULL; + t_u16 tlv, tlv_buf_len = 0; + t_u8 *tlv_buf; + t_u32 i; + t_s32 index; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (resp == MNULL) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + prate_cfg = (HostCmd_DS_TX_RATE_CFG *)&(resp->params.tx_rate_cfg); + + tlv_buf = (t_u8 *)((t_u8 *)prate_cfg) + sizeof(HostCmd_DS_TX_RATE_CFG); + if (tlv_buf) { + tlv_buf_len = *(t_u16 *)(tlv_buf + sizeof(t_u16)); + tlv_buf_len = wlan_le16_to_cpu(tlv_buf_len); + } + + while (tlv_buf && tlv_buf_len > 0) { + tlv = (*tlv_buf); + tlv = tlv | (*(tlv_buf + 1) << 8); + + switch (tlv) { + case TLV_TYPE_RATE_SCOPE: + prate_scope = (MrvlRateScope_t *)tlv_buf; + pmpriv->bitmap_rates[0] = + wlan_le16_to_cpu(prate_scope-> + hr_dsss_rate_bitmap); + pmpriv->bitmap_rates[1] = + wlan_le16_to_cpu(prate_scope->ofdm_rate_bitmap); + for (i = 0; + i < NELEMENTS(prate_scope->ht_mcs_rate_bitmap); + i++) + pmpriv->bitmap_rates[2 + i] = + wlan_le16_to_cpu(prate_scope-> + ht_mcs_rate_bitmap[i]); + break; + /* Add RATE_DROP tlv here */ + } + + head = (MrvlIEtypesHeader_t *)tlv_buf; + head->len = wlan_le16_to_cpu(head->len); + tlv_buf += head->len + sizeof(MrvlIEtypesHeader_t); + tlv_buf_len -= head->len; + } + + pmpriv->is_data_rate_auto = wlan_is_rate_auto(pmpriv); + + if (pmpriv->is_data_rate_auto) { + pmpriv->data_rate = 0; + } else { + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_802_11_TX_RATE_QUERY, + HostCmd_ACT_GEN_GET, 0, MNULL, MNULL); + + } + + if (pioctl_buf) { + ds_rate = (mlan_ds_rate *)pioctl_buf->pbuf; + if (ds_rate == MNULL) { + PRINTM(MERROR, "Request buffer not found!\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + if (pmpriv->is_data_rate_auto) { + ds_rate->param.rate_cfg.is_rate_auto = MTRUE; + ds_rate->param.rate_cfg.rate_format = + MLAN_RATE_FORMAT_AUTO; + } else { + ds_rate->param.rate_cfg.is_rate_auto = MFALSE; + /* check the LG rate */ + index = wlan_get_rate_index(pmadapter, + &pmpriv->bitmap_rates[0], + 4); + if (index != -1) { + if ((index >= MLAN_RATE_BITMAP_OFDM0) && + (index <= MLAN_RATE_BITMAP_OFDM7)) + index -= (MLAN_RATE_BITMAP_OFDM0 - + MLAN_RATE_INDEX_OFDM0); + ds_rate->param.rate_cfg.rate_format = + MLAN_RATE_FORMAT_LG; + ds_rate->param.rate_cfg.rate = index; + } + /* check the HT rate */ + index = wlan_get_rate_index(pmadapter, + &pmpriv->bitmap_rates[2], + 16); + if (index != -1) { + ds_rate->param.rate_cfg.rate_format = + MLAN_RATE_FORMAT_HT; + ds_rate->param.rate_cfg.rate = index; + } + PRINTM(MINFO, "Rate index is %d\n", + ds_rate->param.rate_cfg.rate); + } + for (i = 0; i < MAX_BITMAP_RATES_SIZE; i++) { + ds_rate->param.rate_cfg.bitmap_rates[i] = + pmpriv->bitmap_rates[i]; + } + + } + + LEAVE(); + return ret; +} + +/** + * @brief This function issues adapter specific commands + * to initialize firmware + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return MLAN_STATUS_PENDING or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_adapter_get_hw_spec(IN pmlan_adapter pmadapter) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_private priv = wlan_get_priv(pmadapter, MLAN_BSS_ROLE_ANY); + /* + * This should be issued in the very first to config + * SDIO_GPIO interrupt mode. + */ + if (wlan_set_sdio_gpio_int(priv) != MLAN_STATUS_SUCCESS) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + ret = wlan_prepare_cmd(priv, HostCmd_CMD_FUNC_INIT, + HostCmd_ACT_GEN_SET, 0, MNULL, MNULL); + if (ret) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /** DPD data dnld cmd prepare */ + if ((pmadapter->pdpd_data) && (pmadapter->dpd_data_len > 0)) { + ret = wlan_prepare_cmd(priv, HostCmd_CMD_CFG_DATA, + HostCmd_ACT_GEN_SET, OID_TYPE_DPD, MNULL, + MNULL); + if (ret) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + pmadapter->pdpd_data = MNULL; + pmadapter->dpd_data_len = 0; + } + if ((pmadapter->ptxpwr_data) && (pmadapter->txpwr_data_len > 0)) { + ret = wlan_process_hostcmd_cfg(priv, pmadapter->ptxpwr_data, + pmadapter->txpwr_data_len); + if (ret) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + pmadapter->ptxpwr_data = MNULL; + pmadapter->txpwr_data_len = 0; + } + /** Cal data dnld cmd prepare */ + if ((pmadapter->pcal_data) && (pmadapter->cal_data_len > 0)) { + ret = wlan_prepare_cmd(priv, HostCmd_CMD_CFG_DATA, + HostCmd_ACT_GEN_SET, OID_TYPE_CAL, MNULL, + MNULL); + if (ret) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + pmadapter->pcal_data = MNULL; + pmadapter->cal_data_len = 0; + } + /* Get FW region and cfp tables */ + if (pmadapter->init_para.fw_region) { + ret = wlan_prepare_cmd(priv, HostCmd_CMD_CHAN_REGION_CFG, + HostCmd_ACT_GEN_GET, 0, MNULL, MNULL); + if (ret) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + } + /* + * Get HW spec + */ + ret = wlan_prepare_cmd(priv, HostCmd_CMD_GET_HW_SPEC, + HostCmd_ACT_GEN_GET, 0, MNULL, MNULL); + if (ret) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + ret = MLAN_STATUS_PENDING; +done: + LEAVE(); + return ret; +} + +/** + * @brief This function issues adapter specific commands + * to initialize firmware + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return MLAN_STATUS_PENDING or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_adapter_init_cmd(IN pmlan_adapter pmadapter) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_private pmpriv = MNULL; +#ifdef STA_SUPPORT + pmlan_private pmpriv_sta = MNULL; +#endif + + ENTER(); + + pmpriv = wlan_get_priv(pmadapter, MLAN_BSS_ROLE_ANY); +#ifdef STA_SUPPORT + pmpriv_sta = wlan_get_priv(pmadapter, MLAN_BSS_ROLE_STA); +#endif + + /* Get fw wakeup method */ + if (pmpriv) { + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_802_11_FW_WAKE_METHOD, + HostCmd_ACT_GEN_GET, 0, MNULL, MNULL); + if (ret) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + } +#if defined(SYSKT_MULTI) && defined(OOB_WAKEUP) || defined(SUSPEND_SDIO_PULL_DOWN) + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_SDIO_PULL_CTRL, + HostCmd_ACT_GEN_SET, 0, MNULL, MNULL); + if (ret) { + ret = MLAN_STATUS_FAILURE; + goto done; + } +} +#endif + + /* Reconfigure tx buf size */ +ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_RECONFIGURE_TX_BUFF, + HostCmd_ACT_GEN_SET, 0, MNULL, + &pmadapter->max_tx_buf_size); +if (ret) { + ret = MLAN_STATUS_FAILURE; + goto done; +} +#if defined(STA_SUPPORT) +if (pmpriv_sta && (pmpriv_sta->state_11d.user_enable_11d == ENABLE_11D)) { + /* Send command to FW to enable 11d */ + ret = wlan_prepare_cmd(pmpriv_sta, + HostCmd_CMD_802_11_SNMP_MIB, + HostCmd_ACT_GEN_SET, + Dot11D_i, + MNULL, &pmpriv_sta->state_11d.user_enable_11d); + if (ret) { + ret = MLAN_STATUS_FAILURE; + goto done; + } +} +#endif + +#if defined(STA_SUPPORT) +if (pmpriv_sta && (pmadapter->ps_mode == Wlan802_11PowerModePSP)) { + ret = wlan_prepare_cmd(pmpriv_sta, HostCmd_CMD_802_11_PS_MODE_ENH, + EN_AUTO_PS, BITMAP_STA_PS, MNULL, MNULL); + if (ret) { + ret = MLAN_STATUS_FAILURE; + goto done; + } +} +#endif + +if (pmadapter->init_auto_ds) { + mlan_ds_auto_ds auto_ds; + /* Enable auto deep sleep */ + auto_ds.idletime = pmadapter->idle_time; + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_802_11_PS_MODE_ENH, + EN_AUTO_PS, BITMAP_AUTO_DS, MNULL, &auto_ds); + if (ret) { + ret = MLAN_STATUS_FAILURE; + goto done; + } +} +#define DEF_AUTO_NULL_PKT_PERIOD 30 +if (pmpriv_sta) { + t_u32 value = DEF_AUTO_NULL_PKT_PERIOD; + ret = wlan_prepare_cmd(pmpriv_sta, + HostCmd_CMD_802_11_SNMP_MIB, + HostCmd_ACT_GEN_SET, + NullPktPeriod_i, MNULL, &value); + if (ret) { + ret = MLAN_STATUS_FAILURE; + goto done; + } +} +if (pmadapter->init_para.indrstcfg != 0xffffffff) { + mlan_ds_ind_rst_cfg ind_rst_cfg; + ind_rst_cfg.ir_mode = pmadapter->init_para.indrstcfg & 0xff; + ind_rst_cfg.gpio_pin = (pmadapter->init_para.indrstcfg & 0xff00) >> 8; + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_INDEPENDENT_RESET_CFG, + HostCmd_ACT_GEN_SET, + 0, MNULL, (t_void *)&ind_rst_cfg); + if (ret) { + ret = MLAN_STATUS_FAILURE; + goto done; + } +} + +if (pmadapter->inact_tmo) { + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_802_11_PS_INACTIVITY_TIMEOUT, + HostCmd_ACT_GEN_SET, 0, MNULL, + &pmadapter->inact_tmo); + if (ret) { + ret = MLAN_STATUS_FAILURE; + goto done; + } +} +if (pmadapter->init_para.drcs_chantime_mode) { + mlan_ds_drcs_cfg drcs_init_cfg[2]; + drcs_init_cfg[0].chan_idx = 0x1; + drcs_init_cfg[0].chantime = + (t_u8)(pmadapter->init_para.drcs_chantime_mode >> 8); + /* switchtime use default value in fw */ + drcs_init_cfg[0].switchtime = 10; + drcs_init_cfg[0].undozetime = 5; + drcs_init_cfg[0].mode = (t_u8)(pmadapter->init_para.drcs_chantime_mode); + drcs_init_cfg[1].chan_idx = 0x2; + drcs_init_cfg[1].chantime = + (t_u8)(pmadapter->init_para.drcs_chantime_mode >> 24); + /* switchtime use default value in fw */ + drcs_init_cfg[1].switchtime = 10; + drcs_init_cfg[1].undozetime = 5; + drcs_init_cfg[1].mode = + (t_u8)(pmadapter->init_para.drcs_chantime_mode >> 16); + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_DRCS_CONFIG, + HostCmd_ACT_GEN_SET, 0, MNULL, + (t_void *)drcs_init_cfg); + if (ret) { + ret = MLAN_STATUS_FAILURE; + goto done; + } +} +ret = MLAN_STATUS_PENDING; +done: +LEAVE(); +return ret; +} + +#ifdef RX_PACKET_COALESCE +/** + * @brief This function prepares command of rx_pkt_coalesce_cfg + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action The action: GET or SET + * @param pdata_buf A pointer to data buffer + * + * @return MLAN_STATUS_SUCCESS + */ + +mlan_status +wlan_cmd_rx_pkt_coalesce_cfg(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, IN t_void *pdata_buf) +{ + + mlan_ds_misc_rx_packet_coalesce *rx_pkt_cfg = + (mlan_ds_misc_rx_packet_coalesce *)pdata_buf; + HostCmd_DS_RX_PKT_COAL_CFG *prx_coal_cfg = + (HostCmd_DS_RX_PKT_COAL_CFG *)&cmd->params.rx_pkt_coal_cfg; + + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_RX_PKT_COALESCE_CFG); + prx_coal_cfg->action = wlan_cpu_to_le16(cmd_action); + + if (cmd_action == HostCmd_ACT_GEN_SET) { + prx_coal_cfg->packet_threshold = + wlan_cpu_to_le32(rx_pkt_cfg->packet_threshold); + prx_coal_cfg->delay = wlan_cpu_to_le16(rx_pkt_cfg->delay); + PRINTM(MCMND, + "Set RX coal config: packet threshold=%d delay=%d\n", + rx_pkt_cfg->packet_threshold, rx_pkt_cfg->delay); + cmd->size = + wlan_cpu_to_le16(S_DS_GEN + + sizeof(HostCmd_DS_RX_PKT_COAL_CFG)); + } else { + cmd->size = wlan_cpu_to_le16(S_DS_GEN + sizeof(cmd_action)); + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; + +} + +/** + * @brief This function handles the command response of RX_PACKET_COAL_CFG + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_ret_rx_pkt_coalesce_cfg(IN pmlan_private pmpriv, + const IN HostCmd_DS_COMMAND *resp, + OUT mlan_ioctl_req *pioctl_buf) +{ + mlan_ds_misc_cfg *pcfg = MNULL; + const HostCmd_DS_RX_PKT_COAL_CFG *presp_cfg = + &resp->params.rx_pkt_coal_cfg; + + ENTER(); + + if (pioctl_buf) { + pcfg = (mlan_ds_misc_cfg *)pioctl_buf->pbuf; + pcfg->param.rx_coalesce.packet_threshold = + wlan_le32_to_cpu(presp_cfg->packet_threshold); + pcfg->param.rx_coalesce.delay = + wlan_le16_to_cpu(presp_cfg->delay); + PRINTM(MCMND, + "Get rx pkt coalesce info: packet threshold=%d delay=%d\n", + pcfg->param.rx_coalesce.packet_threshold, + pcfg->param.rx_coalesce.delay); + pioctl_buf->buf_len = sizeof(mlan_ds_misc_rx_packet_coalesce); + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +#endif +/** + * @brief This function handle the multi_chan info event + * + * @param pmpriv A pointer to mlan_private structure + * @param pevent A pointer to event buffer + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_handle_event_multi_chan_info(IN pmlan_private pmpriv, pmlan_buffer pevent) +{ + pmlan_adapter pmadapter = pmpriv->adapter; + t_u32 interfaces = 0; + MrvlIEtypes_multi_chan_info_t *pmulti_chan_info = MNULL; + MrvlIEtypes_multi_chan_group_info_t *pmulti_chan_grp_info = MNULL; + int tlv_buf_left = pevent->data_len - sizeof(mlan_event_id); + t_u16 tlv_type, tlv_len; + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_private intf_priv = MNULL; + int num_intf = 0, bss_type = 0, bss_num = 0; + MrvlIEtypesHeader_t *tlv = MNULL; + + ENTER(); + + PRINTM(MEVENT, "multi channel event\n"); + pmulti_chan_info = + (MrvlIEtypes_multi_chan_info_t *)(pevent->pbuf + + pevent->data_offset + + sizeof(mlan_event_id)); + if (tlv_buf_left < sizeof(MrvlIEtypes_multi_chan_info_t) || + wlan_le16_to_cpu(pmulti_chan_info->header.type) != + TLV_TYPE_MULTI_CHAN_INFO) { + PRINTM(MERROR, "Invalid multi channel event\n"); + goto done; + } + + pmadapter->mc_status = wlan_le16_to_cpu(pmulti_chan_info->status); + PRINTM(MEVENT, "mc_status=%d\n", pmadapter->mc_status); + tlv_buf_left -= sizeof(MrvlIEtypes_multi_chan_info_t); + tlv = (MrvlIEtypesHeader_t *)pmulti_chan_info->tlv_buffer; + + while (tlv_buf_left >= (int)sizeof(MrvlIEtypesHeader_t)) { + tlv_type = wlan_le16_to_cpu(tlv->type); + tlv_len = wlan_le16_to_cpu(tlv->len); + if ((sizeof(MrvlIEtypesHeader_t) + tlv_len) > + (unsigned int)tlv_buf_left) { + PRINTM(MERROR, "wrong tlv: tlvLen=%d, tlvBufLeft=%d\n", + tlv_len, tlv_buf_left); + break; + } + if (tlv_type != TLV_TYPE_MULTI_CHAN_GROUP_INFO_TLV_ID) { + PRINTM(MERROR, "wrong tlv type:0x%x\n", tlv_type); + break; + } + pmulti_chan_grp_info = + (MrvlIEtypes_multi_chan_group_info_t *)tlv; + PRINTM(MEVENT, "mc_info: groupid=%d chan=%d, numintf=%d\n", + pmulti_chan_grp_info->chan_group_id, + pmulti_chan_grp_info->chan_band_info.chan_num, + pmulti_chan_grp_info->num_intf); + num_intf = pmulti_chan_grp_info->num_intf; + for (interfaces = 0; interfaces < num_intf; interfaces++) { + bss_type = + pmulti_chan_grp_info-> + bss_type_numlist[interfaces] >> 4; + bss_num = + pmulti_chan_grp_info-> + bss_type_numlist[interfaces] & BSS_NUM_MASK; + PRINTM(MEVENT, "intf%d: bss_type=%d bss_num=%d\n", + interfaces, bss_type, bss_num); + intf_priv = + wlan_get_priv_by_id(pmadapter, bss_num, + bss_type); + if (intf_priv) { + } else { + PRINTM(MERROR, + "Invalid bss_type, bss_num in multi_channel event\n"); + } + } + + tlv_buf_left -= (sizeof(MrvlIEtypesHeader_t) + tlv_len); + tlv = (MrvlIEtypesHeader_t *)((t_u8 *)tlv + tlv_len + + sizeof(MrvlIEtypesHeader_t)); + } + +done: + LEAVE(); + return ret; +} + +/** + * @brief This function prepares the command MULTI_CHAN_CFG + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action Command action: GET or SET + * @param pdata_buf A pointer to new setting buf + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_cmd_multi_chan_cfg(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, IN t_void *pdata_buf) +{ + mlan_ds_multi_chan_cfg *multi_chan_cfg = + (mlan_ds_multi_chan_cfg *)pdata_buf; + HostCmd_DS_MULTI_CHAN_CFG *pmchan_cfg = + (HostCmd_DS_MULTI_CHAN_CFG *)&cmd->params.multi_chan_cfg; + + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_MULTI_CHAN_CONFIG); + pmchan_cfg->action = wlan_cpu_to_le16(cmd_action); + + if (cmd_action == HostCmd_ACT_GEN_SET) { + pmchan_cfg->buffer_weight = multi_chan_cfg->buffer_weight; + pmchan_cfg->channel_time = + wlan_cpu_to_le32(multi_chan_cfg->channel_time); + PRINTM(MCMND, + "Set multi-channel: buffer_weight=%d channel_time=%d\n", + multi_chan_cfg->buffer_weight, + multi_chan_cfg->channel_time); + cmd->size = + wlan_cpu_to_le16(S_DS_GEN + + sizeof(HostCmd_DS_MULTI_CHAN_CFG)); + } else { + cmd->size = wlan_cpu_to_le16(S_DS_GEN + sizeof(cmd_action)); + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of MULTI_CHAN_CFG + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_ret_multi_chan_cfg(IN pmlan_private pmpriv, + const IN HostCmd_DS_COMMAND *resp, + OUT mlan_ioctl_req *pioctl_buf) +{ + mlan_ds_misc_cfg *pcfg = MNULL; + const HostCmd_DS_MULTI_CHAN_CFG *presp_cfg = + &resp->params.multi_chan_cfg; + + ENTER(); + + if (pioctl_buf) { + pcfg = (mlan_ds_misc_cfg *)pioctl_buf->pbuf; + pcfg->param.multi_chan_cfg.channel_time = + wlan_le32_to_cpu(presp_cfg->channel_time); + pcfg->param.multi_chan_cfg.buffer_weight = + presp_cfg->buffer_weight; + pcfg->param.multi_chan_cfg.tlv_len = + resp->size - (sizeof(HostCmd_DS_GEN) + + sizeof(HostCmd_DS_MULTI_CHAN_CFG)); + PRINTM(MCMND, + "Get multi-channel: buffer_weight=%d channel_time=%d tlv_len=%d\n", + pcfg->param.multi_chan_cfg.buffer_weight, + pcfg->param.multi_chan_cfg.channel_time, + pcfg->param.multi_chan_cfg.tlv_len); + memcpy(pmpriv->adapter, pcfg->param.multi_chan_cfg.tlv_buf, + presp_cfg->tlv_buf, pcfg->param.multi_chan_cfg.tlv_len); + pioctl_buf->buf_len = + sizeof(mlan_ds_multi_chan_cfg) + + pcfg->param.multi_chan_cfg.tlv_len; + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares the command MULTI_CHAN_POLICY + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action Command action: GET or SET + * @param pdata_buf A pointer to new setting buf + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_cmd_multi_chan_policy(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, IN t_void *pdata_buf) +{ + t_u16 policy = 0; + HostCmd_DS_MULTI_CHAN_POLICY *pmulti_chan_policy = + (HostCmd_DS_MULTI_CHAN_POLICY *)&cmd->params.multi_chan_policy; + + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_MULTI_CHAN_POLICY); + pmulti_chan_policy->action = wlan_cpu_to_le16(cmd_action); + cmd->size = + wlan_cpu_to_le16(S_DS_GEN + + sizeof(HostCmd_DS_MULTI_CHAN_POLICY)); + if (cmd_action == HostCmd_ACT_GEN_SET) { + policy = *((t_u16 *)pdata_buf); + pmulti_chan_policy->policy = wlan_cpu_to_le16(policy); + PRINTM(MCMND, "Set multi-channel policy: %d\n", policy); + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of MULTI_CHAN_POLICY + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_ret_multi_chan_policy(IN pmlan_private pmpriv, + const IN HostCmd_DS_COMMAND *resp, + OUT mlan_ioctl_req *pioctl_buf) +{ + mlan_ds_misc_cfg *pcfg = MNULL; + const HostCmd_DS_MULTI_CHAN_POLICY *presp_cfg = + &resp->params.multi_chan_policy; + + ENTER(); + + if (pioctl_buf) { + pcfg = (mlan_ds_misc_cfg *)pioctl_buf->pbuf; + pcfg->param.multi_chan_policy = + wlan_le16_to_cpu(presp_cfg->policy); + + if (pioctl_buf->action == HostCmd_ACT_GEN_SET) { + if (pcfg->param.multi_chan_policy) + pmpriv->adapter->mc_policy = MTRUE; + else + pmpriv->adapter->mc_policy = MFALSE; + } + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares the command DRCD_CFG + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action Command action: GET or SET + * @param pdata_buf A pointer to new setting buf + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_cmd_drcs_cfg(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, IN t_void *pdata_buf) +{ + mlan_ds_drcs_cfg *drcs_cfg = (mlan_ds_drcs_cfg *) pdata_buf; + HostCmd_DS_DRCS_CFG *pdrcs_cfg = + (HostCmd_DS_DRCS_CFG *) & cmd->params.drcs_cfg; + MrvlTypes_DrcsTimeSlice_t *channel_time_slicing = + &pdrcs_cfg->time_slicing; + + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_DRCS_CONFIG); + pdrcs_cfg->action = wlan_cpu_to_le16(cmd_action); + + if (cmd_action == HostCmd_ACT_GEN_SET) { + channel_time_slicing->header.type = + wlan_cpu_to_le16(MRVL_DRCS_TIME_SLICE_TLV_ID); + channel_time_slicing->header.len = + wlan_cpu_to_le16(sizeof(MrvlTypes_DrcsTimeSlice_t) - + sizeof(MrvlIEtypesHeader_t)); + channel_time_slicing->chan_idx = + wlan_cpu_to_le16(drcs_cfg->chan_idx); + channel_time_slicing->chantime = drcs_cfg->chantime; + channel_time_slicing->switchtime = drcs_cfg->switchtime; + channel_time_slicing->undozetime = drcs_cfg->undozetime; + channel_time_slicing->mode = drcs_cfg->mode; + PRINTM(MCMND, + "Set multi-channel: chan_idx=%d chantime=%d switchtime=%d undozetime=%d mode=%d\n", + channel_time_slicing->chan_idx, + channel_time_slicing->chantime, + channel_time_slicing->switchtime, + channel_time_slicing->undozetime, + channel_time_slicing->mode); + cmd->size = + wlan_cpu_to_le16(S_DS_GEN + + sizeof(HostCmd_DS_DRCS_CFG)); + /* Set two channels different parameters */ + if (0x3 != channel_time_slicing->chan_idx) { + drcs_cfg++; + channel_time_slicing = pdrcs_cfg->drcs_buf; + channel_time_slicing->header.type = + wlan_cpu_to_le16(MRVL_DRCS_TIME_SLICE_TLV_ID); + channel_time_slicing->header.len = + wlan_cpu_to_le16(sizeof + (MrvlTypes_DrcsTimeSlice_t) - + sizeof(MrvlIEtypesHeader_t)); + channel_time_slicing->chan_idx = + wlan_cpu_to_le16(drcs_cfg->chan_idx); + channel_time_slicing->chantime = drcs_cfg->chantime; + channel_time_slicing->switchtime = drcs_cfg->switchtime; + channel_time_slicing->undozetime = drcs_cfg->undozetime; + channel_time_slicing->mode = drcs_cfg->mode; + PRINTM(MCMND, + "Set multi-channel: chan_idx=%d chantime=%d switchtime=%d undozetime=%d mode=%d\n", + channel_time_slicing->chan_idx, + channel_time_slicing->chantime, + channel_time_slicing->switchtime, + channel_time_slicing->undozetime, + channel_time_slicing->mode); + cmd->size += + wlan_cpu_to_le16(sizeof + (MrvlTypes_DrcsTimeSlice_t)); + } + } else { + cmd->size = wlan_cpu_to_le16(S_DS_GEN + sizeof(cmd_action)); + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of DRCS_CFG + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_ret_drcs_cfg(IN pmlan_private pmpriv, + const IN HostCmd_DS_COMMAND *resp, + OUT mlan_ioctl_req *pioctl_buf) +{ + mlan_ds_misc_cfg *pcfg = MNULL; + const HostCmd_DS_DRCS_CFG *presp_cfg = &resp->params.drcs_cfg; + const MrvlTypes_DrcsTimeSlice_t *channel_time_slicing = + &presp_cfg->time_slicing; + const MrvlTypes_DrcsTimeSlice_t *channel_time_slicing1 = MNULL; + mlan_ds_drcs_cfg *drcs_cfg1 = MNULL; + + ENTER(); + + if (pioctl_buf) { + pcfg = (mlan_ds_misc_cfg *)pioctl_buf->pbuf; + if (wlan_le16_to_cpu(channel_time_slicing->header.type) != + MRVL_DRCS_TIME_SLICE_TLV_ID || + wlan_le16_to_cpu(channel_time_slicing->header.len) != + sizeof(MrvlTypes_DrcsTimeSlice_t) - + sizeof(MrvlIEtypesHeader_t)) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + pcfg->param.drcs_cfg[0].chan_idx = + wlan_le16_to_cpu(channel_time_slicing->chan_idx); + pcfg->param.drcs_cfg[0].chantime = + channel_time_slicing->chantime; + pcfg->param.drcs_cfg[0].switchtime = + channel_time_slicing->switchtime; + pcfg->param.drcs_cfg[0].undozetime = + channel_time_slicing->undozetime; + pcfg->param.drcs_cfg[0].mode = channel_time_slicing->mode; + PRINTM(MCMND, + "multi-channel: chan_idx=%d chantime=%d switchtime=%d undozetime=%d mode=%d\n", + pcfg->param.drcs_cfg[0].chan_idx, + channel_time_slicing->chantime, + channel_time_slicing->switchtime, + channel_time_slicing->undozetime, + channel_time_slicing->mode); + pioctl_buf->buf_len = sizeof(mlan_ds_drcs_cfg); + /*Channel for chan_idx 1 and 2 have different parameters */ + if (0x3 != pcfg->param.drcs_cfg[0].chan_idx) { + channel_time_slicing1 = presp_cfg->drcs_buf; + if (wlan_le16_to_cpu(channel_time_slicing1->header.type) + != MRVL_DRCS_TIME_SLICE_TLV_ID || + wlan_le16_to_cpu(channel_time_slicing1->header. + len) != + sizeof(MrvlTypes_DrcsTimeSlice_t) - + sizeof(MrvlIEtypesHeader_t)) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + drcs_cfg1 = + (mlan_ds_drcs_cfg *) & pcfg->param.drcs_cfg[1]; + drcs_cfg1->chan_idx = + wlan_le16_to_cpu(channel_time_slicing1-> + chan_idx); + drcs_cfg1->chantime = channel_time_slicing1->chantime; + drcs_cfg1->switchtime = + channel_time_slicing1->switchtime; + drcs_cfg1->undozetime = + channel_time_slicing1->undozetime; + drcs_cfg1->mode = channel_time_slicing1->mode; + PRINTM(MCMND, + "multi-channel: chan_idx=%d chantime=%d switchtime=%d undozetime=%d mode=%d\n", + drcs_cfg1->chan_idx, drcs_cfg1->chantime, + drcs_cfg1->switchtime, drcs_cfg1->undozetime, + drcs_cfg1->mode); + pioctl_buf->buf_len += sizeof(mlan_ds_drcs_cfg); + } + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of get_hw_spec. + * + * @param pmpriv A pointer to mlan_private structure + * @param pcmd A pointer to HostCmd_DS_COMMAND structure + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_cmd_get_hw_spec(IN pmlan_private pmpriv, IN HostCmd_DS_COMMAND *pcmd) +{ + HostCmd_DS_GET_HW_SPEC *hw_spec = &pcmd->params.hw_spec; + + ENTER(); + + pcmd->command = wlan_cpu_to_le16(HostCmd_CMD_GET_HW_SPEC); + pcmd->size = + wlan_cpu_to_le16(sizeof(HostCmd_DS_GET_HW_SPEC) + S_DS_GEN); + memcpy(pmpriv->adapter, hw_spec->permanent_addr, pmpriv->curr_addr, + MLAN_MAC_ADDR_LENGTH); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of sdio rx aggr command. + * + * @param pcmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action Command action: GET or SET + * @param pdata_buf A pointer to new setting buf + + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_cmd_sdio_rx_aggr_cfg(IN HostCmd_DS_COMMAND *pcmd, + IN t_u16 cmd_action, IN t_void *pdata_buf) +{ + HostCmd_DS_SDIO_SP_RX_AGGR_CFG *cfg = &pcmd->params.sdio_rx_aggr; + + pcmd->command = wlan_cpu_to_le16(HostCmd_CMD_SDIO_SP_RX_AGGR_CFG); + pcmd->size = + wlan_cpu_to_le16(sizeof(HostCmd_DS_SDIO_SP_RX_AGGR_CFG) + + S_DS_GEN); + cfg->action = cmd_action; + if (cmd_action == HostCmd_ACT_GEN_SET) + cfg->enable = *(t_u8 *)pdata_buf; + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of sdio rx aggr command + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_ret_sdio_rx_aggr_cfg(IN pmlan_private pmpriv, IN HostCmd_DS_COMMAND *resp) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + HostCmd_DS_SDIO_SP_RX_AGGR_CFG *cfg = &resp->params.sdio_rx_aggr; + + pmadapter->sdio_rx_aggr_enable = cfg->enable; + pmadapter->sdio_rx_block_size = wlan_le16_to_cpu(cfg->sdio_block_size); + PRINTM(MMSG, "SDIO rx aggr: %d block_size=%d\n", + cfg->enable, pmadapter->sdio_rx_block_size); + if (!pmadapter->sdio_rx_block_size) + pmadapter->sdio_rx_aggr_enable = MFALSE; + if (pmadapter->sdio_rx_aggr_enable) { + pmadapter->max_sp_rx_size = SDIO_CMD53_MAX_SIZE; + wlan_re_alloc_sdio_rx_mpa_buffer(pmadapter); + } + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of set_cfg_data. + * + * @param pmpriv A pointer to mlan_private strcture + * @param pcmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action Command action: GET or SET + * @param pdata_buf A pointer to cal_data buf + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_cmd_cfg_data(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *pcmd, + IN t_u16 cmd_action, IN t_u32 cmd_oid, IN t_void *pdata_buf) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + HostCmd_DS_802_11_CFG_DATA *pcfg_data = &(pcmd->params.cfg_data); + pmlan_adapter pmadapter = pmpriv->adapter; + t_u32 len; + t_u32 data_offset; + t_u8 *temp_pcmd = (t_u8 *)pcmd; + + ENTER(); + + data_offset = S_DS_GEN + sizeof(HostCmd_DS_802_11_CFG_DATA); + + if ((cmd_oid == OID_TYPE_CAL) && (pmadapter->pcal_data) && + (pmadapter->cal_data_len > 0)) { + len = wlan_parse_cal_cfg((t_u8 *)pmadapter->pcal_data, + pmadapter->cal_data_len, + (t_u8 *)(temp_pcmd + data_offset)); + } else if ((cmd_oid == OID_TYPE_DPD) && (pmadapter->pdpd_data) && + (pmadapter->dpd_data_len > 0)) { + len = wlan_parse_cal_cfg((t_u8 *)pmadapter->pdpd_data, + pmadapter->dpd_data_len, + (t_u8 *)(temp_pcmd + data_offset)); + } else { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + pcfg_data->action = cmd_action; + pcfg_data->type = cmd_oid; + pcfg_data->data_len = len; + + pcmd->command = HostCmd_CMD_CFG_DATA; + pcmd->size = pcfg_data->data_len + data_offset; + + pcmd->command = wlan_cpu_to_le16(pcmd->command); + pcmd->size = wlan_cpu_to_le16(pcmd->size); + + pcfg_data->action = wlan_cpu_to_le16(pcfg_data->action); + pcfg_data->type = wlan_cpu_to_le16(pcfg_data->type); + pcfg_data->data_len = wlan_cpu_to_le16(pcfg_data->data_len); + +done: + LEAVE(); + return ret; +} + +/** + * @brief This function handles the command response of set_cfg_data + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to A pointer to mlan_ioctl_req + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_ret_cfg_data(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, IN t_void *pioctl_buf) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (resp->result != HostCmd_RESULT_OK) { + PRINTM(MERROR, "CFG data cmd resp failed\n"); + ret = MLAN_STATUS_FAILURE; + } + LEAVE(); + return ret; +} + +/** + * @brief This function prepares command of mac_control. + * + * @param pmpriv A pointer to mlan_private structure + * @param pcmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action Command action + * @param pdata_buf A pointer to command information buffer + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_cmd_mac_control(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *pcmd, + IN t_u16 cmd_action, IN t_void *pdata_buf) +{ + HostCmd_DS_MAC_CONTROL *pmac = &pcmd->params.mac_ctrl; + t_u32 action = *((t_u32 *)pdata_buf); + + ENTER(); + + if (cmd_action != HostCmd_ACT_GEN_SET) { + PRINTM(MERROR, "wlan_cmd_mac_control(): support SET only.\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + pcmd->command = wlan_cpu_to_le16(HostCmd_CMD_MAC_CONTROL); + pcmd->size = + wlan_cpu_to_le16(sizeof(HostCmd_DS_MAC_CONTROL) + S_DS_GEN); + pmac->action = wlan_cpu_to_le32(action); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of mac_control + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_ret_mac_control(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, IN mlan_ioctl_req *pioctl_buf) +{ + ENTER(); + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of get_hw_spec + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to command buffer + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_ret_get_hw_spec(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, IN t_void *pioctl_buf) +{ + HostCmd_DS_GET_HW_SPEC *hw_spec = &resp->params.hw_spec; + mlan_adapter *pmadapter = pmpriv->adapter; + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u32 i; + t_u16 left_len; + t_u16 tlv_type = 0; + t_u16 tlv_len = 0; + MrvlIEtypes_fw_ver_info_t *api_rev = MNULL; + t_u16 api_id = 0; + MrvlIEtypesHeader_t *tlv = MNULL; + pmlan_ioctl_req pioctl_req = (mlan_ioctl_req *)pioctl_buf; + MrvlIEtypes_Max_Conn_t *tlv_max_conn = MNULL; + + ENTER(); + + pmadapter->fw_cap_info = wlan_le32_to_cpu(hw_spec->fw_cap_info); + pmadapter->fw_cap_info &= pmadapter->init_para.dev_cap_mask; + + PRINTM(MMSG, "fw_cap_info=0x%x, dev_cap_mask=0x%x\n", + wlan_le32_to_cpu(hw_spec->fw_cap_info), + pmadapter->init_para.dev_cap_mask); +#ifdef STA_SUPPORT + if (IS_SUPPORT_MULTI_BANDS(pmadapter)) + pmadapter->fw_bands = (t_u8)GET_FW_DEFAULT_BANDS(pmadapter); + else + pmadapter->fw_bands = BAND_B; + + pmadapter->config_bands = pmadapter->fw_bands; + for (i = 0; i < pmadapter->priv_num; i++) { + if (pmadapter->priv[i]) + pmadapter->priv[i]->config_bands = pmadapter->fw_bands; + } + + if (pmadapter->fw_bands & BAND_A) { + if (pmadapter->fw_bands & BAND_GN) { + pmadapter->config_bands |= BAND_AN; + for (i = 0; i < pmadapter->priv_num; i++) { + if (pmadapter->priv[i]) + pmadapter->priv[i]->config_bands |= + BAND_AN; + } + + pmadapter->fw_bands |= BAND_AN; + } + if ((pmadapter->fw_bands & BAND_AN) + ) { + pmadapter->adhoc_start_band = BAND_A | BAND_AN; + pmadapter->adhoc_11n_enabled = MTRUE; + } else + pmadapter->adhoc_start_band = BAND_A; + pmpriv->adhoc_channel = DEFAULT_AD_HOC_CHANNEL_A; + } else if ((pmadapter->fw_bands & BAND_GN) + ) { + pmadapter->adhoc_start_band = BAND_G | BAND_B | BAND_GN; + pmpriv->adhoc_channel = DEFAULT_AD_HOC_CHANNEL; + pmadapter->adhoc_11n_enabled = MTRUE; + } else if (pmadapter->fw_bands & BAND_G) { + pmadapter->adhoc_start_band = BAND_G | BAND_B; + pmpriv->adhoc_channel = DEFAULT_AD_HOC_CHANNEL; + } else if (pmadapter->fw_bands & BAND_B) { + pmadapter->adhoc_start_band = BAND_B; + pmpriv->adhoc_channel = DEFAULT_AD_HOC_CHANNEL; + } +#endif /* STA_SUPPORT */ + + pmadapter->fw_release_number = + wlan_le32_to_cpu(hw_spec->fw_release_number); + pmadapter->number_of_antenna = + wlan_le16_to_cpu(hw_spec->number_of_antenna) & 0x00ff; + pmadapter->antinfo = + (wlan_le16_to_cpu(hw_spec->number_of_antenna) & 0xff00) >> 8; + PRINTM(MCMND, "num_ant=%d, antinfo=0x%x\n", + pmadapter->number_of_antenna, pmadapter->antinfo); + + PRINTM(MINFO, "GET_HW_SPEC: fw_release_number- 0x%X\n", + pmadapter->fw_release_number); + PRINTM(MINFO, "GET_HW_SPEC: Permanent addr- " MACSTR "\n", + MAC2STR(hw_spec->permanent_addr)); + PRINTM(MINFO, "GET_HW_SPEC: hw_if_version=0x%X version=0x%X\n", + wlan_le16_to_cpu(hw_spec->hw_if_version), + wlan_le16_to_cpu(hw_spec->version)); + + if (pmpriv->curr_addr[0] == 0xff) + memmove(pmadapter, pmpriv->curr_addr, hw_spec->permanent_addr, + MLAN_MAC_ADDR_LENGTH); + memmove(pmadapter, pmadapter->permanent_addr, hw_spec->permanent_addr, + MLAN_MAC_ADDR_LENGTH); + pmadapter->hw_dot_11n_dev_cap = + wlan_le32_to_cpu(hw_spec->dot_11n_dev_cap); + pmadapter->hw_dev_mcs_support = hw_spec->dev_mcs_support; + for (i = 0; i < pmadapter->priv_num; i++) { + if (pmadapter->priv[i]) + wlan_update_11n_cap(pmadapter->priv[i]); + } + + wlan_show_dot11ndevcap(pmadapter, pmadapter->hw_dot_11n_dev_cap); + wlan_show_devmcssupport(pmadapter, pmadapter->hw_dev_mcs_support); + if (ISSUPP_BEAMFORMING(pmadapter->hw_dot_11n_dev_cap)) { + PRINTM(MCMND, "Enable Beamforming\n"); + for (i = 0; i < pmadapter->priv_num; i++) { + if (pmadapter->priv[i]) + pmadapter->priv[i]->tx_bf_cap = + DEFAULT_11N_TX_BF_CAP; + } + } + pmadapter->mp_end_port = wlan_le16_to_cpu(hw_spec->mp_end_port); + + for (i = 1; i <= (unsigned)(MAX_PORT - pmadapter->mp_end_port); i++) + pmadapter->mp_data_port_mask &= ~(1 << (MAX_PORT - i)); + + pmadapter->max_mgmt_ie_index = + wlan_le16_to_cpu(hw_spec->mgmt_buf_count); + PRINTM(MINFO, "GET_HW_SPEC: mgmt IE count=%d\n", + pmadapter->max_mgmt_ie_index); + if (!pmadapter->max_mgmt_ie_index) + pmadapter->max_mgmt_ie_index = MAX_MGMT_IE_INDEX; + + pmadapter->region_code = wlan_le16_to_cpu(hw_spec->region_code); + for (i = 0; i < MRVDRV_MAX_REGION_CODE; i++) { + /* Use the region code to search for the index */ + if (pmadapter->region_code == region_code_index[i]) + break; + } + /* If it's unidentified region code, use the default */ + if (i >= MRVDRV_MAX_REGION_CODE) { + pmadapter->region_code = MRVDRV_DEFAULT_REGION_CODE; + PRINTM(MWARN, + "unidentified region code, use the default (0x%02x)\n", + MRVDRV_DEFAULT_REGION_CODE); + } + /* Synchronize CFP code with region code */ + pmadapter->cfp_code_bg = pmadapter->region_code; + pmadapter->cfp_code_a = pmadapter->region_code; + + if (pmadapter->fw_cap_info & ENHANCE_EXT_SCAN_ENABLE) + pmadapter->ext_scan_enh = MTRUE; + + if ((pmadapter->fw_cap_info & SDIO_SP_RX_AGGR_ENABLE) && + pmadapter->sdio_rx_aggr_enable) { + t_u8 sdio_sp_rx_aggr = MTRUE; + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_SDIO_SP_RX_AGGR_CFG, + HostCmd_ACT_GEN_SET, 0, MNULL, + &sdio_sp_rx_aggr); + if (ret) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + } else { + pmadapter->sdio_rx_aggr_enable = MFALSE; + PRINTM(MCMND, "FW: SDIO rx aggr disabled 0x%x\n", + pmadapter->fw_cap_info); + } + + if (wlan_set_regiontable(pmpriv, (t_u8)pmadapter->region_code, + pmadapter->fw_bands)) { + if (pioctl_req) + pioctl_req->status_code = MLAN_ERROR_CMD_SCAN_FAIL; + ret = MLAN_STATUS_FAILURE; + goto done; + } +#ifdef STA_SUPPORT + if (wlan_11d_set_universaltable(pmpriv, pmadapter->fw_bands)) { + if (pioctl_req) + pioctl_req->status_code = MLAN_ERROR_CMD_SCAN_FAIL; + ret = MLAN_STATUS_FAILURE; + goto done; + } +#endif /* STA_SUPPORT */ + if (pmadapter->fw_cap_info & FW_CAPINFO_ECSA) { + t_u8 ecsa_enable = MTRUE; + pmadapter->ecsa_enable = MTRUE; + PRINTM(MCMND, "pmadapter->ecsa_enable=%d\n", + pmadapter->ecsa_enable); + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_802_11_SNMP_MIB, + HostCmd_ACT_GEN_SET, ECSAEnable_i, MNULL, + &ecsa_enable); + if (ret) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + } + if (pmadapter->fw_cap_info & FW_CAPINFO_GET_LOG) { + pmadapter->getlog_enable = MTRUE; + PRINTM(MCMND, "pmadapter->getlog_enable=%d\n", + pmadapter->getlog_enable); + } + + left_len = resp->size - sizeof(HostCmd_DS_GET_HW_SPEC) - S_DS_GEN; + tlv = (MrvlIEtypesHeader_t *)((t_u8 *)hw_spec + + sizeof(HostCmd_DS_GET_HW_SPEC)); + while (left_len > sizeof(MrvlIEtypesHeader_t)) { + tlv_type = wlan_le16_to_cpu(tlv->type); + tlv_len = wlan_le16_to_cpu(tlv->len); + switch (tlv_type) { + case TLV_TYPE_FW_VER_INFO: + api_rev = (MrvlIEtypes_fw_ver_info_t *) tlv; + api_id = wlan_le16_to_cpu(api_rev->api_id); + switch (api_id) { + case FW_API_VER_ID: + pmadapter->fw_ver = api_rev->major_ver; + pmadapter->fw_min_ver = api_rev->minor_ver; + PRINTM(MCMND, "fw ver=%d.%d\n", + api_rev->major_ver, api_rev->minor_ver); + break; + case UAP_FW_API_VER_ID: + pmadapter->uap_fw_ver = api_rev->major_ver; + PRINTM(MCMND, "uap fw ver=%d.%d\n", + api_rev->major_ver, api_rev->minor_ver); + break; + case CHANRPT_API_VER_ID: + pmadapter->chanrpt_param_bandcfg = + api_rev->minor_ver; + PRINTM(MCMND, "chanrpt api ver=%d.%d\n", + api_rev->major_ver, api_rev->minor_ver); + break; + default: + break; + } + break; + case TLV_TYPE_MAX_CONN: + tlv_max_conn = (MrvlIEtypes_Max_Conn_t *) tlv; + PRINTM(MMSG, "max_p2p_conn = %d, max_sta_conn = %d\n", + tlv_max_conn->max_p2p_conn, + tlv_max_conn->max_sta_conn); + if (tlv_max_conn->max_p2p_conn && + tlv_max_conn->max_sta_conn) + pmadapter->max_sta_conn = + MIN(tlv_max_conn->max_sta_conn, + tlv_max_conn->max_p2p_conn); + else if (tlv_max_conn->max_sta_conn) + pmadapter->max_sta_conn = + tlv_max_conn->max_sta_conn; + else if (tlv_max_conn->max_p2p_conn) + pmadapter->max_sta_conn = + tlv_max_conn->max_p2p_conn; + else + pmadapter->max_sta_conn = 0; + break; + default: + break; + } + left_len -= (sizeof(MrvlIEtypesHeader_t) + tlv_len); + tlv = (MrvlIEtypesHeader_t *)((t_u8 *)tlv + tlv_len + + sizeof(MrvlIEtypesHeader_t)); + } +done: + LEAVE(); + return ret; +} + +/** + * @brief This function prepares command of radio_control. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action The action: GET or SET + * @param pdata_buf A pointer to data buffer + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_cmd_802_11_radio_control(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, IN t_void *pdata_buf) +{ + HostCmd_DS_802_11_RADIO_CONTROL *pradio_control = &cmd->params.radio; + t_u32 radio_ctl; + ENTER(); + cmd->size = wlan_cpu_to_le16((sizeof(HostCmd_DS_802_11_RADIO_CONTROL)) + + S_DS_GEN); + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_802_11_RADIO_CONTROL); + pradio_control->action = wlan_cpu_to_le16(cmd_action); + memcpy(pmpriv->adapter, &radio_ctl, pdata_buf, sizeof(t_u32)); + pradio_control->control = wlan_cpu_to_le16((t_u16)radio_ctl); + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of radio_control + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_ret_802_11_radio_control(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf) +{ + HostCmd_DS_802_11_RADIO_CONTROL *pradio_ctrl = + (HostCmd_DS_802_11_RADIO_CONTROL *)&resp->params.radio; + mlan_ds_radio_cfg *radio_cfg = MNULL; + mlan_adapter *pmadapter = pmpriv->adapter; + + ENTER(); + pmadapter->radio_on = wlan_le16_to_cpu(pradio_ctrl->control); + if (pioctl_buf) { + radio_cfg = (mlan_ds_radio_cfg *)pioctl_buf->pbuf; + radio_cfg->param.radio_on_off = (t_u32)pmadapter->radio_on; + pioctl_buf->data_read_written = sizeof(mlan_ds_radio_cfg); + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of remain_on_channel. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action The action: GET or SET + * @param pdata_buf A pointer to data buffer + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_cmd_remain_on_channel(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, IN t_void *pdata_buf) +{ + HostCmd_DS_REMAIN_ON_CHANNEL *remain_channel = + &cmd->params.remain_on_chan; + mlan_ds_remain_chan *cfg = (mlan_ds_remain_chan *)pdata_buf; + ENTER(); + cmd->size = wlan_cpu_to_le16((sizeof(HostCmd_DS_REMAIN_ON_CHANNEL)) + + S_DS_GEN); + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_802_11_REMAIN_ON_CHANNEL); + remain_channel->action = cmd_action; + if (cmd_action == HostCmd_ACT_GEN_SET) { + if (cfg->remove) { + remain_channel->action = HostCmd_ACT_GEN_REMOVE; + } else { + remain_channel->bandcfg = cfg->bandcfg; + remain_channel->channel = cfg->channel; + remain_channel->remain_period = + wlan_cpu_to_le32(cfg->remain_period); + } + } + remain_channel->action = wlan_cpu_to_le16(remain_channel->action); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of remain_on_channel + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_ret_remain_on_channel(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf) +{ + HostCmd_DS_REMAIN_ON_CHANNEL *remain_channel = + &resp->params.remain_on_chan; + mlan_ds_radio_cfg *radio_cfg = MNULL; + + ENTER(); + if (pioctl_buf) { + radio_cfg = (mlan_ds_radio_cfg *)pioctl_buf->pbuf; + radio_cfg->param.remain_chan.status = remain_channel->status; + radio_cfg->param.remain_chan.bandcfg = remain_channel->bandcfg; + radio_cfg->param.remain_chan.channel = remain_channel->channel; + radio_cfg->param.remain_chan.remain_period = + wlan_le32_to_cpu(remain_channel->remain_period); + pioctl_buf->data_read_written = sizeof(mlan_ds_radio_cfg); + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +#ifdef WIFI_DIRECT_SUPPORT + +/** + * @brief This function prepares command of wifi direct mode. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action The action: GET or SET + * @param pdata_buf A pointer to data buffer + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_cmd_wifi_direct_mode(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, IN t_void *pdata_buf) +{ + HostCmd_DS_WIFI_DIRECT_MODE *wfd_mode = &cmd->params.wifi_direct_mode; + t_u16 mode = *((t_u16 *)pdata_buf); + ENTER(); + cmd->size = wlan_cpu_to_le16((sizeof(HostCmd_DS_WIFI_DIRECT_MODE)) + + S_DS_GEN); + cmd->command = wlan_cpu_to_le16(HOST_CMD_WIFI_DIRECT_MODE_CONFIG); + wfd_mode->action = wlan_cpu_to_le16(cmd_action); + if (cmd_action == HostCmd_ACT_GEN_SET) + wfd_mode->mode = wlan_cpu_to_le16(mode); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of wifi direct mode + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_ret_wifi_direct_mode(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf) +{ + HostCmd_DS_WIFI_DIRECT_MODE *wfd_mode = &resp->params.wifi_direct_mode; + mlan_ds_bss *bss = MNULL; + + ENTER(); + if (pioctl_buf) { + bss = (mlan_ds_bss *)pioctl_buf->pbuf; + bss->param.wfd_mode = wlan_le16_to_cpu(wfd_mode->mode); + pioctl_buf->data_read_written = sizeof(mlan_ds_bss); + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of p2p_params_config. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action The action: GET or SET + * @param pdata_buf A pointer to data buffer + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_cmd_p2p_params_config(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, IN t_void *pdata_buf) +{ + HostCmd_DS_WIFI_DIRECT_PARAM_CONFIG *p2p_config = + &cmd->params.p2p_params_config; + mlan_ds_wifi_direct_config *cfg = + (mlan_ds_wifi_direct_config *)pdata_buf; + MrvlIEtypes_NoA_setting_t *pnoa_tlv = MNULL; + MrvlIEtypes_OPP_PS_setting_t *popp_ps_tlv = MNULL; + t_u8 *tlv = MNULL; + ENTER(); + + cmd->size = sizeof(HostCmd_DS_WIFI_DIRECT_PARAM_CONFIG) + S_DS_GEN; + cmd->command = wlan_cpu_to_le16(HOST_CMD_P2P_PARAMS_CONFIG); + p2p_config->action = wlan_cpu_to_le16(cmd_action); + if (cmd_action == HostCmd_ACT_GEN_SET) { + tlv = (t_u8 *)p2p_config + + sizeof(HostCmd_DS_WIFI_DIRECT_PARAM_CONFIG); + if (cfg->flags & WIFI_DIRECT_NOA) { + pnoa_tlv = (MrvlIEtypes_NoA_setting_t *)tlv; + pnoa_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_WIFI_DIRECT_NOA); + pnoa_tlv->header.len = + wlan_cpu_to_le16(sizeof + (MrvlIEtypes_NoA_setting_t) - + sizeof(MrvlIEtypesHeader_t)); + pnoa_tlv->enable = cfg->noa_enable; + pnoa_tlv->index = wlan_cpu_to_le16(cfg->index); + pnoa_tlv->noa_count = cfg->noa_count; + pnoa_tlv->noa_duration = + wlan_cpu_to_le32(cfg->noa_duration); + pnoa_tlv->noa_interval = + wlan_cpu_to_le32(cfg->noa_interval); + cmd->size += sizeof(MrvlIEtypes_NoA_setting_t); + tlv += sizeof(MrvlIEtypes_NoA_setting_t); + PRINTM(MCMND, + "Set NOA: enable=%d index=%d, count=%d, duration=%d interval=%d\n", + cfg->noa_enable, cfg->index, cfg->noa_count, + (int)cfg->noa_duration, (int)cfg->noa_interval); + } + if (cfg->flags & WIFI_DIRECT_OPP_PS) { + popp_ps_tlv = (MrvlIEtypes_OPP_PS_setting_t *)tlv; + popp_ps_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_WIFI_DIRECT_OPP_PS); + popp_ps_tlv->header.len = + wlan_cpu_to_le16(sizeof + (MrvlIEtypes_OPP_PS_setting_t) + - sizeof(MrvlIEtypesHeader_t)); + + popp_ps_tlv->enable = cfg->ct_window; + popp_ps_tlv->enable |= cfg->opp_ps_enable << 7; + cmd->size += sizeof(MrvlIEtypes_OPP_PS_setting_t); + PRINTM(MCMND, "Set OPP_PS: enable=%d ct_win=%d\n", + cfg->opp_ps_enable, cfg->ct_window); + } + } else if (cmd_action == HostCmd_ACT_GEN_GET) { + tlv = (t_u8 *)p2p_config + + sizeof(HostCmd_DS_WIFI_DIRECT_PARAM_CONFIG); + if (cfg->flags & WIFI_DIRECT_NOA) { + pnoa_tlv = (MrvlIEtypes_NoA_setting_t *)tlv; + pnoa_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_WIFI_DIRECT_NOA); + pnoa_tlv->header.len = + wlan_cpu_to_le16(sizeof + (MrvlIEtypes_NoA_setting_t) - + sizeof(MrvlIEtypesHeader_t)); + cmd->size += sizeof(MrvlIEtypes_NoA_setting_t); + tlv += sizeof(MrvlIEtypes_NoA_setting_t); + } + + if (cfg->flags & WIFI_DIRECT_OPP_PS) { + popp_ps_tlv = (MrvlIEtypes_OPP_PS_setting_t *)tlv; + popp_ps_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_WIFI_DIRECT_OPP_PS); + popp_ps_tlv->header.len = + wlan_cpu_to_le16(sizeof + (MrvlIEtypes_OPP_PS_setting_t) + - sizeof(MrvlIEtypesHeader_t)); + cmd->size += sizeof(MrvlIEtypes_OPP_PS_setting_t); + } + } + cmd->size = wlan_cpu_to_le16(cmd->size); + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of p2p_params_config + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_ret_p2p_params_config(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf) +{ + HostCmd_DS_WIFI_DIRECT_PARAM_CONFIG *p2p_config = + &resp->params.p2p_params_config; + mlan_ds_misc_cfg *cfg = MNULL; + MrvlIEtypes_NoA_setting_t *pnoa_tlv = MNULL; + MrvlIEtypes_OPP_PS_setting_t *popp_ps_tlv = MNULL; + MrvlIEtypesHeader_t *tlv = MNULL; + t_u16 tlv_buf_left = 0; + t_u16 tlv_type = 0; + t_u16 tlv_len = 0; + + ENTER(); + if (wlan_le16_to_cpu(p2p_config->action) == HostCmd_ACT_GEN_GET) { + if (pioctl_buf) { + cfg = (mlan_ds_misc_cfg *)pioctl_buf->pbuf; + tlv = (MrvlIEtypesHeader_t *)((t_u8 *)p2p_config + + sizeof + (HostCmd_DS_WIFI_DIRECT_PARAM_CONFIG)); + tlv_buf_left = + resp->size - + (sizeof(HostCmd_DS_WIFI_DIRECT_PARAM_CONFIG) + + S_DS_GEN); + while (tlv_buf_left >= sizeof(MrvlIEtypesHeader_t)) { + tlv_type = wlan_le16_to_cpu(tlv->type); + tlv_len = wlan_le16_to_cpu(tlv->len); + if (tlv_buf_left < + (tlv_len + sizeof(MrvlIEtypesHeader_t))) { + PRINTM(MERROR, + "Error processing p2p param config TLVs, bytes left < TLV length\n"); + break; + } + switch (tlv_type) { + case TLV_TYPE_WIFI_DIRECT_NOA: + pnoa_tlv = + (MrvlIEtypes_NoA_setting_t *) + tlv; + cfg->param.p2p_config.flags |= + WIFI_DIRECT_NOA; + cfg->param.p2p_config.noa_enable = + pnoa_tlv->enable; + cfg->param.p2p_config.index = + wlan_le16_to_cpu(pnoa_tlv-> + index); + cfg->param.p2p_config.noa_count = + pnoa_tlv->noa_count; + cfg->param.p2p_config.noa_duration = + wlan_le32_to_cpu(pnoa_tlv-> + noa_duration); + cfg->param.p2p_config.noa_interval = + wlan_le32_to_cpu(pnoa_tlv-> + noa_interval); + PRINTM(MCMND, + "Get NOA: enable=%d index=%d, count=%d, duration=%d interval=%d\n", + cfg->param.p2p_config.noa_enable, + cfg->param.p2p_config.index, + cfg->param.p2p_config.noa_count, + (int)cfg->param.p2p_config. + noa_duration, + (int)cfg->param.p2p_config. + noa_interval); + break; + case TLV_TYPE_WIFI_DIRECT_OPP_PS: + popp_ps_tlv = + (MrvlIEtypes_OPP_PS_setting_t *) + tlv; + cfg->param.p2p_config.flags |= + WIFI_DIRECT_OPP_PS; + cfg->param.p2p_config.opp_ps_enable = + (popp_ps_tlv-> + enable & 0x80) >> 7; + cfg->param.p2p_config.ct_window = + popp_ps_tlv->enable & 0x7f; + PRINTM(MCMND, + "Get OPP_PS: enable=%d ct_win=%d\n", + cfg->param.p2p_config. + opp_ps_enable, + cfg->param.p2p_config.ct_window); + break; + default: + break; + } + tlv_buf_left -= + tlv_len + sizeof(MrvlIEtypesHeader_t); + tlv = (MrvlIEtypesHeader_t *)((t_u8 *)tlv + + tlv_len + + sizeof + (MrvlIEtypesHeader_t)); + } + pioctl_buf->data_read_written = + sizeof(mlan_ds_wifi_direct_config); + } + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} +#endif + +/** + * @brief This function prepares command of hs wakeup reason. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param pdata_buf A pointer to data buffer + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_cmd_hs_wakeup_reason(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, IN t_void *pdata_buf) +{ + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_HS_WAKEUP_REASON); + cmd->size = + wlan_cpu_to_le16((sizeof(HostCmd_DS_HS_WAKEUP_REASON)) + + S_DS_GEN); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of + * hs wakeup reason + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to command buffer + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_ret_hs_wakeup_reason(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf) +{ + HostCmd_DS_HS_WAKEUP_REASON *hs_wakeup_reason = + (HostCmd_DS_HS_WAKEUP_REASON *)&resp->params.hs_wakeup_reason; + mlan_ds_pm_cfg *pm_cfg = MNULL; + + ENTER(); + + pm_cfg = (mlan_ds_pm_cfg *)pioctl_buf->pbuf; + pm_cfg->param.wakeup_reason.hs_wakeup_reason = + wlan_le16_to_cpu(hs_wakeup_reason->wakeup_reason); + pioctl_buf->data_read_written = sizeof(mlan_ds_pm_cfg); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/* + * @brief This function prepares command of cwmode control. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action The action: GET or SET + * @param pdata_buf A pointer to data buffer + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_cmd_cw_mode_ctrl(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, IN t_void *pdata_buf) +{ + HostCmd_DS_CW_MODE_CTRL *cwmode_ctrl = &cmd->params.cwmode; + mlan_ds_cw_mode_ctrl *cw_mode = (mlan_ds_cw_mode_ctrl *) pdata_buf; + ENTER(); + cmd->size = wlan_cpu_to_le16((sizeof(HostCmd_DS_CW_MODE_CTRL)) + + S_DS_GEN); + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_CW_MODE_CTRL); + cwmode_ctrl->action = wlan_cpu_to_le16(cmd_action); + + if (cmd_action == HostCmd_ACT_GEN_SET) { + cwmode_ctrl->mode = cw_mode->mode; + cwmode_ctrl->channel = cw_mode->channel; + cwmode_ctrl->chanInfo = cw_mode->chanInfo; + cwmode_ctrl->txPower = wlan_cpu_to_le16(cw_mode->txPower); + cwmode_ctrl->rateInfo = wlan_cpu_to_le32(cw_mode->rateInfo); + cwmode_ctrl->pktLength = wlan_cpu_to_le16(cw_mode->pktLength); + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/* + * @brief This function handles the command response of cwmode_ctrl + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_ret_cw_mode_ctrl(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf) +{ + HostCmd_DS_CW_MODE_CTRL *cwmode_resp = &resp->params.cwmode; + mlan_ds_misc_cfg *misc = MNULL; + + ENTER(); + if (pioctl_buf) { + misc = (mlan_ds_misc_cfg *)pioctl_buf->pbuf; + misc->param.cwmode.mode = cwmode_resp->mode; + misc->param.cwmode.channel = cwmode_resp->channel; + misc->param.cwmode.chanInfo = cwmode_resp->chanInfo; + misc->param.cwmode.txPower = + wlan_le16_to_cpu(cwmode_resp->txPower); + misc->param.cwmode.rateInfo = + wlan_le32_to_cpu(cwmode_resp->rateInfo);; + misc->param.cwmode.pktLength = + wlan_le16_to_cpu(cwmode_resp->pktLength);; + pioctl_buf->data_read_written = sizeof(mlan_ds_misc_cfg); + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of rf_antenna. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action The action: GET or SET + * @param pdata_buf A pointer to data buffer + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_cmd_802_11_rf_antenna(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, IN t_void *pdata_buf) +{ + HostCmd_DS_802_11_RF_ANTENNA *pantenna = &cmd->params.antenna; + mlan_ds_ant_cfg_1x1 *ant_cfg_1x1 = (mlan_ds_ant_cfg_1x1 *) pdata_buf; + + ENTER(); + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_802_11_RF_ANTENNA); + cmd->size = + wlan_cpu_to_le16(sizeof(HostCmd_DS_802_11_RF_ANTENNA) + + S_DS_GEN); + + if (cmd_action == HostCmd_ACT_GEN_SET) { + pantenna->action = wlan_cpu_to_le16(HostCmd_ACT_SET_BOTH); + pantenna->antenna_mode = + wlan_cpu_to_le16((t_u16)ant_cfg_1x1->antenna); + pantenna->evaluate_time = + wlan_cpu_to_le16((t_u16)ant_cfg_1x1->evaluate_time); + } else { + pantenna->action = wlan_cpu_to_le16(HostCmd_ACT_GET_BOTH); + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of rf_antenna + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_ret_802_11_rf_antenna(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf) +{ + HostCmd_DS_802_11_RF_ANTENNA *pantenna = &resp->params.antenna; + t_u16 ant_mode = wlan_le16_to_cpu(pantenna->antenna_mode); + t_u16 evaluate_time = wlan_le16_to_cpu(pantenna->evaluate_time); + t_u16 current_antenna = wlan_le16_to_cpu(pantenna->current_antenna); + mlan_ds_radio_cfg *radio = MNULL; + + ENTER(); + + PRINTM(MINFO, + "RF_ANT_RESP: action = 0x%x, Mode = 0x%04x, Evaluate time = %d, Current antenna = %d\n", + wlan_le16_to_cpu(pantenna->action), ant_mode, evaluate_time, + current_antenna); + + if (pioctl_buf) { + radio = (mlan_ds_radio_cfg *)pioctl_buf->pbuf; + radio->param.ant_cfg_1x1.antenna = ant_mode; + radio->param.ant_cfg_1x1.evaluate_time = evaluate_time; + radio->param.ant_cfg_1x1.current_antenna = current_antenna; + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of reg_access. + * + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action the action: GET or SET + * @param pdata_buf A pointer to data buffer + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_cmd_reg_access(IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, IN t_void *pdata_buf) +{ + mlan_ds_reg_rw *reg_rw; + + ENTER(); + + reg_rw = (mlan_ds_reg_rw *)pdata_buf; + switch (cmd->command) { + case HostCmd_CMD_MAC_REG_ACCESS: + { + HostCmd_DS_MAC_REG_ACCESS *mac_reg; + cmd->size = + wlan_cpu_to_le16(sizeof + (HostCmd_DS_MAC_REG_ACCESS) + + S_DS_GEN); + mac_reg = + (HostCmd_DS_MAC_REG_ACCESS *)&cmd->params. + mac_reg; + mac_reg->action = wlan_cpu_to_le16(cmd_action); + mac_reg->offset = + wlan_cpu_to_le16((t_u16)reg_rw->offset); + mac_reg->value = wlan_cpu_to_le32(reg_rw->value); + break; + } + case HostCmd_CMD_BBP_REG_ACCESS: + { + HostCmd_DS_BBP_REG_ACCESS *bbp_reg; + cmd->size = + wlan_cpu_to_le16(sizeof + (HostCmd_DS_BBP_REG_ACCESS) + + S_DS_GEN); + bbp_reg = + (HostCmd_DS_BBP_REG_ACCESS *)&cmd->params. + bbp_reg; + bbp_reg->action = wlan_cpu_to_le16(cmd_action); + bbp_reg->offset = + wlan_cpu_to_le16((t_u16)reg_rw->offset); + bbp_reg->value = (t_u8)reg_rw->value; + break; + } + case HostCmd_CMD_RF_REG_ACCESS: + { + HostCmd_DS_RF_REG_ACCESS *rf_reg; + cmd->size = + wlan_cpu_to_le16(sizeof + (HostCmd_DS_RF_REG_ACCESS) + + S_DS_GEN); + rf_reg = (HostCmd_DS_RF_REG_ACCESS *)&cmd->params. + rf_reg; + rf_reg->action = wlan_cpu_to_le16(cmd_action); + rf_reg->offset = + wlan_cpu_to_le16((t_u16)reg_rw->offset); + rf_reg->value = (t_u8)reg_rw->value; + break; + } + case HostCmd_CMD_CAU_REG_ACCESS: + { + HostCmd_DS_RF_REG_ACCESS *cau_reg; + cmd->size = + wlan_cpu_to_le16(sizeof + (HostCmd_DS_RF_REG_ACCESS) + + S_DS_GEN); + cau_reg = + (HostCmd_DS_RF_REG_ACCESS *)&cmd->params.rf_reg; + cau_reg->action = wlan_cpu_to_le16(cmd_action); + cau_reg->offset = + wlan_cpu_to_le16((t_u16)reg_rw->offset); + cau_reg->value = (t_u8)reg_rw->value; + break; + } + case HostCmd_CMD_TARGET_ACCESS: + { + HostCmd_DS_TARGET_ACCESS *target; + cmd->size = + wlan_cpu_to_le16(sizeof + (HostCmd_DS_TARGET_ACCESS) + + S_DS_GEN); + target = (HostCmd_DS_TARGET_ACCESS *)&cmd->params. + target; + target->action = wlan_cpu_to_le16(cmd_action); + target->csu_target = + wlan_cpu_to_le16(MLAN_CSU_TARGET_PSU); + target->address = + wlan_cpu_to_le16((t_u16)reg_rw->offset); + target->data = (t_u8)reg_rw->value; + break; + } + case HostCmd_CMD_802_11_EEPROM_ACCESS: + { + mlan_ds_read_eeprom *rd_eeprom = + (mlan_ds_read_eeprom *)pdata_buf; + HostCmd_DS_802_11_EEPROM_ACCESS *cmd_eeprom = + (HostCmd_DS_802_11_EEPROM_ACCESS *)&cmd->params. + eeprom; + cmd->size = + wlan_cpu_to_le16(sizeof + (HostCmd_DS_802_11_EEPROM_ACCESS) + + S_DS_GEN); + cmd_eeprom->action = wlan_cpu_to_le16(cmd_action); + cmd_eeprom->offset = + wlan_cpu_to_le16(rd_eeprom->offset); + cmd_eeprom->byte_count = + wlan_cpu_to_le16(rd_eeprom->byte_count); + cmd_eeprom->value = 0; + break; + } + default: + LEAVE(); + return MLAN_STATUS_FAILURE; + } + cmd->command = wlan_cpu_to_le16(cmd->command); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of reg_access + * + * @param pmadapter A pointer to mlan_adapter structure + * @param type The type of reg access (MAC, BBP or RF) + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to command buffer + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_ret_reg_access(mlan_adapter *pmadapter, + t_u16 type, + IN HostCmd_DS_COMMAND *resp, IN mlan_ioctl_req *pioctl_buf) +{ + mlan_ds_reg_mem *reg_mem = MNULL; + mlan_ds_reg_rw *reg_rw = MNULL; + + ENTER(); + + if (pioctl_buf) { + reg_mem = (mlan_ds_reg_mem *)pioctl_buf->pbuf; + reg_rw = ®_mem->param.reg_rw; + switch (type) { + case HostCmd_CMD_MAC_REG_ACCESS: + { + HostCmd_DS_MAC_REG_ACCESS *reg; + reg = (HostCmd_DS_MAC_REG_ACCESS *)&resp-> + params.mac_reg; + reg_rw->offset = + (t_u32)wlan_le16_to_cpu(reg->offset); + reg_rw->value = wlan_le32_to_cpu(reg->value); + break; + } + case HostCmd_CMD_BBP_REG_ACCESS: + { + HostCmd_DS_BBP_REG_ACCESS *reg; + reg = (HostCmd_DS_BBP_REG_ACCESS *)&resp-> + params.bbp_reg; + reg_rw->offset = + (t_u32)wlan_le16_to_cpu(reg->offset); + reg_rw->value = (t_u32)reg->value; + break; + } + + case HostCmd_CMD_RF_REG_ACCESS: + { + HostCmd_DS_RF_REG_ACCESS *reg; + reg = (HostCmd_DS_RF_REG_ACCESS *)&resp->params. + rf_reg; + reg_rw->offset = + (t_u32)wlan_le16_to_cpu(reg->offset); + reg_rw->value = (t_u32)reg->value; + break; + } + case HostCmd_CMD_CAU_REG_ACCESS: + { + HostCmd_DS_RF_REG_ACCESS *reg; + reg = (HostCmd_DS_RF_REG_ACCESS *)&resp->params. + rf_reg; + reg_rw->offset = + (t_u32)wlan_le16_to_cpu(reg->offset); + reg_rw->value = (t_u32)reg->value; + break; + } + case HostCmd_CMD_TARGET_ACCESS: + { + HostCmd_DS_TARGET_ACCESS *reg; + reg = (HostCmd_DS_TARGET_ACCESS *)&resp->params. + target; + reg_rw->offset = + (t_u32)wlan_le16_to_cpu(reg->address); + reg_rw->value = (t_u32)reg->data; + break; + } + case HostCmd_CMD_802_11_EEPROM_ACCESS: + { + mlan_ds_read_eeprom *eeprom = + ®_mem->param.rd_eeprom; + HostCmd_DS_802_11_EEPROM_ACCESS *cmd_eeprom = + (HostCmd_DS_802_11_EEPROM_ACCESS *) + &resp->params.eeprom; + cmd_eeprom->byte_count = + wlan_le16_to_cpu(cmd_eeprom-> + byte_count); + PRINTM(MINFO, "EEPROM read len=%x\n", + cmd_eeprom->byte_count); + if (eeprom->byte_count < cmd_eeprom->byte_count) { + eeprom->byte_count = 0; + PRINTM(MINFO, + "EEPROM read return length is too big\n"); + pioctl_buf->status_code = + MLAN_ERROR_CMD_RESP_FAIL; + LEAVE(); + return MLAN_STATUS_FAILURE; + } + eeprom->offset = + wlan_le16_to_cpu(cmd_eeprom->offset); + eeprom->byte_count = cmd_eeprom->byte_count; + if (eeprom->byte_count > 0) { + memcpy(pmadapter, &eeprom->value, + &cmd_eeprom->value, + MIN(MAX_EEPROM_DATA, + eeprom->byte_count)); + HEXDUMP("EEPROM", + (char *)&eeprom->value, + MIN(MAX_EEPROM_DATA, + eeprom->byte_count)); + } + break; + } + default: + pioctl_buf->status_code = MLAN_ERROR_CMD_RESP_FAIL; + LEAVE(); + return MLAN_STATUS_FAILURE; + } + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of mem_access. + * + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action the action: GET or SET + * @param pdata_buf A pointer to data buffer + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_cmd_mem_access(IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, IN t_void *pdata_buf) +{ + mlan_ds_mem_rw *mem_rw = (mlan_ds_mem_rw *)pdata_buf; + HostCmd_DS_MEM_ACCESS *mem_access = + (HostCmd_DS_MEM_ACCESS *)&cmd->params.mem; + + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_MEM_ACCESS); + cmd->size = wlan_cpu_to_le16(sizeof(HostCmd_DS_MEM_ACCESS) + S_DS_GEN); + + mem_access->action = wlan_cpu_to_le16(cmd_action); + mem_access->addr = wlan_cpu_to_le32(mem_rw->addr); + mem_access->value = wlan_cpu_to_le32(mem_rw->value); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of mem_access + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to command buffer + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_ret_mem_access(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, IN mlan_ioctl_req *pioctl_buf) +{ + mlan_ds_reg_mem *reg_mem = MNULL; + mlan_ds_mem_rw *mem_rw = MNULL; + HostCmd_DS_MEM_ACCESS *mem = (HostCmd_DS_MEM_ACCESS *)&resp->params.mem; + + ENTER(); + + if (pioctl_buf) { + reg_mem = (mlan_ds_reg_mem *)pioctl_buf->pbuf; + mem_rw = ®_mem->param.mem_rw; + + mem_rw->addr = wlan_le32_to_cpu(mem->addr); + mem_rw->value = wlan_le32_to_cpu(mem->value); + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** +* +* @brief This function handles coex events generated by firmware +* +* @param priv A pointer to mlan_private structure +* @param pevent A pointer to event buf +* +* @return N/A +*/ +void +wlan_bt_coex_wlan_param_update_event(pmlan_private priv, pmlan_buffer pevent) +{ + pmlan_adapter pmadapter = priv->adapter; + MrvlIEtypesHeader_t *tlv = MNULL; + MrvlIETypes_BtCoexAggrWinSize_t *pCoexWinsize = MNULL; + MrvlIEtypes_BtCoexScanTime_t *pScantlv = MNULL; + t_s32 len = pevent->data_len - sizeof(t_u32); + t_u8 *pCurrent_ptr = pevent->pbuf + pevent->data_offset + sizeof(t_u32); + t_u16 tlv_type, tlv_len; + + ENTER(); + + while (len >= sizeof(MrvlIEtypesHeader_t)) { + tlv = (MrvlIEtypesHeader_t *)pCurrent_ptr; + tlv_len = wlan_le16_to_cpu(tlv->len); + tlv_type = wlan_le16_to_cpu(tlv->type); + if ((tlv_len + sizeof(MrvlIEtypesHeader_t)) > len) + break; + switch (tlv_type) { + case TLV_BTCOEX_WL_AGGR_WINSIZE: + pCoexWinsize = (MrvlIETypes_BtCoexAggrWinSize_t *) tlv; + pmadapter->coex_win_size = pCoexWinsize->coex_win_size; + pmadapter->coex_tx_win_size = pCoexWinsize->tx_win_size; + pmadapter->coex_rx_win_size = pCoexWinsize->rx_win_size; + wlan_coex_ampdu_rxwinsize(pmadapter); + wlan_update_ampdu_txwinsize(pmadapter); + break; + case TLV_BTCOEX_WL_SCANTIME: + pScantlv = (MrvlIEtypes_BtCoexScanTime_t *) tlv; + pmadapter->coex_scan = pScantlv->coex_scan; + pmadapter->coex_min_scan_time = + wlan_le16_to_cpu(pScantlv->min_scan_time); + pmadapter->coex_max_scan_time = + wlan_le16_to_cpu(pScantlv->max_scan_time); + break; + default: + break; + } + len -= tlv_len + sizeof(MrvlIEtypesHeader_t); + pCurrent_ptr += tlv_len + sizeof(MrvlIEtypesHeader_t); + } + PRINTM(MEVENT, + "coex_scan=%d min_scan=%d coex_win=%d, tx_win=%d rx_win=%d\n", + pmadapter->coex_scan, pmadapter->coex_min_scan_time, + pmadapter->coex_win_size, pmadapter->coex_tx_win_size, + pmadapter->coex_rx_win_size); + + LEAVE(); +} + +/** + * @brief This function prepares command of supplicant pmk + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action The action: GET or SET + * @param pdata_buf A pointer to data buffer + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_cmd_802_11_supplicant_pmk(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, IN t_void *pdata_buf) +{ + MrvlIEtypes_PMK_t *ppmk_tlv = MNULL; + MrvlIEtypes_Passphrase_t *ppassphrase_tlv = MNULL; + MrvlIEtypes_SsIdParamSet_t *pssid_tlv = MNULL; + MrvlIEtypes_Bssid_t *pbssid_tlv = MNULL; + HostCmd_DS_802_11_SUPPLICANT_PMK *pesupplicant_psk = + &cmd->params.esupplicant_psk; + t_u8 *ptlv_buffer = (t_u8 *)pesupplicant_psk->tlv_buffer; + mlan_ds_sec_cfg *sec = (mlan_ds_sec_cfg *)pdata_buf; + mlan_ds_passphrase *psk = MNULL; + t_u8 zero_mac[] = { 0, 0, 0, 0, 0, 0 }; + t_u8 ssid_flag = 0, bssid_flag = 0, pmk_flag = 0, passphrase_flag = 0; + t_u8 zero[MLAN_MAX_KEY_LENGTH] = { 0 }; + MrvlIEtypes_fw_roam_enable_t *proam_tlv = MNULL; + MrvlIEtypes_keyParams_t *key_tlv = MNULL; + int length = 0; + t_u8 userset_passphrase = 0; + + ENTER(); + if (sec->multi_passphrase) + psk = (mlan_ds_passphrase *)&sec->param. + roam_passphrase[userset_passphrase]; + else + psk = (mlan_ds_passphrase *)&sec->param.passphrase; + if (cmd_action == HostCmd_ACT_GEN_REMOVE) { + cmd->size = + sizeof(HostCmd_DS_802_11_SUPPLICANT_PMK) + S_DS_GEN - 1; + proam_tlv = (MrvlIEtypes_fw_roam_enable_t *) ptlv_buffer; + proam_tlv->header.type = wlan_cpu_to_le16(TLV_TYPE_ROAM); + proam_tlv->header.len = + sizeof(MrvlIEtypes_fw_roam_enable_t) - + sizeof(MrvlIEtypesHeader_t); + proam_tlv->roam_enable = MTRUE; + ptlv_buffer += + (proam_tlv->header.len + sizeof(MrvlIEtypesHeader_t)); + cmd->size += + (proam_tlv->header.len + sizeof(MrvlIEtypesHeader_t)); + proam_tlv->header.len = wlan_cpu_to_le16(proam_tlv->header.len); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_SUPPLICANT_PMK); + pesupplicant_psk->action = wlan_cpu_to_le16(cmd_action); + pesupplicant_psk->cache_result = 0; + cmd->size = wlan_cpu_to_le16(cmd->size); + LEAVE(); + return MLAN_STATUS_SUCCESS; + } + + /* + * Parse the rest of the buf here + * 1) - This will get the passphrase, AKMP + * for specified ssid, if none specified then it will get all. + * Eg: iwpriv passphrase 0:ssid=marvell + * 2) :: + * - passphrase and psk cannot be provided to + * the same SSID, Takes one SSID at a time, If ssid= is present + * the it should contain a passphrase or psk. If no arguments are + * provided then AKMP=802.1x, and passphrase should be provided + * after association. + * End of each parameter should be followed by a ':'(except for the + * last parameter) as the delimiter. If ':' has to be used in + * an SSID then a '/' should be preceded to ':' as a escape. + * Eg:iwpriv passphrase + * "1:ssid=mrvl AP:psk=abcdefgh:bssid=00:50:43:ef:23:f3" + * iwpriv passphrase + * "1:ssid=mrvl/: AP:psk=abcdefgd:bssid=00:50:43:ef:23:f3" + * iwpriv passphrase "1:ssid=mrvlAP:psk=abcdefgd" + * 3) - This will clear the passphrase + * for specified ssid, if none specified then it will clear all. + * Eg: iwpriv passphrase 2:ssid=marvell + */ + + /* -1 is for t_u8 TlvBuffer[1] as this should not be included */ + cmd->size = sizeof(HostCmd_DS_802_11_SUPPLICANT_PMK) + S_DS_GEN - 1; + if (psk && + memcmp(pmpriv->adapter, (t_u8 *)&psk->bssid, zero_mac, + sizeof(zero_mac))) { + pbssid_tlv = (MrvlIEtypes_Bssid_t *)ptlv_buffer; + pbssid_tlv->header.type = wlan_cpu_to_le16(TLV_TYPE_BSSID); + pbssid_tlv->header.len = MLAN_MAC_ADDR_LENGTH; + memcpy(pmpriv->adapter, pbssid_tlv->bssid, (t_u8 *)&psk->bssid, + MLAN_MAC_ADDR_LENGTH); + ptlv_buffer += + (pbssid_tlv->header.len + sizeof(MrvlIEtypesHeader_t)); + cmd->size += + (pbssid_tlv->header.len + sizeof(MrvlIEtypesHeader_t)); + pbssid_tlv->header.len = + wlan_cpu_to_le16(pbssid_tlv->header.len); + bssid_flag = 1; + } + if (psk && (psk->psk_type == MLAN_PSK_PMK)) { + ppmk_tlv = (MrvlIEtypes_PMK_t *)ptlv_buffer; + ppmk_tlv->header.type = wlan_cpu_to_le16(TLV_TYPE_PMK); + ppmk_tlv->header.len = MLAN_MAX_KEY_LENGTH; + memcpy(pmpriv->adapter, ppmk_tlv->pmk, psk->psk.pmk.pmk, + MLAN_MAX_KEY_LENGTH); + ptlv_buffer += + (ppmk_tlv->header.len + sizeof(MrvlIEtypesHeader_t)); + cmd->size += + (ppmk_tlv->header.len + sizeof(MrvlIEtypesHeader_t)); + ppmk_tlv->header.len = wlan_cpu_to_le16(ppmk_tlv->header.len); + pmk_flag = 1; + if (memcmp + (pmpriv->adapter, psk->psk.pmk.pmk_r0, zero, + MLAN_MAX_KEY_LENGTH)) { + ppmk_tlv = (MrvlIEtypes_PMK_t *)ptlv_buffer; + ppmk_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_PMK_R0); + ppmk_tlv->header.len = MLAN_MAX_KEY_LENGTH; + memcpy(pmpriv->adapter, ppmk_tlv->pmk, + psk->psk.pmk.pmk_r0, MLAN_MAX_KEY_LENGTH); + ptlv_buffer += + (ppmk_tlv->header.len + + sizeof(MrvlIEtypesHeader_t)); + cmd->size += + (ppmk_tlv->header.len + + sizeof(MrvlIEtypesHeader_t)); + ppmk_tlv->header.len = + wlan_cpu_to_le16(ppmk_tlv->header.len); + } + if (memcmp + (pmpriv->adapter, psk->psk.pmk.pmk_r0_name, zero, + MLAN_MAX_PMKR0_NAME_LENGTH)) { + ppmk_tlv = (MrvlIEtypes_PMK_t *)ptlv_buffer; + ppmk_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_PMK_R0_NAME); + ppmk_tlv->header.len = MLAN_MAX_PMKR0_NAME_LENGTH; + memcpy(pmpriv->adapter, ppmk_tlv->pmk, + psk->psk.pmk.pmk_r0_name, + MLAN_MAX_PMKR0_NAME_LENGTH); + ptlv_buffer += + (ppmk_tlv->header.len + + sizeof(MrvlIEtypesHeader_t)); + cmd->size += + (ppmk_tlv->header.len + + sizeof(MrvlIEtypesHeader_t)); + ppmk_tlv->header.len = + wlan_cpu_to_le16(ppmk_tlv->header.len); + } + } + if (pmpriv->adapter->fw_roaming && + (pmpriv->adapter->userset_passphrase || + psk->psk_type == MLAN_PSK_PMK)) { + proam_tlv = (MrvlIEtypes_fw_roam_enable_t *) ptlv_buffer; + proam_tlv->header.type = wlan_cpu_to_le16(TLV_TYPE_ROAM); + proam_tlv->header.len = + sizeof(MrvlIEtypes_fw_roam_enable_t) - + sizeof(MrvlIEtypesHeader_t); + proam_tlv->roam_enable = MTRUE; + proam_tlv->userset_passphrase = + pmpriv->adapter->userset_passphrase; + ptlv_buffer += + (proam_tlv->header.len + sizeof(MrvlIEtypesHeader_t)); + cmd->size += + (proam_tlv->header.len + sizeof(MrvlIEtypesHeader_t)); + proam_tlv->header.len = wlan_cpu_to_le16(proam_tlv->header.len); + } + do { + if (pmpriv->adapter->userset_passphrase && + sec->multi_passphrase) { + key_tlv = (MrvlIEtypes_keyParams_t *) ptlv_buffer; + key_tlv->header.type = + wlan_cpu_to_le16 + (TLV_TYPE_ROAM_OFFLOAD_USER_SET_PMK); + ptlv_buffer += sizeof(MrvlIEtypesHeader_t); + cmd->size += sizeof(MrvlIEtypesHeader_t); + length = cmd->size; + } + if (psk->ssid.ssid_len) { + pssid_tlv = (MrvlIEtypes_SsIdParamSet_t *)ptlv_buffer; + pssid_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_SSID); + pssid_tlv->header.len = + (t_u16)MIN(MLAN_MAX_SSID_LENGTH, + psk->ssid.ssid_len); + memcpy(pmpriv->adapter, (t_u8 *)pssid_tlv->ssid, + (t_u8 *)psk->ssid.ssid, MIN(MLAN_MAX_SSID_LENGTH, + psk->ssid.ssid_len)); + ptlv_buffer += + (pssid_tlv->header.len + + sizeof(MrvlIEtypesHeader_t)); + cmd->size += + (pssid_tlv->header.len + + sizeof(MrvlIEtypesHeader_t)); + pssid_tlv->header.len = + wlan_cpu_to_le16(pssid_tlv->header.len); + ssid_flag = 1; + } + if (psk->psk_type == MLAN_PSK_PASSPHRASE) { + ppassphrase_tlv = + (MrvlIEtypes_Passphrase_t *)ptlv_buffer; + ppassphrase_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_PASSPHRASE); + ppassphrase_tlv->header.len = + (t_u16)MIN(MLAN_MAX_PASSPHRASE_LENGTH, + psk->psk.passphrase.passphrase_len); + memcpy(pmpriv->adapter, ppassphrase_tlv->passphrase, + psk->psk.passphrase.passphrase, + MIN(MLAN_MAX_PASSPHRASE_LENGTH, + psk->psk.passphrase.passphrase_len)); + ptlv_buffer += + (ppassphrase_tlv->header.len + + sizeof(MrvlIEtypesHeader_t)); + cmd->size += + (ppassphrase_tlv->header.len + + sizeof(MrvlIEtypesHeader_t)); + ppassphrase_tlv->header.len = + wlan_cpu_to_le16(ppassphrase_tlv->header.len); + passphrase_flag = 1; + } + if (key_tlv) + key_tlv->header.len = + wlan_cpu_to_le16(cmd->size - length); + userset_passphrase++; + psk = (mlan_ds_passphrase *)&sec->param. + roam_passphrase[userset_passphrase]; + } while (psk && sec->multi_passphrase && + userset_passphrase < pmpriv->adapter->userset_passphrase); + pmpriv->adapter->userset_passphrase = 0; + if ((cmd_action == HostCmd_ACT_GEN_SET) && + ((ssid_flag || bssid_flag) && (!pmk_flag && !passphrase_flag))) { + PRINTM(MERROR, + "Invalid case,ssid/bssid present without pmk or passphrase\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_SUPPLICANT_PMK); + pesupplicant_psk->action = wlan_cpu_to_le16(cmd_action); + pesupplicant_psk->cache_result = 0; + cmd->size = wlan_cpu_to_le16(cmd->size); + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Handle the supplicant pmk response + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS MLAN_STATUS_FAILURE + */ +mlan_status +wlan_ret_802_11_supplicant_pmk(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf) +{ + HostCmd_DS_802_11_SUPPLICANT_PMK *supplicant_pmk_resp = + &resp->params.esupplicant_psk; + mlan_ds_sec_cfg sec_buf; + mlan_ds_sec_cfg *sec = MNULL; + MrvlIEtypes_PMK_t *ppmk_tlv = MNULL; + MrvlIEtypes_Passphrase_t *passphrase_tlv = MNULL; + MrvlIEtypes_SsIdParamSet_t *pssid_tlv = MNULL; + MrvlIEtypes_Bssid_t *pbssid_tlv = MNULL; + t_u8 *tlv_buf = (t_u8 *)supplicant_pmk_resp->tlv_buffer; + t_u16 action = wlan_le16_to_cpu(supplicant_pmk_resp->action); + int tlv_buf_len = 0; + t_u16 tlv; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + tlv_buf_len = resp->size - (sizeof(HostCmd_DS_802_11_SUPPLICANT_PMK) + + S_DS_GEN - 1); + if (pioctl_buf) { + if (((mlan_ds_bss *)pioctl_buf->pbuf)->sub_command == + MLAN_OID_BSS_FIND_BSS) + sec = &sec_buf; + else + sec = (mlan_ds_sec_cfg *)pioctl_buf->pbuf; + if (action == HostCmd_ACT_GEN_GET) { + while (tlv_buf_len > 0) { + tlv = (*tlv_buf) | (*(tlv_buf + 1) << 8); + if ((tlv != TLV_TYPE_SSID) && + (tlv != TLV_TYPE_BSSID) && + (tlv != TLV_TYPE_PASSPHRASE) + && (tlv != TLV_TYPE_PMK)) + break; + switch (tlv) { + case TLV_TYPE_SSID: + pssid_tlv = + (MrvlIEtypes_SsIdParamSet_t *) + tlv_buf; + pssid_tlv->header.len = + wlan_le16_to_cpu(pssid_tlv-> + header.len); + memcpy(pmpriv->adapter, + sec->param.passphrase.ssid.ssid, + pssid_tlv->ssid, + MIN(MLAN_MAX_SSID_LENGTH, + pssid_tlv->header.len)); + sec->param.passphrase.ssid.ssid_len = + MIN(MLAN_MAX_SSID_LENGTH, + pssid_tlv->header.len); + tlv_buf += + pssid_tlv->header.len + + sizeof(MrvlIEtypesHeader_t); + tlv_buf_len -= + (pssid_tlv->header.len + + sizeof(MrvlIEtypesHeader_t)); + break; + case TLV_TYPE_BSSID: + pbssid_tlv = + (MrvlIEtypes_Bssid_t *)tlv_buf; + pbssid_tlv->header.len = + wlan_le16_to_cpu(pbssid_tlv-> + header.len); + memcpy(pmpriv->adapter, + &sec->param.passphrase.bssid, + pbssid_tlv->bssid, + MLAN_MAC_ADDR_LENGTH); + tlv_buf += + pbssid_tlv->header.len + + sizeof(MrvlIEtypesHeader_t); + tlv_buf_len -= + (pbssid_tlv->header.len + + sizeof(MrvlIEtypesHeader_t)); + break; + case TLV_TYPE_PASSPHRASE: + passphrase_tlv = + (MrvlIEtypes_Passphrase_t *) + tlv_buf; + passphrase_tlv->header.len = + wlan_le16_to_cpu + (passphrase_tlv->header.len); + sec->param.passphrase.psk_type = + MLAN_PSK_PASSPHRASE; + sec->param.passphrase.psk.passphrase. + passphrase_len = + passphrase_tlv->header.len; + memcpy(pmpriv->adapter, + sec->param.passphrase.psk. + passphrase.passphrase, + passphrase_tlv->passphrase, + MIN(MLAN_MAX_PASSPHRASE_LENGTH, + passphrase_tlv->header.len)); + tlv_buf += + passphrase_tlv->header.len + + sizeof(MrvlIEtypesHeader_t); + tlv_buf_len -= + (passphrase_tlv->header.len + + sizeof(MrvlIEtypesHeader_t)); + break; + case TLV_TYPE_PMK: + ppmk_tlv = (MrvlIEtypes_PMK_t *)tlv_buf; + ppmk_tlv->header.len = + wlan_le16_to_cpu(ppmk_tlv-> + header.len); + sec->param.passphrase.psk_type = + MLAN_PSK_PMK; + memcpy(pmpriv->adapter, + sec->param.passphrase.psk.pmk. + pmk, ppmk_tlv->pmk, + MIN(MLAN_MAX_KEY_LENGTH, + ppmk_tlv->header.len)); + tlv_buf += + ppmk_tlv->header.len + + sizeof(MrvlIEtypesHeader_t); + tlv_buf_len -= + (ppmk_tlv->header.len + + sizeof(MrvlIEtypesHeader_t)); + break; + + } + } +#ifdef STA_SUPPORT + if (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_STA && + ((mlan_ds_bss *)pioctl_buf->pbuf)->sub_command == + MLAN_OID_BSS_FIND_BSS) { + wlan_set_ewpa_mode(pmpriv, + &sec->param.passphrase); + ret = wlan_find_bss(pmpriv, pioctl_buf); + } +#endif + } else if (action == HostCmd_ACT_GEN_SET) { + PRINTM(MINFO, "Esupp PMK set: enable ewpa query\n"); + pmpriv->ewpa_query = MTRUE; + } + } + + LEAVE(); + return ret; +} + +/** + * @brief This function prepares command of independent reset. + * + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action the action: GET or SET + * @param pdata_buf A pointer to data buffer + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_cmd_ind_rst_cfg(IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, IN t_void *pdata_buf) +{ + mlan_ds_ind_rst_cfg *pdata_ind_rst = (mlan_ds_ind_rst_cfg *) pdata_buf; + HostCmd_DS_INDEPENDENT_RESET_CFG *ind_rst_cfg = + (HostCmd_DS_INDEPENDENT_RESET_CFG *) & cmd->params.ind_rst_cfg; + + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_INDEPENDENT_RESET_CFG); + cmd->size = + wlan_cpu_to_le16(sizeof(HostCmd_DS_INDEPENDENT_RESET_CFG) + + S_DS_GEN); + + ind_rst_cfg->action = wlan_cpu_to_le16(cmd_action); + if (cmd_action == HostCmd_ACT_GEN_SET) { + ind_rst_cfg->ir_mode = pdata_ind_rst->ir_mode; + ind_rst_cfg->gpio_pin = pdata_ind_rst->gpio_pin; + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of independent reset + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to command buffer + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_ret_ind_rst_cfg(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, IN mlan_ioctl_req *pioctl_buf) +{ + mlan_ds_misc_cfg *misc = MNULL; + const HostCmd_DS_INDEPENDENT_RESET_CFG *ind_rst_cfg = + (HostCmd_DS_INDEPENDENT_RESET_CFG *) & resp->params.ind_rst_cfg; + + ENTER(); + + if (pioctl_buf) { + misc = (mlan_ds_misc_cfg *)pioctl_buf->pbuf; + + if (wlan_le16_to_cpu(ind_rst_cfg->action) == + HostCmd_ACT_GEN_GET) { + misc->param.ind_rst_cfg.ir_mode = ind_rst_cfg->ir_mode; + misc->param.ind_rst_cfg.gpio_pin = + ind_rst_cfg->gpio_pin; + } + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of ps inactivity timeout. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action the action: GET or SET + * @param pdata_buf A pointer to data buffer + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_cmd_ps_inactivity_timeout(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, IN t_void *pdata_buf) +{ + t_u16 timeout = *((t_u16 *)pdata_buf); + HostCmd_DS_802_11_PS_INACTIVITY_TIMEOUT *ps_inact_tmo = + (HostCmd_DS_802_11_PS_INACTIVITY_TIMEOUT *) & cmd->params. + ps_inact_tmo; + + ENTER(); + + cmd->command = + wlan_cpu_to_le16(HostCmd_CMD_802_11_PS_INACTIVITY_TIMEOUT); + cmd->size = + wlan_cpu_to_le16(sizeof(HostCmd_DS_802_11_PS_INACTIVITY_TIMEOUT) + + S_DS_GEN); + + ps_inact_tmo->action = wlan_cpu_to_le16(cmd_action); + if (cmd_action == HostCmd_ACT_GEN_SET) + ps_inact_tmo->inact_tmo = wlan_cpu_to_le16(timeout); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of HostCmd_CMD_GET_TSF + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action The action: GET + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_cmd_get_tsf(pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, IN t_u16 cmd_action) +{ + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_GET_TSF); + cmd->size = wlan_cpu_to_le16((sizeof(HostCmd_DS_TSF)) + S_DS_GEN); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of HostCmd_CMD_GET_TSF + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_ret_get_tsf(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, IN mlan_ioctl_req *pioctl_buf) +{ + mlan_ds_misc_cfg *misc_cfg = MNULL; + HostCmd_DS_TSF *tsf_pointer = (HostCmd_DS_TSF *) & resp->params.tsf; + + ENTER(); + if (pioctl_buf) { + misc_cfg = (mlan_ds_misc_cfg *)pioctl_buf->pbuf; + misc_cfg->param.misc_tsf = wlan_le64_to_cpu(tsf_pointer->tsf); + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of sending host_clock_cfg. + * + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action the action: GET or SET + * @param pdata_buf A pointer to data buffer + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_cmd_host_clock_cfg(IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, IN t_void *pdata_buf) +{ + mlan_ds_host_clock *hostclk = (mlan_ds_host_clock *) pdata_buf; + HostCmd_DS_HOST_CLOCK_CFG *host_clock = + (HostCmd_DS_HOST_CLOCK_CFG *) & cmd->params.host_clock_cfg; + + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_HOST_CLOCK_CFG); + cmd->size = + wlan_cpu_to_le16(sizeof(HostCmd_DS_HOST_CLOCK_CFG) + S_DS_GEN); + + host_clock->action = wlan_cpu_to_le16(cmd_action); + host_clock->time = wlan_cpu_to_le64(hostclk->time); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of host_clock_cfg + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to command buffer + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_ret_host_clock_cfg(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf) +{ + mlan_ds_misc_cfg *cfg = MNULL; + mlan_ds_host_clock *hostclk = MNULL; + HostCmd_DS_HOST_CLOCK_CFG *host_clock = + (HostCmd_DS_HOST_CLOCK_CFG *) & resp->params.host_clock_cfg; + mlan_adapter *pmadapter = pmpriv->adapter; + t_u64 cmd_rtt; + + ENTER(); + + if (pioctl_buf) { + cfg = (mlan_ds_misc_cfg *)pioctl_buf->pbuf; + hostclk = &cfg->param.host_clock; + + hostclk->time = wlan_le64_to_cpu(host_clock->time); + hostclk->fw_time = wlan_le64_to_cpu(host_clock->time); + cmd_rtt = (pmadapter->d2 - pmadapter->d1) / 2; + pmadapter->host_bbu_clk_delta = + wlan_le64_to_cpu(host_clock->host_bbu_clk_delta); + PRINTM(MINFO, "HW time: %ld, Host Time: %ld, RTT: %ld\n", + host_clock->hw_time, hostclk->time, cmd_rtt); + hostclk->fw_time = wlan_le64_to_cpu(host_clock->hw_time) /*- cmd_rtt*/ ; // Not adjusting cmd_rtt gave better results with 802.1as + hostclk->host_bbu_clk_delta = pmadapter->host_bbu_clk_delta; + + /* Indicate ioctl complete */ + pioctl_buf->data_read_written = + sizeof(mlan_ds_misc_cfg) + MLAN_SUB_COMMAND_SIZE; + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of chan_region_cfg + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to command buffer + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_ret_chan_region_cfg(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf) +{ + t_u16 action; + t_u16 tlv, tlv_buf_len, tlv_buf_left; + MrvlIEtypesHeader_t *head; + HostCmd_DS_CHAN_REGION_CFG *reg = MNULL; + t_u8 *tlv_buf = MNULL; + mlan_ds_misc_cfg *misc_cfg = MNULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + reg = (HostCmd_DS_CHAN_REGION_CFG *) & resp->params.reg_cfg; + if (!reg) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + action = wlan_le16_to_cpu(reg->action); + if (action != HostCmd_ACT_GEN_GET) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + tlv_buf = (t_u8 *)reg + sizeof(*reg); + tlv_buf_left = wlan_le16_to_cpu(resp->size) - S_DS_GEN - sizeof(*reg); + + /* Add FW cfp tables and region info */ + wlan_add_fw_cfp_tables(pmpriv, tlv_buf, tlv_buf_left); + + if (!pioctl_buf) + goto done; + + if (!pioctl_buf->pbuf) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + misc_cfg = (mlan_ds_misc_cfg *)pioctl_buf->pbuf; + + while (tlv_buf_left >= sizeof(*head)) { + head = (MrvlIEtypesHeader_t *)tlv_buf; + tlv = wlan_le16_to_cpu(head->type); + tlv_buf_len = wlan_le16_to_cpu(head->len); + + if (tlv_buf_left < (sizeof(*head) + tlv_buf_len)) + break; + + switch (tlv) { + case TLV_TYPE_CHAN_ATTR_CFG: + DBG_HEXDUMP(MCMD_D, "CHAN:", + (t_u8 *)head + sizeof(*head), tlv_buf_left); + if (tlv_buf_len > + misc_cfg->param.custom_reg_domain.cfg_len) { + tlv_buf_len = + misc_cfg->param.custom_reg_domain. + cfg_len; + } + misc_cfg->param.custom_reg_domain.cfg_len = tlv_buf_len; + memcpy(pmpriv->adapter, + misc_cfg->param.custom_reg_domain.cfg_buf, + (t_u8 *)head + sizeof(*head), tlv_buf_len); + pioctl_buf->data_read_written = tlv_buf_len; + break; + } + + tlv_buf += (sizeof(*head) + tlv_buf_len); + tlv_buf_left -= (sizeof(*head) + tlv_buf_len); + } +done: + LEAVE(); + return ret; +} + +#if defined(SYSKT_MULTI) && defined(OOB_WAKEUP) || defined(SUSPEND_SDIO_PULL_DOWN) +/** + * @brief This function prepares command of sdio_pull_ctl + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action The action: GET or SET + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_cmd_sdio_pull_ctl(pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, IN t_u16 cmd_action) +{ + HostCmd_DS_SDIO_PULL_CTRL *pull_ctrl = &cmd->params.sdio_pull_ctl; + + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_SDIO_PULL_CTRL); + cmd->size = + wlan_cpu_to_le16((sizeof(HostCmd_DS_SDIO_PULL_CTRL)) + + S_DS_GEN); + + memset(pmpriv->adapter, pull_ctrl, 0, + sizeof(HostCmd_DS_SDIO_PULL_CTRL)); + pull_ctrl->action = wlan_cpu_to_le16(cmd_action); + if (cmd_action == HostCmd_ACT_GEN_SET) { + pull_ctrl->pull_up = wlan_cpu_to_le16(DEFAULT_PULLUP_DELAY); + pull_ctrl->pull_down = wlan_cpu_to_le16(DEFAULT_PULLDOWN_DELAY); + pull_ctrl->gpio_pullup_req = DEFAULT_GPIO_PULLUP_REQ; + pull_ctrl->gpio_pullup_ack = DEFAULT_GPIO_ACK_PULLUP; + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} +#endif + +/** + * @brief This function sends fw dump event command to firmware. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd HostCmd_DS_COMMAND structure + * @param cmd_action the action: GET or SET + * @param pdata_buf A void pointer to information buffer + * @return N/A + */ +mlan_status +wlan_cmd_fw_dump_event(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, IN t_void *pdata_buf) +{ + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_FW_DUMP_EVENT); + cmd->size = S_DS_GEN; + cmd->size = wlan_cpu_to_le16(cmd->size); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function sends boot sleep configure command to firmware. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd Hostcmd ID + * @param cmd_action Command action + * @param pdata_buf A void pointer to information buffer + * @return MLAN_STATUS_SUCCESS/ MLAN_STATUS_FAILURE + */ +mlan_status +wlan_cmd_boot_sleep(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, IN t_void *pdata_buf) +{ + HostCmd_DS_BOOT_SLEEP *boot_sleep = MNULL; + t_u16 enable = *(t_u16 *)pdata_buf; + + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_BOOT_SLEEP); + boot_sleep = &cmd->params.boot_sleep; + boot_sleep->action = wlan_cpu_to_le16(cmd_action); + boot_sleep->enable = wlan_cpu_to_le16(enable); + + cmd->size = S_DS_GEN + sizeof(HostCmd_DS_BOOT_SLEEP); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of boot sleep cfg + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_ret_boot_sleep(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, IN mlan_ioctl_req *pioctl_buf) +{ + HostCmd_DS_BOOT_SLEEP *boot_sleep = &resp->params.boot_sleep; + mlan_ds_misc_cfg *cfg = (mlan_ds_misc_cfg *)pioctl_buf->pbuf; + + ENTER(); + + cfg->param.boot_sleep = wlan_le16_to_cpu(boot_sleep->enable); + PRINTM(MCMND, "boot sleep cfg status %u", cfg->param.boot_sleep); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_decl.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_decl.h new file mode 100644 index 000000000000..57bc23e81df6 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_decl.h @@ -0,0 +1,1441 @@ +/** @file mlan_decl.h + * + * @brief This file declares the generic data structures and APIs. + * + * Copyright (C) 2008-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 11/07/2008: initial version +******************************************************/ + +#ifndef _MLAN_DECL_H_ +#define _MLAN_DECL_H_ + +/** MLAN release version */ +#define MLAN_RELEASE_VERSION "C506" + +/** Re-define generic data types for MLAN/MOAL */ +/** Signed char (1-byte) */ +typedef signed char t_s8; +/** Unsigned char (1-byte) */ +typedef unsigned char t_u8; +/** Signed short (2-bytes) */ +typedef short t_s16; +/** Unsigned short (2-bytes) */ +typedef unsigned short t_u16; +/** Signed long (4-bytes) */ +typedef int t_s32; +/** Unsigned long (4-bytes) */ +typedef unsigned int t_u32; +/** Signed long long 8-bytes) */ +typedef long long t_s64; +/** Unsigned long long 8-bytes) */ +typedef unsigned long long t_u64; +/** Void pointer (4-bytes) */ +typedef void t_void; +/** Size type */ +typedef t_u32 t_size; +/** Boolean type */ +typedef t_u8 t_bool; + +#ifdef MLAN_64BIT +/** Pointer type (64-bit) */ +typedef t_u64 t_ptr; +/** Signed value (64-bit) */ +typedef t_s64 t_sval; +#else +/** Pointer type (32-bit) */ +typedef t_u32 t_ptr; +/** Signed value (32-bit) */ +typedef t_s32 t_sval; +#endif + +/** Constants below */ + +#ifdef __GNUC__ +/** Structure packing begins */ +#define MLAN_PACK_START +/** Structure packeing end */ +#define MLAN_PACK_END __attribute__((packed)) +#else /* !__GNUC__ */ +#ifdef PRAGMA_PACK +/** Structure packing begins */ +#define MLAN_PACK_START +/** Structure packeing end */ +#define MLAN_PACK_END +#else /* !PRAGMA_PACK */ +/** Structure packing begins */ +#define MLAN_PACK_START __packed +/** Structure packing end */ +#define MLAN_PACK_END +#endif /* PRAGMA_PACK */ +#endif /* __GNUC__ */ + +#ifndef INLINE +#ifdef __GNUC__ +/** inline directive */ +#define INLINE inline +#else +/** inline directive */ +#define INLINE __inline +#endif +#endif + +/** MLAN TRUE */ +#define MTRUE (1) +/** MLAN FALSE */ +#define MFALSE (0) + +#define CHANNEL_SPEC_SNIFFER_MODE 1 + +#ifndef MACSTR +/** MAC address security format */ +#define MACSTR "%02x:XX:XX:XX:%02x:%02x" +#endif + +#ifndef MAC2STR +/** MAC address security print arguments */ +#define MAC2STR(a) (a)[0], (a)[4], (a)[5] +#endif + +#ifndef FULL_MACSTR +#define FULL_MACSTR "%02x:%02x:%02x:%02x:%02x:%02x" +#endif +#ifndef FULL_MAC2STR +#define FULL_MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] +#endif + +/** Macros for Data Alignment : size */ +#define ALIGN_SZ(p, a) \ + (((p) + ((a) - 1)) & ~((a) - 1)) + +/** Macros for Data Alignment : address */ +#define ALIGN_ADDR(p, a) \ + ((((t_ptr)(p)) + (((t_ptr)(a)) - 1)) & ~(((t_ptr)(a)) - 1)) + +/** Return the byte offset of a field in the given structure */ +#define MLAN_FIELD_OFFSET(type, field) ((t_u32)(t_ptr)&(((type *)0)->field)) +/** Return aligned offset */ +#define OFFSET_ALIGN_ADDR(p, a) (t_u32)(ALIGN_ADDR(p, a) - (t_ptr)p) + +/** Maximum BSS numbers */ +#define MLAN_MAX_BSS_NUM (16) + +/** NET IP alignment */ +#define MLAN_NET_IP_ALIGN 2 + +/** DMA alignment */ +/* SDIO3.0 Inrevium Adapter require 32 bit DMA alignment */ +#define DMA_ALIGNMENT 32 + +/** max size of TxPD */ +#define MAX_TXPD_SIZE 32 + +/** Minimum data header length */ +#define MLAN_MIN_DATA_HEADER_LEN (DMA_ALIGNMENT+MAX_TXPD_SIZE) + +/** rx data header length */ +#define MLAN_RX_HEADER_LEN MLAN_MIN_DATA_HEADER_LEN + +/** This is current limit on Maximum Tx AMPDU allowed */ +#define MLAN_MAX_TX_BASTREAM_SUPPORTED 16 +#define MLAN_MAX_TX_BASTREAM_DEFAULT 2 +/** This is current limit on Maximum Rx AMPDU allowed */ +#define MLAN_MAX_RX_BASTREAM_SUPPORTED 16 + +#ifdef STA_SUPPORT +/** Default Win size attached during ADDBA request */ +#define MLAN_STA_AMPDU_DEF_TXWINSIZE 64 +/** Default Win size attached during ADDBA response */ +#define MLAN_STA_AMPDU_DEF_RXWINSIZE 64 +/** RX winsize for COEX */ +#define MLAN_STA_COEX_AMPDU_DEF_RXWINSIZE 16 +#endif /* STA_SUPPORT */ +#ifdef UAP_SUPPORT +/** Default Win size attached during ADDBA request */ +#define MLAN_UAP_AMPDU_DEF_TXWINSIZE 48 +/** Default Win size attached during ADDBA response */ +#define MLAN_UAP_AMPDU_DEF_RXWINSIZE 32 +/** RX winsize for COEX */ +#define MLAN_UAP_COEX_AMPDU_DEF_RXWINSIZE 16 +#endif /* UAP_SUPPORT */ + +#ifdef WIFI_DIRECT_SUPPORT +/** WFD use the same window size for tx/rx */ +#define MLAN_WFD_AMPDU_DEF_TXRXWINSIZE 64 +/** RX winsize for COEX */ +#define MLAN_WFD_COEX_AMPDU_DEF_RXWINSIZE 16 +#endif + +/** NAN use the same window size for tx/rx */ +#define MLAN_NAN_AMPDU_DEF_TXRXWINSIZE 16 +/** RX winsize for COEX */ +#define MLAN_NAN_COEX_AMPDU_DEF_RXWINSIZE 16 + +/** Block ack timeout value */ +#define MLAN_DEFAULT_BLOCK_ACK_TIMEOUT 0xffff +/** Maximum Tx Win size configured for ADDBA request [10 bits] */ +#define MLAN_AMPDU_MAX_TXWINSIZE 0x3ff +/** Maximum Rx Win size configured for ADDBA request [10 bits] */ +#define MLAN_AMPDU_MAX_RXWINSIZE 0x3ff + +/** Rate index for HR/DSSS 0 */ +#define MLAN_RATE_INDEX_HRDSSS0 0 +/** Rate index for HR/DSSS 3 */ +#define MLAN_RATE_INDEX_HRDSSS3 3 +/** Rate index for OFDM 0 */ +#define MLAN_RATE_INDEX_OFDM0 4 +/** Rate index for OFDM 7 */ +#define MLAN_RATE_INDEX_OFDM7 11 +/** Rate index for MCS 0 */ +#define MLAN_RATE_INDEX_MCS0 0 +/** Rate index for MCS 7 */ +#define MLAN_RATE_INDEX_MCS7 7 +/** Rate index for MCS 9 */ +#define MLAN_RATE_INDEX_MCS9 9 +/** Rate index for MCS 32 */ +#define MLAN_RATE_INDEX_MCS32 32 +/** Rate index for MCS 127 */ +#define MLAN_RATE_INDEX_MCS127 127 + +/** Rate bitmap for OFDM 0 */ +#define MLAN_RATE_BITMAP_OFDM0 16 +/** Rate bitmap for OFDM 7 */ +#define MLAN_RATE_BITMAP_OFDM7 23 +/** Rate bitmap for MCS 0 */ +#define MLAN_RATE_BITMAP_MCS0 32 +/** Rate bitmap for MCS 127 */ +#define MLAN_RATE_BITMAP_MCS127 159 + +/** Size of rx data buffer */ +#define MLAN_RX_DATA_BUF_SIZE (4 * 1024) +/** Size of rx command buffer */ +#define MLAN_RX_CMD_BUF_SIZE (2 * 1024) + +#define MLAN_USB_RX_DATA_BUF_SIZE MLAN_RX_DATA_BUF_SIZE + +/** MLAN MAC Address Length */ +#define MLAN_MAC_ADDR_LENGTH (6) +/** MLAN 802.11 MAC Address */ +typedef t_u8 mlan_802_11_mac_addr[MLAN_MAC_ADDR_LENGTH]; + +/** MLAN Maximum SSID Length */ +#define MLAN_MAX_SSID_LENGTH (32) + +/** RTS/FRAG related defines */ +/** Minimum RTS value */ +#define MLAN_RTS_MIN_VALUE (0) +/** Maximum RTS value */ +#define MLAN_RTS_MAX_VALUE (2347) +/** Minimum FRAG value */ +#define MLAN_FRAG_MIN_VALUE (256) +/** Maximum FRAG value */ +#define MLAN_FRAG_MAX_VALUE (2346) + +/** Minimum tx retry count */ +#define MLAN_TX_RETRY_MIN (0) +/** Maximum tx retry count */ +#define MLAN_TX_RETRY_MAX (14) + +/** max Wmm AC queues */ +#define MAX_AC_QUEUES 4 + +/** define SDIO block size for data Tx/Rx */ +/* We support up to 480-byte block size due to FW buffer limitation. */ +#define MLAN_SDIO_BLOCK_SIZE 256 + +/** define SDIO block size for firmware download */ +#define MLAN_SDIO_BLOCK_SIZE_FW_DNLD MLAN_SDIO_BLOCK_SIZE + +/** define allocated buffer size */ +#define ALLOC_BUF_SIZE (4 * 1024) +/** SDIO MP aggr pkt limit */ +#define SDIO_MP_AGGR_DEF_PKT_LIMIT (16) + +/** SDIO IO Port mask */ +#define MLAN_SDIO_IO_PORT_MASK 0xfffff +/** SDIO Block/Byte mode mask */ +#define MLAN_SDIO_BYTE_MODE_MASK 0x80000000 + +/** Max retry number of IO write */ +#define MAX_WRITE_IOMEM_RETRY 2 + +/** IN parameter */ +#define IN +/** OUT parameter */ +#define OUT + +/** BIT value */ +#define MBIT(x) (((t_u32)1) << (x)) + +/** Buffer flag for requeued packet */ +#define MLAN_BUF_FLAG_REQUEUED_PKT MBIT(0) +/** Buffer flag for transmit buf from moal */ +#define MLAN_BUF_FLAG_MOAL_TX_BUF MBIT(1) +/** Buffer flag for malloc mlan_buffer */ +#define MLAN_BUF_FLAG_MALLOC_BUF MBIT(2) + +/** Buffer flag for bridge packet */ +#define MLAN_BUF_FLAG_BRIDGE_BUF MBIT(3) + +/** Buffer flag for TDLS */ +#define MLAN_BUF_FLAG_TDLS MBIT(8) + +/** Buffer flag for TCP_ACK */ +#define MLAN_BUF_FLAG_TCP_ACK MBIT(9) + +/** Buffer flag for TX_STATUS */ +#define MLAN_BUF_FLAG_TX_STATUS MBIT(10) + +/** Buffer flag for NET_MONITOR */ +#define MLAN_BUF_FLAG_NET_MONITOR MBIT(11) + +/** Buffer flag for NULL data packet */ +#define MLAN_BUF_FLAG_NULL_PKT MBIT(12) + +#define MLAN_BUF_FLAG_TX_CTRL MBIT(14) + +#ifdef DEBUG_LEVEL1 +/** Debug level bit definition */ +#define MMSG MBIT(0) +#define MFATAL MBIT(1) +#define MERROR MBIT(2) +#define MDATA MBIT(3) +#define MCMND MBIT(4) +#define MEVENT MBIT(5) +#define MINTR MBIT(6) +#define MIOCTL MBIT(7) + +#define MMPA_D MBIT(15) +#define MDAT_D MBIT(16) +#define MCMD_D MBIT(17) +#define MEVT_D MBIT(18) +#define MFW_D MBIT(19) +#define MIF_D MBIT(20) + +#define MENTRY MBIT(28) +#define MWARN MBIT(29) +#define MINFO MBIT(30) +#define MHEX_DUMP MBIT(31) +#endif /* DEBUG_LEVEL1 */ + +/** Memory allocation type: DMA */ +#define MLAN_MEM_DMA MBIT(0) + +/** Default memory allocation flag */ +#define MLAN_MEM_DEF 0 + +/** mlan_status */ +typedef enum _mlan_status { + MLAN_STATUS_FAILURE = 0xffffffff, + MLAN_STATUS_SUCCESS = 0, + MLAN_STATUS_PENDING, + MLAN_STATUS_RESOURCE, + MLAN_STATUS_COMPLETE, +} mlan_status; + +/** mlan_error_code */ +typedef enum _mlan_error_code { + /** No error */ + MLAN_ERROR_NO_ERROR = 0, + /** Firmware/device errors below (MSB=0) */ + MLAN_ERROR_FW_NOT_READY = 0x00000001, + MLAN_ERROR_FW_BUSY = 0x00000002, + MLAN_ERROR_FW_CMDRESP = 0x00000003, + MLAN_ERROR_DATA_TX_FAIL = 0x00000004, + MLAN_ERROR_DATA_RX_FAIL = 0x00000005, + /** Driver errors below (MSB=1) */ + MLAN_ERROR_PKT_SIZE_INVALID = 0x80000001, + MLAN_ERROR_PKT_TIMEOUT = 0x80000002, + MLAN_ERROR_PKT_INVALID = 0x80000003, + MLAN_ERROR_CMD_INVALID = 0x80000004, + MLAN_ERROR_CMD_TIMEOUT = 0x80000005, + MLAN_ERROR_CMD_DNLD_FAIL = 0x80000006, + MLAN_ERROR_CMD_CANCEL = 0x80000007, + MLAN_ERROR_CMD_RESP_FAIL = 0x80000008, + MLAN_ERROR_CMD_ASSOC_FAIL = 0x80000009, + MLAN_ERROR_CMD_SCAN_FAIL = 0x8000000A, + MLAN_ERROR_IOCTL_INVALID = 0x8000000B, + MLAN_ERROR_IOCTL_FAIL = 0x8000000C, + MLAN_ERROR_EVENT_UNKNOWN = 0x8000000D, + MLAN_ERROR_INVALID_PARAMETER = 0x8000000E, + MLAN_ERROR_NO_MEM = 0x8000000F, + /** More to add */ +} mlan_error_code; + +/** mlan_buf_type */ +typedef enum _mlan_buf_type { + MLAN_BUF_TYPE_CMD = 1, + MLAN_BUF_TYPE_DATA, + MLAN_BUF_TYPE_EVENT, + MLAN_BUF_TYPE_RAW_DATA, + MLAN_BUF_TYPE_SPA_DATA, +} mlan_buf_type; + +/** MLAN BSS type */ +typedef enum _mlan_bss_type { + MLAN_BSS_TYPE_STA = 0, + MLAN_BSS_TYPE_UAP = 1, +#ifdef WIFI_DIRECT_SUPPORT + MLAN_BSS_TYPE_WIFIDIRECT = 2, +#endif + MLAN_BSS_TYPE_NAN = 4, + MLAN_BSS_TYPE_ANY = 0xff, +} mlan_bss_type; + +/** MLAN BSS role */ +typedef enum _mlan_bss_role { + MLAN_BSS_ROLE_STA = 0, + MLAN_BSS_ROLE_UAP = 1, + MLAN_BSS_ROLE_ANY = 0xff, +} mlan_bss_role; + +/** BSS role bit mask */ +#define BSS_ROLE_BIT_MASK MBIT(0) + +/** Get BSS role */ +#define GET_BSS_ROLE(priv) ((priv)->bss_role & BSS_ROLE_BIT_MASK) + +/** mlan_data_frame_type */ +typedef enum _mlan_data_frame_type { + MLAN_DATA_FRAME_TYPE_ETH_II = 0, + MLAN_DATA_FRAME_TYPE_802_11, +} mlan_data_frame_type; + +/** mlan_event_id */ +typedef enum _mlan_event_id { + /* Event generated by firmware (MSB=0) */ + MLAN_EVENT_ID_FW_UNKNOWN = 0x00000001, + MLAN_EVENT_ID_FW_ADHOC_LINK_SENSED = 0x00000002, + MLAN_EVENT_ID_FW_ADHOC_LINK_LOST = 0x00000003, + MLAN_EVENT_ID_FW_DISCONNECTED = 0x00000004, + MLAN_EVENT_ID_FW_MIC_ERR_UNI = 0x00000005, + MLAN_EVENT_ID_FW_MIC_ERR_MUL = 0x00000006, + MLAN_EVENT_ID_FW_BCN_RSSI_LOW = 0x00000007, + MLAN_EVENT_ID_FW_BCN_RSSI_HIGH = 0x00000008, + MLAN_EVENT_ID_FW_BCN_SNR_LOW = 0x00000009, + MLAN_EVENT_ID_FW_BCN_SNR_HIGH = 0x0000000A, + MLAN_EVENT_ID_FW_MAX_FAIL = 0x0000000B, + MLAN_EVENT_ID_FW_DATA_RSSI_LOW = 0x0000000C, + MLAN_EVENT_ID_FW_DATA_RSSI_HIGH = 0x0000000D, + MLAN_EVENT_ID_FW_DATA_SNR_LOW = 0x0000000E, + MLAN_EVENT_ID_FW_DATA_SNR_HIGH = 0x0000000F, + MLAN_EVENT_ID_FW_LINK_QUALITY = 0x00000010, + MLAN_EVENT_ID_FW_PORT_RELEASE = 0x00000011, + MLAN_EVENT_ID_FW_PRE_BCN_LOST = 0x00000012, + MLAN_EVENT_ID_FW_DEBUG_INFO = 0x00000013, + MLAN_EVENT_ID_FW_WMM_CONFIG_CHANGE = 0x0000001A, + MLAN_EVENT_ID_FW_HS_WAKEUP = 0x0000001B, + MLAN_EVENT_ID_FW_BG_SCAN = 0x0000001D, + MLAN_EVENT_ID_FW_BG_SCAN_STOPPED = 0x0000001E, + MLAN_EVENT_ID_FW_WEP_ICV_ERR = 0x00000020, + MLAN_EVENT_ID_FW_STOP_TX = 0x00000021, + MLAN_EVENT_ID_FW_START_TX = 0x00000022, + MLAN_EVENT_ID_FW_CHANNEL_SWITCH_ANN = 0x00000023, + MLAN_EVENT_ID_FW_RADAR_DETECTED = 0x00000024, + MLAN_EVENT_ID_FW_CHANNEL_REPORT_RDY = 0x00000025, + MLAN_EVENT_ID_FW_BW_CHANGED = 0x00000026, + MLAN_EVENT_ID_FW_REMAIN_ON_CHAN_EXPIRED = 0x0000002B, +#ifdef UAP_SUPPORT + MLAN_EVENT_ID_UAP_FW_BSS_START = 0x0000002C, + MLAN_EVENT_ID_UAP_FW_BSS_ACTIVE = 0x0000002D, + MLAN_EVENT_ID_UAP_FW_BSS_IDLE = 0x0000002E, + MLAN_EVENT_ID_UAP_FW_STA_CONNECT = 0x00000030, + MLAN_EVENT_ID_UAP_FW_STA_DISCONNECT = 0x00000031, +#endif + + MLAN_EVENT_ID_FW_DUMP_INFO = 0x00000033, + + MLAN_EVENT_ID_FW_TX_STATUS = 0x00000034, + MLAN_EVENT_ID_FW_CHAN_SWITCH_COMPLETE = 0x00000036, + /* Event generated by MLAN driver (MSB=1) */ + MLAN_EVENT_ID_DRV_CONNECTED = 0x80000001, + MLAN_EVENT_ID_DRV_DEFER_HANDLING = 0x80000002, + MLAN_EVENT_ID_DRV_HS_ACTIVATED = 0x80000003, + MLAN_EVENT_ID_DRV_HS_DEACTIVATED = 0x80000004, + MLAN_EVENT_ID_DRV_MGMT_FRAME = 0x80000005, + MLAN_EVENT_ID_DRV_OBSS_SCAN_PARAM = 0x80000006, + MLAN_EVENT_ID_DRV_PASSTHRU = 0x80000007, + MLAN_EVENT_ID_DRV_SCAN_REPORT = 0x80000009, + MLAN_EVENT_ID_DRV_MEAS_REPORT = 0x8000000A, + MLAN_EVENT_ID_DRV_ASSOC_FAILURE_REPORT = 0x8000000B, + MLAN_EVENT_ID_DRV_REPORT_STRING = 0x8000000F, + MLAN_EVENT_ID_DRV_DBG_DUMP = 0x80000012, + MLAN_EVENT_ID_DRV_BGSCAN_RESULT = 0x80000013, + MLAN_EVENT_ID_DRV_FLUSH_RX_WORK = 0x80000015, + MLAN_EVENT_ID_DRV_DEFER_RX_WORK = 0x80000016, + MLAN_EVENT_ID_DRV_TDLS_TEARDOWN_REQ = 0x80000017, + MLAN_EVENT_ID_DRV_FT_RESPONSE = 0x80000018, + MLAN_EVENT_ID_DRV_FLUSH_MAIN_WORK = 0x80000019, +#ifdef UAP_SUPPORT + MLAN_EVENT_ID_DRV_UAP_CHAN_INFO = 0x80000020, +#endif + MLAN_EVENT_ID_FW_ROAM_OFFLOAD_RESULT = 0x80000023, + MLAN_EVENT_ID_NAN_STARTED = 0x80000024, +} mlan_event_id; + +/** Data Structures */ +/** mlan_image data structure */ +typedef struct _mlan_fw_image { + /** Helper image buffer pointer */ + t_u8 *phelper_buf; + /** Helper image length */ + t_u32 helper_len; + /** Firmware image buffer pointer */ + t_u8 *pfw_buf; + /** Firmware image length */ + t_u32 fw_len; + /** Firmware reload flag */ + t_u8 fw_reload; +} mlan_fw_image, *pmlan_fw_image; + +#define OID_TYPE_CAL 0x2 +#define OID_TYPE_DPD 0xa + +/** Custom data structure */ +typedef struct _mlan_init_param { + /** DPD data buffer pointer */ + t_u8 *pdpd_data_buf; + /** DPD data length */ + t_u32 dpd_data_len; + /** region txpowerlimit cfg data buffer pointer */ + t_u8 *ptxpwr_data_buf; + /** region txpowerlimit cfg data length */ + t_u32 txpwr_data_len; + /** Cal data buffer pointer */ + t_u8 *pcal_data_buf; + /** Cal data length */ + t_u32 cal_data_len; + /** Other custom data */ +} mlan_init_param, *pmlan_init_param; + +/** channel band */ +enum { + BAND_2GHZ = 0, + BAND_5GHZ = 1, + BAND_4GHZ = 2, +}; + +/** channel offset */ +enum { + SEC_CHAN_NONE = 0, + SEC_CHAN_ABOVE = 1, + SEC_CHAN_5MHZ = 2, + SEC_CHAN_BELOW = 3 +}; + +/** channel bandwidth */ +enum { + CHAN_BW_20MHZ = 0, + CHAN_BW_10MHZ, + CHAN_BW_40MHZ, +}; + +/** scan mode */ +enum { + SCAN_MODE_MANUAL = 0, + SCAN_MODE_ACS, + SCAN_MODE_USER, +}; + +/** Band_Config_t */ +typedef MLAN_PACK_START struct _Band_Config_t { +#ifdef BIG_ENDIAN_SUPPORT + /** Channel Selection Mode - (00)=manual, (01)=ACS, (02)=user*/ + t_u8 scanMode:2; + /** Secondary Channel Offset - (00)=None, (01)=Above, (11)=Below */ + t_u8 chan2Offset:2; + /** Channel Width - (00)=20MHz, (10)=40MHz, (11)=80MHz */ + t_u8 chanWidth:2; + /** Band Info - (00)=2.4GHz, (01)=5GHz */ + t_u8 chanBand:2; +#else + /** Band Info - (00)=2.4GHz, (01)=5GHz */ + t_u8 chanBand:2; + /** Channel Width - (00)=20MHz, (10)=40MHz, (11)=80MHz */ + t_u8 chanWidth:2; + /** Secondary Channel Offset - (00)=None, (01)=Above, (11)=Below */ + t_u8 chan2Offset:2; + /** Channel Selection Mode - (00)=manual, (01)=ACS, (02)=Adoption mode*/ + t_u8 scanMode:2; +#endif +} MLAN_PACK_END Band_Config_t; + +/** channel_band_t */ +typedef MLAN_PACK_START struct _chan_band_info { + /** Band Configuration */ + Band_Config_t bandcfg; + /** channel */ + t_u8 channel; + /** 11n flag */ + t_u8 is_11n_enabled; + /** center channel */ + t_u8 center_chan; +} MLAN_PACK_END chan_band_info; + +/** mlan_event data structure */ +typedef struct _mlan_event { + /** BSS index number for multiple BSS support */ + t_u32 bss_index; + /** Event ID */ + mlan_event_id event_id; + /** Event length */ + t_u32 event_len; + /** Event buffer */ + t_u8 event_buf[0]; +} mlan_event, *pmlan_event; + +/** mlan_ioctl_req data structure */ +typedef struct _mlan_ioctl_req { + /** Pointer to previous mlan_ioctl_req */ + struct _mlan_ioctl_req *pprev; + /** Pointer to next mlan_ioctl_req */ + struct _mlan_ioctl_req *pnext; + /** Status code from firmware/driver */ + t_u32 status_code; + /** BSS index number for multiple BSS support */ + t_u32 bss_index; + /** Request id */ + t_u32 req_id; + /** Action: set or get */ + t_u32 action; + /** Pointer to buffer */ + t_u8 *pbuf; + /** Length of buffer */ + t_u32 buf_len; + /** Length of the data read/written in buffer */ + t_u32 data_read_written; + /** Length of buffer needed */ + t_u32 buf_len_needed; + /** Reserved for MOAL module */ + t_ptr reserved_1; +} mlan_ioctl_req, *pmlan_ioctl_req; + +/** mix rate information structure */ +typedef MLAN_PACK_START struct _mix_rate_info { + /** bit0: LGI: gi=0, SGI: gi= 1 */ + /** bit1-2: 20M: bw=0, 40M: bw=1, 80M: bw=2, 160M: bw=3 */ + /** bit3-4: LG: format=0, HT: format=1, VHT: format=2 */ + /** bit5: LDPC: 0-not support, 1-support */ + /** bit6-7:reserved */ + t_u8 rate_info; + /** MCS index */ + t_u8 mcs_index; + /** bitrate, in 500Kbps */ + t_u16 bitrate; +} MLAN_PACK_END mix_rate_info, *pmix_rate_info; + +/** rxpd extra information structure */ +typedef MLAN_PACK_START struct _rxpd_extra_info { + /** flags */ + t_u8 flags; + /** channel.flags */ + t_u16 channel_flags; + /** mcs.known */ + t_u8 mcs_known; + /** mcs.flags */ + t_u8 mcs_flags; +} MLAN_PACK_END rxpd_extra_info, *prxpd_extra_info; + +/** rdaio tap information structure */ +typedef MLAN_PACK_START struct _radiotap_info { + /** Rate Info */ + mix_rate_info rate_info; + /** SNR */ + t_s8 snr; + /** Noise Floor */ + t_s8 nf; + /** band config */ + t_u8 band_config; + /** chan number */ + t_u8 chan_num; + /** antenna */ + t_u8 antenna; + /** extra rxpd info from FW */ + rxpd_extra_info extra_info; +} MLAN_PACK_END radiotap_info, *pradiotap_info; + +/** txpower structure */ +typedef MLAN_PACK_START struct { +#ifdef BIG_ENDIAN_SUPPORT + /** Host tx power ctrl: + 0x0: use fw setting for TX power + 0x1: value specified in bit[6] and bit[5:0] are valid */ + t_u8 hostctl:1; + /** Sign of the power specified in bit[5:0] */ + t_u8 sign:1; + /** Power to be used for transmission(in dBm) */ + t_u8 abs_val:6; +#else + /** Power to be used for transmission(in dBm) */ + t_u8 abs_val:6; + /** Sign of the power specified in bit[5:0] */ + t_u8 sign:1; + /** Host tx power ctrl: + 0x0: use fw setting for TX power + 0x1: value specified in bit[6] and bit[5:0] are valid */ + t_u8 hostctl:1; +#endif +} MLAN_PACK_END tx_power_t; +/* pkt_txctrl */ +typedef MLAN_PACK_START struct _pkt_txctrl { + /**Data rate in unit of 0.5Mbps */ + t_u16 data_rate; + /*Channel number to transmit the frame */ + t_u8 channel; + /** Bandwidth to transmit the frame*/ + t_u8 bw; + /** Power to be used for transmission*/ + union { + tx_power_t tp; + t_u8 val; + } tx_power; + /** Retry time of tx transmission*/ + t_u8 retry_limit; +} MLAN_PACK_END pkt_txctrl, *ppkt_txctrl; + +/** pkt_rxinfo */ +typedef MLAN_PACK_START struct _pkt_rxinfo { + /** Data rate of received paccket*/ + t_u16 data_rate; + /** Channel on which packet was received*/ + t_u8 channel; + /** Rx antenna*/ + t_u8 antenna; + /** Rx Rssi*/ + t_u8 rssi; +} MLAN_PACK_END pkt_rxinfo, *ppkt_rxinfo; + +/** mlan_buffer data structure */ +typedef struct _mlan_buffer { + /** Pointer to previous mlan_buffer */ + struct _mlan_buffer *pprev; + /** Pointer to next mlan_buffer */ + struct _mlan_buffer *pnext; + /** Status code from firmware/driver */ + t_u32 status_code; + /** Flags for this buffer */ + t_u32 flags; + /** BSS index number for multiple BSS support */ + t_u32 bss_index; + /** Buffer descriptor, e.g. skb in Linux */ + t_void *pdesc; + /** Pointer to buffer */ + t_u8 *pbuf; + /** Offset to data */ + t_u32 data_offset; + /** Data length */ + t_u32 data_len; + /** Buffer type: data, cmd, event etc. */ + mlan_buf_type buf_type; + + /** Fields below are valid for data packet only */ + /** QoS priority */ + t_u32 priority; + /** Time stamp when packet is received (seconds) */ + t_u32 in_ts_sec; + /** Time stamp when packet is received (micro seconds) */ + t_u32 in_ts_usec; + /** Time stamp when packet is processed (seconds) */ + t_u32 out_ts_sec; + /** Time stamp when packet is processed (micro seconds) */ + t_u32 out_ts_usec; + /** tx_seq_num */ + t_u32 tx_seq_num; + + /** Fields below are valid for MLAN module only */ + /** Pointer to parent mlan_buffer */ + struct _mlan_buffer *pparent; + /** Use count for this buffer */ + t_u32 use_count; + union { + pkt_txctrl tx_info; + pkt_rxinfo rx_info; + } u; +} mlan_buffer, *pmlan_buffer; + +/** mlan_fw_info data structure */ +typedef struct _mlan_hw_info { + /** Firmware capabilities */ + t_u32 fw_cap; +} mlan_hw_info, *pmlan_hw_info; + +/** mlan_bss_attr data structure */ +typedef struct _mlan_bss_attr { + /** BSS type */ + t_u32 bss_type; + /** Data frame type: Ethernet II, 802.11, etc. */ + t_u32 frame_type; + /** The BSS is active (non-0) or not (0). */ + t_u32 active; + /** BSS Priority */ + t_u32 bss_priority; + /** BSS number */ + t_u32 bss_num; + /** The BSS is virtual */ + t_u32 bss_virtual; +} mlan_bss_attr, *pmlan_bss_attr; + +/** bss tbl data structure */ +typedef struct _mlan_bss_tbl { + /** BSS Attributes */ + mlan_bss_attr bss_attr[MLAN_MAX_BSS_NUM]; +} mlan_bss_tbl, *pmlan_bss_tbl; + +#ifdef PRAGMA_PACK +#pragma pack(push, 1) +#endif + +/** Type enumeration for the command result */ +typedef MLAN_PACK_START enum _mlan_cmd_result_e { + MLAN_CMD_RESULT_SUCCESS = 0, + MLAN_CMD_RESULT_FAILURE = 1, + MLAN_CMD_RESULT_TIMEOUT = 2, + MLAN_CMD_RESULT_INVALID_DATA = 3 +} MLAN_PACK_END mlan_cmd_result_e; + +/** Type enumeration of WMM AC_QUEUES */ +typedef MLAN_PACK_START enum _mlan_wmm_ac_e { + WMM_AC_BK, + WMM_AC_BE, + WMM_AC_VI, + WMM_AC_VO +} MLAN_PACK_END mlan_wmm_ac_e; + +/** Type enumeration for the action field in the Queue Config command */ +typedef MLAN_PACK_START enum _mlan_wmm_queue_config_action_e { + MLAN_WMM_QUEUE_CONFIG_ACTION_GET = 0, + MLAN_WMM_QUEUE_CONFIG_ACTION_SET = 1, + MLAN_WMM_QUEUE_CONFIG_ACTION_DEFAULT = 2, + MLAN_WMM_QUEUE_CONFIG_ACTION_MAX +} MLAN_PACK_END mlan_wmm_queue_config_action_e; + +/** Type enumeration for the action field in the queue stats command */ +typedef MLAN_PACK_START enum _mlan_wmm_queue_stats_action_e { + MLAN_WMM_STATS_ACTION_START = 0, + MLAN_WMM_STATS_ACTION_STOP = 1, + MLAN_WMM_STATS_ACTION_GET_CLR = 2, + MLAN_WMM_STATS_ACTION_SET_CFG = 3, /* Not currently used */ + MLAN_WMM_STATS_ACTION_GET_CFG = 4, /* Not currently used */ + MLAN_WMM_STATS_ACTION_MAX +} MLAN_PACK_END mlan_wmm_queue_stats_action_e; + +/** + * @brief IOCTL structure for a Traffic stream status. + * + */ +typedef MLAN_PACK_START struct { + /** TSID: Range: 0->7 */ + t_u8 tid; + /** TSID specified is valid */ + t_u8 valid; + /** AC TSID is active on */ + t_u8 access_category; + /** UP specified for the TSID */ + t_u8 user_priority; + /** Power save mode for TSID: 0 (legacy), 1 (UAPSD) */ + t_u8 psb; + /** Upstream(0), Downlink(1), Bidirectional(3) */ + t_u8 flow_dir; + /** Medium time granted for the TSID */ + t_u16 medium_time; +} MLAN_PACK_END wlan_ioctl_wmm_ts_status_t, +/** Type definition of mlan_ds_wmm_ts_status for MLAN_OID_WMM_CFG_TS_STATUS */ +mlan_ds_wmm_ts_status, *pmlan_ds_wmm_ts_status; + +/** Max Ie length */ +#define MAX_IE_SIZE 256 + +/** custom IE */ +typedef MLAN_PACK_START struct _custom_ie { + /** IE Index */ + t_u16 ie_index; + /** Mgmt Subtype Mask */ + t_u16 mgmt_subtype_mask; + /** IE Length */ + t_u16 ie_length; + /** IE buffer */ + t_u8 ie_buffer[MAX_IE_SIZE]; +} MLAN_PACK_END custom_ie; + +/** Max IE index to FW */ +#define MAX_MGMT_IE_INDEX_TO_FW 4 +/** Max IE index per BSS */ +#define MAX_MGMT_IE_INDEX 16 + +/** custom IE info */ +typedef MLAN_PACK_START struct _custom_ie_info { + /** size of buffer */ + t_u16 buf_size; + /** no of buffers of buf_size */ + t_u16 buf_count; +} MLAN_PACK_END custom_ie_info; + +/** TLV buffer : Max Mgmt IE */ +typedef MLAN_PACK_START struct _tlvbuf_max_mgmt_ie { + /** Type */ + t_u16 type; + /** Length */ + t_u16 len; + /** No of tuples */ + t_u16 count; + /** custom IE info tuples */ + custom_ie_info info[MAX_MGMT_IE_INDEX]; +} MLAN_PACK_END tlvbuf_max_mgmt_ie; + +/** TLV buffer : custom IE */ +typedef MLAN_PACK_START struct _tlvbuf_custom_ie { + /** Type */ + t_u16 type; + /** Length */ + t_u16 len; + /** IE data */ + custom_ie ie_data_list[MAX_MGMT_IE_INDEX_TO_FW]; + /** Max mgmt IE TLV */ + tlvbuf_max_mgmt_ie max_mgmt_ie; +} MLAN_PACK_END mlan_ds_misc_custom_ie; + +/** Max TDLS config data length */ +#define MAX_TDLS_DATA_LEN 1024 + +/** Action commands for TDLS enable/disable */ +#define WLAN_TDLS_CONFIG 0x00 +/** Action commands for TDLS configuration :Set */ +#define WLAN_TDLS_SET_INFO 0x01 +/** Action commands for TDLS configuration :Discovery Request */ +#define WLAN_TDLS_DISCOVERY_REQ 0x02 +/** Action commands for TDLS configuration :Setup Request */ +#define WLAN_TDLS_SETUP_REQ 0x03 +/** Action commands for TDLS configuration :Tear down Request */ +#define WLAN_TDLS_TEAR_DOWN_REQ 0x04 +/** Action ID for TDLS power mode */ +#define WLAN_TDLS_POWER_MODE 0x05 +/**Action ID for init TDLS Channel Switch*/ +#define WLAN_TDLS_INIT_CHAN_SWITCH 0x06 +/** Action ID for stop TDLS Channel Switch */ +#define WLAN_TDLS_STOP_CHAN_SWITCH 0x07 +/** Action ID for configure CS related parameters */ +#define WLAN_TDLS_CS_PARAMS 0x08 +/** Action ID for Disable CS */ +#define WLAN_TDLS_CS_DISABLE 0x09 +/** Action ID for TDLS link status */ +#define WLAN_TDLS_LINK_STATUS 0x0A +/** Action ID for Host TDLS config uapsd and CS */ +#define WLAN_HOST_TDLS_CONFIG 0x0D +/** Action ID for TDLS CS immediate return */ +#define WLAN_TDLS_DEBUG_CS_RET_IM 0xFFF7 +/** Action ID for TDLS Stop RX */ +#define WLAN_TDLS_DEBUG_STOP_RX 0xFFF8 +/** Action ID for TDLS Allow weak security for links establish */ +#define WLAN_TDLS_DEBUG_ALLOW_WEAK_SECURITY 0xFFF9 +/** Action ID for TDLS Ignore key lifetime expiry */ +#define WLAN_TDLS_DEBUG_IGNORE_KEY_EXPIRY 0xFFFA +/** Action ID for TDLS Higher/Lower mac Test */ +#define WLAN_TDLS_DEBUG_HIGHER_LOWER_MAC 0xFFFB +/** Action ID for TDLS Prohibited Test */ +#define WLAN_TDLS_DEBUG_SETUP_PROHIBITED 0xFFFC +/** Action ID for TDLS Existing link Test */ +#define WLAN_TDLS_DEBUG_SETUP_SAME_LINK 0xFFFD +/** Action ID for TDLS Fail Setup Confirm */ +#define WLAN_TDLS_DEBUG_FAIL_SETUP_CONFIRM 0xFFFE +/** Action commands for TDLS debug: Wrong BSS Request */ +#define WLAN_TDLS_DEBUG_WRONG_BSS 0xFFFF + +/** tdls each link rate information */ +typedef MLAN_PACK_START struct _tdls_link_rate_info { + /** Tx Data Rate */ + t_u8 tx_data_rate; + /** Tx Rate HT info*/ + t_u8 tx_rate_htinfo; +} MLAN_PACK_END tdls_link_rate_info; + +/** tdls each link status */ +typedef MLAN_PACK_START struct _tdls_each_link_status { + /** peer mac Address */ + t_u8 peer_mac[MLAN_MAC_ADDR_LENGTH]; + /** Link Flags */ + t_u8 link_flags; + /** Traffic Status */ + t_u8 traffic_status; + /** Tx Failure Count */ + t_u8 tx_fail_count; + /** Channel Number */ + t_u32 active_channel; + /** Last Data RSSI in dBm */ + t_s16 data_rssi_last; + /** Last Data NF in dBm */ + t_s16 data_nf_last; + /** AVG DATA RSSI in dBm */ + t_s16 data_rssi_avg; + /** AVG DATA NF in dBm */ + t_s16 data_nf_avg; + union { + /** tdls rate info */ + tdls_link_rate_info rate_info; + /** tdls link final rate*/ + t_u16 final_data_rate; + } u; + /** Security Method */ + t_u8 security_method; + /** Key Lifetime in milliseconds */ + t_u32 key_lifetime; + /** Key Length */ + t_u8 key_length; + /** actual key */ + t_u8 key[0]; +} MLAN_PACK_END tdls_each_link_status; + +/** TDLS configuration data */ +typedef MLAN_PACK_START struct _tdls_all_config { + union { + /** TDLS state enable disable */ + MLAN_PACK_START struct _tdls_config { + /** enable or disable */ + t_u16 enable; + } MLAN_PACK_END tdls_config; + /** Host tdls config */ + MLAN_PACK_START struct _host_tdls_cfg { + /** support uapsd */ + t_u8 uapsd_support; + /** channel_switch */ + t_u8 cs_support; + /** TLV length */ + t_u16 tlv_len; + /** tdls info */ + t_u8 tlv_buffer[0]; + } MLAN_PACK_END host_tdls_cfg; + /** TDLS set info */ + MLAN_PACK_START struct _tdls_set_data { + /** (tlv + capInfo) length */ + t_u16 tlv_length; + /** Cap Info */ + t_u16 cap_info; + /** TLV buffer */ + t_u8 tlv_buffer[0]; + } MLAN_PACK_END tdls_set; + + /** TDLS discovery and others having mac argument */ + MLAN_PACK_START struct _tdls_discovery_data { + /** peer mac Address */ + t_u8 peer_mac_addr[MLAN_MAC_ADDR_LENGTH]; + } MLAN_PACK_END tdls_discovery, tdls_stop_chan_switch, + tdls_link_status_req; + + /** TDLS discovery Response */ + MLAN_PACK_START struct _tdls_discovery_resp { + /** payload length */ + t_u16 payload_len; + /** peer mac Address */ + t_u8 peer_mac_addr[MLAN_MAC_ADDR_LENGTH]; + /** RSSI */ + t_s8 rssi; + /** Cap Info */ + t_u16 cap_info; + /** TLV buffer */ + t_u8 tlv_buffer[0]; + } MLAN_PACK_END tdls_discovery_resp; + + /** TDLS setup request */ + MLAN_PACK_START struct _tdls_setup_data { + /** peer mac Address */ + t_u8 peer_mac_addr[MLAN_MAC_ADDR_LENGTH]; + /** timeout value in milliseconds */ + t_u32 setup_timeout; + /** key lifetime in milliseconds */ + t_u32 key_lifetime; + } MLAN_PACK_END tdls_setup; + + /** TDLS tear down info */ + MLAN_PACK_START struct _tdls_tear_down_data { + /** peer mac Address */ + t_u8 peer_mac_addr[MLAN_MAC_ADDR_LENGTH]; + /** reason code */ + t_u16 reason_code; + } MLAN_PACK_END tdls_tear_down, tdls_cmd_resp; + + /** TDLS power mode info */ + MLAN_PACK_START struct _tdls_power_mode_data { + /** peer mac Address */ + t_u8 peer_mac_addr[MLAN_MAC_ADDR_LENGTH]; + /** Power Mode */ + t_u16 power_mode; + } MLAN_PACK_END tdls_power_mode; + + /** TDLS channel switch info */ + MLAN_PACK_START struct _tdls_chan_switch { + /** peer mac Address */ + t_u8 peer_mac_addr[MLAN_MAC_ADDR_LENGTH]; + /** Channel Switch primary channel no */ + t_u8 primary_channel; + /** Channel Switch secondary channel offset */ + t_u8 secondary_channel_offset; + /** Channel Switch Band */ + t_u8 band; + /** Channel Switch time in milliseconds */ + t_u16 switch_time; + /** Channel Switch timeout in milliseconds */ + t_u16 switch_timeout; + /** Channel Regulatory class*/ + t_u8 regulatory_class; + /** peridicity flag*/ + t_u8 periodicity; + } MLAN_PACK_END tdls_chan_switch; + + /** TDLS channel switch paramters */ + MLAN_PACK_START struct _tdls_cs_params { + /** unit time, multiples of 10ms */ + t_u8 unit_time; + /** threshold for other link */ + t_u8 threshold_otherlink; + /** threshold for direct link */ + t_u8 threshold_directlink; + } MLAN_PACK_END tdls_cs_params; + + /** tdls disable channel switch */ + MLAN_PACK_START struct _tdls_disable_cs { + /** Data*/ + t_u16 data; + } MLAN_PACK_END tdls_disable_cs; + /** TDLS debug data */ + MLAN_PACK_START struct _tdls_debug_data { + /** debug data */ + t_u16 debug_data; + } MLAN_PACK_END tdls_debug_data; + + /** TDLS link status Response */ + MLAN_PACK_START struct _tdls_link_status_resp { + /** payload length */ + t_u16 payload_len; + /** number of links */ + t_u8 active_links; + /** structure for link status */ + tdls_each_link_status link_stats[1]; + } MLAN_PACK_END tdls_link_status_resp; + + } u; +} MLAN_PACK_END tdls_all_config; + +/** TDLS configuration buffer */ +typedef MLAN_PACK_START struct _buf_tdls_config { + /** TDLS Action */ + t_u16 tdls_action; + /** TDLS data */ + t_u8 tdls_data[MAX_TDLS_DATA_LEN]; +} MLAN_PACK_END mlan_ds_misc_tdls_config; + +/** Event structure for tear down */ +typedef struct _tdls_tear_down_event { + /** Peer mac address */ + t_u8 peer_mac_addr[MLAN_MAC_ADDR_LENGTH]; + /** Reason code */ + t_u16 reason_code; +} tdls_tear_down_event; + +#ifdef PRAGMA_PACK +#pragma pack(pop) +#endif + +/** mlan_callbacks data structure */ +typedef struct _mlan_callbacks { + /** moal_get_fw_data */ + mlan_status (*moal_get_fw_data) (IN t_void *pmoal_handle, + IN t_u32 offset, + IN t_u32 len, OUT t_u8 *pbuf); + /** moal_get_hw_spec_complete */ + mlan_status (*moal_get_hw_spec_complete) (IN t_void *pmoal_handle, + IN mlan_status status, + IN mlan_hw_info * phw, + IN pmlan_bss_tbl ptbl); + /** moal_init_fw_complete */ + mlan_status (*moal_init_fw_complete) (IN t_void *pmoal_handle, + IN mlan_status status); + /** moal_shutdown_fw_complete */ + mlan_status (*moal_shutdown_fw_complete) (IN t_void *pmoal_handle, + IN mlan_status status); + /** moal_send_packet_complete */ + mlan_status (*moal_send_packet_complete) (IN t_void *pmoal_handle, + IN pmlan_buffer pmbuf, + IN mlan_status status); + /** moal_recv_complete */ + mlan_status (*moal_recv_complete) (IN t_void *pmoal_handle, + IN pmlan_buffer pmbuf, + IN t_u32 port, + IN mlan_status status); + /** moal_recv_packet */ + mlan_status (*moal_recv_packet) (IN t_void *pmoal_handle, + IN pmlan_buffer pmbuf); + /** moal_recv_event */ + mlan_status (*moal_recv_event) (IN t_void *pmoal_handle, + IN pmlan_event pmevent); + /** moal_ioctl_complete */ + mlan_status (*moal_ioctl_complete) (IN t_void *pmoal_handle, + IN pmlan_ioctl_req pioctl_req, + IN mlan_status status); + + /** moal_alloc_mlan_buffer */ + mlan_status (*moal_alloc_mlan_buffer) (IN t_void *pmoal_handle, + IN t_u32 size, + OUT pmlan_buffer *pmbuf); + /** moal_free_mlan_buffer */ + mlan_status (*moal_free_mlan_buffer) (IN t_void *pmoal_handle, + IN pmlan_buffer pmbuf); + + /** moal_write_reg */ + mlan_status (*moal_write_reg) (IN t_void *pmoal_handle, + IN t_u32 reg, IN t_u32 data); + /** moal_read_reg */ + mlan_status (*moal_read_reg) (IN t_void *pmoal_handle, + IN t_u32 reg, OUT t_u32 *data); + /** moal_write_data_sync */ + mlan_status (*moal_write_data_sync) (IN t_void *pmoal_handle, + IN pmlan_buffer pmbuf, + IN t_u32 port, IN t_u32 timeout); + /** moal_read_data_sync */ + mlan_status (*moal_read_data_sync) (IN t_void *pmoal_handle, + IN OUT pmlan_buffer pmbuf, + IN t_u32 port, IN t_u32 timeout); + /** moal_malloc */ + mlan_status (*moal_malloc) (IN t_void *pmoal_handle, + IN t_u32 size, + IN t_u32 flag, OUT t_u8 **ppbuf); + /** moal_mfree */ + mlan_status (*moal_mfree) (IN t_void *pmoal_handle, IN t_u8 *pbuf); + /** moal_vmalloc */ + mlan_status (*moal_vmalloc) (IN t_void *pmoal_handle, + IN t_u32 size, OUT t_u8 **ppbuf); + /** moal_vfree */ + mlan_status (*moal_vfree) (IN t_void *pmoal_handle, IN t_u8 *pbuf); + /** moal_memset */ + t_void *(*moal_memset) (IN t_void *pmoal_handle, + IN t_void *pmem, IN t_u8 byte, IN t_u32 num); + /** moal_memcpy */ + t_void *(*moal_memcpy) (IN t_void *pmoal_handle, + IN t_void *pdest, + IN const t_void *psrc, IN t_u32 num); + /** moal_memmove */ + t_void *(*moal_memmove) (IN t_void *pmoal_handle, + IN t_void *pdest, + IN const t_void *psrc, IN t_u32 num); + /** moal_memcmp */ + t_s32 (*moal_memcmp) (IN t_void *pmoal_handle, + IN const t_void *pmem1, + IN const t_void *pmem2, IN t_u32 num); + /** moal_udelay */ + t_void (*moal_udelay) (IN t_void *pmoal_handle, IN t_u32 udelay); + /** moal_get_system_time */ + mlan_status (*moal_get_system_time) (IN t_void *pmoal_handle, + OUT t_u32 *psec, OUT t_u32 *pusec); + /** moal_init_timer*/ + mlan_status (*moal_init_timer) (IN t_void *pmoal_handle, + OUT t_void **pptimer, + IN t_void (*callback) (t_void + *pcontext), + IN t_void *pcontext); + /** moal_free_timer */ + mlan_status (*moal_free_timer) (IN t_void *pmoal_handle, + IN t_void *ptimer); + /** moal_start_timer*/ + mlan_status (*moal_start_timer) (IN t_void *pmoal_handle, + IN t_void *ptimer, + IN t_u8 periodic, IN t_u32 msec); + /** moal_stop_timer*/ + mlan_status (*moal_stop_timer) (IN t_void *pmoal_handle, + IN t_void *ptimer); + /** moal_init_lock */ + mlan_status (*moal_init_lock) (IN t_void *pmoal_handle, + OUT t_void **pplock); + /** moal_free_lock */ + mlan_status (*moal_free_lock) (IN t_void *pmoal_handle, + IN t_void *plock); + /** moal_spin_lock */ + mlan_status (*moal_spin_lock) (IN t_void *pmoal_handle, + IN t_void *plock); + /** moal_spin_unlock */ + mlan_status (*moal_spin_unlock) (IN t_void *pmoal_handle, + IN t_void *plock); + /** moal_print */ + t_void (*moal_print) (IN t_void *pmoal_handle, + IN t_u32 level, IN char *pformat, IN ... + ); + /** moal_print_netintf */ + t_void (*moal_print_netintf) (IN t_void *pmoal_handle, + IN t_u32 bss_index, IN t_u32 level); + /** moal_assert */ + t_void (*moal_assert) (IN t_void *pmoal_handle, IN t_u32 cond); + /** moal_hist_data_add */ + t_void (*moal_hist_data_add) (IN t_void *pmoal_handle, + IN t_u32 bss_index, + IN t_u8 rx_rate, + IN t_s8 snr, + IN t_s8 nflr, IN t_u8 antenna); + t_void (*moal_updata_peer_signal) (IN t_void *pmoal_handle, + IN t_u32 bss_index, + IN t_u8 *peer_addr, + IN t_s8 snr, IN t_s8 nflr); + mlan_status (*moal_get_host_time_ns) (OUT t_u64 *time); + t_u32 (*moal_do_div) (IN t_u64 num, IN t_u32 base); +} mlan_callbacks, *pmlan_callbacks; + +/** Parameter unchanged, use MLAN default setting */ +#define ROBUSTCOEX_GPIO_UNCHANGED 0 +/** Parameter enabled, override MLAN default setting */ +#define ROBUSTCOEX_GPIO_CFG 1 + +/** Interrupt Mode SDIO */ +#define INT_MODE_SDIO 0 +/** Interrupt Mode GPIO */ +#define INT_MODE_GPIO 1 +/** New mode: GPIO-1 as a duplicated signal of interrupt as appear of SDIO_DAT1 */ +#define GPIO_INT_NEW_MODE 255 + +/** Parameter unchanged, use MLAN default setting */ +#define MLAN_INIT_PARA_UNCHANGED 0 +/** Parameter enabled, override MLAN default setting */ +#define MLAN_INIT_PARA_ENABLED 1 +/** Parameter disabled, override MLAN default setting */ +#define MLAN_INIT_PARA_DISABLED 2 + +/** mlan_device data structure */ +typedef struct _mlan_device { + /** MOAL Handle */ + t_void *pmoal_handle; + /** BSS Attributes */ + mlan_bss_attr bss_attr[MLAN_MAX_BSS_NUM]; + /** Callbacks */ + mlan_callbacks callbacks; +#ifdef MFG_CMD_SUPPORT + /** MFG mode */ + t_u32 mfg_mode; +#endif + /** SDIO interrupt mode (0: INT_MODE_SDIO, 1: INT_MODE_GPIO) */ + t_u32 int_mode; + /** GPIO interrupt pin number */ + t_u32 gpio_pin; +#ifdef DEBUG_LEVEL1 + /** Driver debug bit masks */ + t_u32 drvdbg; +#endif + /** allocate fixed buffer size for scan beacon buffer*/ + t_u32 fixed_beacon_buffer; +#ifdef SDIO_MULTI_PORT_TX_AGGR + /** SDIO MPA Tx */ + t_u32 mpa_tx_cfg; +#endif +#ifdef SDIO_MULTI_PORT_RX_AGGR + /** SDIO MPA Rx */ + t_u32 mpa_rx_cfg; +#endif + /** SDIO Single port rx aggr */ + t_u8 sdio_rx_aggr_enable; +#if defined(SDIO_MULTI_PORT_TX_AGGR) || defined(SDIO_MULTI_PORT_RX_AGGR) + /* see blk_queue_max_segment_size */ + t_u32 max_seg_size; + /* see blk_queue_max_segments */ + t_u16 max_segs; +#endif + /** Auto deep sleep */ + t_u32 auto_ds; + /** IEEE PS mode */ + t_u32 ps_mode; + /** Max Tx buffer size */ + t_u32 max_tx_buf; +#if defined(STA_SUPPORT) + /** 802.11d configuration */ + t_u32 cfg_11d; +#endif + /** enable/disable rx work */ + t_u8 rx_work; + /** dev cap mask */ + t_u32 dev_cap_mask; + /** oob independent reset */ + t_u32 indrstcfg; + /** dtim interval */ + t_u32 multi_dtim; + /** IEEE ps inactivity timeout value */ + t_u32 inact_tmo; + /** Host sleep wakeup interval */ + t_u32 hs_wake_interval; + /** GPIO to indicate wakeup source */ + t_u8 indication_gpio; + /** channel time and mode for DRCS*/ + t_u32 drcs_chantime_mode; + t_bool fw_region; +} mlan_device, *pmlan_device; + +/** MLAN API function prototype */ +#define MLAN_API + +/** Registration */ +MLAN_API mlan_status mlan_register(IN pmlan_device pmdevice, + OUT t_void **ppmlan_adapter); + +/** Un-registration */ +MLAN_API mlan_status mlan_unregister(IN t_void *pmlan_adapter + ); + +/** Firmware Downloading */ +MLAN_API mlan_status mlan_dnld_fw(IN t_void *pmlan_adapter, + IN pmlan_fw_image pmfw); + +/** Custom data pass API */ +MLAN_API mlan_status mlan_set_init_param(IN t_void *pmlan_adapter, + IN pmlan_init_param pparam); + +/** Firmware Initialization */ +MLAN_API mlan_status mlan_init_fw(IN t_void *pmlan_adapter + ); + +/** Firmware Shutdown */ +MLAN_API mlan_status mlan_shutdown_fw(IN t_void *pmlan_adapter + ); + +/** Main Process */ +MLAN_API mlan_status mlan_main_process(IN t_void *pmlan_adapter + ); + +/** Rx process */ +mlan_status mlan_rx_process(IN t_void *pmlan_adapter, IN t_u8 *rx_pkts); + +/** Packet Transmission */ +MLAN_API mlan_status mlan_send_packet(IN t_void *pmlan_adapter, + IN pmlan_buffer pmbuf); + +/** Packet Reception complete callback */ +MLAN_API mlan_status mlan_recv_packet_complete(IN t_void *pmlan_adapter, + IN pmlan_buffer pmbuf, + IN mlan_status status); + +/** interrupt handler */ +MLAN_API mlan_status mlan_interrupt(IN t_void *pmlan_adapter); + +#if defined(SYSKT) +/** GPIO IRQ callback function */ +MLAN_API t_void mlan_hs_callback(IN t_void *pctx); +#endif /* SYSKT_MULTI || SYSKT */ + +MLAN_API t_void mlan_pm_wakeup_card(IN t_void *pmlan_adapter); + +MLAN_API t_u8 mlan_is_main_process_running(IN t_void *adapter); + +/** mlan ioctl */ +MLAN_API mlan_status mlan_ioctl(IN t_void *pmlan_adapter, + IN pmlan_ioctl_req pioctl_req); +/** mlan select wmm queue */ +MLAN_API t_u8 mlan_select_wmm_queue(IN t_void *pmlan_adapter, + IN t_u8 bss_num, IN t_u8 tid); +#endif /* !_MLAN_DECL_H_ */ diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_fw.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_fw.h new file mode 100644 index 000000000000..598be20e3046 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_fw.h @@ -0,0 +1,6240 @@ +/** @file mlan_fw.h + * + * @brief This file contains firmware specific defines. + * structures and declares global function prototypes used + * in MLAN module. + * + * Copyright (C) 2008-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 10/27/2008: initial version +******************************************************/ + +#ifndef _MLAN_FW_H_ +#define _MLAN_FW_H_ + +/** Interface header length */ +#define INTF_HEADER_LEN 4 + +#ifdef PRAGMA_PACK +#pragma pack(push, 1) +#endif + +/** Ethernet header */ +typedef MLAN_PACK_START struct { + /** Ethernet header destination address */ + t_u8 dest_addr[MLAN_MAC_ADDR_LENGTH]; + /** Ethernet header source address */ + t_u8 src_addr[MLAN_MAC_ADDR_LENGTH]; + /** Ethernet header length */ + t_u16 h803_len; + +} MLAN_PACK_END Eth803Hdr_t; + +/** RFC 1042 header */ +typedef MLAN_PACK_START struct { + /** LLC DSAP */ + t_u8 llc_dsap; + /** LLC SSAP */ + t_u8 llc_ssap; + /** LLC CTRL */ + t_u8 llc_ctrl; + /** SNAP OUI */ + t_u8 snap_oui[3]; + /** SNAP type */ + t_u16 snap_type; + +} MLAN_PACK_END Rfc1042Hdr_t; + +/** Rx packet header */ +typedef MLAN_PACK_START struct { + /** Etherner header */ + Eth803Hdr_t eth803_hdr; + /** RFC 1042 header */ + Rfc1042Hdr_t rfc1042_hdr; + +} MLAN_PACK_END RxPacketHdr_t; + +/** Rates supported in band B */ +#define B_SUPPORTED_RATES 5 +/** Rates supported in band G */ +#define G_SUPPORTED_RATES 9 +/** Rates supported in band BG */ +#define BG_SUPPORTED_RATES 13 + +/** Setup the number of rates passed in the driver/firmware API */ +#define A_SUPPORTED_RATES 9 + +/** CapInfo Short Slot Time Disabled */ +/* #define SHORT_SLOT_TIME_DISABLED(CapInfo) ((IEEEtypes_CapInfo_t)(CapInfo).short_slot_time = 0) */ +#define SHORT_SLOT_TIME_DISABLED(CapInfo) (CapInfo &= ~MBIT(10)) +/** CapInfo Short Slot Time Enabled */ +#define SHORT_SLOT_TIME_ENABLED(CapInfo) (CapInfo |= MBIT(10)) +/** CapInfo Spectrum Mgmt Disabled */ +#define SPECTRUM_MGMT_DISABLED(CapInfo) (CapInfo &= ~MBIT(8)) +/** CapInfo Spectrum Mgmt Enabled */ +#define SPECTRUM_MGMT_ENABLED(CapInfo) ( CapInfo |= MBIT(8)) +/** CapInfo Radio Measurement Disabled */ +#define RADIO_MEASUREMENT_DISABLED(CapInfo) (CapInfo &= ~MBIT(12)) +/** CapInfo Radio Measurement Enabled */ +#define RADIO_MEASUREMENT_ENABLED(CapInfo) ( CapInfo |= MBIT(12)) + +/** Setup the number of rates passed in the driver/firmware API */ +#define HOSTCMD_SUPPORTED_RATES 14 + +/** Rates supported in band N */ +#define N_SUPPORTED_RATES 3 +#ifdef STA_SUPPORT +/** All bands (B, G, N) */ +#define ALL_802_11_BANDS (BAND_A | BAND_B | BAND_G | BAND_GN) +#else +/** All bands (B, G, A) */ +#define ALL_802_11_BANDS (BAND_B | BAND_G | BAND_A) +#endif /* STA_SUPPORT */ + +#ifdef STA_SUPPORT +/** Firmware multiple bands support */ +#define FW_MULTI_BANDS_SUPPORT (MBIT(8) | MBIT(9) | MBIT(10) | MBIT(11)) +#else +/** Firmware multiple bands support */ +#define FW_MULTI_BANDS_SUPPORT (MBIT(8) | MBIT(9) | MBIT(10)) +#endif /* STA_SUPPORT */ +/** Check if multiple bands support is enabled in firmware */ +#define IS_SUPPORT_MULTI_BANDS(_adapter) \ + (_adapter->fw_cap_info & FW_MULTI_BANDS_SUPPORT) +/** Get default bands of the firmware */ +#define GET_FW_DEFAULT_BANDS(_adapter) \ + ((_adapter->fw_cap_info >> 8) & ALL_802_11_BANDS) + +extern t_u8 SupportedRates_B[B_SUPPORTED_RATES]; +extern t_u8 SupportedRates_G[G_SUPPORTED_RATES]; +extern t_u8 SupportedRates_BG[BG_SUPPORTED_RATES]; +extern t_u8 SupportedRates_A[A_SUPPORTED_RATES]; +extern t_u8 SupportedRates_N[N_SUPPORTED_RATES]; +extern t_u8 AdhocRates_G[G_SUPPORTED_RATES]; +extern t_u8 AdhocRates_B[B_SUPPORTED_RATES]; +extern t_u8 AdhocRates_BG[BG_SUPPORTED_RATES]; +extern t_u8 AdhocRates_A[A_SUPPORTED_RATES]; + +/** Firmware wakeup method : Unchanged */ +#define WAKEUP_FW_UNCHANGED 0 +/** Firmware wakeup method : Through interface */ +#define WAKEUP_FW_THRU_INTERFACE 1 +/** Firmware wakeup method : Through GPIO*/ +#define WAKEUP_FW_THRU_GPIO 2 +/** Default value of GPIO */ +#define DEF_WAKEUP_FW_GPIO 0 + +/** Default auto deep sleep mode */ +#define DEFAULT_AUTO_DS_MODE MTRUE +/** Default power save mode */ +#define DEFAULT_PS_MODE Wlan802_11PowerModePSP + +#define EVENT_NAN_GENERIC 0x00000075 +#define NAN_EVT_SUBTYPE_SD_EVENT 0 +#define NAN_EVT_SUBTYPE_NAN_STARTED 1 +#define NAN_EVT_SUBTYPE_SDF_TX_DONE 2 + +#define EVENT_WLS_FTM_COMPLETE 0x00000086 + +#define WLS_SUB_EVENT_FTM_COMPLETE 0 +#define WLS_SUB_EVENT_RADIO_RECEIVED 1 +#define WLS_SUB_EVENT_RADIO_RPT_RECEIVED 2 +#define WLS_SUB_EVENT_ANQP_RESP_RECEIVED 3 + +/** WEP Key index mask */ +#define HostCmd_WEP_KEY_INDEX_MASK 0x3fff +/** Length of WEP 40 bit key */ +#define WEP_40_BIT_LEN 5 +/** Length of WEP 104 bit key */ +#define WEP_104_BIT_LEN 13 + +/** Key information enabled */ +#define KEY_INFO_ENABLED 0x01 +/** KEY_TYPE_ID */ +typedef enum _KEY_TYPE_ID { + /** Key type : WEP */ + KEY_TYPE_ID_WEP = 0, + /** Key type : TKIP */ + KEY_TYPE_ID_TKIP = 1, + /** Key type : AES */ + KEY_TYPE_ID_AES = 2, + KEY_TYPE_ID_WAPI = 3, + KEY_TYPE_ID_AES_CMAC = 4, +} KEY_TYPE_ID; + +/** Key Info flag for multicast key */ +#define KEY_INFO_MCAST_KEY 0x01 +/** Key Info flag for unicast key */ +#define KEY_INFO_UCAST_KEY 0x02 + +/** KEY_INFO_WEP*/ +typedef enum _KEY_INFO_WEP { + KEY_INFO_WEP_MCAST = 0x01, + KEY_INFO_WEP_UNICAST = 0x02, + KEY_INFO_WEP_ENABLED = 0x04 +} KEY_INFO_WEP; + +/** KEY_INFO_TKIP */ +typedef enum _KEY_INFO_TKIP { + KEY_INFO_TKIP_MCAST = 0x01, + KEY_INFO_TKIP_UNICAST = 0x02, + KEY_INFO_TKIP_ENABLED = 0x04 +} KEY_INFO_TKIP; + +/** KEY_INFO_AES*/ +typedef enum _KEY_INFO_AES { + KEY_INFO_AES_MCAST = 0x01, + KEY_INFO_AES_UNICAST = 0x02, + KEY_INFO_AES_ENABLED = 0x04, + KEY_INFO_AES_MCAST_IGTK = 0x400, +} KEY_INFO_AES; + +/** WPA AES key length */ +#define WPA_AES_KEY_LEN 16 +/** WPA TKIP key length */ +#define WPA_TKIP_KEY_LEN 32 +/** WPA AES IGTK key length */ +#define CMAC_AES_KEY_LEN 16 +/** IGTK key length */ +#define WPA_IGTK_KEY_LEN 16 + +/** WAPI key length */ +#define WAPI_KEY_LEN 50 +/** KEY_INFO_WAPI*/ +typedef enum _KEY_INFO_WAPI { + KEY_INFO_WAPI_MCAST = 0x01, + KEY_INFO_WAPI_UNICAST = 0x02, + KEY_INFO_WAPI_ENABLED = 0x04 +} KEY_INFO_WAPI; + +/** Maximum ethernet frame length sans FCS */ +#define MV_ETH_FRAME_LEN 1514 + +/** Length of SNAP header */ +#define MRVDRV_SNAP_HEADER_LEN 8 + +/** The number of times to try when polling for status bits */ +#define MAX_POLL_TRIES 100 + +/** The number of times to try when waiting for downloaded firmware to + become active when multiple interface is present */ +#define MAX_MULTI_INTERFACE_POLL_TRIES 150 + +/** The number of times to try when waiting for downloaded firmware to + become active. (polling the scratch register). */ +#define MAX_FIRMWARE_POLL_TRIES 100 + +/** This is for firmware specific length */ +#define EXTRA_LEN 36 + +/** Buffer size for ethernet Tx packets */ +#define MRVDRV_ETH_TX_PACKET_BUFFER_SIZE \ + (MV_ETH_FRAME_LEN + sizeof(TxPD) + EXTRA_LEN) + +/** Buffer size for ethernet Rx packets */ +#define MRVDRV_ETH_RX_PACKET_BUFFER_SIZE \ + (MV_ETH_FRAME_LEN + sizeof(RxPD) \ + + MRVDRV_SNAP_HEADER_LEN + EXTRA_LEN) + +/* Macros in interface module */ +/** Firmware ready */ +#define FIRMWARE_READY 0xfedc + +/** Number of firmware blocks to transfer */ +#define FIRMWARE_TRANSFER_NBLOCK 2 + +/** Enumeration definition*/ +/** WLAN_802_11_PRIVACY_FILTER */ +typedef enum _WLAN_802_11_PRIVACY_FILTER { + Wlan802_11PrivFilterAcceptAll, + Wlan802_11PrivFilter8021xWEP +} WLAN_802_11_PRIVACY_FILTER; + +/** WLAN_802_11_WEP_STATUS */ +typedef enum _WLAN_802_11_WEP_STATUS { + Wlan802_11WEPEnabled, + Wlan802_11WEPDisabled, + Wlan802_11WEPKeyAbsent, + Wlan802_11WEPNotSupported +} WLAN_802_11_WEP_STATUS; + +/** SNR calculation */ +#define CAL_SNR(RSSI, NF) ((t_s16)((t_s16)(RSSI) - (t_s16)(NF))) + +/** 2K buf size */ +#define MLAN_TX_DATA_BUF_SIZE_2K 2048 + +/** TLV type ID definition */ +#define PROPRIETARY_TLV_BASE_ID 0x0100 + +/** Terminating TLV Type */ +#define MRVL_TERMINATE_TLV_ID 0xffff + +/** TLV type : SSID */ +#define TLV_TYPE_SSID 0x0000 +/** TLV type : Rates */ +#define TLV_TYPE_RATES 0x0001 +/** TLV type : PHY FH */ +#define TLV_TYPE_PHY_FH 0x0002 +/** TLV type : PHY DS */ +#define TLV_TYPE_PHY_DS 0x0003 +/** TLV type : CF */ +#define TLV_TYPE_CF 0x0004 +/** TLV type : IBSS */ +#define TLV_TYPE_IBSS 0x0006 + +/** TLV type : Domain */ +#define TLV_TYPE_DOMAIN 0x0007 + +/** TLV type : Power constraint */ +#define TLV_TYPE_POWER_CONSTRAINT 0x0020 + +/** TLV type : Power capability */ +#define TLV_TYPE_POWER_CAPABILITY 0x0021 + +#define TLV_TYPE_HT_CAPABILITY 0x002d + +/** TLV type : Vendor Specific IE */ +#define TLV_TYPE_VENDOR_SPECIFIC_IE 0x00dd + +/** TLV type : Key material */ +#define TLV_TYPE_KEY_MATERIAL (PROPRIETARY_TLV_BASE_ID + 0x00) /* 0x0100 */ +/** TLV type : Channel list */ +#define TLV_TYPE_CHANLIST (PROPRIETARY_TLV_BASE_ID + 0x01) /* 0x0101 */ +/** TLV type : Number of probes */ +#define TLV_TYPE_NUMPROBES (PROPRIETARY_TLV_BASE_ID + 0x02) /* 0x0102 */ +/** TLV type : Beacon RSSI low */ +#define TLV_TYPE_RSSI_LOW (PROPRIETARY_TLV_BASE_ID + 0x04) /* 0x0104 */ +/** TLV type : Beacon SNR low */ +#define TLV_TYPE_SNR_LOW (PROPRIETARY_TLV_BASE_ID + 0x05) /* 0x0105 */ +/** TLV type : Fail count */ +#define TLV_TYPE_FAILCOUNT (PROPRIETARY_TLV_BASE_ID + 0x06) /* 0x0106 */ +/** TLV type : BCN miss */ +#define TLV_TYPE_BCNMISS (PROPRIETARY_TLV_BASE_ID + 0x07) /* 0x0107 */ +/** TLV type : LED behavior */ +#define TLV_TYPE_LEDBEHAVIOR (PROPRIETARY_TLV_BASE_ID + 0x09) /* 0x0109 */ +/** TLV type : Passthrough */ +#define TLV_TYPE_PASSTHROUGH (PROPRIETARY_TLV_BASE_ID + 0x0a) /* 0x010a */ +/** TLV type : Power TBL 2.4 Ghz */ +#define TLV_TYPE_POWER_TBL_2_4GHZ (PROPRIETARY_TLV_BASE_ID + 0x0c) /* 0x010c */ +/** TLV type : Power TBL 5 GHz */ +#define TLV_TYPE_POWER_TBL_5GHZ (PROPRIETARY_TLV_BASE_ID + 0x0d) /* 0x010d */ +/** TLV type : WMM queue status */ +#define TLV_TYPE_WMMQSTATUS (PROPRIETARY_TLV_BASE_ID + 0x10) /* 0x0110 */ +/** TLV type : Wildcard SSID */ +#define TLV_TYPE_WILDCARDSSID (PROPRIETARY_TLV_BASE_ID + 0x12) /* 0x0112 */ +/** TLV type : TSF timestamp */ +#define TLV_TYPE_TSFTIMESTAMP (PROPRIETARY_TLV_BASE_ID + 0x13) /* 0x0113 */ +/** TLV type : ARP filter */ +#define TLV_TYPE_ARP_FILTER (PROPRIETARY_TLV_BASE_ID + 0x15) /* 0x0115 */ +/** TLV type : Beacon RSSI high */ +#define TLV_TYPE_RSSI_HIGH (PROPRIETARY_TLV_BASE_ID + 0x16) /* 0x0116 */ +/** TLV type : Beacon SNR high */ +#define TLV_TYPE_SNR_HIGH (PROPRIETARY_TLV_BASE_ID + 0x17) /* 0x0117 */ +/** TLV type : Start BG scan later */ +#define TLV_TYPE_STARTBGSCANLATER (PROPRIETARY_TLV_BASE_ID + 0x1e) /* 0x011e */ +/** TLV type: BG scan repeat count */ +#define TLV_TYPE_REPEAT_COUNT (PROPRIETARY_TLV_BASE_ID + 0xb0) /* 0x01b0 */ +/** TLV type : Authentication type */ +#define TLV_TYPE_AUTH_TYPE (PROPRIETARY_TLV_BASE_ID + 0x1f) /* 0x011f */ +/** TLV type : BSSID */ +#define TLV_TYPE_BSSID (PROPRIETARY_TLV_BASE_ID + 0x23) /* 0x0123 */ + +/** TLV type : Link Quality */ +#define TLV_TYPE_LINK_QUALITY (PROPRIETARY_TLV_BASE_ID + 0x24) /* 0x0124 */ + +/** TLV type : Data RSSI low */ +#define TLV_TYPE_RSSI_LOW_DATA (PROPRIETARY_TLV_BASE_ID + 0x26) /* 0x0126 */ +/** TLV type : Data SNR low */ +#define TLV_TYPE_SNR_LOW_DATA (PROPRIETARY_TLV_BASE_ID + 0x27) /* 0x0127 */ +/** TLV type : Data RSSI high */ +#define TLV_TYPE_RSSI_HIGH_DATA (PROPRIETARY_TLV_BASE_ID + 0x28) /* 0x0128 */ +/** TLV type : Data SNR high */ +#define TLV_TYPE_SNR_HIGH_DATA (PROPRIETARY_TLV_BASE_ID + 0x29) /* 0x0129 */ + +/** TLV type : Channel band list */ +#define TLV_TYPE_CHANNELBANDLIST (PROPRIETARY_TLV_BASE_ID + 0x2a) /* 0x012a */ + +/** TLV type : Passphrase */ +#define TLV_TYPE_PASSPHRASE (PROPRIETARY_TLV_BASE_ID + 0x3c) /* 0x013c */ + +/** TLV type : Encryption Protocol TLV */ +#define TLV_TYPE_ENCRYPTION_PROTO (PROPRIETARY_TLV_BASE_ID + 0x40) /* 0x0140 */ +/** TLV type : Cipher TLV */ +#define TLV_TYPE_CIPHER (PROPRIETARY_TLV_BASE_ID + 0x42) /* 0x0142 */ +/** TLV type : PMK */ +#define TLV_TYPE_PMK (PROPRIETARY_TLV_BASE_ID + 0x44) /* 0x0144 */ + +/** TLV type : BCN miss */ +#define TLV_TYPE_PRE_BCNMISS (PROPRIETARY_TLV_BASE_ID + 0x49) /* 0x0149 */ + +/** TLV type : ENABLE ROAM IE */ +#define TLV_TYPE_ROAM (PROPRIETARY_TLV_BASE_ID + 245) +/** TLV type : AP LIST IE */ +#define TLV_TYPE_APLIST (PROPRIETARY_TLV_BASE_ID + 246) +/** TLV type : PMK */ +#define TLV_TYPE_PMK_R0 (PROPRIETARY_TLV_BASE_ID + 247) +/** TLV type : PMK */ +#define TLV_TYPE_PMK_R0_NAME (PROPRIETARY_TLV_BASE_ID + 248) +/** TLV type : TRIGGER CONDITION*/ +#define TLV_TYPE_ROM_TRIGGER (PROPRIETARY_TLV_BASE_ID + 264) +/** TLV type : RETRY_COUNT*/ +#define TLV_TYPE_ROM_RETRY_COUNT (PROPRIETARY_TLV_BASE_ID + 265) +/** TLV type : BGSCAN SETTING*/ +#define TLV_TYPE_ROM_BGSCAN (PROPRIETARY_TLV_BASE_ID + 266) +/** TLV type : PARA RSSI*/ +#define TLV_TYPE_ROM_PARA_RSSI (PROPRIETARY_TLV_BASE_ID + 267) +/** TLV type : BSSID blacklist*/ +#define TLV_TYPE_BLACKLIST_BSSID (PROPRIETARY_TLV_BASE_ID + 0x11d) +/** TLV type : BAND & RSSI*/ +#define TLV_TYPE_BAND_RSSI (PROPRIETARY_TLV_BASE_ID + 0x11e) +/** TLV type : ESS scan*/ +#define TLV_TYPE_ENERGYEFFICIENTSCAN (PROPRIETARY_TLV_BASE_ID + 0xda) +/** TLV type : KEY params*/ +#define TLV_TYPE_ROAM_OFFLOAD_USER_SET_PMK (PROPRIETARY_TLV_BASE_ID + 291) + +/** TLV type: WAPI IE */ +#define TLV_TYPE_WAPI_IE (PROPRIETARY_TLV_BASE_ID + 0x5e) /* 0x015e */ + +/** TLV type: MGMT IE */ +#define TLV_TYPE_MGMT_IE (PROPRIETARY_TLV_BASE_ID + 0x69) /* 0x0169 */ +/** TLV type: MAX_MGMT_IE */ +#define TLV_TYPE_MAX_MGMT_IE (PROPRIETARY_TLV_BASE_ID + 0xaa) /* 0x01aa */ + +/** TLV type: key param v2 */ +#define TLV_TYPE_KEY_PARAM_V2 (PROPRIETARY_TLV_BASE_ID + 0x9C) /* 0x019C */ + +/** TLV type: ps params in hs */ +#define TLV_TYPE_PS_PARAMS_IN_HS (PROPRIETARY_TLV_BASE_ID + 0xB5) /* 0x01b5 */ +/** TLV type: hs wake hold off */ +#define TLV_TYPE_HS_WAKE_HOLDOFF (PROPRIETARY_TLV_BASE_ID + 0xB6) /* 0x01b6 */ +/** TLV type: wake up source */ +#define TLV_TYPE_HS_WAKEUP_SOURCE_GPIO (PROPRIETARY_TLV_BASE_ID + 0x105) /* 0x0205 */ +/** TLV type: management filter */ +#define TLV_TYPE_MGMT_FRAME_WAKEUP (PROPRIETARY_TLV_BASE_ID + 0x116) /* 0x0216 */ +/** TLV type: extend wakeup source */ +#define TLV_TYPE_WAKEUP_EXTEND (PROPRIETARY_TLV_BASE_ID + 0x118) /* 0x0218 */ + +/** TLV type: robustcoex mode */ +#define TLV_TYPE_ROBUSTCOEX (PROPRIETARY_TLV_BASE_ID + 0x11B) /* 0x021B */ + +/** TLV type : TDLS IDLE TIMEOUT */ +#define TLV_TYPE_TDLS_IDLE_TIMEOUT (PROPRIETARY_TLV_BASE_ID + 0xC2) /* 0x01C2 */ + +/** TLV type : HT Capabilities */ +#define TLV_TYPE_HT_CAP (PROPRIETARY_TLV_BASE_ID + 0x4a) /* 0x014a */ +/** TLV type : HT Information */ +#define TLV_TYPE_HT_INFO (PROPRIETARY_TLV_BASE_ID + 0x4b) /* 0x014b */ +/** TLV type : Secondary Channel Offset */ +#define TLV_SECONDARY_CHANNEL_OFFSET (PROPRIETARY_TLV_BASE_ID + 0x4c) /* 0x014c */ +/** TLV type : 20/40 BSS Coexistence */ +#define TLV_TYPE_2040BSS_COEXISTENCE (PROPRIETARY_TLV_BASE_ID + 0x4d) /* 0x014d */ +/** TLV type : Overlapping BSS Scan Parameters */ +#define TLV_TYPE_OVERLAP_BSS_SCAN_PARAM (PROPRIETARY_TLV_BASE_ID + 0x4e) /* 0x014e */ +/** TLV type : Extended capabilities */ +#define TLV_TYPE_EXTCAP (PROPRIETARY_TLV_BASE_ID + 0x4f) /* 0x014f */ +/** TLV type : Set of MCS values that STA desires to use within the BSS */ +#define TLV_TYPE_HT_OPERATIONAL_MCS_SET (PROPRIETARY_TLV_BASE_ID + 0x50) /* 0x0150 */ +/** TLV ID : Management Frame */ +#define TLV_TYPE_MGMT_FRAME (PROPRIETARY_TLV_BASE_ID + 0x68) /* 0x0168 */ +/** TLV type : RXBA_SYNC */ +#define TLV_TYPE_RXBA_SYNC (PROPRIETARY_TLV_BASE_ID + 0x99) /* 0x0199 */ + +#ifdef WIFI_DIRECT_SUPPORT +/** TLV type : AP PSK */ +#define TLV_TYPE_UAP_PSK (PROPRIETARY_TLV_BASE_ID + 0xa8) /* 0x01a8 */ +/** TLV type : p2p NOA */ +#define TLV_TYPE_WIFI_DIRECT_NOA (PROPRIETARY_TLV_BASE_ID + 0x83) +/** TLV type : p2p opp ps */ +#define TLV_TYPE_WIFI_DIRECT_OPP_PS (PROPRIETARY_TLV_BASE_ID + 0x84) +#endif /* WIFI_DIRECT_SUPPORT */ + +/** TLV : 20/40 coex config */ +#define TLV_TYPE_2040_BSS_COEX_CONTROL\ + (PROPRIETARY_TLV_BASE_ID + 0x98) /* 0x0198 */ + +/** TLV type : aggr win size */ +#define TLV_BTCOEX_WL_AGGR_WINSIZE (PROPRIETARY_TLV_BASE_ID + 0xca) +/** TLV type : scan time */ +#define TLV_BTCOEX_WL_SCANTIME (PROPRIETARY_TLV_BASE_ID + 0Xcb) +/** TLV type : Ewpa_eapol_pkt */ +#define TLV_TYPE_EAPOL_PKT (PROPRIETARY_TLV_BASE_ID + 0xcf) + +#define TLV_TYPE_COALESCE_RULE (PROPRIETARY_TLV_BASE_ID + 0x9a) + +/** ADDBA TID mask */ +#define ADDBA_TID_MASK (MBIT(2) | MBIT(3) | MBIT(4) | MBIT(5)) +/** DELBA TID mask */ +#define DELBA_TID_MASK (MBIT(12) | MBIT(13) | MBIT(14) | MBIT(15)) +/** ADDBA Starting Sequence Number Mask */ +#define SSN_MASK 0xfff0 + +/** Block Ack result status */ +/** Block Ack Result : Success */ +#define BA_RESULT_SUCCESS 0x0 +/** Block Ack Result : Execution failure */ +#define BA_RESULT_FAILURE 0x1 +/** Block Ack Result : Timeout */ +#define BA_RESULT_TIMEOUT 0x2 +/** Block Ack Result : Data invalid */ +#define BA_RESULT_DATA_INVALID 0x3 + +/** Get the baStatus (NOT_SETUP, COMPLETE, IN_PROGRESS) + * in Tx BA stream table */ +#define IS_BASTREAM_SETUP(ptr) (ptr->ba_status) + +/** An AMPDU/AMSDU could be disallowed for certain TID. 0xff means + * no aggregation is enabled for the assigned TID */ +#define BA_STREAM_NOT_ALLOWED 0xff + +#ifdef STA_SUPPORT +/** Test if adhoc 11n is enabled */ +#define IS_11N_ADHOC_ENABLED(priv) ((priv->bss_mode == MLAN_BSS_MODE_IBSS) && pmadapter->adhoc_11n_enabled) +#endif + +/** Test if 11n is enabled by checking the HTCap IE */ +#define IS_11N_ENABLED(priv) ((priv->config_bands & BAND_GN || priv->config_bands & BAND_AN) \ + && priv->curr_bss_params.bss_descriptor.pht_cap) +/** Find out if we are the initiator or not */ +#define INITIATOR_BIT(DelBAParamSet) (((DelBAParamSet) & \ + MBIT(DELBA_INITIATOR_POS)) >> DELBA_INITIATOR_POS) + +/** 4K buf size */ +#define MLAN_TX_DATA_BUF_SIZE_4K 4096 +/** 8K buf size */ +#define MLAN_TX_DATA_BUF_SIZE_8K 8192 +/** Max Rx AMPDU Size */ +#define MAX_RX_AMPDU_SIZE_64K 0x03 +/** Non green field station */ +#define NON_GREENFIELD_STAS 0x04 + +/** Greenfield support */ +#define HWSPEC_GREENFIELD_SUPP MBIT(29) +/** RX STBC support */ +#define HWSPEC_RXSTBC_SUPP MBIT(26) +/** ShortGI @ 40Mhz support */ +#define HWSPEC_SHORTGI40_SUPP MBIT(24) +/** ShortGI @ 20Mhz support */ +#define HWSPEC_SHORTGI20_SUPP MBIT(23) +/** RX LDPC support */ +#define HWSPEC_LDPC_SUPP MBIT(22) +/** Channel width 40Mhz support */ +#define HWSPEC_CHANBW40_SUPP MBIT(17) +/** 40Mhz intolarent enable */ +#define CAPINFO_40MHZ_INTOLARENT MBIT(8) + +/** Default 11n capability mask for 2.4GHz */ +#define DEFAULT_11N_CAP_MASK_BG (HWSPEC_SHORTGI20_SUPP | HWSPEC_RXSTBC_SUPP | HWSPEC_LDPC_SUPP) +/** Default 11n capability mask for 5GHz */ +#define DEFAULT_11N_CAP_MASK_A (HWSPEC_CHANBW40_SUPP | HWSPEC_SHORTGI20_SUPP | \ + HWSPEC_SHORTGI40_SUPP | HWSPEC_RXSTBC_SUPP | HWSPEC_LDPC_SUPP) + +/** Default 11n TX BF capability **/ +#define DEFAULT_11N_TX_BF_CAP 0x19E74608 + +/** Bits to ignore in hw_dev_cap as these bits are set in get_hw_spec */ +#define IGN_HW_DEV_CAP (CAPINFO_40MHZ_INTOLARENT) + +/** HW_SPEC FwCapInfo */ +#define ISSUPP_11NENABLED(FwCapInfo) (FwCapInfo & MBIT(11)) + +/** HW_SPEC Dot11nDevCap : MAX AMSDU supported */ +#define ISSUPP_MAXAMSDU(Dot11nDevCap) (Dot11nDevCap & MBIT(31)) +/** HW_SPEC Dot11nDevCap : Beamforming support */ +#define ISSUPP_BEAMFORMING(Dot11nDevCap) (Dot11nDevCap & MBIT(30)) +/** HW_SPEC Dot11nDevCap : Green field support */ +#define ISSUPP_GREENFIELD(Dot11nDevCap) (Dot11nDevCap & MBIT(29)) +/** HW_SPEC Dot11nDevCap : AMPDU support */ +#define ISSUPP_AMPDU(Dot11nDevCap) (Dot11nDevCap & MBIT(28)) +/** HW_SPEC Dot11nDevCap : MIMO PS support */ +#define ISSUPP_MIMOPS(Dot11nDevCap) (Dot11nDevCap & MBIT(27)) +/** HW_SPEC Dot11nDevCap : Rx STBC support */ +#define ISSUPP_RXSTBC(Dot11nDevCap) (Dot11nDevCap & MBIT(26)) +/** HW_SPEC Dot11nDevCap : Tx STBC support */ +#define ISSUPP_TXSTBC(Dot11nDevCap) (Dot11nDevCap & MBIT(25)) +/** HW_SPEC Dot11nDevCap : Short GI @ 40Mhz support */ +#define ISSUPP_SHORTGI40(Dot11nDevCap) (Dot11nDevCap & MBIT(24)) +/** HW_SPEC Dot11nDevCap : Reset Short GI @ 40Mhz support */ +#define RESETSUPP_SHORTGI40(Dot11nDevCap) (Dot11nDevCap &= ~MBIT(24)) +/** HW_SPEC Dot11nDevCap : Short GI @ 20Mhz support */ +#define ISSUPP_SHORTGI20(Dot11nDevCap) (Dot11nDevCap & MBIT(23)) +/** HW_SPEC Dot11nDevCap : Rx LDPC support */ +#define ISSUPP_RXLDPC(Dot11nDevCap) (Dot11nDevCap & MBIT(22)) +/** HW_SPEC Dot11nDevCap : Number of TX BA streams supported */ +#define ISSUPP_GETTXBASTREAM(Dot11nDevCap) ((Dot11nDevCap >> 18) & 0xF) +/** HW_SPEC Dot11nDevCap : Channel BW support @ 40Mhz support */ +#define ISSUPP_CHANWIDTH40(Dot11nDevCap) (Dot11nDevCap & MBIT(17)) +/** HW_SPEC Dot11nDevCap : Channel BW support @ 20Mhz support */ +#define ISSUPP_CHANWIDTH20(Dot11nDevCap) (Dot11nDevCap & MBIT(16)) +/** HW_SPEC Dot11nDevCap : Channel BW support @ 10Mhz support */ +#define ISSUPP_CHANWIDTH10(Dot11nDevCap) (Dot11nDevCap & MBIT(15)) +/** Dot11nUsrCap : 40Mhz intolarance enabled */ +#define ISENABLED_40MHZ_INTOLARENT(Dot11nDevCap) (Dot11nDevCap & MBIT(8)) +/** Dot11nUsrCap : Reset 40Mhz intolarance enabled */ +#define RESET_40MHZ_INTOLARENT(Dot11nDevCap) (Dot11nDevCap &= ~MBIT(8)) +/** HW_SPEC Dot11nDevCap : Rx AntennaD support */ +#define ISSUPP_RXANTENNAD(Dot11nDevCap) (Dot11nDevCap & MBIT(7)) +/** HW_SPEC Dot11nDevCap : Rx AntennaC support */ +#define ISSUPP_RXANTENNAC(Dot11nDevCap) (Dot11nDevCap & MBIT(6)) +/** HW_SPEC Dot11nDevCap : Rx AntennaB support */ +#define ISSUPP_RXANTENNAB(Dot11nDevCap) (Dot11nDevCap & MBIT(5)) +/** HW_SPEC Dot11nDevCap : Rx AntennaA support */ +#define ISSUPP_RXANTENNAA(Dot11nDevCap) (Dot11nDevCap & MBIT(4)) +/** HW_SPEC Dot11nDevCap : Tx AntennaD support */ +#define ISSUPP_TXANTENNAD(Dot11nDevCap) (Dot11nDevCap & MBIT(3)) +/** HW_SPEC Dot11nDevCap : Tx AntennaC support */ +#define ISSUPP_TXANTENNAC(Dot11nDevCap) (Dot11nDevCap & MBIT(2)) +/** HW_SPEC Dot11nDevCap : Tx AntennaB support */ +#define ISSUPP_TXANTENNAB(Dot11nDevCap) (Dot11nDevCap & MBIT(1)) +/** HW_SPEC Dot11nDevCap : Tx AntennaA support */ +#define ISSUPP_TXANTENNAA(Dot11nDevCap) (Dot11nDevCap & MBIT(0)) + +/** HW_SPEC Dot11nDevCap : Set support of channel bw @ 40Mhz */ +#define SETSUPP_CHANWIDTH40(Dot11nDevCap) (Dot11nDevCap |= MBIT(17)) +/** HW_SPEC Dot11nDevCap : Reset support of channel bw @ 40Mhz */ +#define RESETSUPP_CHANWIDTH40(Dot11nDevCap) (Dot11nDevCap &= ~MBIT(17)) + +/** DevMCSSupported : Tx MCS supported */ +#define GET_TXMCSSUPP(DevMCSSupported) (DevMCSSupported >> 4) +/** DevMCSSupported : Rx MCS supported */ +#define GET_RXMCSSUPP(DevMCSSupported) (DevMCSSupported & 0x0f) + +/** GET HTCapInfo : Supported Channel BW */ +#define GETHT_SUPPCHANWIDTH(HTCapInfo) (HTCapInfo & MBIT(1)) +/** GET HTCapInfo : Support for Greenfield */ +#define GETHT_GREENFIELD(HTCapInfo) (HTCapInfo & MBIT(4)) +/** GET HTCapInfo : Support for Short GI @ 20Mhz */ +#define GETHT_SHORTGI20(HTCapInfo) (HTCapInfo & MBIT(5)) +/** GET HTCapInfo : Support for Short GI @ 40Mhz */ +#define GETHT_SHORTGI40(HTCapInfo) (HTCapInfo & MBIT(6)) +/** GET HTCapInfo : Support for Tx STBC */ +#define GETHT_TXSTBC(HTCapInfo) (HTCapInfo & MBIT(7)) + +/** GET HTCapInfo : Support for Rx STBC */ +#define GETHT_RXSTBC(HTCapInfo) ((HTCapInfo >> 8) & 0x03) +/** GET HTCapInfo : Support for Delayed ACK */ +#define GETHT_DELAYEDBACK(HTCapInfo) (HTCapInfo & MBIT(10)) +/** GET HTCapInfo : Support for Max AMSDU */ +#define GETHT_MAXAMSDU(HTCapInfo) (HTCapInfo & MBIT(11)) + +/** SET HTCapInfo : Set support for LDPC coding capability */ +#define SETHT_LDPCCODINGCAP(HTCapInfo) (HTCapInfo |= MBIT(0)) +/** SET HTCapInfo : Set support for Channel BW */ +#define SETHT_SUPPCHANWIDTH(HTCapInfo) (HTCapInfo |= MBIT(1)) +/** SET HTCapInfo : Set support for Greenfield */ +#define SETHT_GREENFIELD(HTCapInfo) (HTCapInfo |= MBIT(4)) +/** SET HTCapInfo : Set support for Short GI @ 20Mhz */ +#define SETHT_SHORTGI20(HTCapInfo) (HTCapInfo |= MBIT(5)) +/** SET HTCapInfo : Set support for Short GI @ 40Mhz */ +#define SETHT_SHORTGI40(HTCapInfo) (HTCapInfo |= MBIT(6)) +/** SET HTCapInfo : Set support for Tx STBC */ +#define SETHT_TXSTBC(HTCapInfo) (HTCapInfo |= MBIT(7)) +/** SET HTCapInfo : Set support for Rx STBC */ +#define SETHT_RXSTBC(HTCapInfo, value) (HTCapInfo |= (value << 8)) +/** SET HTCapInfo : Set support for delayed block ack */ +#define SETHT_DELAYEDBACK(HTCapInfo) (HTCapInfo |= MBIT(10)) +/** SET HTCapInfo : Set support for Max size AMSDU */ +#define SETHT_MAXAMSDU(HTCapInfo) (HTCapInfo |= MBIT(11)) +/** SET HTCapInfo : Set support for DSSS/CCK Rates @ 40Mhz */ +#define SETHT_DSSSCCK40(HTCapInfo) (HTCapInfo |= MBIT(12)) +/** SET HTCapInfo : Enable 40Mhz Intolarence */ +#define SETHT_40MHZ_INTOLARANT(HTCapInfo) (HTCapInfo |= MBIT(14)) +/** SET HTCapInfo : Disable Static SM power save */ +#define SETHT_STATIC_SMPS(HTCapInfo) ((HTCapInfo) |= (MBIT(2) | MBIT(3))) + +/** RESET HTCapInfo : Set support for LDPC coding capability */ +#define RESETHT_LDPCCODINGCAP(HTCapInfo) (HTCapInfo &= ~MBIT(0)) +/** RESET HTCapInfo : Set support for Channel BW */ +#define RESETHT_SUPPCHANWIDTH(HTCapInfo) (HTCapInfo &= ~MBIT(1)) +/** RESET HTCapInfo : Set support for Greenfield */ +#define RESETHT_GREENFIELD(HTCapInfo) (HTCapInfo &= ~MBIT(4)) +/** RESET HTCapInfo : Set support for Short GI @ 20Mhz */ +#define RESETHT_SHORTGI20(HTCapInfo) (HTCapInfo &= ~MBIT(5)) +/** RESET HTCapInfo : Set support for Short GI @ 40Mhz */ +#define RESETHT_SHORTGI40(HTCapInfo) (HTCapInfo &= ~MBIT(6)) +/** RESET HTCapInfo : Set support for Tx STBC */ +#define RESETHT_TXSTBC(HTCapInfo) (HTCapInfo &= ~MBIT(7)) +/** RESET HTCapInfo : Set support for Rx STBC */ +#define RESETHT_RXSTBC(HTCapInfo) (HTCapInfo &= ~(0x03 << 8)) +/** RESET HTCapInfo : Set support for delayed block ack */ +#define RESETHT_DELAYEDBACK(HTCapInfo) (HTCapInfo &= ~MBIT(10)) +/** RESET HTCapInfo : Set support for Max size AMSDU */ +#define RESETHT_MAXAMSDU(HTCapInfo) (HTCapInfo &= ~MBIT(11)) +/** RESET HTCapInfo : Disable 40Mhz Intolarence */ +#define RESETHT_40MHZ_INTOLARANT(HTCapInfo) (HTCapInfo &= ~MBIT(14)) +/** RESET HTCapInfo: Enable SM power save */ +#define RESETHT_SM_POWERSAVE(HTCapInfo) ((HTCapInfo) &= ~(MBIT(2) | MBIT(3))) +/** RESET HTExtCap : Clear RD Responder bit */ +#define RESETHT_EXTCAP_RDG(HTExtCap) (HTExtCap &= ~MBIT(11)) +/** SET MCS32 */ +#define SETHT_MCS32(x) (x[4] |= 1) +/** Set mcs set defined bit */ +#define SETHT_MCS_SET_DEFINED(x) (x[12] |= 1) +/** Set the highest Rx data rate */ +#define SETHT_RX_HIGHEST_DT_SUPP(x, y) ((*(t_u16 *) (x + 10)) = y) +/** AMPDU factor size */ +#define AMPDU_FACTOR_64K 0x03 +/** Set AMPDU size in A-MPDU paramter field */ +#define SETAMPDU_SIZE(x, y) do { \ + x = x & ~0x03; \ + x |= y & 0x03; \ +} while (0) \ +/** Set AMPDU spacing in A-MPDU paramter field */ +#define SETAMPDU_SPACING(x, y) do { \ + x = x & ~0x1c; \ + x |= (y & 0x07) << 2; \ +} while (0) \ + +/** RadioType : Support for Band A */ +#define ISSUPP_BANDA(FwCapInfo) (FwCapInfo & MBIT(10)) +/** RadioType : Support for 40Mhz channel BW */ +#define ISALLOWED_CHANWIDTH40(Field2) (Field2 & MBIT(2)) +/** RadioType : Set support 40Mhz channel */ +#define SET_CHANWIDTH40(Field2) (Field2 |= MBIT(2)) +/** RadioType : Reset support 40Mhz channel */ +#define RESET_CHANWIDTH40(Field2) (Field2 &= ~(MBIT(0) | MBIT(1) | MBIT(2))) +/** RadioType : Get secondary channel */ +#define GET_SECONDARYCHAN(Field2) (Field2 & (MBIT(0) | MBIT(1))) + +/** ExtCap : Support for FILS */ +#define ISSUPP_EXTCAP_FILS(ext_cap) (ext_cap.FILS) +/** ExtCap : Set support FILS */ +#define SET_EXTCAP_FILS(ext_cap) (ext_cap.FILS = 1) +/** ExtCap : Reset support FILS */ +#define RESET_EXTCAP_FILS(ext_cap) (ext_cap.FILS = 0) + +/** ExtCap : Support for TDLS */ +#define ISSUPP_EXTCAP_TDLS(ext_cap) (ext_cap.TDLSSupport) +/** ExtCap : Set support TDLS */ +#define SET_EXTCAP_TDLS(ext_cap) (ext_cap.TDLSSupport = 1) +/** ExtCap : Reset support TDLS */ +#define RESET_EXTCAP_TDLS(ext_cap) (ext_cap.TDLSSupport = 0) +/** ExtCap : Support for TDLS UAPSD */ +#define ISSUPP_EXTCAP_TDLS_UAPSD(ext_cap) (ext_cap.TDLSPeerUAPSDSupport) +/** ExtCap : Set support TDLS UAPSD */ +#define SET_EXTCAP_TDLS_UAPSD(ext_cap) (ext_cap.TDLSPeerUAPSDSupport = 1) +/** ExtCap : Reset support TDLS UAPSD */ +#define RESET_EXTCAP_TDLS_UAPSD(ext_cap) (ext_cap.TDLSPeerUAPSDSupport = 0) +/** ExtCap : Support for TDLS CHANNEL SWITCH */ +#define ISSUPP_EXTCAP_TDLS_CHAN_SWITCH(ext_cap) (ext_cap.TDLSChannelSwitching) +/** ExtCap : Set support TDLS CHANNEL SWITCH */ +#define SET_EXTCAP_TDLS_CHAN_SWITCH(ext_cap) (ext_cap.TDLSChannelSwitching = 1) +/** ExtCap : Reset support TDLS CHANNEL SWITCH */ +#define RESET_EXTCAP_TDLS_CHAN_SWITCH(ext_cap) (ext_cap.TDLSChannelSwitching = 0) + +/** ExtCap : Support for Interworking */ +#define ISSUPP_EXTCAP_INTERWORKING(ext_cap) (ext_cap.Interworking) +/** ExtCap : Set support Interworking */ +#define SET_EXTCAP_INTERWORKING(ext_cap) (ext_cap.Interworking = 1) +/** ExtCap : Reset support Interworking */ +#define RESET_EXTCAP_INTERWORKING(ext_cap) (ext_cap.Interworking = 0) +/** ExtCap : Support for Operation Mode Notification */ +#define ISSUPP_EXTCAP_OPERMODENTF(ext_cap) (ext_cap.OperModeNtf) +/** ExtCap : Set support Operation Mode Notification */ +#define SET_EXTCAP_OPERMODENTF(ext_cap) (ext_cap.OperModeNtf = 1) +/** ExtCap : Reset support Operation Mode Notification */ +#define RESET_EXTCAP_OPERMODENTF(ext_cap) (ext_cap.OperModeNtf = 0) +/** ExtCap : Support for QosMap */ +#define ISSUPP_EXTCAP_QOS_MAP(ext_cap) (ext_cap.Qos_Map) +/** ExtCap : Set Support QosMap */ +#define SET_EXTCAP_QOS_MAP(ext_cap) (ext_cap.Qos_Map = 1) +/** ExtCap : Reset support QosMap */ +#define RESET_EXTCAP_QOS_MAP(ext_cap) (ext_cap.Qos_Map = 0) +/** ExtCap : Support for BSS_Transition */ +#define ISSUPP_EXTCAP_BSS_TRANSITION(ext_cap) (ext_cap.BSS_Transition) +/** ExtCap : Set Support BSS_Transition */ +#define SET_EXTCAP_BSS_TRANSITION(ext_cap) (ext_cap.BSS_Transition = 1) +/** ExtCap : Reset support BSS_Transition */ +#define RESET_EXTCAP_BSS_TRANSITION(ext_cap) (ext_cap.BSS_Transition = 0) + +/** ExtCap : Support for TDLS wider bandwidth */ +#define ISSUPP_EXTCAP_TDLS_WIDER_BANDWIDTH(ext_cap) (ext_cap.TDLSWildBandwidth) +/** ExtCap : Set support TDLS wider bandwidth */ +#define SET_EXTCAP_TDLS_WIDER_BANDWIDTH(ext_cap) (ext_cap.TDLSWildBandwidth = 1) +/** ExtCap : Reset support TDLS wider bandwidth */ +#define RESET_EXTCAP_TDLS_WIDER_BANDWIDTH(ext_cap) (ext_cap.TDLSWildBandwidth = 0) + +/** ExtCap : Support for extend channel switch */ +#define ISSUPP_EXTCAP_EXT_CHANNEL_SWITCH(ext_cap) (ext_cap.ExtChanSwitching) +/** ExtCap : Set support Ext Channel Switch */ +#define SET_EXTCAP_EXT_CHANNEL_SWITCH(ext_cap) (ext_cap.ExtChanSwitching = 1) +/** ExtCap: Set Timing Measurement */ +#define SET_EXTCAP_EXT_TIMING_MEASUREMENT(ext_cap) (ext_cap.TimingMeasurement = 1) +/** ExtCap : Reset support Ext Channel Switch */ +#define RESET_EXTCAP_EXT_CHANNEL_SWITCH(ext_cap) (ext_cap.ExtChanSwitching = 0) + +/** ExtCap : Set FTMI bit(bit 71) */ +#define SET_EXTCAP_FTMI(ext_cap) (ext_cap.FTMI = 1) +#define SET_EXTCAP_INTERNETWORKING(ext_cap) (ext_cap.Interworking = 1) +/** LLC/SNAP header len */ +#define LLC_SNAP_LEN 8 + +/** TLV type : Rate scope */ +#define TLV_TYPE_RATE_DROP_PATTERN (PROPRIETARY_TLV_BASE_ID + 0x51) /* 0x0151 */ +/** TLV type : Rate drop pattern */ +#define TLV_TYPE_RATE_DROP_CONTROL (PROPRIETARY_TLV_BASE_ID + 0x52) /* 0x0152 */ +/** TLV type : Rate scope */ +#define TLV_TYPE_RATE_SCOPE (PROPRIETARY_TLV_BASE_ID + 0x53) /* 0x0153 */ + +/** TLV type : Power group */ +#define TLV_TYPE_POWER_GROUP (PROPRIETARY_TLV_BASE_ID + 0x54) /* 0x0154 */ + +/** Modulation class for DSSS Rates */ +#define MOD_CLASS_HR_DSSS 0x03 +/** Modulation class for OFDM Rates */ +#define MOD_CLASS_OFDM 0x07 +/** Modulation class for HT Rates */ +#define MOD_CLASS_HT 0x08 +/** Modulation class for VHT Rates */ +#define MOD_CLASS_VHT 0x09 +/** HT bandwidth 20 MHz */ +#define HT_BW_20 0 +/** HT bandwidth 40 MHz */ +#define HT_BW_40 1 +/** HT bandwidth 80 MHz */ +#define HT_BW_80 2 + +/** TLV type : Scan Response */ +#define TLV_TYPE_BSS_SCAN_RSP (PROPRIETARY_TLV_BASE_ID + 0x56) /* 0x0156 */ +/** TLV type : Scan Response Stats */ +#define TLV_TYPE_BSS_SCAN_INFO (PROPRIETARY_TLV_BASE_ID + 0x57) /* 0x0157 */ + +/** TLV type : 11h Basic Rpt */ +#define TLV_TYPE_CHANRPT_11H_BASIC (PROPRIETARY_TLV_BASE_ID + 0x5b) /* 0x015b */ + +/** TLV type : Action frame */ +#define TLV_TYPE_IEEE_ACTION_FRAME (PROPRIETARY_TLV_BASE_ID + 0x8c) /* 0x018c */ + +/** TLV type : SCAN channel gap */ +#define TLV_TYPE_SCAN_CHANNEL_GAP (PROPRIETARY_TLV_BASE_ID + 0xc5) /* 0x01c5 */ +/** TLV type : Channel statistics */ +#define TLV_TYPE_CHANNEL_STATS (PROPRIETARY_TLV_BASE_ID + 0xc6) /* 0x01c6 */ +/** TLV type : BSS_MODE */ +#define TLV_TYPE_BSS_MODE (PROPRIETARY_TLV_BASE_ID + 0xce) /* 0x01ce */ + +/** Firmware Host Command ID Constants */ +/** Host Command ID : Get hardware specifications */ +#define HostCmd_CMD_GET_HW_SPEC 0x0003 +/** Host Command ID : 802.11 scan */ +#define HostCmd_CMD_802_11_SCAN 0x0006 +/** Host Command ID : 802.11 get log */ +#define HostCmd_CMD_802_11_GET_LOG 0x000b + +/** Host Command ID : MAC multicast address */ +#define HostCmd_CMD_MAC_MULTICAST_ADR 0x0010 +/** Host Command ID : 802.11 EEPROM access */ +#define HostCmd_CMD_802_11_EEPROM_ACCESS 0x0059 +/** Host Command ID : 802.11 associate */ +#define HostCmd_CMD_802_11_ASSOCIATE 0x0012 + +/** Host Command ID : 802.11 SNMP MIB */ +#define HostCmd_CMD_802_11_SNMP_MIB 0x0016 +/** Host Command ID : MAC register access */ +#define HostCmd_CMD_MAC_REG_ACCESS 0x0019 +/** Host Command ID : BBP register access */ +#define HostCmd_CMD_BBP_REG_ACCESS 0x001a +/** Host Command ID : RF register access */ +#define HostCmd_CMD_RF_REG_ACCESS 0x001b + +/** Host Command ID : 802.11 radio control */ +#define HostCmd_CMD_802_11_RADIO_CONTROL 0x001c +/** Host Command ID : 802.11 RF channel */ +#define HostCmd_CMD_802_11_RF_CHANNEL 0x001d +/** Host Command ID : 802.11 RF Tx power */ +#define HostCmd_CMD_802_11_RF_TX_POWER 0x001e + +/** Host Command ID : 802.11 RF antenna */ +#define HostCmd_CMD_802_11_RF_ANTENNA 0x0020 + +/** Host Command ID : 802.11 deauthenticate */ +#define HostCmd_CMD_802_11_DEAUTHENTICATE 0x0024 +/** Host Command ID: 802.11 disassoicate */ +#define HostCmd_CMD_802_11_DISASSOCIATE 0x0026 +/** Host Command ID : MAC control */ +#define HostCmd_CMD_MAC_CONTROL 0x0028 +/** Host Command ID : 802.11 Ad-Hoc start */ +#define HostCmd_CMD_802_11_AD_HOC_START 0x002b +/** Host Command ID : 802.11 Ad-Hoc join */ +#define HostCmd_CMD_802_11_AD_HOC_JOIN 0x002c + +/** Host Command ID: CW Mode */ +#define HostCmd_CMD_CW_MODE_CTRL 0x0239 + +/** Host Command ID : 802.11 key material */ +#define HostCmd_CMD_802_11_KEY_MATERIAL 0x005e + +/** Host Command ID : 802.11 Ad-Hoc stop */ +#define HostCmd_CMD_802_11_AD_HOC_STOP 0x0040 + +/** Host Command ID : 802.22 MAC address */ +#define HostCmd_CMD_802_11_MAC_ADDRESS 0x004D + +/** Host Command ID : WMM Traffic Stream Status */ +#define HostCmd_CMD_WMM_TS_STATUS 0x005d + +/** Host Command ID : 802.11 D domain information */ +#define HostCmd_CMD_802_11D_DOMAIN_INFO 0x005b + +/** Host Command ID : 802.11 K Feature Control */ +#define HostCmd_CMD_OFFLOAD_FEATURE_CONTROL 0x00fd +/** Host Command ID : 802.11 K Get Neighbor AP list*/ +#define HostCmd_CMD_802_11K_GET_NLIST 0x0231 + +/** Host Command ID : 802.11 TPC information */ +#define HostCmd_CMD_802_11_TPC_INFO 0x005f +/** Host Command ID : 802.11 TPC adapt req */ +#define HostCmd_CMD_802_11_TPC_ADAPT_REQ 0x0060 +/** Host Command ID : 802.11 channel SW ann */ +#define HostCmd_CMD_802_11_CHAN_SW_ANN 0x0061 + +/** Host Command ID : Measurement request */ +#define HostCmd_CMD_MEASUREMENT_REQUEST 0x0062 +/** Host Command ID : Measurement report */ +#define HostCmd_CMD_MEASUREMENT_REPORT 0x0063 + +/** Host Command ID : 802.11 sleep parameters */ +#define HostCmd_CMD_802_11_SLEEP_PARAMS 0x0066 + +/** Host Command ID : 802.11 ps inactivity timeout */ +#define HostCmd_CMD_802_11_PS_INACTIVITY_TIMEOUT 0x0067 + +/** Host Command ID : 802.11 sleep period */ +#define HostCmd_CMD_802_11_SLEEP_PERIOD 0x0068 + +/** Host Command ID: 802.11 BG scan config */ +#define HostCmd_CMD_802_11_BG_SCAN_CONFIG 0x006b +/** Host Command ID : 802.11 BG scan query */ +#define HostCmd_CMD_802_11_BG_SCAN_QUERY 0x006c + +/** Host Command ID : WMM ADDTS req */ +#define HostCmd_CMD_WMM_ADDTS_REQ 0x006E +/** Host Command ID : WMM DELTS req */ +#define HostCmd_CMD_WMM_DELTS_REQ 0x006F +/** Host Command ID : WMM queue configuration */ +#define HostCmd_CMD_WMM_QUEUE_CONFIG 0x0070 +/** Host Command ID : 802.11 get status */ +#define HostCmd_CMD_WMM_GET_STATUS 0x0071 + +/** Host Command ID : 802.11 firmware wakeup method */ +#define HostCmd_CMD_802_11_FW_WAKE_METHOD 0x0074 +/** Host Command ID : 802.11 subscribe event */ +#define HostCmd_CMD_802_11_SUBSCRIBE_EVENT 0x0075 + +/** Host Command ID : 802.11 Tx rate query */ +#define HostCmd_CMD_802_11_TX_RATE_QUERY 0x007f +/** Host Command ID :Get timestamp value */ +#define HostCmd_CMD_GET_TSF 0x0080 + +/** Host Command ID : WMM queue stats */ +#define HostCmd_CMD_WMM_QUEUE_STATS 0x0081 + +/** Host Command ID : 802.11 IBSS coalescing status */ +#define HostCmd_CMD_802_11_IBSS_COALESCING_STATUS 0x0083 + +/** Host Command ID : Memory access */ +#define HostCmd_CMD_MEM_ACCESS 0x0086 + +/** Host Command ID : SDIO GPIO interrupt configuration */ +#define HostCmd_CMD_SDIO_GPIO_INT_CONFIG 0x0088 + +#ifdef MFG_CMD_SUPPORT +/** Host Command ID : Mfg command */ +#define HostCmd_CMD_MFG_COMMAND 0x0089 +#endif +/** Host Command ID : Inactivity timeout ext */ +#define HostCmd_CMD_INACTIVITY_TIMEOUT_EXT 0x008a + +/** Host Command ID : DBGS configuration */ +#define HostCmd_CMD_DBGS_CFG 0x008b +/** Host Command ID : Get memory */ +#define HostCmd_CMD_GET_MEM 0x008c + +/** Host Command ID : Cal data dnld */ +#define HostCmd_CMD_CFG_DATA 0x008f + +/** Host Command ID : SDIO pull control */ +#define HostCmd_CMD_SDIO_PULL_CTRL 0x0093 + +/** Host Command ID : ECL system clock configuration */ +#define HostCmd_CMD_ECL_SYSTEM_CLOCK_CONFIG 0x0094 + +/** Host Command ID : Extended version */ +#define HostCmd_CMD_VERSION_EXT 0x0097 + +/** Host Command ID : MEF configuration */ +#define HostCmd_CMD_MEF_CFG 0x009a + +/** Host Command ID : 802.11 RSSI INFO*/ +#define HostCmd_CMD_RSSI_INFO 0x00a4 + +/** Host Command ID : Function initialization */ +#define HostCmd_CMD_FUNC_INIT 0x00a9 +/** Host Command ID : Function shutdown */ +#define HostCmd_CMD_FUNC_SHUTDOWN 0x00aa + +/** Host Command ID : Robustcoex */ +#define HostCmd_CMD_802_11_ROBUSTCOEX 0x00e0 + +/** Host Command ID :EAPOL PKT */ +#define HostCmd_CMD_802_11_EAPOL_PKT 0x012e + +/** Host Command ID : 802.11 RSSI INFO EXT*/ +#define HostCmd_CMD_RSSI_INFO_EXT 0x0237 +/** Host Command ID : ROAMING OFFLOAD TO FW*/ +#define HostCmd_CMD_ROAM_OFFLOAD 0x0245 + +/** Host Command ID: Multi chan config */ +#define HostCmd_CMD_MULTI_CHAN_CONFIG 0x011e +/** Host Command ID: Multi chan policy */ +#define HostCmd_CMD_MULTI_CHAN_POLICY 0x0121 +/** TLV ID for multi chan info */ +#define TLV_TYPE_MULTI_CHAN_INFO (PROPRIETARY_TLV_BASE_ID + 0xb7) +/** TLV ID for multi chan group info */ +#define TLV_TYPE_MULTI_CHAN_GROUP_INFO_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0xb8) +/** TLV ID for DRCS TimeSlice */ +#define MRVL_DRCS_TIME_SLICE_TLV_ID (PROPRIETARY_TLV_BASE_ID + 263) +/** Host Command ID: DRCS config */ +#define HostCmd_CMD_DRCS_CONFIG 0x024a + +#ifdef RX_PACKET_COALESCE +/** TLV ID for RX pkt coalesce config */ +#define TLV_TYPE_RX_PKT_COAL_CONFIG (PROPRIETARY_TLV_BASE_ID + 0xC9) +#endif + +/** Host Command ID : Channel report request */ +#define HostCmd_CMD_CHAN_REPORT_REQUEST 0x00dd + +/** Host Command ID: SUPPLICANT_PMK */ +#define HostCmd_CMD_SUPPLICANT_PMK 0x00c4 +/** Host Command ID: SUPPLICANT_PROFILE */ +#define HostCmd_CMD_SUPPLICANT_PROFILE 0x00c5 + +/** Host Command ID : Add Block Ack Request */ +#define HostCmd_CMD_11N_ADDBA_REQ 0x00ce +/** Host Command ID : Delete a Block Ack Request */ +#define HostCmd_CMD_11N_CFG 0x00cd +/** Host Command ID : Add Block Ack Response */ +#define HostCmd_CMD_11N_ADDBA_RSP 0x00cf +/** Host Command ID : Delete a Block Ack Request */ +#define HostCmd_CMD_11N_DELBA 0x00d0 +/** Host Command ID: Configure Tx Buf size */ +#define HostCmd_CMD_RECONFIGURE_TX_BUFF 0x00d9 +/** Host Command ID: AMSDU Aggr Ctrl */ +#define HostCmd_CMD_AMSDU_AGGR_CTRL 0x00df +/** Host Command ID: Configure TX Beamforming capability */ +#define HostCmd_CMD_TX_BF_CFG 0x0104 + +/** Host Command ID : 802.11 TX power configuration */ +#define HostCmd_CMD_TXPWR_CFG 0x00d1 + +/** Host Command ID : Soft Reset */ +#define HostCmd_CMD_SOFT_RESET 0x00d5 + +/** Host Command ID : 802.11 b/g/n rate configration */ +#define HostCmd_CMD_TX_RATE_CFG 0x00d6 + +/** Host Command ID : Enhanced PS mode */ +#define HostCmd_CMD_802_11_PS_MODE_ENH 0x00e4 + +/** Host command action : Host sleep configuration */ +#define HostCmd_CMD_802_11_HS_CFG_ENH 0x00e5 + +/** Host Command ID : CAU register access */ +#define HostCmd_CMD_CAU_REG_ACCESS 0x00ed + +/** Host Command ID : mgmt IE list */ +#define HostCmd_CMD_MGMT_IE_LIST 0x00f2 + +/** Host Command ID : TDLS configuration */ +#define HostCmd_CMD_TDLS_CONFIG 0x0100 +/** Host Command ID : TDLS operation */ +#define HostCmd_CMD_TDLS_OPERATION 0x0122 + +/** Host Command ID : SDIO single port RX aggr */ +#define HostCmd_CMD_SDIO_SP_RX_AGGR_CFG 0x0223 +/** fw_cap_info bit16 for sdio sp rx aggr flag*/ +#define SDIO_SP_RX_AGGR_ENABLE MBIT(16) + +/* fw_cap_info bit18 for ecsa support*/ +#define FW_CAPINFO_ECSA MBIT(18) + +/* fw_cap_info bit20 for get log*/ +#define FW_CAPINFO_GET_LOG MBIT(20) + +/** fw_cap_info bit22 for embedded supplicant support*/ +#define FW_CAPINFO_SUPPLICANT_SUPPORT MBIT(21) + +/** fw_cap_info bit23 for embedded authenticator support*/ +#define FW_CAPINFO_AUTH_SUPPORT MBIT(22) + +/** fw_cap_info bit23 for firmware roaming*/ +#define FW_ROAMING_SUPPORT MBIT(23) + +/** fw_cap_info bit25 for adhoc support*/ +#define FW_CAPINFO_ADHOC_SUPPORT MBIT(25) +/** Check if adhoc is supported by firmware */ +#define IS_FW_SUPPORT_ADHOC(_adapter) (_adapter->fw_cap_info & FW_CAPINFO_ADHOC_SUPPORT) + +/** Check if supplicant is supported by firmware */ +#define IS_FW_SUPPORT_SUPPLICANT(_adapter) (_adapter->fw_cap_info & FW_CAPINFO_SUPPLICANT_SUPPORT) + +/** Check if authenticator is supported by firmware */ +#define IS_FW_SUPPORT_AUTHENTICATOR(_adapter) (_adapter->fw_cap_info & FW_CAPINFO_AUTH_SUPPORT) + +#ifdef RX_PACKET_COALESCE +/** Host Command ID : Rx packet coalescing configuration */ +#define HostCmd_CMD_RX_PKT_COALESCE_CFG 0x012c +#endif + +/** Host Command ID : Extended scan support */ +#define HostCmd_CMD_802_11_SCAN_EXT 0x0107 + +/** Host Command ID : Forward mgmt frame */ +#define HostCmd_CMD_RX_MGMT_IND 0x010c + +/** Host Command ID : Set BSS_MODE */ +#define HostCmd_CMD_SET_BSS_MODE 0x00f7 + +#ifdef UAP_SUPPORT +/** Host Command id: SYS_INFO */ +#define HOST_CMD_APCMD_SYS_INFO 0x00ae +/** Host Command id: sys_reset */ +#define HOST_CMD_APCMD_SYS_RESET 0x00af +/** Host Command id: SYS_CONFIGURE */ +#define HOST_CMD_APCMD_SYS_CONFIGURE 0x00b0 +/** Host Command id: BSS_START */ +#define HOST_CMD_APCMD_BSS_START 0x00b1 +/** Host Command id: BSS_STOP */ +#define HOST_CMD_APCMD_BSS_STOP 0x00b2 +/** Host Command id: sta_list */ +#define HOST_CMD_APCMD_STA_LIST 0x00b3 +/** Host Command id: STA_DEAUTH */ +#define HOST_CMD_APCMD_STA_DEAUTH 0x00b5 + +/** Host Command id: UAP_OPER_CTRL */ +#define HOST_CMD_APCMD_OPER_CTRL 0x0233 +#endif /* UAP_SUPPORT */ + +/** Host Command id: PMIC CONFIGURE*/ +#define HOST_CMD_PMIC_CONFIGURE 0x23E + +/** Host Command ID: 802.11 Network Monitor */ +#define HostCmd_CMD_802_11_NET_MONITOR 0x0102 + +/** Host Command ID: Tx data pause */ +#define HostCmd_CMD_CFG_TX_DATA_PAUSE 0x0103 + +#ifdef WIFI_DIRECT_SUPPORT +/** Host Command ID: P2P PARAMS CONFIG */ +#define HOST_CMD_P2P_PARAMS_CONFIG 0x00ea +/** Host Command ID: WIFI_DIRECT_MODE_CONFIG */ +#define HOST_CMD_WIFI_DIRECT_MODE_CONFIG 0x00eb +#endif + +/** Host Command ID: Remain On Channel */ +#define HostCmd_CMD_802_11_REMAIN_ON_CHANNEL 0x010d + +#define HostCmd_CMD_COALESCE_CFG 0x010a + +/** Host Command ID: GTK REKEY OFFLOAD CFG */ +#define HostCmd_CMD_GTK_REKEY_OFFLOAD_CFG 0x010f + +/** Host Command ID : OTP user data */ +#define HostCmd_CMD_OTP_READ_USER_DATA 0x0114 + +/** Host Command ID: HS wakeup reason */ +#define HostCmd_CMD_HS_WAKEUP_REASON 0x0116 + +/** Host Command ID: reject addba request */ +#define HostCmd_CMD_REJECT_ADDBA_REQ 0x0119 + +#define HostCmd_CMD_FW_DUMP_EVENT 0x0125 + +/** Host Command ID : Target device access */ +#define HostCmd_CMD_TARGET_ACCESS 0x012a + +/** Host Command ID: DFS repeater mode */ +#define HostCmd_DFS_REPEATER_MODE 0x012b + +/** Host Command ID: Get sensor temp*/ +#define HostCmd_DS_GET_SENSOR_TEMP 0x0227 + +/** Host Command ID : Configure ADHOC_OVER_IP parameters */ +#define HostCmd_CMD_WMM_PARAM_CONFIG 0x023a + +#ifdef STA_SUPPORT +/** Host Command ID : set/get sta configure */ +#define HostCmd_CMD_STA_CONFIGURE 0x023f +#endif + +/** Host Command ID : GPIO independent reset configure */ +#define HostCmd_CMD_INDEPENDENT_RESET_CFG 0x0243 + +#define HostCmd_CMD_HOST_CLOCK_CFG 0x0246 + +/** Channel usability flags */ +#define MARVELL_CHANNEL_DISABLED MBIT(7) +#define MARVELL_CHANNEL_NOHT160 MBIT(4) +#define MARVELL_CHANNEL_NOHT80 MBIT(3) +#define MARVELL_CHANNEL_NOHT40 MBIT(2) +#define MARVELL_CHANNEL_DFS MBIT(1) +#define MARVELL_CHANNEL_PASSIVE MBIT(0) + +#define HostCmd_CMD_CHAN_REGION_CFG 0x0242 +/* OTP Region info */ +typedef MLAN_PACK_START struct _otp_region_info { + t_u8 country_code[2]; + t_u8 region_code; + t_u8 environment; + t_u16 force_reg:1; + t_u16 reserved:15; +} MLAN_PACK_END otp_region_info_t; + +#define FW_CFP_TABLE_MAX_ROWS_BG 14 +#define FW_CFP_TABLE_MAX_COLS_BG 11 + +#define FW_CFP_TABLE_MAX_ROWS_A 39 +#define FW_CFP_TABLE_MAX_COLS_A 17 + +#define HostCmd_CMD_BOOT_SLEEP 0x0258 + +/** Enhanced PS modes */ +typedef enum _ENH_PS_MODES { + GET_PS = 0, + SLEEP_CONFIRM = 5, + DIS_AUTO_PS = 0xfe, + EN_AUTO_PS = 0xff, +} ENH_PS_MODES; + +/** Command RET code, MSB is set to 1 */ +#define HostCmd_RET_BIT 0x8000 + +/** General purpose action : Get */ +#define HostCmd_ACT_GEN_GET 0x0000 +/** General purpose action : Set */ +#define HostCmd_ACT_GEN_SET 0x0001 +/** General purpose action : Get_Current */ +#define HostCmd_ACT_GEN_GET_CURRENT 0x0003 +/** General purpose action : Remove */ +#define HostCmd_ACT_GEN_REMOVE 0x0004 +/** General purpose action : Reset */ +#define HostCmd_ACT_GEN_RESET 0x0005 + +/** Host command action : Set Rx */ +#define HostCmd_ACT_SET_RX 0x0001 +/** Host command action : Set Tx */ +#define HostCmd_ACT_SET_TX 0x0002 +/** Host command action : Set both Rx and Tx */ +#define HostCmd_ACT_SET_BOTH 0x0003 +/** Host command action : Get Rx */ +#define HostCmd_ACT_GET_RX 0x0004 +/** Host command action : Get Tx */ +#define HostCmd_ACT_GET_TX 0x0008 +/** Host command action : Get both Rx and Tx */ +#define HostCmd_ACT_GET_BOTH 0x000c + +/** General Result Code*/ +/** General result code OK */ +#define HostCmd_RESULT_OK 0x0000 +/** Genenral error */ +#define HostCmd_RESULT_ERROR 0x0001 +/** Command is not valid */ +#define HostCmd_RESULT_NOT_SUPPORT 0x0002 +/** Command is pending */ +#define HostCmd_RESULT_PENDING 0x0003 +/** System is busy (command ignored) */ +#define HostCmd_RESULT_BUSY 0x0004 +/** Data buffer is not big enough */ +#define HostCmd_RESULT_PARTIAL_DATA 0x0005 + +/* Define action or option for HostCmd_CMD_MAC_CONTROL */ +/** MAC action : Rx on */ +#define HostCmd_ACT_MAC_RX_ON 0x0001 +/** MAC action : Tx on */ +#define HostCmd_ACT_MAC_TX_ON 0x0002 +/** MAC action : WEP enable */ +#define HostCmd_ACT_MAC_WEP_ENABLE 0x0008 +/** MAC action : EthernetII enable */ +#define HostCmd_ACT_MAC_ETHERNETII_ENABLE 0x0010 +/** MAC action : Promiscous mode enable */ +#define HostCmd_ACT_MAC_PROMISCUOUS_ENABLE 0x0080 +/** MAC action : All multicast enable */ +#define HostCmd_ACT_MAC_ALL_MULTICAST_ENABLE 0x0100 +/** MAC action : RTS/CTS enable */ +#define HostCmd_ACT_MAC_RTS_CTS_ENABLE 0x0200 +/** MAC action : Strict protection enable */ +#define HostCmd_ACT_MAC_STRICT_PROTECTION_ENABLE 0x0400 +/** MAC action : Force 11n protection disable */ +#define HostCmd_ACT_MAC_FORCE_11N_PROTECTION_OFF 0x0800 +/** MAC action : Ad-Hoc G protection on */ +#define HostCmd_ACT_MAC_ADHOC_G_PROTECTION_ON 0x2000 + +/* Define action or option for HostCmd_CMD_802_11_SCAN */ +/** Scan type : BSS */ +#define HostCmd_BSS_MODE_BSS 0x0001 +/** Scan type : IBSS */ +#define HostCmd_BSS_MODE_IBSS 0x0002 +/** Scan type : Any */ +#define HostCmd_BSS_MODE_ANY 0x0003 + +/** Define bitmap conditions for HOST_SLEEP_CFG : GPIO FF */ +#define HOST_SLEEP_CFG_GPIO_FF 0xff +/** Define bitmap conditions for HOST_SLEEP_CFG : GAP FF */ +#define HOST_SLEEP_CFG_GAP_FF 0xff + +/** Buffer Constants */ +/** Number of command buffers */ +#define MRVDRV_NUM_OF_CMD_BUFFER 30 +/** Size of command buffer */ +#define MRVDRV_SIZE_OF_CMD_BUFFER (2 * 1024) + +/** Maximum number of BSS Descriptors */ +#define MRVDRV_MAX_BSSID_LIST 200 + +/** Host command flag in command */ +#define CMD_F_HOSTCMD (1 << 0) +/** command cancel flag in command */ +#define CMD_F_CANCELED (1 << 1) +/** scan command flag */ +#define CMD_F_SCAN (1 << 2) + +/** Host Command ID bit mask (bit 11:0) */ +#define HostCmd_CMD_ID_MASK 0x0fff + +/** Host Command Sequence number mask (bit 7:0) */ +#define HostCmd_SEQ_NUM_MASK 0x00ff + +/** Host Command BSS number mask (bit 11:8) */ +#define HostCmd_BSS_NUM_MASK 0x0f00 + +/** Host Command BSS type mask (bit 15:12) */ +#define HostCmd_BSS_TYPE_MASK 0xf000 + +/** Set BSS information to Host Command */ +#define HostCmd_SET_SEQ_NO_BSS_INFO(seq, num, type) \ + ((((seq) & 0x00ff) | \ + (((num) & 0x000f) << 8)) | \ + (((type) & 0x000f) << 12)) + +/** Get Sequence Number from Host Command (bit 7:0) */ +#define HostCmd_GET_SEQ_NO(seq) \ + ((seq) & HostCmd_SEQ_NUM_MASK) + +/** Get BSS number from Host Command (bit 11:8) */ +#define HostCmd_GET_BSS_NO(seq) \ + (((seq) & HostCmd_BSS_NUM_MASK) >> 8) + +/** Get BSS type from Host Command (bit 15:12) */ +#define HostCmd_GET_BSS_TYPE(seq) \ + (((seq) & HostCmd_BSS_TYPE_MASK) >> 12) + +/** Card Event definition : Dummy host wakeup signal */ +#define EVENT_DUMMY_HOST_WAKEUP_SIGNAL 0x00000001 +/** Card Event definition : Link lost */ +#define EVENT_LINK_LOST 0x00000003 +/** Card Event definition : Link sensed */ +#define EVENT_LINK_SENSED 0x00000004 +/** Card Event definition : MIB changed */ +#define EVENT_MIB_CHANGED 0x00000006 +/** Card Event definition : Init done */ +#define EVENT_INIT_DONE 0x00000007 +/** Card Event definition : Deauthenticated */ +#define EVENT_DEAUTHENTICATED 0x00000008 +/** Card Event definition : Disassociated */ +#define EVENT_DISASSOCIATED 0x00000009 +/** Card Event definition : Power save awake */ +#define EVENT_PS_AWAKE 0x0000000a +/** Card Event definition : Power save sleep */ +#define EVENT_PS_SLEEP 0x0000000b +/** Card Event definition : MIC error multicast */ +#define EVENT_MIC_ERR_MULTICAST 0x0000000d +/** Card Event definition : MIC error unicast */ +#define EVENT_MIC_ERR_UNICAST 0x0000000e + +/** Card Event definition : Ad-Hoc BCN lost */ +#define EVENT_ADHOC_BCN_LOST 0x00000011 + +/** Card Event definition : Stop Tx */ +#define EVENT_STOP_TX 0x00000013 +/** Card Event definition : Start Tx */ +#define EVENT_START_TX 0x00000014 +/** Card Event definition : Channel switch */ +#define EVENT_CHANNEL_SWITCH 0x00000015 + +/** Card Event definition : MEAS report ready */ +#define EVENT_MEAS_REPORT_RDY 0x00000016 + +/** Card Event definition : WMM status change */ +#define EVENT_WMM_STATUS_CHANGE 0x00000017 + +/** Card Event definition : BG scan report */ +#define EVENT_BG_SCAN_REPORT 0x00000018 +/** Card Event definition : BG scan stopped */ +#define EVENT_BG_SCAN_STOPPED 0x00000065 + +/** Card Event definition : Beacon RSSI low */ +#define EVENT_RSSI_LOW 0x00000019 +/** Card Event definition : Beacon SNR low */ +#define EVENT_SNR_LOW 0x0000001a +/** Card Event definition : Maximum fail */ +#define EVENT_MAX_FAIL 0x0000001b +/** Card Event definition : Beacon RSSI high */ +#define EVENT_RSSI_HIGH 0x0000001c +/** Card Event definition : Beacon SNR high */ +#define EVENT_SNR_HIGH 0x0000001d + +/** Card Event definition : IBSS coalsced */ +#define EVENT_IBSS_COALESCED 0x0000001e + +/** Event definition : IBSS station connected */ +#define EVENT_IBSS_STATION_CONNECT 0x00000020 +/** Event definition : IBSS station dis-connected */ +#define EVENT_IBSS_STATION_DISCONNECT 0x00000021 + +/** Card Event definition : Data RSSI low */ +#define EVENT_DATA_RSSI_LOW 0x00000024 +/** Card Event definition : Data SNR low */ +#define EVENT_DATA_SNR_LOW 0x00000025 +/** Card Event definition : Data RSSI high */ +#define EVENT_DATA_RSSI_HIGH 0x00000026 +/** Card Event definition : Data SNR high */ +#define EVENT_DATA_SNR_HIGH 0x00000027 + +/** Card Event definition : Link Quality */ +#define EVENT_LINK_QUALITY 0x00000028 + +/** Card Event definition : Port release event */ +#define EVENT_PORT_RELEASE 0x0000002b + +/** Card Event definition : Pre-Beacon Lost */ +#define EVENT_PRE_BEACON_LOST 0x00000031 + +/** Card Event definition : Add BA event */ +#define EVENT_ADDBA 0x00000033 +/** Card Event definition : Del BA event */ +#define EVENT_DELBA 0x00000034 +/** Card Event definition: BA stream timeout*/ +#define EVENT_BA_STREAM_TIMEOUT 0x00000037 + +/** Card Event definition : AMSDU aggr control */ +#define EVENT_AMSDU_AGGR_CTRL 0x00000042 + +/** Card Event definition: WEP ICV error */ +#define EVENT_WEP_ICV_ERR 0x00000046 + +/** Card Event definition : Host sleep enable */ +#define EVENT_HS_ACT_REQ 0x00000047 + +/** Card Event definition : BW changed */ +#define EVENT_BW_CHANGE 0x00000048 + +#ifdef WIFI_DIRECT_SUPPORT +/** WIFIDIRECT generic event */ +#define EVENT_WIFIDIRECT_GENERIC_EVENT 0x00000049 +/** WIFIDIRECT service discovery event */ +#define EVENT_WIFIDIRECT_SERVICE_DISCOVERY 0x0000004a +#endif + +/** Remain on Channel expired event */ +#define EVENT_REMAIN_ON_CHANNEL_EXPIRED 0x0000005f + +/** TDLS generic event */ +#define EVENT_TDLS_GENERIC_EVENT 0x00000052 + +/** Card Event definition: Channel switch pending announcment */ +#define EVENT_CHANNEL_SWITCH_ANN 0x00000050 + +/** Event definition: Radar Detected by card */ +#define EVENT_RADAR_DETECTED 0x00000053 + +/** Event definition: Radar Detected by card */ +#define EVENT_CHANNEL_REPORT_RDY 0x00000054 + +/** Event definition: Scan results through event */ +#define EVENT_EXT_SCAN_REPORT 0x00000058 +/** Enhance ext scan done event */ +#define EVENT_EXT_SCAN_STATUS_REPORT 0x0000007f + +/** Event definition : FW debug information */ +#define EVENT_FW_DEBUG_INFO 0x00000063 + +/** Event definition: RXBA_SYNC */ +#define EVENT_RXBA_SYNC 0x00000059 + +#ifdef UAP_SUPPORT +/** Event ID: STA deauth */ +#define EVENT_MICRO_AP_STA_DEAUTH 0x0000002c +/** Event ID: STA assoicated */ +#define EVENT_MICRO_AP_STA_ASSOC 0x0000002d +/** Event ID: BSS started */ +#define EVENT_MICRO_AP_BSS_START 0x0000002e +/** Event ID: BSS idle event */ +#define EVENT_MICRO_AP_BSS_IDLE 0x00000043 +/** Event ID: BSS active event */ +#define EVENT_MICRO_AP_BSS_ACTIVE 0x00000044 + +#endif /* UAP_SUPPORT */ + +/** Event ID: TX data pause event */ +#define EVENT_TX_DATA_PAUSE 0x00000055 + +/** Event ID: SAD Report */ +#define EVENT_SAD_REPORT 0x00000066 + +/** Event ID: Multi Chan Info*/ +#define EVENT_MULTI_CHAN_INFO 0x0000006a + +/** Event ID: Tx status */ +#define EVENT_TX_STATUS_REPORT 0x00000074 + +#define EVENT_BT_COEX_WLAN_PARA_CHANGE 0x00000076 + +#define EVENT_ROAM_OFFLOAD 0x00000083 + +#define EVENT_NLIST_REPORT 0x00000079 + +#define EVENT_EXCEED_MAX_P2P_CONN 0x00000089 + +#define EVENT_FW_DUMP_INFO 0x00000073 +/** Event ID mask */ +#define EVENT_ID_MASK 0xffff + +/** BSS number mask */ +#define BSS_NUM_MASK 0xf + +/** Get BSS number from event cause (bit 23:16) */ +#define EVENT_GET_BSS_NUM(event_cause) \ + (((event_cause) >> 16) & BSS_NUM_MASK) + +/** Get BSS type from event cause (bit 31:24) */ +#define EVENT_GET_BSS_TYPE(event_cause) \ + (((event_cause) >> 24) & 0x00ff) + +/** event type for tdls setup failure */ +#define TDLS_EVENT_TYPE_SETUP_FAILURE 1 +/** event type for tdls setup request received */ +#define TDLS_EVENT_TYPE_SETUP_REQ 2 +/** event type for tdls link torn down */ +#define TDLS_EVENT_TYPE_LINK_TORN_DOWN 3 +/** event type for tdls link established */ +#define TDLS_EVENT_TYPE_LINK_ESTABLISHED 4 +/** event type for tdls debug */ +#define TDLS_EVENT_TYPE_DEBUG 5 +/** event type for tdls packet */ +#define TDLS_EVENT_TYPE_PACKET 6 +/** event type for channel switch result */ +#define TDLS_EVENT_TYPE_CHAN_SWITCH_RESULT 7 +/** event type for start channel switch */ +#define TDLS_EVENT_TYPE_START_CHAN_SWITCH 8 +/** event type for stop channel switch */ +#define TDLS_EVENT_TYPE_CHAN_SWITCH_STOPPED 9 + +/** Packet received on direct link */ +#define RXPD_FLAG_PKT_DIRECT_LINK 1 +/** TDLS base channel */ +#define TDLS_BASE_CHANNEL 0 +/** TDLS off channel */ +#define TDLS_OFF_CHANNEL 1 + +/** structure for channel switch result from TDLS FW */ +typedef MLAN_PACK_START struct _chan_switch_result { + /** current channel, 0 - base channel, 1 - off channel*/ + t_u8 current_channel; + /** channel switch status*/ + t_u8 status; + /** channel switch fauilure reason code*/ + t_u8 reason; +} MLAN_PACK_END chan_switch_result; + +typedef MLAN_PACK_START struct _ie_data { + /** IE Length */ + t_u16 ie_length; + /** IE pointer */ + t_u8 ie_ptr[0]; +} MLAN_PACK_END tdls_ie_data; + +/** Event structure for generic events from TDLS FW */ +typedef MLAN_PACK_START struct _Event_tdls_generic { + /** Event Type */ + t_u16 event_type; + /** Peer mac address */ + t_u8 peer_mac_addr[MLAN_MAC_ADDR_LENGTH]; + union { + /** channel switch result structure*/ + chan_switch_result switch_result; + /** channel switch stop reason*/ + t_u8 cs_stop_reason; + /** Reason code */ + t_u16 reason_code; + /** IE data */ + tdls_ie_data ie_data; + } u; +} MLAN_PACK_END Event_tdls_generic; + +typedef enum _tdls_error_code_e { + NO_ERROR = 0, + INTERNAL_ERROR, + MAX_TDLS_LINKS_EST, + TDLS_LINK_EXISTS, + TDLS_LINK_NONEXISTENT, + TDLS_PEER_STA_UNREACHABLE = 25, +} tdls_error_code_e; + +/** Event structure for generic events from NAN FW */ +typedef MLAN_PACK_START struct _event_nan_generic { + /** NAN Event SubType */ + t_u16 event_sub_type; +} MLAN_PACK_END event_nan_generic; + +#define RXPD_FLAG_EXTRA_HEADER (1 << 1) + +/** Event_WEP_ICV_ERR structure */ +typedef MLAN_PACK_START struct _Event_WEP_ICV_ERR { + /** Reason code */ + t_u16 reason_code; + /** Source MAC address */ + t_u8 src_mac_addr[MLAN_MAC_ADDR_LENGTH]; + /** WEP decryption used key */ + t_u8 wep_key_index; + /** WEP key length */ + t_u8 wep_key_length; + /** WEP key */ + t_u8 key[MAX_WEP_KEY_SIZE]; +} MLAN_PACK_END Event_WEP_ICV_ERR; + +/** WLAN_802_11_FIXED_IEs */ +typedef MLAN_PACK_START struct _WLAN_802_11_FIXED_IEs { + /** Timestamp */ + t_u8 time_stamp[8]; + /** Beacon interval */ + t_u16 beacon_interval; + /** Capabilities*/ + t_u16 capabilities; +} MLAN_PACK_END WLAN_802_11_FIXED_IEs; + +/** WLAN_802_11_VARIABLE_IEs */ +typedef MLAN_PACK_START struct _WLAN_802_11_VARIABLE_IEs { + /** Element ID */ + t_u8 element_id; + /** Length */ + t_u8 length; + /** IE data */ + t_u8 data[1]; +} MLAN_PACK_END WLAN_802_11_VARIABLE_IEs; + +/** TLV related data structures*/ +/** MrvlIEtypesHeader_t */ +typedef MLAN_PACK_START struct _MrvlIEtypesHeader { + /** Header type */ + t_u16 type; + /** Header length */ + t_u16 len; +} MLAN_PACK_END MrvlIEtypesHeader_t; + +/** MrvlIEtypes_Data_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_Data_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** Data */ + t_u8 data[1]; +} MLAN_PACK_END MrvlIEtypes_Data_t; + +/*TDLS TIMEOUT VALUE (seconds)*/ +#define TDLS_IDLE_TIMEOUT 60 +/** MrvlIEtypes_Data_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_TDLS_Idle_Timeout_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** value */ + t_u16 value; +} MLAN_PACK_END MrvlIEtypes_TDLS_Idle_Timeout_t; +#if defined(STA_SUPPORT) +/** Pairwise Cipher Suite length */ +#define PAIRWISE_CIPHER_SUITE_LEN 4 +/** AKM Suite length */ +#define AKM_SUITE_LEN 4 +/** MFPC bit in RSN capability */ +#define MFPC_BIT 7 +/** MFPR bit in RSN capability */ +#define MFPR_BIT 6 +#endif +/** Bit mask for TxPD status field for null packet */ +#define MRVDRV_TxPD_POWER_MGMT_NULL_PACKET 0x01 +/** Bit mask for TxPD status field for last packet */ +#define MRVDRV_TxPD_POWER_MGMT_LAST_PACKET 0x08 + +/** Bit mask for TxPD flags field for TDLS packet */ +#define MRVDRV_TxPD_FLAGS_TDLS_PACKET MBIT(4) + +/** Bit mask for TxPD flags field for Tx status report */ +#define MRVDRV_TxPD_FLAGS_TX_PACKET_STATUS MBIT(5) + +/** Packet type: 802.11 */ +#define PKT_TYPE_802DOT11 0x05 +#define PKT_TYPE_MGMT_FRAME 0xE5 +/** Packet type: AMSDU */ +#define PKT_TYPE_AMSDU 0xE6 +/** Packet type: BAR */ +#define PKT_TYPE_BAR 0xE7 + +/** Packet type: debugging */ +#define PKT_TYPE_DEBUG 0xEF + +/** channel number at bit 5-13 */ +#define RXPD_CHAN_MASK 0x3FE0 +/** Rate control mask 15-23 */ +#define TXPD_RATE_MASK 0xff8000 +/** enable bw ctrl in TxPD */ +#define TXPD_BW_ENABLE MBIT(20) +/** enable tx power ctrl in TxPD */ +#define TXPD_TXPW_ENABLE MBIT(7) +/** sign of power */ +#define TXPD_TXPW_NEGATIVE MBIT(6) +/** Enable Rate ctrl in TxPD */ +#define TXPD_TXRATE_ENABLE MBIT(15) +/** enable retry limit in TxPD */ +#define TXPD_RETRY_ENABLE MBIT(12) + +/** TxPD descriptor */ +typedef MLAN_PACK_START struct _TxPD { + /** BSS type */ + t_u8 bss_type; + /** BSS number */ + t_u8 bss_num; + /** Tx packet length */ + t_u16 tx_pkt_length; + /** Tx packet offset */ + t_u16 tx_pkt_offset; + /** Tx packet type */ + t_u16 tx_pkt_type; + /** Tx Control */ + t_u32 tx_control; + /** Pkt Priority */ + t_u8 priority; + /** Transmit Pkt Flags*/ + t_u8 flags; + /** Amount of time the packet has been queued + * in the driver (units = 2ms)*/ + t_u8 pkt_delay_2ms; + /** reserved */ + t_u8 reserved; + /** Tx Control */ + t_u32 tx_control_1; +} MLAN_PACK_END TxPD, *PTxPD; + +/** RxPD Descriptor */ +typedef MLAN_PACK_START struct _RxPD { + /** BSS type */ + t_u8 bss_type; + /** BSS number */ + t_u8 bss_num; + /** Rx Packet Length */ + t_u16 rx_pkt_length; + /** Rx Pkt offset */ + t_u16 rx_pkt_offset; + /** Rx packet type */ + t_u16 rx_pkt_type; + /** Sequence number */ + t_u16 seq_num; + /** Packet Priority */ + t_u8 priority; + /** Rx Packet Rate */ + t_u8 rx_rate; + /** SNR */ + t_s8 snr; + /** Noise Floor */ + t_s8 nf; + /** [Bit 1] [Bit 0] RxRate format: legacy rate = 00 HT = 01 VHT = 10 + * [Bit 3] [Bit 2] HT/VHT Bandwidth BW20 = 00 BW40 = 01 BW80 = 10 BW160 = 11 + * [Bit 4] HT/VHT Guard interval LGI = 0 SGI = 1 + * [Bit 5] STBC support Enabled = 1 + * [Bit 6] LDPC support Enabled = 1 + * [Bit 7] Reserved */ + t_u8 rate_info; + /** Reserved */ + t_u8 reserved[3]; + /** TDLS flags, bit 0: 0=InfraLink, 1=DirectLink */ + t_u8 flags; + /**For SD8887 antenna info: 0 = 2.4G antenna a; 1 = 2.4G antenna b; 3 = 5G antenna; 0xff = invalid value */ + t_u8 antenna; + /* [31:0] ToA of the rx packet, [63:32] ToD of the ack for the rx packet Both ToA and ToD are in nanoseconds */ + t_u64 toa_tod_tstamps; + /** rx info */ + t_u32 rx_info; +} MLAN_PACK_END RxPD, *PRxPD; + +/** IEEEtypes_FrameCtl_t*/ +#ifdef BIG_ENDIAN_SUPPORT +typedef MLAN_PACK_START struct _IEEEtypes_FrameCtl_t { + + /** Order */ + t_u8 order:1; + /** Wep */ + t_u8 wep:1; + /** More Data */ + t_u8 more_data:1; + /** Power Mgmt */ + t_u8 pwr_mgmt:1; + /** Retry */ + t_u8 retry:1; + /** More Frag */ + t_u8 more_frag:1; + /** From DS */ + t_u8 from_ds:1; + /** To DS */ + t_u8 to_ds:1; + /** Sub Type */ + t_u8 sub_type:4; + /** Type */ + t_u8 type:2; + /** Protocol Version */ + t_u8 protocol_version:2; +} MLAN_PACK_END IEEEtypes_FrameCtl_t; +#else +typedef MLAN_PACK_START struct _IEEEtypes_FrameCtl_t { + /** Protocol Version */ + t_u8 protocol_version:2; + /** Type */ + t_u8 type:2; + /** Sub Type */ + t_u8 sub_type:4; + /** To DS */ + t_u8 to_ds:1; + /** From DS */ + t_u8 from_ds:1; + /** More Frag */ + t_u8 more_frag:1; + /** Retry */ + t_u8 retry:1; + /** Power Mgmt */ + t_u8 pwr_mgmt:1; + /** More Data */ + t_u8 more_data:1; + /** Wep */ + t_u8 wep:1; + /** Order */ + t_u8 order:1; +} MLAN_PACK_END IEEEtypes_FrameCtl_t; +#endif + +/** MrvlIETypes_MgmtFrameSet_t */ +typedef MLAN_PACK_START struct _MrvlIETypes_MgmtFrameSet_t { + /** Type */ + t_u16 type; + /** Length */ + t_u16 len; + /** Frame Control */ + IEEEtypes_FrameCtl_t frame_control; + /* t_u8 frame_contents[0]; */ +} MLAN_PACK_END MrvlIETypes_MgmtFrameSet_t; + +/** Beacon */ +typedef MLAN_PACK_START struct _IEEEtypes_Beacon_t { + /** time stamp */ + t_u8 time_stamp[8]; + /** beacon interval */ + t_u16 beacon_interval; + /** cap info */ + t_u16 cap_info; +} MLAN_PACK_END IEEEtypes_Beacon_t; + +/** Fixed size of station association event */ +#define ASSOC_EVENT_FIX_SIZE 12 + +/** MrvlIEtypes_channel_band_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_channel_band_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** Band Configuration */ + Band_Config_t bandcfg; + /** channel */ + t_u8 channel; +} MLAN_PACK_END MrvlIEtypes_channel_band_t; + +#ifdef UAP_SUPPORT +/** TxPD descriptor */ +typedef MLAN_PACK_START struct _UapTxPD { + /** BSS type */ + t_u8 bss_type; + /** BSS number */ + t_u8 bss_num; + /** Tx packet length */ + t_u16 tx_pkt_length; + /** Tx packet offset */ + t_u16 tx_pkt_offset; + /** Tx packet type */ + t_u16 tx_pkt_type; + /** Tx Control */ + t_u32 tx_control; + /** Pkt Priority */ + t_u8 priority; + /** Transmit Pkt Flags*/ + t_u8 flags; + /** Amount of time the packet has been queued + * in the driver (units = 2ms)*/ + t_u8 pkt_delay_2ms; + /** reserved */ + t_u8 reserved; + /** Tx Control */ + t_u32 tx_control_1; +} MLAN_PACK_END UapTxPD, *PUapTxPD; + +/** RxPD Descriptor */ +typedef MLAN_PACK_START struct _UapRxPD { + /** BSS Type */ + t_u8 bss_type; + /** BSS number*/ + t_u8 bss_num; + /** Rx packet length */ + t_u16 rx_pkt_length; + /** Rx packet offset */ + t_u16 rx_pkt_offset; + /** Rx packet type */ + t_u16 rx_pkt_type; + /** Sequence nunmber */ + t_u16 seq_num; + /** Packet Priority */ + t_u8 priority; + /** Rx Packet Rate */ + t_u8 rx_rate; + /** SNR */ + t_s8 snr; + /** Noise Floor */ + t_s8 nf; + /** [Bit 1] [Bit 0] RxRate format: legacy rate = 00 HT = 01 VHT = 10 + * [Bit 3] [Bit 2] HT/VHT Bandwidth BW20 = 00 BW40 = 01 BW80 = 10 BW160 = 11 + * [Bit 4] HT/VHT Guard interval LGI = 0 SGI = 1 + * [Bit 5] STBC support Enabled = 1 + * [Bit 6] LDPC support Enabled = 1 + * [Bit 7] Reserved */ + t_u8 rate_info; + /** Reserved */ + t_u8 reserved1[3]; + /** TDLS flags, bit 0: 0=InfraLink, 1=DirectLink */ + t_u8 flags; + /** For SD8887 ntenna info: 0 = 2.4G antenna a; 1 = 2.4G antenna b; 3 = 5G antenna; 0xff = invalid value */ + t_u8 antenna; + /* [31:0] ToA of the rx packet, [63:32] ToD of the ack for the rx packet Both ToA and ToD are in nanoseconds */ + t_u64 toa_tod_tstamps; + /** rx info */ + t_u32 rx_info; +} MLAN_PACK_END UapRxPD, *PUapRxPD; + +/** IEEEtypes_AssocRqst_t */ +typedef MLAN_PACK_START struct _IEEEtypes_AssocRqst_t { + /** Capability Info */ + t_u16 cap_info; + /** Listen Interval */ + t_u16 listen_interval; + /* t_u8 ie_buffer[0]; */ +} MLAN_PACK_END IEEEtypes_AssocRqst_t; + +/** IEEEtypes_ReAssocRqst_t */ +typedef MLAN_PACK_START struct _IEEEtypes_ReAssocRqst_t { + /** Capability Info */ + t_u16 cap_info; + /** Listen Interval */ + t_u16 listen_interval; + /** Current AP Address */ + t_u8 current_ap_addr[MLAN_MAC_ADDR_LENGTH]; + /* t_u8 ie_buffer[0]; */ +} MLAN_PACK_END IEEEtypes_ReAssocRqst_t; +#endif /* UAP_SUPPORT */ + +/** wlan_802_11_header */ +typedef MLAN_PACK_START struct _wlan_802_11_header { + /** Frame Control */ + t_u16 frm_ctl; + /** Duration ID */ + t_u16 duration_id; + /** Address1 */ + mlan_802_11_mac_addr addr1; + /** Address2 */ + mlan_802_11_mac_addr addr2; + /** Address3 */ + mlan_802_11_mac_addr addr3; + /** Sequence Control */ + t_u16 seq_ctl; + /** Address4 */ + mlan_802_11_mac_addr addr4; +} MLAN_PACK_END wlan_802_11_header; + +/** wlan_802_11_header packet from FW with length */ +typedef MLAN_PACK_START struct _wlan_mgmt_pkt { + /** Packet Length */ + t_u16 frm_len; + /** wlan_802_11_header */ + wlan_802_11_header wlan_header; +} MLAN_PACK_END wlan_mgmt_pkt; + +#ifdef STA_SUPPORT +/** (Beaconsize(256)-5(IEId,len,contrystr(3))/3(FirstChan,NoOfChan,MaxPwr) */ +#define MAX_NO_OF_CHAN 40 + +/** Channel-power table entries */ +typedef MLAN_PACK_START struct _chan_power_11d { + /** 11D channel */ + t_u8 chan; + /** Band for channel */ + t_u8 band; + /** 11D channel power */ + t_u8 pwr; + /** AP seen on channel */ + t_u8 ap_seen; +} MLAN_PACK_END chan_power_11d_t; + +/** Region channel info */ +typedef MLAN_PACK_START struct _parsed_region_chan_11d { + /** 11D channel power per channel */ + chan_power_11d_t chan_pwr[MAX_NO_OF_CHAN]; + /** 11D number of channels */ + t_u8 no_of_chan; +} MLAN_PACK_END parsed_region_chan_11d_t; +#endif /* STA_SUPPORT */ + +/** ChanScanMode_t */ +typedef MLAN_PACK_START struct _ChanScanMode_t { +#ifdef BIG_ENDIAN_SUPPORT + /** Reserved */ + t_u8 reserved_6_7:2; + /** First channel in scan */ + t_u8 first_chan:1; + /** Enable hidden ssid report */ + t_u8 hidden_ssid_report:1; + /** Enable probe response timeout */ + t_u8 rsp_timeout_en:1; + /** Multidomain scan mode */ + t_u8 multidomain_scan:1; + /** Disble channel filtering flag */ + t_u8 disable_chan_filt:1; + /** Channel scan mode passive flag */ + t_u8 passive_scan:1; +#else + /** Channel scan mode passive flag */ + t_u8 passive_scan:1; + /** Disble channel filtering flag */ + t_u8 disable_chan_filt:1; + /** Multidomain scan mode */ + t_u8 multidomain_scan:1; + /** Enable probe response timeout */ + t_u8 rsp_timeout_en:1; + /** Enable hidden ssid report */ + t_u8 hidden_ssid_report:1; + /** First channel in scan */ + t_u8 first_chan:1; + /** Reserved */ + t_u8 reserved_6_7:2; +#endif +} MLAN_PACK_END ChanScanMode_t; + +/** ChanScanParamSet_t */ +typedef MLAN_PACK_START struct _ChanScanParamSet_t { + /** Channel scan parameter : band config */ + Band_Config_t bandcfg; + /** Channel scan parameter : Channel number */ + t_u8 chan_number; + /** Channel scan parameter : Channel scan mode */ + ChanScanMode_t chan_scan_mode; + /** Channel scan parameter : Minimum scan time */ + t_u16 min_scan_time; + /** Channel scan parameter : Maximum scan time */ + t_u16 max_scan_time; +} MLAN_PACK_END ChanScanParamSet_t; + +/** MrvlIEtypes_ChanListParamSet_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_ChanListParamSet_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** Channel scan parameters */ + ChanScanParamSet_t chan_scan_param[1]; +} MLAN_PACK_END MrvlIEtypes_ChanListParamSet_t; + +/** ChanBandParamSet_t */ +typedef struct _ChanBandParamSet_t { + /** Channel scan parameter : band config */ + Band_Config_t bandcfg; + /** Channel number */ + t_u8 chan_number; +} ChanBandParamSet_t; + +/** MrvlIEtypes_ChanBandListParamSet_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_ChanBandListParamSet_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** Channel Band parameters */ + ChanBandParamSet_t chan_band_param[1]; +} MLAN_PACK_END MrvlIEtypes_ChanBandListParamSet_t; + +/** MrvlIEtypes_RatesParamSet_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_RatesParamSet_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** Rates */ + t_u8 rates[1]; +} MLAN_PACK_END MrvlIEtypes_RatesParamSet_t; + +/** _MrvlIEtypes_Bssid_List_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_Bssid_List_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** BSSID */ + t_u8 bssid[MLAN_MAC_ADDR_LENGTH]; +} MLAN_PACK_END MrvlIEtypes_Bssid_List_t; + +/** MrvlIEtypes_SsIdParamSet_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_SsIdParamSet_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** SSID */ + t_u8 ssid[1]; +} MLAN_PACK_END MrvlIEtypes_SsIdParamSet_t; + +/** MrvlIEtypes_NumProbes_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_NumProbes_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** Number of probes */ + t_u16 num_probes; +} MLAN_PACK_END MrvlIEtypes_NumProbes_t; + +/** MrvlIEtypes_WildCardSsIdParamSet_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_WildCardSsIdParamSet_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** Maximum SSID length */ + t_u8 max_ssid_length; + /** SSID */ + t_u8 ssid[1]; +} MLAN_PACK_END MrvlIEtypes_WildCardSsIdParamSet_t; + +/**TSF data size */ +#define TSF_DATA_SIZE 8 +/** Table of TSF values returned in the scan result */ +typedef MLAN_PACK_START struct _MrvlIEtypes_TsfTimestamp_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** the length of each TSF data is 8 bytes, could be multiple TSF here */ + t_u8 tsf_data[1]; +} MLAN_PACK_END MrvlIEtypes_TsfTimestamp_t; + +/** CfParamSet_t */ +typedef MLAN_PACK_START struct _CfParamSet_t { + /** CF parameter : Count */ + t_u8 cfp_cnt; + /** CF parameter : Period */ + t_u8 cfp_period; + /** CF parameter : Duration */ + t_u16 cfp_max_duration; + /** CF parameter : Duration remaining */ + t_u16 cfp_duration_remaining; +} MLAN_PACK_END CfParamSet_t; + +/** IbssParamSet_t */ +typedef MLAN_PACK_START struct _IbssParamSet_t { + /** ATIM window value */ + t_u16 atim_window; +} MLAN_PACK_END IbssParamSet_t; + +/** MrvlIEtypes_SsParamSet_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_SsParamSet_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** CF/IBSS parameters sets */ + union { + /** CF parameter set */ + CfParamSet_t cf_param_set[1]; + /** IBSS parameter set */ + IbssParamSet_t ibss_param_set[1]; + } cf_ibss; +} MLAN_PACK_END MrvlIEtypes_SsParamSet_t; + +/** FhParamSet_t */ +typedef MLAN_PACK_START struct _FhParamSet_t { + /** FH parameter : Dwell time */ + t_u16 dwell_time; + /** FH parameter : Hop set */ + t_u8 hop_set; + /** FH parameter : Hop pattern */ + t_u8 hop_pattern; + /** FH parameter : Hop index */ + t_u8 hop_index; +} MLAN_PACK_END FhParamSet_t; + +/** DsParamSet_t */ +typedef MLAN_PACK_START struct _DsParamSet_t { + /** Current channel number */ + t_u8 current_chan; +} MLAN_PACK_END DsParamSet_t; + +/** MrvlIEtypes_PhyParamSet_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_PhyParamSet_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** FH/DS parameters */ + union { + /** FH parameter set */ + FhParamSet_t fh_param_set[1]; + /** DS parameter set */ + DsParamSet_t ds_param_set[1]; + } fh_ds; +} MLAN_PACK_END MrvlIEtypes_PhyParamSet_t; + +/* Auth type to be used in the Authentication portion of an Assoc seq */ +/** MrvlIEtypes_AuthType_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_AuthType_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** Authentication type */ + t_u16 auth_type; +} MLAN_PACK_END MrvlIEtypes_AuthType_t; + +/** MrvlIEtypes_ScanChanGap_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_ScanChanGap_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** Time gap in units to TUs to be used between + * two consecutive channels scan */ + t_u16 gap; +} MLAN_PACK_END MrvlIEtypes_ScanChanGap_t; + +/** channel statictics tlv */ +typedef MLAN_PACK_START struct _MrvlIEtypes_ChannelStats_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** channel statictics */ + ChanStatistics_t chanStat[0]; +} MLAN_PACK_END MrvlIEtypes_ChannelStats_t; + +/** MrvlIETypes_ActionFrame_t */ +typedef MLAN_PACK_START struct { + MrvlIEtypesHeader_t header; + /**< Header */ + + /** Source Address */ + t_u8 srcAddr[MLAN_MAC_ADDR_LENGTH]; + /** Destination Address */ + t_u8 dstAddr[MLAN_MAC_ADDR_LENGTH]; + + /** IEEEtypes Action frame structure */ + IEEEtypes_ActionFrame_t actionFrame; + +} MLAN_PACK_END MrvlIETypes_ActionFrame_t; + +/** MrvlIEtypes_RxBaSync_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_RxBaSync_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** mac address */ + t_u8 mac[MLAN_MAC_ADDR_LENGTH]; + /** tid */ + t_u8 tid; + /** reserved field */ + t_u8 reserved; + /** start seq num */ + t_u16 seq_num; + /** bitmap len */ + t_u16 bitmap_len; + /** bitmap */ + t_u8 bitmap[1]; +} MLAN_PACK_END MrvlIEtypes_RxBaSync_t; + +/** MrvlIEtypes_RsnParamSet_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_RsnParamSet_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** RSN IE */ + t_u8 rsn_ie[0]; +} MLAN_PACK_END MrvlIEtypes_RsnParamSet_t; + +/** Key Info flag for multicast key */ +#define KEY_INFO_MCAST_KEY 0x01 +/** Key Info flag for unicast key */ +#define KEY_INFO_UCAST_KEY 0x02 +/** Key Info flag for enable key */ +#define KEY_INFO_ENABLE_KEY 0x04 +/** Key Info flag for default key */ +#define KEY_INFO_DEFAULT_KEY 0x08 +/** Key Info flag for TX key */ +#define KEY_INFO_TX_KEY 0x10 +/** Key Info flag for RX key */ +#define KEY_INFO_RX_KEY 0x20 +#define KEY_INFO_CMAC_AES_KEY 0x400 +/** PN size for WPA/WPA2 */ +#define WPA_PN_SIZE 8 +/** PN size for PMF IGTK */ +#define IGTK_PN_SIZE 8 +/** WAPI KEY size */ +#define WAPI_KEY_SIZE 32 +/** key params fix size */ +#define KEY_PARAMS_FIXED_LEN 10 +/** key index mask */ +#define KEY_INDEX_MASK 0xf + +/** wep_param */ +typedef MLAN_PACK_START struct _wep_param_t { + /** key_len */ + t_u16 key_len; + /** wep key */ + t_u8 key[MAX_WEP_KEY_SIZE]; +} MLAN_PACK_END wep_param_t; + +/** tkip_param */ +typedef MLAN_PACK_START struct _tkip_param { + /** Rx packet num */ + t_u8 pn[WPA_PN_SIZE]; + /** key_len */ + t_u16 key_len; + /** tkip key */ + t_u8 key[WPA_TKIP_KEY_LEN]; +} MLAN_PACK_END tkip_param; + +/** aes_param */ +typedef MLAN_PACK_START struct _aes_param { + /** Rx packet num */ + t_u8 pn[WPA_PN_SIZE]; + /** key_len */ + t_u16 key_len; + /** aes key */ + t_u8 key[WPA_AES_KEY_LEN]; +} MLAN_PACK_END aes_param; + +/** wapi_param */ +typedef MLAN_PACK_START struct _wapi_param { + /** Rx packet num */ + t_u8 pn[PN_SIZE]; + /** key_len */ + t_u16 key_len; + /** wapi key */ + t_u8 key[WAPI_KEY_SIZE]; +} MLAN_PACK_END wapi_param; + +/** cmac_aes_param */ +typedef MLAN_PACK_START struct _cmac_aes_param { + /** IGTK pn */ + t_u8 ipn[IGTK_PN_SIZE]; + /** key_len */ + t_u16 key_len; + /** aes key */ + t_u8 key[CMAC_AES_KEY_LEN]; +} MLAN_PACK_END cmac_aes_param; + +/** MrvlIEtype_KeyParamSet_t */ +typedef MLAN_PACK_START struct _MrvlIEtype_KeyParamSetV2_t { + /** Type ID */ + t_u16 type; + /** Length of Payload */ + t_u16 length; + /** mac address */ + t_u8 mac_addr[MLAN_MAC_ADDR_LENGTH]; + /** key index */ + t_u8 key_idx; + /** Type of Key: WEP=0, TKIP=1, AES=2, WAPI=3 AES_CMAC=4 */ + t_u8 key_type; + /** Key Control Info specific to a key_type_id */ + t_u16 key_info; + union { + /** wep key param */ + wep_param_t wep; + /** tkip key param */ + tkip_param tkip; + /** aes key param */ + aes_param aes; + /** wapi key param */ + wapi_param wapi; + /** IGTK key param */ + cmac_aes_param cmac_aes; + } key_params; +} MLAN_PACK_END MrvlIEtype_KeyParamSetV2_t; + +/** HostCmd_DS_802_11_KEY_MATERIAL */ +typedef MLAN_PACK_START struct _HostCmd_DS_802_11_KEY_MATERIAL { + /** Action */ + t_u16 action; + /** Key parameter set */ + MrvlIEtype_KeyParamSetV2_t key_param_set; +} MLAN_PACK_END HostCmd_DS_802_11_KEY_MATERIAL; + +/** HostCmd_DS_GTK_REKEY_PARAMS */ +typedef MLAN_PACK_START struct _HostCmd_DS_GTK_REKEY_PARAMS { + /** Action */ + t_u16 action; + /** Key confirmation key */ + t_u8 kck[MLAN_KCK_LEN]; + /** Key encryption key */ + t_u8 kek[MLAN_KEK_LEN]; + /** Replay counter low 32 bit */ + t_u32 replay_ctr_low; + /** Replay counter high 32 bit */ + t_u32 replay_ctr_high; +} MLAN_PACK_END HostCmd_DS_GTK_REKEY_PARAMS; + +/** Data structure of WMM QoS information */ +typedef MLAN_PACK_START struct _WmmQosInfo_t { +#ifdef BIG_ENDIAN_SUPPORT + /** QoS UAPSD */ + t_u8 qos_uapsd:1; + /** Reserved */ + t_u8 reserved:3; + /** Parameter set count */ + t_u8 para_set_count:4; +#else + /** Parameter set count */ + t_u8 para_set_count:4; + /** Reserved */ + t_u8 reserved:3; + /** QoS UAPSD */ + t_u8 qos_uapsd:1; +#endif /* BIG_ENDIAN_SUPPORT */ +} MLAN_PACK_END WmmQosInfo_t, *pWmmQosInfo_t; + +/** Data structure of WMM ECW */ +typedef MLAN_PACK_START struct _WmmEcw_t { +#ifdef BIG_ENDIAN_SUPPORT + /** Maximum Ecw */ + t_u8 ecw_max:4; + /** Minimum Ecw */ + t_u8 ecw_min:4; +#else + /** Minimum Ecw */ + t_u8 ecw_min:4; + /** Maximum Ecw */ + t_u8 ecw_max:4; +#endif /* BIG_ENDIAN_SUPPORT */ +} MLAN_PACK_END WmmEcw_t, *pWmmEcw_t; + +/** Data structure of WMM Aci/Aifsn */ +typedef MLAN_PACK_START struct _WmmAciAifsn_t { +#ifdef BIG_ENDIAN_SUPPORT + /** Reserved */ + t_u8 reserved:1; + /** Aci */ + t_u8 aci:2; + /** Acm */ + t_u8 acm:1; + /** Aifsn */ + t_u8 aifsn:4; +#else + /** Aifsn */ + t_u8 aifsn:4; + /** Acm */ + t_u8 acm:1; + /** Aci */ + t_u8 aci:2; + /** Reserved */ + t_u8 reserved:1; +#endif /* BIG_ENDIAN_SUPPORT */ +} MLAN_PACK_END WmmAciAifsn_t, *pWmmAciAifsn_t; + +/** Data structure of WMM AC parameters */ +typedef MLAN_PACK_START struct _WmmAcParameters_t { + WmmAciAifsn_t aci_aifsn; /**< AciAifSn */ + WmmEcw_t ecw; /**< Ecw */ + t_u16 tx_op_limit; /**< Tx op limit */ +} MLAN_PACK_END WmmAcParameters_t, *pWmmAcParameters_t; + +/** Data structure of WMM parameter */ +typedef MLAN_PACK_START struct _WmmParameter_t { + /** OuiType: 00:50:f2:02 */ + t_u8 ouitype[4]; + /** Oui subtype: 01 */ + t_u8 ouisubtype; + /** version: 01 */ + t_u8 version; + /** QoS information */ + t_u8 qos_info; + /** Reserved */ + t_u8 reserved; + /** AC Parameters Record WMM_AC_BE, WMM_AC_BK, WMM_AC_VI, WMM_AC_VO */ + WmmAcParameters_t ac_params[MAX_AC_QUEUES]; +} MLAN_PACK_END WmmParameter_t, *pWmmParameter_t; + +/** Data structure of Host command WMM_PARAM_CFG */ +typedef MLAN_PACK_START struct _HostCmd_DS_WMM_PARAM_CONFIG { + /** action */ + t_u16 action; + /** AC Parameters Record WMM_AC_BE, WMM_AC_BK, WMM_AC_VI, WMM_AC_VO */ + WmmAcParameters_t ac_params[MAX_AC_QUEUES]; +} MLAN_PACK_END HostCmd_DS_WMM_PARAM_CONFIG; + +/* Definition of firmware host command */ +/** HostCmd_DS_GEN */ +typedef MLAN_PACK_START struct _HostCmd_DS_GEN { + /** Command */ + t_u16 command; + /** Size */ + t_u16 size; + /** Sequence number */ + t_u16 seq_num; + /** Result */ + t_u16 result; +} MLAN_PACK_END HostCmd_DS_GEN +; + +/** Size of HostCmd_DS_GEN */ +#define S_DS_GEN sizeof(HostCmd_DS_GEN) + +/** Address type: broadcast */ +#define ADDR_TYPE_BROADCAST 1 +/* Address type: unicast */ +#define ADDR_TYPE_UNICAST 2 +/* Address type: multicast */ +#define ADDR_TYPE_MULTICAST 3 + +/** Ether type: any */ +#define ETHER_TYPE_ANY 0xffff +/** Ether type: ARP */ +#define ETHER_TYPE_ARP 0x0608 + +/** IPv4 address any */ +#define IPV4_ADDR_ANY 0xffffffff + +/** Header structure for ARP filter */ +typedef MLAN_PACK_START struct _arpfilter_header { + /** Type */ + t_u16 type; + /** TLV length */ + t_u16 len; +} MLAN_PACK_END arpfilter_header; + +/** Filter entry structure */ +typedef MLAN_PACK_START struct _filter_entry { + /** Address type */ + t_u16 addr_type; + /** Ether type */ + t_u16 eth_type; + /** IPv4 address */ + t_u32 ipv4_addr; +} MLAN_PACK_END filter_entry; + +typedef MLAN_PACK_START struct _HostCmd_DS_MEF_CFG { + /** Criteria */ + t_u32 criteria; + /** Number of entries */ + t_u16 nentries; +} MLAN_PACK_END HostCmd_DS_MEF_CFG; + +/* HostCmd_DS_802_11_SLEEP_PERIOD */ +typedef MLAN_PACK_START struct _HostCmd_DS_802_11_SLEEP_PERIOD { + /** ACT_GET/ACT_SET */ + t_u16 action; + + /** Sleep Period in msec */ + t_u16 sleep_pd; +} MLAN_PACK_END HostCmd_DS_802_11_SLEEP_PERIOD; + +/* HostCmd_DS_802_11_SLEEP_PARAMS */ +typedef MLAN_PACK_START struct _HostCmd_DS_802_11_SLEEP_PARAMS { + /** ACT_GET/ACT_SET */ + t_u16 action; + /** Sleep clock error in ppm */ + t_u16 error; + /** Wakeup offset in usec */ + t_u16 offset; + /** Clock stabilization time in usec */ + t_u16 stable_time; + /** Control periodic calibration */ + t_u8 cal_control; + /** Control the use of external sleep clock */ + t_u8 external_sleep_clk; + /** Reserved field, should be set to zero */ + t_u16 reserved; +} MLAN_PACK_END HostCmd_DS_802_11_SLEEP_PARAMS; + +/** Sleep response control */ +typedef enum _sleep_resp_ctrl { + RESP_NOT_NEEDED = 0, + RESP_NEEDED, +} sleep_resp_ctrl; + +/** Structure definition for the new ieee power save parameters*/ +typedef MLAN_PACK_START struct __ps_param { + /** Null packet interval */ + t_u16 null_pkt_interval; + /** Num dtims */ + t_u16 multiple_dtims; + /** becaon miss interval */ + t_u16 bcn_miss_timeout; + /** local listen interval */ + t_u16 local_listen_interval; + /** Adhoc awake period */ + t_u16 adhoc_wake_period; + /** mode - (0x01 - firmware to automatically choose PS_POLL or NULL mode, + * 0x02 - PS_POLL, 0x03 - NULL mode ) + */ + t_u16 mode; + /** Delay to PS in milliseconds */ + t_u16 delay_to_ps; +} MLAN_PACK_END ps_param; + +/** Structure definition for the new auto deep sleep command */ +typedef MLAN_PACK_START struct __auto_ds_param { + /** Deep sleep inactivity timeout */ + t_u16 deep_sleep_timeout; +} MLAN_PACK_END auto_ds_param; + +/** Structure definition for sleep confirmation in the new ps command */ +typedef MLAN_PACK_START struct __sleep_confirm_param { + /** response control 0x00 - response not needed, 0x01 - response needed */ + t_u16 resp_ctrl; +} MLAN_PACK_END sleep_confirm_param; + +/** bitmap for get auto deepsleep */ +#define BITMAP_AUTO_DS 0x01 +/** bitmap for sta power save */ +#define BITMAP_STA_PS 0x10 +/** bitmap for beacon timeout */ +#define BITMAP_BCN_TMO 0x20 +/** bitmap for uap inactivity based PS */ +#define BITMAP_UAP_INACT_PS 0x100 +/** bitmap for uap DTIM PS */ +#define BITMAP_UAP_DTIM_PS 0x200 +/** Structure definition for the new ieee power save parameters*/ +typedef MLAN_PACK_START struct _auto_ps_param { + /** bitmap for enable power save mode */ + t_u16 ps_bitmap; + /* auto deep sleep parameter, + * sta power save parameter + * uap inactivity parameter + * uap DTIM parameter */ +} MLAN_PACK_END auto_ps_param; + +/** fix size for auto ps */ +#define AUTO_PS_FIX_SIZE 4 + +/** TLV type : auto ds param */ +#define TLV_TYPE_AUTO_DS_PARAM (PROPRIETARY_TLV_BASE_ID + 0x71) /* 0x0171 */ +/** TLV type : ps param */ +#define TLV_TYPE_PS_PARAM (PROPRIETARY_TLV_BASE_ID + 0x72) /* 0x0172 */ +/** TLV type : beacon timeout */ +#define TLV_TYPE_BCN_TIMEOUT (PROPRIETARY_TLV_BASE_ID + 0x11F) /* 0x011F */ + +/** MrvlIEtypes_auto_ds_param_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_auto_ds_param_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** auto ds param */ + auto_ds_param param; +} MLAN_PACK_END MrvlIEtypes_auto_ds_param_t; + +/** MrvlIEtypes_ps_param_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_ps_param_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** ps param */ + ps_param param; +} MLAN_PACK_END MrvlIEtypes_ps_param_t; + +/** MrvlIEtypes_bcn_timeout_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_bcn_timeout_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** Beacon miss timeout period window */ + t_u16 bcn_miss_tmo_window; + /** Beacon miss timeout period */ + t_u16 bcn_miss_tmo_period; + /** Beacon reacquire timeout period window */ + t_u16 bcn_rq_tmo_window; + /** Beacon reacquire timeout period */ + t_u16 bcn_rq_tmo_period; +} MLAN_PACK_END MrvlIEtypes_bcn_timeout_t; + +/** Structure definition for new power save command */ +typedef MLAN_PACK_START struct _HostCmd_DS_PS_MODE_ENH { + /** Action */ + t_u16 action; + /** Data speciifc to action */ + /* For IEEE power save data will be as + * UINT16 mode (0x01 - firmware to automatically choose PS_POLL or NULL mode, 0x02 - PS_POLL, 0x03 - NULL mode ) + * UINT16 NullpacketInterval + * UINT16 NumDtims + * UINT16 BeaconMissInterval + * UINT16 locallisteninterval + * UINT16 adhocawakeperiod */ + + /* For auto deep sleep */ + /* UINT16 Deep sleep inactivity timeout */ + + /* For PS sleep confirm + * UINT16 responeCtrl - 0x00 - reponse from fw not needed, 0x01 - response from fw is needed */ + + union { + /** PS param definition */ + ps_param opt_ps; + /** Auto ds param definition */ + auto_ds_param auto_ds; + /** Sleep comfirm param definition */ + sleep_confirm_param sleep_cfm; + /** bitmap for get PS info and Disable PS mode */ + t_u16 ps_bitmap; + /** auto ps param */ + auto_ps_param auto_ps; + } params; +} MLAN_PACK_END HostCmd_DS_802_11_PS_MODE_ENH; + +/** FW VERSION tlv */ +#define TLV_TYPE_FW_VER_INFO (PROPRIETARY_TLV_BASE_ID + 0xC7) /* 0x1C7 */ + +/** MrvlIEtypes_fw_ver_info_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_fw_ver_info_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** API id */ + t_u16 api_id; + /** major version */ + t_u8 major_ver; + /** minor version */ + t_u8 minor_ver; +} MLAN_PACK_END MrvlIEtypes_fw_ver_info_t; + +/** API ID */ +enum API_VER_ID { + KEY_API_VER_ID = 1, + FW_API_VER_ID = 2, + UAP_FW_API_VER_ID = 3, + CHANRPT_API_VER_ID = 4, +}; + +/** FW AP V15 */ +#define HOST_API_VERSION_V15 15 +/** FW minor version 1 */ +#define FW_MINOR_VERSION_1 1 + +/** HostCmd_DS_GET_HW_SPEC */ +typedef MLAN_PACK_START struct _HostCmd_DS_GET_HW_SPEC { + /** HW Interface version number */ + t_u16 hw_if_version; + /** HW version number */ + t_u16 version; + /** Reserved field */ + t_u16 reserved; + /** Max no of Multicast address */ + t_u16 num_of_mcast_adr; + /** MAC address */ + t_u8 permanent_addr[MLAN_MAC_ADDR_LENGTH]; + /** Region Code */ + t_u16 region_code; + /** Number of antenna used */ + t_u16 number_of_antenna; + /** FW release number, example 0x1234=1.2.3.4 */ + t_u32 fw_release_number; + /** Reserved field */ + t_u32 reserved_1; + /** Reserved field */ + t_u32 reserved_2; + /** Reserved field */ + t_u32 reserved_3; + /** FW/HW Capability */ + t_u32 fw_cap_info; + /** 802.11n Device Capabilities */ + t_u32 dot_11n_dev_cap; + /** MIMO abstraction of MCSs supported by device */ + t_u8 dev_mcs_support; + /** Valid end port at init */ + t_u16 mp_end_port; + /** mgmt IE buffer count */ + t_u16 mgmt_buf_count; + /** Reserved */ + t_u32 reserved_8; + /** Reserved */ + t_u32 reserved_9; + /** Reserved */ + t_u32 reserved_10; + /** Reserved */ + t_u32 reserved_11; +} MLAN_PACK_END HostCmd_DS_GET_HW_SPEC; + +/* HostCmd_DS_SDIO_SP_RX_AGGR_CFG */ +typedef MLAN_PACK_START struct _HostCmd_DS_SDIO_SP_RX_AGGR_CFG { + /** Action */ + t_u8 action; + /** Enable */ + t_u8 enable; + /** Sdio block size */ + t_u16 sdio_block_size; +} MLAN_PACK_END HostCmd_DS_SDIO_SP_RX_AGGR_CFG; + +/** HostCmd_DS_802_11_CFG_DATA */ +typedef MLAN_PACK_START struct _HostCmd_DS_802_11_CFG_DATA { + /** Action */ + t_u16 action; + /** Type */ + t_u16 type; + /** Data length */ + t_u16 data_len; + /** Data */ +} MLAN_PACK_END HostCmd_DS_802_11_CFG_DATA; + +/** HostCmd_DS_CMD_802_11_RSSI_INFO_EXT */ +typedef MLAN_PACK_START struct _HostCmd_DS_802_11_RSSI_INFO_EXT { + /** Action */ + t_u16 action; + /** Parameter used for exponential averaging for Data */ + t_u16 ndata; + /** Parameter used for exponential averaging for Beacon */ + t_u16 nbcn; + /** Last RSSI beacon TSF(only for Get action) */ + t_u64 tsfbcn; + /** TLV info**/ + t_u8 *tlv_buf[0]; +} MLAN_PACK_END HostCmd_DS_802_11_RSSI_INFO_EXT; + +/** TLV rssi info */ +#define TLV_TYPE_RSSI_INFO (PROPRIETARY_TLV_BASE_ID + 0xe5) /* 0x01E5 */ + +/** MrvlIEtypes_eapol_pkt_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_RSSI_EXT_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** Path ID + [Bit1:Bit0] = [0:1]: path A + [Bit1:Bit0] = [1:0]: path B + [Bit1:Bit0] = [1:1]: combined signal of path A and path B + [Bit7:Bit2] : Reserved + **/ + t_u16 path_id; + /** Last Data RSSI in dBm */ + t_s16 data_rssi_last; + /** Last Data NF in dBm */ + t_s16 data_nf_last; + /** AVG DATA RSSI in dBm */ + t_s16 data_rssi_avg; + /** AVG DATA NF in dBm */ + t_s16 data_nf_avg; + /** Last BEACON RSSI in dBm */ + t_s16 bcn_rssi_last; + /** Last BEACON NF in dBm */ + t_s16 bcn_nf_last; + /** AVG BEACON RSSI in dBm */ + t_s16 bcn_rssi_avg; + /** AVG BEACON NF in dBm */ + t_s16 bcn_nf_avg; +} MLAN_PACK_END MrvlIEtypes_RSSI_EXT_t; + +/** HostCmd_DS_CMD_802_11_RSSI_INFO */ +typedef MLAN_PACK_START struct _HostCmd_DS_802_11_RSSI_INFO { + /** Action */ + t_u16 action; + /** Parameter used for exponential averaging for Data */ + t_u16 ndata; + /** Parameter used for exponential averaging for Beacon */ + t_u16 nbcn; + /** Reserved field 0 */ + t_u16 reserved[9]; + /** Reserved field 1 */ + t_u64 reserved_1; +} MLAN_PACK_END HostCmd_DS_802_11_RSSI_INFO; + +/** HostCmd_DS_802_11_RSSI_INFO_RSP */ +typedef MLAN_PACK_START struct _HostCmd_DS_802_11_RSSI_INFO_RSP { + /** Action */ + t_u16 action; + /** Parameter used for exponential averaging for Data */ + t_u16 ndata; + /** Parameter used for exponential averaging for beacon */ + t_u16 nbcn; + /** Last Data RSSI in dBm */ + t_s16 data_rssi_last; + /** Last Data NF in dBm */ + t_s16 data_nf_last; + /** AVG DATA RSSI in dBm */ + t_s16 data_rssi_avg; + /** AVG DATA NF in dBm */ + t_s16 data_nf_avg; + /** Last BEACON RSSI in dBm */ + t_s16 bcn_rssi_last; + /** Last BEACON NF in dBm */ + t_s16 bcn_nf_last; + /** AVG BEACON RSSI in dBm */ + t_s16 bcn_rssi_avg; + /** AVG BEACON NF in dBm */ + t_s16 bcn_nf_avg; + /** Last RSSI Beacon TSF */ + t_u64 tsf_bcn; +} MLAN_PACK_END HostCmd_DS_802_11_RSSI_INFO_RSP; + +/** HostCmd_DS_802_11_MAC_ADDRESS */ +typedef MLAN_PACK_START struct _HostCmd_DS_802_11_MAC_ADDRESS { + /** Action */ + t_u16 action; + /** MAC address */ + t_u8 mac_addr[MLAN_MAC_ADDR_LENGTH]; +} MLAN_PACK_END HostCmd_DS_802_11_MAC_ADDRESS; + +/** HostCmd_DS_MAC_CONTROL */ +typedef MLAN_PACK_START struct _HostCmd_DS_MAC_CONTROL { + /** Action */ + t_u32 action; +} MLAN_PACK_END HostCmd_DS_MAC_CONTROL; + +/** HostCmd_DS_802_11_NET_MONITOR */ +typedef MLAN_PACK_START struct _HostCmd_802_11_DS_NET_MONITOR { + /** Action */ + t_u16 action; + /** Enable/disable net monitor */ + t_u16 enable_net_mon; + /** set net monitor filer flag */ + t_u16 filter_flag; + /** Channel to monitor */ + MrvlIEtypes_ChanBandListParamSet_t monitor_chan; +} MLAN_PACK_END HostCmd_DS_802_11_NET_MONITOR; + +/** HostCmd_DS_CMD_TX_DATA_PAUSE */ +typedef MLAN_PACK_START struct _HostCmd_DS_CMD_TX_DATA_PAUSE { + /** Action */ + t_u16 action; + /** Enable/disable Tx data pause */ + t_u8 enable_tx_pause; + /** Max number of TX buffers allowed for all PS clients*/ + t_u8 pause_tx_count; +} MLAN_PACK_END HostCmd_DS_CMD_TX_DATA_PAUSE; + +/** TLV type : TX pause TLV */ +#define TLV_TYPE_TX_PAUSE (PROPRIETARY_TLV_BASE_ID + 0x94) /* 0x0194 */ +/** MrvlIEtypes_SsIdParamSet_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_tx_pause_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** peer mac address */ + t_u8 peermac[MLAN_MAC_ADDR_LENGTH]; + /** Tx pause state, 1--pause, 0--free flowing */ + t_u8 tx_pause; + /** total packets queued for the client */ + t_u8 pkt_cnt; +} MLAN_PACK_END MrvlIEtypes_tx_pause_t; + +/** HostCmd_CMD_MAC_MULTICAST_ADR */ +typedef MLAN_PACK_START struct _HostCmd_DS_MAC_MULTICAST_ADR { + /** Action */ + t_u16 action; + /** Number of addresses */ + t_u16 num_of_adrs; + /** List of MAC */ + t_u8 mac_list[MLAN_MAC_ADDR_LENGTH * MLAN_MAX_MULTICAST_LIST_SIZE]; +} MLAN_PACK_END HostCmd_DS_MAC_MULTICAST_ADR; + +/** HostCmd_CMD_802_11_DEAUTHENTICATE */ +typedef MLAN_PACK_START struct _HostCmd_DS_802_11_DEAUTHENTICATE { + /** MAC address */ + t_u8 mac_addr[MLAN_MAC_ADDR_LENGTH]; + /** Deauthentication resaon code */ + t_u16 reason_code; +} MLAN_PACK_END HostCmd_DS_802_11_DEAUTHENTICATE; + +/** HostCmd_DS_802_11_ASSOCIATE */ +typedef MLAN_PACK_START struct _HostCmd_DS_802_11_ASSOCIATE { + /** Peer STA address */ + t_u8 peer_sta_addr[MLAN_MAC_ADDR_LENGTH]; + /** Capability information */ + IEEEtypes_CapInfo_t cap_info; + /** Listen interval */ + t_u16 listen_interval; + /** Beacon period */ + t_u16 beacon_period; + /** DTIM period */ + t_u8 dtim_period; + + /** + * MrvlIEtypes_SsIdParamSet_t SsIdParamSet; + * MrvlIEtypes_PhyParamSet_t PhyParamSet; + * MrvlIEtypes_SsParamSet_t SsParamSet; + * MrvlIEtypes_RatesParamSet_t RatesParamSet; + */ +} MLAN_PACK_END HostCmd_DS_802_11_ASSOCIATE; + +/** HostCmd_CMD_802_11_ASSOCIATE response */ +typedef MLAN_PACK_START struct _HostCmd_DS_802_11_ASSOCIATE_RSP { + /** Association response structure */ + IEEEtypes_AssocRsp_t assoc_rsp; +} MLAN_PACK_END HostCmd_DS_802_11_ASSOCIATE_RSP; + +/** HostCmd_DS_802_11_AD_HOC_START*/ +typedef MLAN_PACK_START struct _HostCmd_DS_802_11_AD_HOC_START { + /** AdHoc SSID */ + t_u8 ssid[MLAN_MAX_SSID_LENGTH]; + /** BSS mode */ + t_u8 bss_mode; + /** Beacon period */ + t_u16 beacon_period; + /** DTIM period */ + t_u8 dtim_period; + /** SS parameter set */ + IEEEtypes_SsParamSet_t ss_param_set; + /** PHY parameter set */ + IEEEtypes_PhyParamSet_t phy_param_set; + /** Reserved field */ + t_u16 reserved1; + /** Capability information */ + IEEEtypes_CapInfo_t cap; + /** Supported data rates */ + t_u8 DataRate[HOSTCMD_SUPPORTED_RATES]; +} MLAN_PACK_END HostCmd_DS_802_11_AD_HOC_START; + +/** HostCmd_CMD_802_11_AD_HOC_START response */ +typedef MLAN_PACK_START struct _HostCmd_DS_802_11_AD_HOC_START_RESULT { + /** Padding */ + t_u8 pad[3]; + /** AdHoc BSSID */ + t_u8 bssid[MLAN_MAC_ADDR_LENGTH]; + /** Padding to sync with FW structure*/ + t_u8 pad2[2]; + /** Result */ + t_u8 result; +} MLAN_PACK_END HostCmd_DS_802_11_AD_HOC_START_RESULT; + +/** HostCmd_CMD_802_11_AD_HOC_START response */ +typedef MLAN_PACK_START struct _HostCmd_DS_802_11_AD_HOC_JOIN_RESULT { + /** Result */ + t_u8 result; +} MLAN_PACK_END HostCmd_DS_802_11_AD_HOC_JOIN_RESULT; + +/** AdHoc_BssDesc_t */ +typedef MLAN_PACK_START struct _AdHoc_BssDesc_t { + /** BSSID */ + t_u8 bssid[MLAN_MAC_ADDR_LENGTH]; + /** SSID */ + t_u8 ssid[MLAN_MAX_SSID_LENGTH]; + /** BSS mode */ + t_u8 bss_mode; + /** Beacon period */ + t_u16 beacon_period; + /** DTIM period */ + t_u8 dtim_period; + /** Timestamp */ + t_u8 time_stamp[8]; + /** Local time */ + t_u8 local_time[8]; + /** PHY parameter set */ + IEEEtypes_PhyParamSet_t phy_param_set; + /** SS parameter set */ + IEEEtypes_SsParamSet_t ss_param_set; + /** Capability information */ + IEEEtypes_CapInfo_t cap; + /** Supported data rates */ + t_u8 data_rates[HOSTCMD_SUPPORTED_RATES]; + + /* + * DO NOT ADD ANY FIELDS TO THIS STRUCTURE. + * It is used in the Adhoc join command and will cause a + * binary layout mismatch with the firmware + */ +} MLAN_PACK_END AdHoc_BssDesc_t; + +/** HostCmd_DS_802_11_AD_HOC_JOIN */ +typedef MLAN_PACK_START struct _HostCmd_DS_802_11_AD_HOC_JOIN { + /** AdHoc BSS descriptor */ + AdHoc_BssDesc_t bss_descriptor; + /** Reserved field */ + t_u16 reserved1; + /** Reserved field */ + t_u16 reserved2; +} MLAN_PACK_END HostCmd_DS_802_11_AD_HOC_JOIN; + +/** Interrupt Raising Edge */ +#define INT_RASING_EDGE 0 +/** Interrupt Falling Edge */ +#define INT_FALLING_EDGE 1 + +/** Delay 1 usec */ +#define DELAY_1_US 1 + +typedef MLAN_PACK_START struct _HostCmd_DS_SDIO_GPIO_INT_CONFIG { + /** Action */ + t_u16 action; + /** GPIO interrupt pin */ + t_u16 gpio_pin; + /** GPIO interrupt edge, 1: failing edge; 0: raising edge */ + t_u16 gpio_int_edge; + /** GPIO interrupt pulse widthin usec units */ + t_u16 gpio_pulse_width; +} MLAN_PACK_END HostCmd_DS_SDIO_GPIO_INT_CONFIG; + +typedef MLAN_PACK_START struct _HostCmd_DS_SDIO_PULL_CTRL { + /** Action */ + t_u16 action; + /** The delay of pulling up in us */ + t_u16 pull_up; + /** The delay of pulling down in us */ + t_u16 pull_down; +} MLAN_PACK_END HostCmd_DS_SDIO_PULL_CTRL; + +/** HostCmd_DS_802_11_GET_LOG */ +typedef MLAN_PACK_START struct _HostCmd_DS_802_11_GET_LOG { + /** Number of multicast transmitted frames */ + t_u32 mcast_tx_frame; + /** Number of failures */ + t_u32 failed; + /** Number of retries */ + t_u32 retry; + /** Number of multiretries */ + t_u32 multiretry; + /** Number of duplicate frames */ + t_u32 frame_dup; + /** Number of RTS success */ + t_u32 rts_success; + /** Number of RTS failure */ + t_u32 rts_failure; + /** Number of acknowledgement failure */ + t_u32 ack_failure; + /** Number of fragmented packets received */ + t_u32 rx_frag; + /** Number of multicast frames received */ + t_u32 mcast_rx_frame; + /** FCS error */ + t_u32 fcs_error; + /** Number of transmitted frames */ + t_u32 tx_frame; + /** Reserved field */ + t_u32 reserved; + /** Number of WEP icv error for each key */ + t_u32 wep_icv_err_cnt[4]; + /** Beacon received count */ + t_u32 bcn_rcv_cnt; + /** Beacon missed count */ + t_u32 bcn_miss_cnt; + /** Tx frag count */ + t_u32 tx_frag_cnt; + /** Qos Tx frag count */ + t_u32 qos_tx_frag_cnt[8]; + /** Qos failed count */ + t_u32 qos_failed_cnt[8]; + /** Qos retry count */ + t_u32 qos_retry_cnt[8]; + /** Qos multi retry count */ + t_u32 qos_multi_retry_cnt[8]; + /** Qos frame dup count */ + t_u32 qos_frm_dup_cnt[8]; + /** Qos rts success count */ + t_u32 qos_rts_suc_cnt[8]; + /** Qos rts failure count */ + t_u32 qos_rts_failure_cnt[8]; + /** Qos ack failure count */ + t_u32 qos_ack_failure_cnt[8]; + /** Qos Rx frag count */ + t_u32 qos_rx_frag_cnt[8]; + /** Qos Tx frame count */ + t_u32 qos_tx_frm_cnt[8]; + /** Qos discarded frame count */ + t_u32 qos_discarded_frm_cnt[8]; + /** Qos mpdus Rx count */ + t_u32 qos_mpdus_rx_cnt[8]; + /** Qos retry rx count */ + t_u32 qos_retries_rx_cnt[8]; + /** CMAC ICV errors count */ + t_u32 cmacicv_errors; + /** CMAC replays count */ + t_u32 cmac_replays; + /** mgmt CCMP replays count */ + t_u32 mgmt_ccmp_replays; + /** TKIP ICV errors count */ + t_u32 tkipicv_errors; + /** TKIP replays count */ + t_u32 tkip_replays; + /** CCMP decrypt errors count */ + t_u32 ccmp_decrypt_errors; + /** CCMP replays count */ + t_u32 ccmp_replays; + /** Tx amsdu count */ + t_u32 tx_amsdu_cnt; + /** failed amsdu count */ + t_u32 failed_amsdu_cnt; + /** retry amsdu count */ + t_u32 retry_amsdu_cnt; + /** multi-retry amsdu count */ + t_u32 multi_retry_amsdu_cnt; + /** Tx octets in amsdu count */ + t_u64 tx_octets_in_amsdu_cnt; + /** amsdu ack failure count */ + t_u32 amsdu_ack_failure_cnt; + /** Rx amsdu count */ + t_u32 rx_amsdu_cnt; + /** Rx octets in amsdu count */ + t_u64 rx_octets_in_amsdu_cnt; + /** Tx ampdu count */ + t_u32 tx_ampdu_cnt; + /** tx mpdus in ampdu count */ + t_u32 tx_mpdus_in_ampdu_cnt; + /** tx octets in ampdu count */ + t_u64 tx_octets_in_ampdu_cnt; + /** ampdu Rx count */ + t_u32 ampdu_rx_cnt; + /** mpdu in Rx ampdu count */ + t_u32 mpdu_in_rx_ampdu_cnt; + /** Rx octets ampdu count */ + t_u64 rx_octets_in_ampdu_cnt; + /** ampdu delimiter CRC error count */ + t_u32 ampdu_delimiter_crc_error_cnt; +} MLAN_PACK_END HostCmd_DS_802_11_GET_LOG; + +/**_HostCmd_TX_RATE_QUERY */ +typedef MLAN_PACK_START struct _HostCmd_TX_RATE_QUERY { + /** Tx rate */ + t_u8 tx_rate; + /** Tx Rate Info: + * [Bit 0-1] tx rate formate: LG = 0, HT = 1, VHT = 2 + * [Bit 2-3] HT/VHT Bandwidth: BW20 = 0, BW40 = 1, BW80 = 2, BW160 = 3 + * [Bit 4] HT/VHT Guard Interval: LGI = 0, SGI = 1 */ + t_u8 tx_rate_info; +} MLAN_PACK_END HostCmd_TX_RATE_QUERY; + +typedef MLAN_PACK_START struct _hs_config_param { + /** bit0=1: broadcast data + * bit1=1: unicast data + * bit2=1: mac events + * bit3=1: multicast data + */ + t_u32 conditions; + /** GPIO pin or 0xff for interface */ + t_u8 gpio; + /** gap in milliseconds or or 0xff for special setting when + * GPIO is used to wakeup host + */ + t_u8 gap; +} MLAN_PACK_END hs_config_param; + +/** HS Action 0x0001 - Configure enhanced host sleep mode, + * 0x0002 - Activate enhanced host sleep mode + */ +typedef enum _Host_Sleep_Action { + HS_CONFIGURE = 0x0001, + HS_ACTIVATE = 0x0002, +} Host_Sleep_Action; + +/** Structure definition for activating enhanced hs */ +typedef MLAN_PACK_START struct __hs_activate_param { + /** response control 0x00 - response not needed, 0x01 - response needed */ + t_u16 resp_ctrl; +} MLAN_PACK_END hs_activate_param; + +/** HostCmd_DS_802_11_HS_CFG_ENH */ +typedef MLAN_PACK_START struct _HostCmd_DS_802_11_HS_CFG_ENH { + /** Action 0x0001 - Configure enhanced host sleep mode, + * 0x0002 - Activate enhanced host sleep mode + */ + t_u16 action; + + union { + /** Configure enhanced hs */ + hs_config_param hs_config; + /** Activate enhanced hs */ + hs_activate_param hs_activate; + } params; +} MLAN_PACK_END HostCmd_DS_802_11_HS_CFG_ENH; + +/** HostCmd_CMD_802_11_FW_WAKE_METHOD */ +typedef MLAN_PACK_START struct _HostCmd_DS_802_11_FW_WAKEUP_METHOD { + /** Action */ + t_u16 action; + /** Method */ + t_u16 method; +} MLAN_PACK_END HostCmd_DS_802_11_FW_WAKEUP_METHOD; + +/** HostCmd_CMD_802_11_ROBUSTCOEX */ +typedef MLAN_PACK_START struct _HostCmd_DS_802_11_ROBUSTCOEX { + /** Action */ + t_u16 action; + /** RSVD */ + t_u16 rsvd; +} MLAN_PACK_END HostCmd_DS_802_11_ROBUSTCOEX; + +/** SNMP_MIB_INDEX */ +typedef enum _SNMP_MIB_INDEX { + OpRateSet_i = 1, + DtimPeriod_i = 3, + RtsThresh_i = 5, + ShortRetryLim_i = 6, + LongRetryLim_i = 7, + FragThresh_i = 8, + Dot11D_i = 9, + Dot11H_i = 10, + WwsMode_i = 17, + Thermal_i = 34, + NullPktPeriod_i = 37, + SignalextEnable_i = 41, + ECSAEnable_i = 42, + StopDeauth_i = 44, +} SNMP_MIB_INDEX; + +/** max SNMP buf size */ +#define MAX_SNMP_BUF_SIZE 128 + +/** HostCmd_CMD_802_11_SNMP_MIB */ +typedef MLAN_PACK_START struct _HostCmd_DS_802_11_SNMP_MIB { + /** SNMP query type */ + t_u16 query_type; + /** SNMP object ID */ + t_u16 oid; + /** SNMP buffer size */ + t_u16 buf_size; + /** Value */ + t_u8 value[1]; +} MLAN_PACK_END HostCmd_DS_802_11_SNMP_MIB; + +/** Radio on */ +#define RADIO_ON 0x01 +/** Radio off */ +#define RADIO_OFF 0x00 + +/** HostCmd_CMD_802_11_RADIO_CONTROL */ +typedef MLAN_PACK_START struct _HostCmd_DS_802_11_RADIO_CONTROL { + /** Action */ + t_u16 action; + /** Control */ + t_u16 control; +} MLAN_PACK_END HostCmd_DS_802_11_RADIO_CONTROL; + +/** MrvlRateScope_t */ +typedef MLAN_PACK_START struct _MrvlRateScope_t { + /** Header Type */ + t_u16 type; + /** Header Length */ + t_u16 length; + /** Bitmap of HR/DSSS rates */ + t_u16 hr_dsss_rate_bitmap; + /** Bitmap of OFDM rates */ + t_u16 ofdm_rate_bitmap; + /** Bitmap of HT-MCSs allowed for initial rate */ + t_u16 ht_mcs_rate_bitmap[8]; + /** Reserved */ + t_u16 reserved_1[8]; +} MLAN_PACK_END MrvlRateScope_t; + +/** MrvlRateDropPattern_t */ +typedef MLAN_PACK_START struct _MrvlRateDropPattern_t { + /** Header Type */ + t_u16 type; + /** Header Length */ + t_u16 length; + /** Rate Drop Mode */ + t_u32 rate_drop_mode; + /* MrvlRateDropControl_t RateDropControl[0]; */ +} MLAN_PACK_END MrvlRateDropPattern_t; + +/** HostCmd_DS_TX_RATE_CFG */ +typedef MLAN_PACK_START struct _HostCmd_DS_TX_RATE_CFG { + /** Action */ + t_u16 action; + /** Reserved */ + t_u16 reserved_1; + /* MrvlRateScope_t RateScope; + * MrvlRateDropPattern_t RateDrop; */ +} MLAN_PACK_END HostCmd_DS_TX_RATE_CFG; + +/** Power_Group_t */ +typedef MLAN_PACK_START struct _Power_Group_t { + /** Modulation Class */ + t_u8 modulation_class; + /** MCS Code or Legacy RateID */ + t_u8 first_rate_code; + /** MCS Code or Legacy RateID */ + t_u8 last_rate_code; + /** Power Adjustment Step */ + t_s8 power_step; + /** Minimal Tx Power Level [dBm] */ + t_s8 power_min; + /** Maximal Tx Power Level [dBm] */ + t_s8 power_max; + /** 0: HTBW20, 1: HTBW40 */ + t_u8 ht_bandwidth; + /** Reserved */ + t_u8 reserved; +} MLAN_PACK_END Power_Group_t; + +/** MrvlTypes_Power_Group_t */ +typedef MLAN_PACK_START struct _MrvlTypes_Power_Group_t { + /** Header Type */ + t_u16 type; + /** Header Length */ + t_u16 length; + /* Power_Group_t PowerGroups */ +} MLAN_PACK_END MrvlTypes_Power_Group_t; + +/** HostCmd_CMD_TXPWR_CFG */ +typedef MLAN_PACK_START struct _HostCmd_DS_TXPWR_CFG { + /** Action */ + t_u16 action; + /** Power group configuration index */ + t_u16 cfg_index; + /** Power group configuration mode */ + t_u32 mode; + /* MrvlTypes_Power_Group_t PowerGrpCfg[0] */ +} MLAN_PACK_END HostCmd_DS_TXPWR_CFG; + +/** HostCmd_CMD_802_11_RF_TX_POWER */ +typedef MLAN_PACK_START struct _HostCmd_DS_802_11_RF_TX_POWER { + /** Action */ + t_u16 action; + /** Current power level */ + t_s16 current_level; + /** Maximum power */ + t_s8 max_power; + /** Minimum power */ + t_s8 min_power; +} MLAN_PACK_END HostCmd_DS_802_11_RF_TX_POWER; + +/** Connection type infra */ +#define CONNECTION_TYPE_INFRA 0 +/** Connection type adhoc */ +#define CONNECTION_TYPE_ADHOC 1 +#ifdef WIFI_DIRECT_SUPPORT +/** BSS Mode: WIFIDIRECT Client */ +#define BSS_MODE_WIFIDIRECT_CLIENT 0 +/** BSS Mode: WIFIDIRECT GO */ +#define BSS_MODE_WIFIDIRECT_GO 2 +#endif +/** HostCmd_DS_SET_BSS_MODE */ +typedef MLAN_PACK_START struct _HostCmd_DS_SET_BSS_MODE { + /** connection type */ + t_u8 con_type; +} MLAN_PACK_END HostCmd_DS_SET_BSS_MODE; + +#ifdef WIFI_DIRECT_SUPPORT +/** HostCmd_DS_WIFI_DIRECT_MODE */ +typedef MLAN_PACK_START struct _HostCmd_DS_WIFI_DIRECT_MODE { + /** Action 0-GET, 1-SET*/ + t_u16 action; + /**0:disable 1:listen 2:GO 3:p2p client 4:find 5:stop find*/ + t_u16 mode; +} MLAN_PACK_END HostCmd_DS_WIFI_DIRECT_MODE; + +/** MrvlIEtypes_NoA_setting_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_NoA_setting_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** enable/disable */ + t_u8 enable; + /** index */ + t_u16 index; + /** NoA count */ + t_u8 noa_count; + /** NoA duration */ + t_u32 noa_duration; + /** NoA interval */ + t_u32 noa_interval; +} MLAN_PACK_END MrvlIEtypes_NoA_setting_t; + +/** MrvlIEtypes_NoA_setting_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_OPP_PS_setting_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** enable/disable && ct_window */ + t_u8 enable; +} MLAN_PACK_END MrvlIEtypes_OPP_PS_setting_t; + +/** HostCmd_DS_WIFI_DIRECT_PARAM_CONFIG */ +typedef MLAN_PACK_START struct _HostCmd_DS_WIFI_DIRECT_PARAM_CONFIG { + /** Action 0-GET, 1-SET */ + t_u16 action; + /** MrvlIEtypes_NoA_setting_t + * MrvlIEtypes_OPP_PS_setting_t + */ +} MLAN_PACK_END HostCmd_DS_WIFI_DIRECT_PARAM_CONFIG; +#endif + +/** Coalesce filter field parameter */ +MLAN_PACK_START struct coalesce_filt_field_param { + /** Operation */ + t_u8 operation; + /** Operation length */ + t_u8 operand_len; + /** Offset */ + t_u16 offset; + /** Operand byte stream */ + t_u8 operand_byte_stream[4]; +} MLAN_PACK_END; + +/** Coalesce receive filter rule */ +MLAN_PACK_START struct coalesce_receive_filt_rule { + /** header */ + MrvlIEtypesHeader_t header; + /** Number of fields */ + t_u8 num_of_fields; + /** packet type */ + t_u8 pkt_type; + /** maximum coalescing delay */ + t_u16 max_coalescing_delay; + struct coalesce_filt_field_param params[0]; +} MLAN_PACK_END; + +/** HostCmd_DS_COALESCE_CONFIG */ +typedef MLAN_PACK_START struct _HostCmd_DS_COALESCE_CONFIG { + /** Action 0-GET, 1-SET */ + t_u16 action; + /** Number of rules */ + t_u16 num_of_rules; + struct coalesce_receive_filt_rule rule[0]; +} MLAN_PACK_END HostCmd_DS_COALESCE_CONFIG; + +/** TLV type : FW support max connection TLV */ +#define TLV_TYPE_MAX_CONN (PROPRIETARY_TLV_BASE_ID + 0x117) /* 0x0217 */ +/** MrvlIEtypes_Max_Conn_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_Max_Conn_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** FW support max P2P connection */ + t_u8 max_p2p_conn; + /** FW support max STA connection */ + t_u8 max_sta_conn; +} MLAN_PACK_END MrvlIEtypes_Max_Conn_t; + +/** exceed max p2p connection event */ +typedef MLAN_PACK_START struct _event_exceed_max_p2p_conn { + /** Event ID */ + t_u16 event_id; + /** BSS index number for multiple BSS support */ + t_u8 bss_index; + /** BSS type */ + t_u8 bss_type; + /** When exceed max, the mac address who request p2p connect */ + t_u8 peer_mac_addr[MLAN_MAC_ADDR_LENGTH]; +} MLAN_PACK_END event_exceed_max_p2p_conn; + +#ifdef STA_SUPPORT + +/** + * @brief Structure used internally in the wlan driver to configure a scan. + * + * Sent to the command process module to configure the firmware + * scan command prepared by wlan_cmd_802_11_scan. + * + * @sa wlan_scan_networks + * + */ +typedef MLAN_PACK_START struct _wlan_scan_cmd_config { + /** + * BSS Type to be sent in the firmware command + * + * Field can be used to restrict the types of networks returned in the + * scan. Valid settings are: + * + * - MLAN_SCAN_MODE_BSS (infrastructure) + * - MLAN_SCAN_MODE_IBSS (adhoc) + * - MLAN_SCAN_MODE_ANY (unrestricted, adhoc and infrastructure) + */ + t_u8 bss_mode; + + /** + * Specific BSSID used to filter scan results in the firmware + */ + t_u8 specific_bssid[MLAN_MAC_ADDR_LENGTH]; + + /** + * Length of TLVs sent in command starting at tlvBuffer + */ + t_u32 tlv_buf_len; + + /** + * SSID TLV(s) and ChanList TLVs to be sent in the firmware command + * + * TLV_TYPE_CHANLIST, MrvlIEtypes_ChanListParamSet_t + * TLV_TYPE_SSID, MrvlIEtypes_SsIdParamSet_t + */ + t_u8 tlv_buf[1]; /* SSID TLV(s) and ChanList TLVs are stored here */ +} MLAN_PACK_END wlan_scan_cmd_config; + +/** + * Sructure to retrieve the scan table + */ +typedef MLAN_PACK_START struct { + /** + * - Zero based scan entry to start retrieval in command request + * - Number of scans entries returned in command response + */ + t_u32 scan_number; + /** + * Buffer marker for multiple wlan_ioctl_get_scan_table_entry structures. + * Each struct is padded to the nearest 32 bit boundary. + */ + t_u8 scan_table_entry_buf[1]; +} MLAN_PACK_END wlan_get_scan_table_info; + +/** Generic structure defined for parsing WPA/RSN IEs for GTK/PTK OUIs */ +typedef MLAN_PACK_START struct { + /** Group key oui */ + t_u8 GrpKeyOui[4]; + /** Number of PTKs */ + t_u8 PtkCnt[2]; + /** Ptk body starts here */ + t_u8 PtkBody[4]; +} MLAN_PACK_END IEBody; +#endif /* STA_SUPPORT */ + +/* + * This scan handle Country Information IE(802.11d compliant) + * Define data structure for HostCmd_CMD_802_11_SCAN + */ +/** HostCmd_DS_802_11_SCAN */ +typedef MLAN_PACK_START struct _HostCmd_DS_802_11_SCAN { + /** BSS mode */ + t_u8 bss_mode; + /** BSSID */ + t_u8 bssid[MLAN_MAC_ADDR_LENGTH]; + /** TLV buffer */ + t_u8 tlv_buffer[1]; + /** MrvlIEtypes_SsIdParamSet_t SsIdParamSet; + * MrvlIEtypes_ChanListParamSet_t ChanListParamSet; + * MrvlIEtypes_RatesParamSet_t OpRateSet; + */ +} MLAN_PACK_END HostCmd_DS_802_11_SCAN; + +/** fw_cap_info bit to indicate enhance ext scan type */ +#define ENHANCE_EXT_SCAN_ENABLE MBIT(19) +/** mlan_event_scan_result data structure */ +typedef MLAN_PACK_START struct _mlan_event_scan_result { + /** Event ID */ + t_u16 event_id; + /** BSS index number for multiple BSS support */ + t_u8 bss_index; + /** BSS type */ + t_u8 bss_type; + /** More event available or not */ + t_u8 more_event; + /** Reserved */ + t_u8 reserved[3]; + /** Size of the response buffer */ + t_u16 buf_size; + /** Number of BSS in scan response */ + t_u8 num_of_set; +} MLAN_PACK_END mlan_event_scan_result, *pmlan_event_scan_result; + +/** ext scan status report event */ +typedef MLAN_PACK_START struct _mlan_event_scan_status { + /** Event ID */ + t_u16 event_id; + /** BSS index number for multiple BSS support */ + t_u8 bss_index; + /** BSS type */ + t_u8 bss_type; + /** scan status */ + t_u8 scan_status; + /** result */ + t_u16 buf_len; + /** event buf */ + t_u8 event_buf[0]; +} MLAN_PACK_END mlan_event_scan_status, *pmlan_event_scan_status; + +/* + * This scan handle Country Information IE(802.11d compliant) + * Define data structure for HostCmd_CMD_802_11_SCAN_EXT + */ +/** HostCmd_DS_802_11_SCAN_EXT */ +typedef MLAN_PACK_START struct _HostCmd_DS_802_11_SCAN_EXT { + /** Scan type for ext scan + * 0: default type: cmd resp after ext scan report event + * 1: enhanced type: cmd resp before ext scan report event + * 2: scan cancelled: cancel scan during scan processing + */ + t_u8 ext_scan_type; + /** Reserved */ + t_u8 reserved[3]; + /** TLV buffer */ + t_u8 tlv_buffer[1]; + /** MrvlIEtypes_Bssid_List_t BssIdList; + * MrvlIEtypes_SsIdParamSet_t SSIDParamSet; + * MrvlIEtypes_ChanListParamSet_t ChanListParamSet; + * MrvlIEtypes_RatesParamSet_t OpRateSet; + * MrvlIEtypes_NumProbes_t NumProbes; + * MrvlIEtypes_WildCardSsIdParamSet_t WildCardSSIDParamSet; + * MrvlIEtypes_BssMode_t BssMode; + */ +} MLAN_PACK_END HostCmd_DS_802_11_SCAN_EXT; + +/** MrvlIEtypes_BssMode */ +typedef MLAN_PACK_START struct _MrvlIEtypes_BssMode_t { + /** Header */ + MrvlIEtypesHeader_t header; + /* INFRA/IBSS/AUTO */ + t_u8 bss_mode; +} MLAN_PACK_END MrvlIEtypes_BssMode_t; + +/** BSS scan Rsp */ +typedef MLAN_PACK_START struct _MrvlIEtypes_Bss_Scan_Rsp_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** BSSID of the BSS descriptor */ + t_u8 bssid[MLAN_MAC_ADDR_LENGTH]; + /** Beacon/Probe response buffer */ + t_u8 frame_body[1]; +} MLAN_PACK_END MrvlIEtypes_Bss_Scan_Rsp_t; + +typedef MLAN_PACK_START struct _MrvlIEtypes_Bss_Scan_Info_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** RSSI for scan entry */ + t_s16 rssi; + /** Channel ANPI */ + t_s16 anpi; + /** Channel load (parts per 255) */ + t_u8 cca_busy_fraction; + /** Band */ + Band_Config_t bandcfg; + /** Channel */ + t_u8 channel; + /** Reserved */ + t_u8 reserved; + /** TSF data */ + t_u64 tsf; +} MLAN_PACK_END MrvlIEtypes_Bss_Scan_Info_t; + +/** HostCmd_DS_RX_MGMT_IND */ +typedef MLAN_PACK_START struct _HostCmd_DS_RX_MGMT_IND { + /** Action */ + t_u16 action; + /** Mgmt frame subtype mask */ + t_u32 mgmt_subtype_mask; +} MLAN_PACK_END HostCmd_DS_RX_MGMT_IND; + +/** HostCmd_DS_802_11_SCAN_RSP */ +typedef MLAN_PACK_START struct _HostCmd_DS_802_11_SCAN_RSP { + /** Size of BSS descriptor */ + t_u16 bss_descript_size; + /** Numner of sets */ + t_u8 number_of_sets; + /** BSS descriptor and TLV buffer */ + t_u8 bss_desc_and_tlv_buffer[1]; +} MLAN_PACK_END HostCmd_DS_802_11_SCAN_RSP; + +/** HostCmd_DS_802_11_BG_SCAN_CONFIG */ +typedef MLAN_PACK_START struct _HostCmd_DS_802_11_BG_SCAN_CONFIG { + /** action */ + t_u16 action; + /** 0: disable, 1: enable */ + t_u8 enable; + /** bss type */ + t_u8 bss_type; + /** num of channel per scan */ + t_u8 chan_per_scan; + /** reserved field */ + t_u8 reserved; + /** reserved field */ + t_u16 reserved1; + /** interval between consecutive scans */ + t_u32 scan_interval; + /** reserved field */ + t_u32 reserved2; + /** condition to trigger report to host */ + t_u32 report_condition; + /** reserved field */ + t_u16 reserved3; +} MLAN_PACK_END HostCmd_DS_802_11_BG_SCAN_CONFIG; + +/** HostCmd_DS_802_11_BG_SCAN_QUERY */ +typedef MLAN_PACK_START struct _HostCmd_DS_802_11_BG_SCAN_QUERY { + /** Flush */ + t_u8 flush; +} MLAN_PACK_END HostCmd_DS_802_11_BG_SCAN_QUERY; + +/** HostCmd_DS_802_11_BG_SCAN_QUERY_RSP */ +typedef MLAN_PACK_START struct _HostCmd_DS_802_11_BG_SCAN_QUERY_RSP { + /** Report condition */ + t_u32 report_condition; + /** Scan response */ + HostCmd_DS_802_11_SCAN_RSP scan_resp; +} MLAN_PACK_END HostCmd_DS_802_11_BG_SCAN_QUERY_RSP; + +/** MrvlIEtypes_StartLater_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_StartLater_t { + /** Header */ + MrvlIEtypesHeader_t header; + /* 0 - BGScan start immediately, 1 - BGScan will start later after "Scan Interval" */ + t_u16 value; +} MLAN_PACK_END MrvlIEtypes_StartLater_t; + +/** MrvlIEtypes_RepeatCount_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_RepeatCount_t { + /** Header */ + MrvlIEtypesHeader_t header; + /* Repeat count */ + t_u16 repeat_count; +} MLAN_PACK_END MrvlIEtypes_RepeatCount_t; + +/** MrvlIEtypes_DomainParamSet_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_DomainParamSet { + /** Header */ + MrvlIEtypesHeader_t header; + /** Country code */ + t_u8 country_code[COUNTRY_CODE_LEN]; + /** Set of subbands */ + IEEEtypes_SubbandSet_t sub_band[1]; +} MLAN_PACK_END MrvlIEtypes_DomainParamSet_t; + +/** HostCmd_DS_802_11D_DOMAIN_INFO */ +typedef MLAN_PACK_START struct _HostCmd_DS_802_11D_DOMAIN_INFO { + /** Action */ + t_u16 action; + /** Domain parameter set */ + MrvlIEtypes_DomainParamSet_t domain; +} MLAN_PACK_END HostCmd_DS_802_11D_DOMAIN_INFO; + +/** HostCmd_DS_802_11D_DOMAIN_INFO_RSP */ +typedef MLAN_PACK_START struct _HostCmd_DS_802_11D_DOMAIN_INFO_RSP { + /** Action */ + t_u16 action; + /** Domain parameter set */ + MrvlIEtypes_DomainParamSet_t domain; +} MLAN_PACK_END HostCmd_DS_802_11D_DOMAIN_INFO_RSP; + +/** HostCmd_DS_802_11K_GET_NLIST */ +typedef MLAN_PACK_START struct _HostCmd_DS_802_11K_GET_NLIST { + /** Action */ + t_u16 action; + /** Dialog token */ + t_u8 dialog_token; +} MLAN_PACK_END HostCmd_DS_802_11K_GET_NLIST; + +#ifdef BIG_ENDIAN_SUPPORT +/** host_OffloadFeatureStdControl_t*/ +typedef MLAN_PACK_START struct { + t_u8 dot11k_tsm:1; + t_u8 dot11k_lm:1; + t_u8 dot11k_nbor_support:1; + t_u8 dot11k_rm:1; + t_u8 dot11h_rm:1; + t_u8 wmm_ac_tpsec_modify:1; + t_u8 wmm_ac_dynamic_ps:1; + t_u8 client_radar_detect:1; + + t_u8 reserved:3; + t_u8 rbc:1; + t_u8 dot11v_bss_trans:1; + t_u8 vowifi_probe_tpc_rpt:1; + t_u8 pmf_required:1; + t_u8 pmf_capable:1; +} MLAN_PACK_END host_OffloadFeatureStdControl_t; +#else +/** host_OffloadFeatureStdControl_t */ +typedef MLAN_PACK_START struct { + t_u8 client_radar_detect:1; + t_u8 wmm_ac_dynamic_ps:1; + t_u8 wmm_ac_tpsec_modify:1; + t_u8 dot11h_rm:1; + t_u8 dot11k_rm:1; + t_u8 dot11k_nbor_support:1; + t_u8 dot11k_lm:1; + t_u8 dot11k_tsm:1; + + t_u8 pmf_capable:1; + t_u8 pmf_required:1; + t_u8 vowifi_probe_tpc_rpt:1; + t_u8 dot11v_bss_trans:1; + t_u8 rbc:1; + t_u8 reserved:3; +} MLAN_PACK_END host_OffloadFeatureStdControl_t; +#endif + +/** END HostCmd_OFFLOAD_FEATURE_CTRL */ +typedef MLAN_PACK_START struct _HostCmd_OFFLOAD_FEATURE_CTRL { + t_u8 featureSelect; + union { + host_OffloadFeatureStdControl_t std; + t_u8 empty; + } control; +} MLAN_PACK_END HostCmd_OFFLOAD_FEATURE_CTRL; +/** HostCmd_DS_11N_ADDBA_REQ */ +typedef MLAN_PACK_START struct _HostCmd_DS_11N_ADDBA_REQ { + /** Result of the ADDBA Request Operation */ + t_u8 add_req_result; + /** Peer MAC address */ + t_u8 peer_mac_addr[MLAN_MAC_ADDR_LENGTH]; + /** Dialog Token */ + t_u8 dialog_token; + /** Block Ack Parameter Set */ + t_u16 block_ack_param_set; + /** Block Act Timeout Value */ + t_u16 block_ack_tmo; + /** Starting Sequence Number */ + t_u16 ssn; +} MLAN_PACK_END HostCmd_DS_11N_ADDBA_REQ; + +/** HostCmd_DS_11N_ADDBA_RSP */ +typedef MLAN_PACK_START struct _HostCmd_DS_11N_ADDBA_RSP { + /** Result of the ADDBA Response Operation */ + t_u8 add_rsp_result; + /** Peer MAC address */ + t_u8 peer_mac_addr[MLAN_MAC_ADDR_LENGTH]; + /** Dialog Token */ + t_u8 dialog_token; + /** Status Code */ + t_u16 status_code; + /** Block Ack Parameter Set */ + t_u16 block_ack_param_set; + /** Block Act Timeout Value */ + t_u16 block_ack_tmo; + /** Starting Sequence Number */ + t_u16 ssn; +} MLAN_PACK_END HostCmd_DS_11N_ADDBA_RSP; + +/** HostCmd_DS_11N_DELBA */ +typedef MLAN_PACK_START struct _HostCmd_DS_11N_DELBA { + /** Result of the ADDBA Request Operation */ + t_u8 del_result; + /** Peer MAC address */ + t_u8 peer_mac_addr[MLAN_MAC_ADDR_LENGTH]; + /** Delete Block Ack Parameter Set */ + t_u16 del_ba_param_set; + /** Reason Code sent for DELBA */ + t_u16 reason_code; + /** Reserved */ + t_u8 reserved; +} MLAN_PACK_END HostCmd_DS_11N_DELBA; + +/** HostCmd_DS_11N_BATIMEOUT */ +typedef MLAN_PACK_START struct _HostCmd_DS_11N_BATIMEOUT { + /** TID */ + t_u8 tid; + /** Peer MAC address */ + t_u8 peer_mac_addr[MLAN_MAC_ADDR_LENGTH]; + /** Delete Block Ack Parameter Set */ + t_u8 origninator; +} MLAN_PACK_END HostCmd_DS_11N_BATIMEOUT; + +/** HostCmd_DS_11N_CFG */ +typedef MLAN_PACK_START struct _HostCmd_DS_11N_CFG { + /** Action */ + t_u16 action; + /** HTTxCap */ + t_u16 ht_tx_cap; + /** HTTxInfo */ + t_u16 ht_tx_info; + /** Misc configuration */ + t_u16 misc_config; +} MLAN_PACK_END HostCmd_DS_11N_CFG; + +/** HostCmd_DS_11N_CFG */ +typedef MLAN_PACK_START struct _HostCmd_DS_REJECT_ADDBA_REQ { + /** Action */ + t_u16 action; + /** Bit0 : host sleep activated + * Bit1 : auto reconnect enabled + * Others : reserved + */ + t_u32 conditions; +} MLAN_PACK_END HostCmd_DS_REJECT_ADDBA_REQ; + +/** HostCmd_DS_TXBUF_CFG*/ +typedef MLAN_PACK_START struct _HostCmd_DS_TXBUF_CFG { + /** Action */ + t_u16 action; + /** Buffer Size */ + t_u16 buff_size; + /** End Port_for Multiport */ + t_u16 mp_end_port; + /** Reserved */ + t_u16 reserved3; +} MLAN_PACK_END HostCmd_DS_TXBUF_CFG; + +/** HostCmd_DS_AMSDU_AGGR_CTRL */ +typedef MLAN_PACK_START struct _HostCmd_DS_AMSDU_AGGR_CTRL { + /** Action */ + t_u16 action; + /** Enable */ + t_u16 enable; + /** Get the current Buffer Size valid */ + t_u16 curr_buf_size; +} MLAN_PACK_END HostCmd_DS_AMSDU_AGGR_CTRL; + +/** HostCmd_DS_ECL_SYSTEM_CLOCK_CONFIG */ +typedef MLAN_PACK_START struct _HostCmd_DS_ECL_SYSTEM_CLOCK_CONFIG { + /** Action */ + t_u16 action; + /** Current system clock */ + t_u16 cur_sys_clk; + /** Clock type */ + t_u16 sys_clk_type; + /** Length of clocks */ + t_u16 sys_clk_len; + /** System clocks */ + t_u16 sys_clk[16]; +} MLAN_PACK_END HostCmd_DS_ECL_SYSTEM_CLOCK_CONFIG; + +/** MrvlIEtypes_WmmParamSet_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_WmmParamSet_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** WMM IE */ + t_u8 wmm_ie[1]; +} MLAN_PACK_END MrvlIEtypes_WmmParamSet_t; + +/** MrvlIEtypes_WmmQueueStatus_t */ +typedef MLAN_PACK_START struct { + /** Header */ + MrvlIEtypesHeader_t header; + /** Queue index */ + t_u8 queue_index; + /** Disabled flag */ + t_u8 disabled; + /** Medium time allocation in 32us units*/ + t_u16 medium_time; + /** Flow required flag */ + t_u8 flow_required; + /** Flow created flag */ + t_u8 flow_created; + /** Reserved */ + t_u32 reserved; +} MLAN_PACK_END MrvlIEtypes_WmmQueueStatus_t; + +/** Size of a TSPEC. Used to allocate necessary buffer space in commands */ +#define WMM_TSPEC_SIZE 63 + +/** Extra IE bytes allocated in messages for appended IEs after a TSPEC */ +#define WMM_ADDTS_EXTRA_IE_BYTES 256 + +/** Extra TLV bytes allocated in messages for configuring WMM Queues */ +#define WMM_QUEUE_CONFIG_EXTRA_TLV_BYTES 64 + +/** Number of bins in the histogram for the HostCmd_DS_WMM_QUEUE_STATS */ +#define WMM_STATS_PKTS_HIST_BINS 7 + +/** + * @brief Firmware command structure to retrieve the firmware WMM status. + * + * Used to retrieve the status of each WMM AC Queue in TLV + * format (MrvlIEtypes_WmmQueueStatus_t) as well as the current WMM + * parameter IE advertised by the AP. + * + * Used in response to a EVENT_WMM_STATUS_CHANGE event signaling + * a QOS change on one of the ACs or a change in the WMM Parameter in + * the Beacon. + * + * TLV based command, byte arrays used for max sizing purpose. There are no + * arguments sent in the command, the TLVs are returned by the firmware. + */ +typedef MLAN_PACK_START struct { + /** Queue status TLV */ + t_u8 queue_status_tlv[sizeof(MrvlIEtypes_WmmQueueStatus_t) + * MAX_AC_QUEUES]; + /** WMM parameter TLV */ + t_u8 wmm_param_tlv[sizeof(IEEEtypes_WmmParameter_t) + 2]; +} MLAN_PACK_END HostCmd_DS_WMM_GET_STATUS; + +/** + * @brief Command structure for the HostCmd_CMD_WMM_ADDTS_REQ firmware command + */ +typedef MLAN_PACK_START struct { + mlan_cmd_result_e command_result;/**< Command result */ + t_u32 timeout_ms; /**< Timeout value in milliseconds */ + t_u8 dialog_token; /**< Dialog token */ + t_u8 ieee_status_code; /**< IEEE status code */ + t_u8 tspec_data[WMM_TSPEC_SIZE]; /**< TSPEC data */ + t_u8 addts_extra_ie_buf[WMM_ADDTS_EXTRA_IE_BYTES]; + /**< Extra IE buffer */ +} MLAN_PACK_END HostCmd_DS_WMM_ADDTS_REQ; + +/** + * @brief Command structure for the HostCmd_CMD_WMM_DELTS_REQ firmware command + */ +typedef MLAN_PACK_START struct { + mlan_cmd_result_e command_result; + /**< Command result */ + t_u8 dialog_token; /**< Dialog token */ + t_u8 ieee_reason_code; /**< IEEE reason code */ + t_u8 tspec_data[WMM_TSPEC_SIZE];/**< TSPEC data */ +} MLAN_PACK_END HostCmd_DS_WMM_DELTS_REQ; + +/** + * @brief Command structure for the HostCmd_CMD_WMM_QUEUE_CONFIG firmware cmd + * + * Set/Get/Default the Queue parameters for a specific AC in the firmware. + */ +typedef MLAN_PACK_START struct { + mlan_wmm_queue_config_action_e action; + /**< Set, Get, or Default */ + mlan_wmm_ac_e access_category; /**< WMM_AC_BK(0) to WMM_AC_VO(3) */ + /** @brief MSDU lifetime expiry per 802.11e + * + * - Ignored if 0 on a set command + * - Set to the 802.11e specified 500 TUs when defaulted + */ + t_u16 msdu_lifetime_expiry; + t_u8 tlv_buffer[WMM_QUEUE_CONFIG_EXTRA_TLV_BYTES]; + /**< Not supported */ +} MLAN_PACK_END HostCmd_DS_WMM_QUEUE_CONFIG; + +/** + * @brief Command structure for the HostCmd_CMD_WMM_QUEUE_STATS firmware cmd + * + * Turn statistical collection on/off for a given AC or retrieve the + * accumulated stats for an AC and clear them in the firmware. + */ +typedef MLAN_PACK_START struct { + mlan_wmm_queue_stats_action_e action; + /**< Start, Stop, or Get */ +#ifdef BIG_ENDIAN_SUPPORT + t_u8 select_bin:7; /**< WMM_AC_BK(0) to WMM_AC_VO(3), or TID */ + t_u8 select_is_userpri:1; + /**< Set if select_bin is UP, Clear for AC */ +#else + t_u8 select_is_userpri:1; + /**< Set if select_bin is UP, Clear for AC */ + t_u8 select_bin:7; /**< WMM_AC_BK(0) to WMM_AC_VO(3), or TID */ +#endif + t_u16 pkt_count; /**< Number of successful packets transmitted */ + t_u16 pkt_loss; /**< Packets lost; not included in pktCount */ + t_u32 avg_queue_delay; + /**< Average Queue delay in microsec */ + t_u32 avg_tx_delay; /**< Average Transmission delay in microsec */ + t_u16 used_time; /**< Calc used time - units of 32 microsec */ + t_u16 policed_time; /**< Calc policed time - units of 32 microsec */ + /** @brief Queue Delay Histogram; number of packets per queue delay range + * + * [0] - 0ms <= delay < 5ms + * [1] - 5ms <= delay < 10ms + * [2] - 10ms <= delay < 20ms + * [3] - 20ms <= delay < 30ms + * [4] - 30ms <= delay < 40ms + * [5] - 40ms <= delay < 50ms + * [6] - 50ms <= delay < msduLifetime (TUs) + */ + t_u16 delay_histogram[WMM_STATS_PKTS_HIST_BINS]; + /** Reserved */ + t_u16 reserved_1; +} MLAN_PACK_END HostCmd_DS_WMM_QUEUE_STATS; + +/** + * @brief Command structure for the HostCmd_CMD_WMM_TS_STATUS firmware cmd + * + * Query the firmware to get the status of the WMM Traffic Streams + */ +typedef MLAN_PACK_START struct { + /** TSID: Range: 0->7 */ + t_u8 tid; + /** TSID specified is valid */ + t_u8 valid; + /** AC TSID is active on */ + t_u8 access_category; + /** UP specified for the TSID */ + t_u8 user_priority; + /** Power save mode for TSID: 0 (legacy), 1 (UAPSD) */ + t_u8 psb; + /** Uplink(1), Downlink(2), Bidirectional(3) */ + t_u8 flow_dir; + /** Medium time granted for the TSID */ + t_u16 medium_time; +} MLAN_PACK_END HostCmd_DS_WMM_TS_STATUS; + +/** Firmware status for a specific AC */ +typedef MLAN_PACK_START struct { + /** Disabled flag */ + t_u8 disabled; + /** Flow required flag */ + t_u8 flow_required; + /** Flow created flag */ + t_u8 flow_created; +} MLAN_PACK_END WmmAcStatus_t; + +/** Local Power Capability */ +typedef MLAN_PACK_START struct _MrvlIEtypes_PowerCapability_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** Minmum power */ + t_s8 min_power; + /** Maximum power */ + t_s8 max_power; +} MLAN_PACK_END MrvlIEtypes_PowerCapability_t; + +/** HT Capabilities element */ +typedef MLAN_PACK_START struct _MrvlIETypes_HTCap_t { + /** Header */ + MrvlIEtypesHeader_t header; + + /** HTCap struct */ + HTCap_t ht_cap; +} MLAN_PACK_END MrvlIETypes_HTCap_t; + +/** HT Information element */ +typedef MLAN_PACK_START struct _MrvlIETypes_HTInfo_t { + /** Header */ + MrvlIEtypesHeader_t header; + + /** HTInfo struct */ + HTInfo_t ht_info; +} MLAN_PACK_END MrvlIETypes_HTInfo_t; + +/** 20/40 BSS Coexistence element */ +typedef MLAN_PACK_START struct _MrvlIETypes_2040BSSCo_t { + /** Header */ + MrvlIEtypesHeader_t header; + + /** BSSCo2040_t struct */ + BSSCo2040_t bss_co_2040; +} MLAN_PACK_END MrvlIETypes_2040BSSCo_t; + +/** Extended Capabilities element */ +typedef MLAN_PACK_START struct _MrvlIETypes_ExtCap_t { + /** Header */ + MrvlIEtypesHeader_t header; + + /** ExtCap_t struct */ + ExtCap_t ext_cap; +} MLAN_PACK_END MrvlIETypes_ExtCap_t; + +/** Supported operating classes element */ +typedef MLAN_PACK_START struct _MrvlIETypes_SuppOperClass_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** Current operationg class **/ + t_u8 current_oper_class; + /** Operating class list */ + t_u8 oper_class[1]; +} MLAN_PACK_END MrvlIETypes_SuppOperClass_t; + +/** Oper_class channel bandwidth element */ +typedef MLAN_PACK_START struct _MrvlIEtypes_chan_bw_oper_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** channel oper bandwidth*/ + mlan_ds_bw_chan_oper ds_chan_bw_oper; +} MLAN_PACK_END MrvlIEtypes_chan_bw_oper_t; + +/** Qos Info */ +typedef MLAN_PACK_START struct _MrvlIETypes_qosinfo_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** qos_info*/ + t_u8 qos_info; +} MLAN_PACK_END MrvlIETypes_qosinfo_t; + +/** Overlapping BSS Scan Parameters element */ +typedef MLAN_PACK_START struct _MrvlIETypes_OverlapBSSScanParam_t { + /** Header */ + MrvlIEtypesHeader_t header; + + /** OBSSScanParam_t struct */ + OBSSScanParam_t obss_scan_param; +} MLAN_PACK_END MrvlIETypes_OverlapBSSScanParam_t; + +/** Set of MCS values that STA desires to use within the BSS */ +typedef MLAN_PACK_START struct _MrvlIETypes_HTOperationalMCSSet_t { + /** Header */ + MrvlIEtypesHeader_t header; + + /** Bitmap indicating MCSs that STA desires to use within the BSS */ + t_u8 ht_operational_mcs_bitmap[16]; +} MLAN_PACK_END MrvlIETypes_HTOperationalMCSSet_t; + +/** bf global args */ +typedef struct MLAN_PACK_START _bf_global_cfg_args { + /** Global enable/disable bf */ + t_u8 bf_enbl; + /** Global enable/disable sounding */ + t_u8 sounding_enbl; + /** FB Type */ + t_u8 fb_type; + /** SNR Threshold */ + t_u8 snr_threshold; + /** Sounding interval */ + t_u16 sounding_interval; + /** BF mode */ + t_u8 bf_mode; + /** Reserved */ + t_u8 reserved; +} MLAN_PACK_END bf_global_cfg_args; + +/** bf_trigger_sound_args_t */ +typedef MLAN_PACK_START struct _bf_trigger_sound_args_t { + /** Peer MAC address */ + t_u8 peer_mac[MLAN_MAC_ADDR_LENGTH]; + /** Status */ + t_u8 status; +} MLAN_PACK_END bf_trigger_sound_args_t; + +/** bf periodicity args */ +typedef MLAN_PACK_START struct _bf_periodicity_args { + /** Peer MAC address */ + t_u8 peer_mac[MLAN_MAC_ADDR_LENGTH]; + /** Current Tx BF Interval */ + t_u16 interval; + /** Status */ + t_u8 status; +} MLAN_PACK_END bf_periodicity_args; + +/** bf peer configuration args */ +typedef struct MLAN_PACK_START _bf_peer_args { + /** Peer MAC address */ + t_u8 peer_mac[MLAN_MAC_ADDR_LENGTH]; + /** Reserved */ + t_u16 reserved; + /** Enable/Disable Beamforming */ + t_u8 bf_enbl; + /** Enable/Disable sounding */ + t_u8 sounding_enbl; + /** FB Type */ + t_u8 fb_type; +} MLAN_PACK_END bf_peer_args; + +/** bf_snr_thr_t */ +typedef MLAN_PACK_START struct _bf_snr_thr_t { + /** Peer MAC address */ + t_u8 peer_mac[MLAN_MAC_ADDR_LENGTH]; + /** SNR */ + t_u8 snr; +} MLAN_PACK_END bf_snr_thr_t; + +/** HostCmd_DS_TX_BF_CFG */ +typedef MLAN_PACK_START struct _HostCmd_DS_TX_BF_CFG { + /* Beamforming action */ + t_u16 bf_action; + /* action - SET/GET */ + t_u16 action; + + MLAN_PACK_START union { + bf_global_cfg_args bf_global_cfg; + bf_trigger_sound_args_t bf_sound_args; + bf_periodicity_args bf_periodicity; + bf_peer_args tx_bf_peer; + bf_snr_thr_t bf_snr; + } MLAN_PACK_END body; +} MLAN_PACK_END HostCmd_DS_TX_BF_CFG; + +#ifdef WIFI_DIRECT_SUPPORT +/** MrvlIEtypes_psk_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_psk_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** PSK */ + t_u8 psk[MLAN_MAX_KEY_LENGTH]; +} MLAN_PACK_END MrvlIEtypes_psk_t; +#endif /* WIFI_DIRECT_SUPPORT */ + +/** Data structure for Link ID */ +typedef MLAN_PACK_START struct _MrvlIETypes_LinkIDElement_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** Bssid */ + t_u8 bssid[MLAN_MAC_ADDR_LENGTH]; + /** initial sta address*/ + t_u8 init_sta[MLAN_MAC_ADDR_LENGTH]; + /** respose sta address */ + t_u8 resp_sta[MLAN_MAC_ADDR_LENGTH]; +} MLAN_PACK_END MrvlIETypes_LinkIDElement_t; + +/** MrvlIEtypes_PMK_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_PMK_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** PMK */ + t_u8 pmk[1]; +} MLAN_PACK_END MrvlIEtypes_PMK_t; + +/** MrvlIEtypes_Passphrase_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_Passphrase_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** Passphrase */ + char passphrase[1]; +} MLAN_PACK_END MrvlIEtypes_Passphrase_t; + +/* unicastCipher - + * Bit 0 : RFU + * Bit 1 : RFU + * Bit 2 : TKIP + * Bit 3 : AES CCKM + * Bit 2-7 : RFU + * multicastCipher - + * Bit 0 : WEP40 + * Bit 1 : WEP104 + * Bit 2 : TKIP + * Bit 3 : AES + * Bit 4-7 : Reserved for now + */ +/** MrvlIEtypes_Cipher_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_Cipher_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** PairCipher */ + t_u8 pair_cipher; + /** GroupCipher */ + t_u8 group_cipher; +} MLAN_PACK_END MrvlIEtypes_Cipher_t; + +/* rsnMode - + * Bit 0 : No RSN + * Bit 1-2 : RFU + * Bit 3 : WPA + * Bit 4 : WPA-NONE + * Bit 5 : WPA2 + * Bit 6 : AES CCKM + * Bit 7-15 : RFU + */ +/** MrvlIEtypes_EncrProto_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_EncrProto_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** EncrProto */ + t_u16 rsn_mode; +} MLAN_PACK_END MrvlIEtypes_EncrProto_t; + +/** MrvlIEtypes_Bssid_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_Bssid_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** Bssid */ + t_u8 bssid[MLAN_MAC_ADDR_LENGTH]; +} MLAN_PACK_END MrvlIEtypes_Bssid_t; + +/* + * This struct will handle GET,SET,CLEAR function for embedded + * supplicant. + * Define data structure for HostCmd_CMD_802_11_SUPPLICANT_PMK + */ +/** HostCmd_DS_802_11_SUPPLICANT_PMK */ +typedef MLAN_PACK_START struct _HostCmd_DS_802_11_SUPPLICANT_PMK { + /** CMD Action GET/SET/CLEAR */ + t_u16 action; + /** CacheResult initialized to 0 */ + t_u16 cache_result; + /** TLV Buffer */ + t_u8 tlv_buffer[1]; + /** MrvlIEtypes_SsidParamSet_t SsidParamSet; + * MrvlIEtypes_PMK_t Pmk; + * MrvlIEtypes_Passphrase_t Passphrase; + * MrvlIEtypes_Bssid_t Bssid; + **/ +} MLAN_PACK_END HostCmd_DS_802_11_SUPPLICANT_PMK; + +/* + * This struct will GET the Supplicant supported bitmaps + * The GET_CURRENT action will get the network profile used + * for the current assocation. + * Define data structure for HostCmd_CMD_802_11_SUPPLICANT_PROFILE + */ +typedef MLAN_PACK_START struct _HostCmd_DS_802_11_SUPPLICANT_PROFILE { + /** GET/SET/GET_CURRENT */ + t_u16 action; + /** Reserved */ + t_u16 reserved; + /** TLVBuffer */ + t_u8 tlv_buf[1]; + /* MrvlIEtypes_EncrProto_t */ +} MLAN_PACK_END HostCmd_DS_802_11_SUPPLICANT_PROFILE; + +/** RFType */ +typedef MLAN_PACK_START struct _RFType_t { + /** band info */ + Band_Config_t bandcfg; + /** reserved */ + t_u8 reserved; +} MLAN_PACK_END RFType_t; + +/** HostCmd_CMD_802_11_RF_CHANNEL */ +typedef MLAN_PACK_START struct _HostCmd_DS_802_11_RF_CHANNEL { + /** Action */ + t_u16 action; + /** Current channel */ + t_u16 current_channel; + /** RF type */ + RFType_t rf_type; + /** Reserved field */ + t_u16 reserved; +#ifdef STA_SUPPORT + /** Reserved */ + t_u8 reserved_1[32]; +#else /* STA_SUPPORT */ + /** List of channels */ + t_u8 channel_list[32]; +#endif /* !STA_SUPPORT */ +} MLAN_PACK_END HostCmd_DS_802_11_RF_CHANNEL; + +/** HostCmd_DS_VERSION_EXT */ +typedef MLAN_PACK_START struct _HostCmd_DS_VERSION_EXT { + /** Selected version string */ + t_u8 version_str_sel; + /** Version string */ + char version_str[128]; +} MLAN_PACK_END HostCmd_DS_VERSION_EXT; + +#define TLV_TYPE_CHAN_ATTR_CFG (PROPRIETARY_TLV_BASE_ID + 237) +#define TLV_TYPE_REGION_INFO (PROPRIETARY_TLV_BASE_ID + 238) +#define TLV_TYPE_POWER_TABLE (PROPRIETARY_TLV_BASE_ID + 262) +/** HostCmd_DS_CHAN_REGION_CFG */ +typedef MLAN_PACK_START struct _HostCmd_DS_CHAN_REGION_CFG { + /** Action */ + t_u16 action; +} MLAN_PACK_END HostCmd_DS_CHAN_REGION_CFG; + +/** HostCmd_CMD_CW_MODE_CTRL */ +typedef MLAN_PACK_START struct _HostCmd_DS_CW_MODE_CTRL { + /** Action for CW Tone Control */ + t_u16 action; + /** Mode of Operation 0: Disbale 1: Tx Continuous Packet 2: Tx Continuous Wave */ + t_u8 mode; + /** channel */ + t_u8 channel; + /** channel info*/ + t_u8 chanInfo; + /** Tx Power level in dBm */ + t_u16 txPower; + /** Packet Length */ + t_u16 pktLength; + /** bit rate Info */ + t_u32 rateInfo; +} MLAN_PACK_END HostCmd_DS_CW_MODE_CTRL; + +/** HostCmd_CMD_802_11_RF_ANTENNA */ +typedef MLAN_PACK_START struct _HostCmd_DS_802_11_RF_ANTENNA { + /** Action */ + t_u16 action; + /** Antenna or 0xffff (diversity) */ + t_u16 antenna_mode; + /** Evaluate time */ + t_u16 evaluate_time; + /** Current antenna */ + t_u16 current_antenna; +} MLAN_PACK_END HostCmd_DS_802_11_RF_ANTENNA; + +/** HostCmd_DS_802_11_IBSS_STATUS */ +typedef MLAN_PACK_START struct _HostCmd_DS_802_11_IBSS_STATUS { + /** Action */ + t_u16 action; + /** Enable */ + t_u16 enable; + /** BSSID */ + t_u8 bssid[MLAN_MAC_ADDR_LENGTH]; + /** Beacon interval */ + t_u16 beacon_interval; + /** ATIM window interval */ + t_u16 atim_window; + /** User G rate protection */ + t_u16 use_g_rate_protect; +} MLAN_PACK_END HostCmd_DS_802_11_IBSS_STATUS; + +/** HostCmd_DS_MGMT_IE_LIST_CFG */ +typedef MLAN_PACK_START struct _HostCmd_DS_MGMT_IE_LIST { + /** Action */ + t_u16 action; + /** Get/Set mgmt IE */ + mlan_ds_misc_custom_ie ds_mgmt_ie; +} MLAN_PACK_END HostCmd_DS_MGMT_IE_LIST_CFG; + +/** HostCmd_DS_TDLS_CONFIG */ +typedef MLAN_PACK_START struct _HostCmd_DS_TDLS_CONFIG { + /** Set TDLS configuration */ + mlan_ds_misc_tdls_config tdls_info; +} MLAN_PACK_END HostCmd_DS_TDLS_CONFIG; + +/**Action ID for TDLS delete link*/ +#define TDLS_DELETE 0x00 +/**Action ID for TDLS create link*/ +#define TDLS_CREATE 0x01 +/**Action ID for TDLS config link*/ +#define TDLS_CONFIG 0x02 +/** HostCmd_DS_TDLS_OPER */ +typedef MLAN_PACK_START struct _HostCmd_DS_TDLS_OPER { + /** Action */ + t_u16 tdls_action; + /**reason*/ + t_u16 reason; + /** peer mac */ + t_u8 peer_mac[MLAN_MAC_ADDR_LENGTH]; +} MLAN_PACK_END HostCmd_DS_TDLS_OPER; + +/** HostCmd_CMD_MAC_REG_ACCESS */ +typedef MLAN_PACK_START struct _HostCmd_DS_MAC_REG_ACCESS { + /** Action */ + t_u16 action; + /** MAC register offset */ + t_u16 offset; + /** MAC register value */ + t_u32 value; +} MLAN_PACK_END HostCmd_DS_MAC_REG_ACCESS; + +/** HostCmd_CMD_BBP_REG_ACCESS */ +typedef MLAN_PACK_START struct _HostCmd_DS_BBP_REG_ACCESS { + /** Acion */ + t_u16 action; + /** BBP register offset */ + t_u16 offset; + /** BBP register value */ + t_u8 value; + /** Reserved field */ + t_u8 reserved[3]; +} MLAN_PACK_END HostCmd_DS_BBP_REG_ACCESS; + +/** HostCmd_CMD_RF_REG_ACCESS */ +typedef MLAN_PACK_START struct _HostCmd_DS_RF_REG_ACCESS { + /** Action */ + t_u16 action; + /** RF register offset */ + t_u16 offset; + /** RF register value */ + t_u8 value; + /** Reserved field */ + t_u8 reserved[3]; +} MLAN_PACK_END HostCmd_DS_RF_REG_ACCESS; + +/** HostCmd_DS_802_11_EEPROM_ACCESS */ +typedef MLAN_PACK_START struct _HostCmd_DS_802_11_EEPROM_ACCESS { + /** Action */ + t_u16 action; + + /** multiple 4 */ + t_u16 offset; + /** Number of bytes */ + t_u16 byte_count; + /** Value */ + t_u8 value; +} MLAN_PACK_END HostCmd_DS_802_11_EEPROM_ACCESS; + +/** HostCmd_DS_MEM_ACCESS */ +typedef MLAN_PACK_START struct _HostCmd_DS_MEM_ACCESS { + /** Action */ + t_u16 action; + /** Reserved field */ + t_u16 reserved; + /** Address */ + t_u32 addr; + /** Value */ + t_u32 value; +} MLAN_PACK_END HostCmd_DS_MEM_ACCESS; + +/** HostCmd_DS_TARGET_ACCESS */ +typedef MLAN_PACK_START struct _HostCmd_DS_TARGET_ACCESS { + /** Action */ + t_u16 action; + /** CSU Target Device. 1: CSU, 2: PSU */ + t_u16 csu_target; + /** Target Device Address */ + t_u16 address; + /** Data */ + t_u8 data; +} MLAN_PACK_END HostCmd_DS_TARGET_ACCESS; + +/** HostCmd_DS_SUBSCRIBE_EVENT */ +typedef MLAN_PACK_START struct _HostCmd_DS_SUBSCRIBE_EVENT { + /** Action */ + t_u16 action; + /** Bitmap of subscribed events */ + t_u16 event_bitmap; +} MLAN_PACK_END HostCmd_DS_SUBSCRIBE_EVENT; + +/** HostCmd_DS_OTP_USER_DATA */ +typedef MLAN_PACK_START struct _HostCmd_DS_OTP_USER_DATA { + /** Action */ + t_u16 action; + /** Reserved field */ + t_u16 reserved; + /** User data length */ + t_u16 user_data_length; + /** User data */ + t_u8 user_data[1]; +} MLAN_PACK_END HostCmd_DS_OTP_USER_DATA; + +/** HostCmd_CMD_HS_WAKEUP_REASON */ +typedef MLAN_PACK_START struct _HostCmd_DS_HS_WAKEUP_REASON { + /** wakeupReason: + * 0: unknown + * 1: Broadcast data matched + * 2: Multicast data matched + * 3: Unicast data matched + * 4: Maskable event matched + * 5. Non-maskable event matched + * 6: Non-maskable condition matched (EAPoL rekey) + * 7: Magic pattern matched + * Others: reserved. (set to 0) */ + t_u16 wakeup_reason; +} MLAN_PACK_END HostCmd_DS_HS_WAKEUP_REASON; + +/** MrvlIEtypes_HsWakeHoldoff_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_HsWakeHoldoff_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** Minimum delay between HsActive and HostWake (in msec) */ + t_u16 min_wake_holdoff; +} MLAN_PACK_END MrvlIEtypes_HsWakeHoldoff_t; + +/** MrvlIEtypes_PsParamsInHs_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_PsParamsInHs_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** Host sleep wake interval(in msec) */ + t_u32 hs_wake_interval; + /** Host sleep inactivity timeout (in msec) */ + t_u32 hs_inactivity_timeout; +} MLAN_PACK_END MrvlIEtypes_PsParamsInHs_t; + +/** MrvlIEtypes_WakeupSourceGPIO_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_WakeupSourceGPIO_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** GPIO for indication of wakeup source */ + t_u8 ind_gpio; + /** Level on ind_gpio for normal wakeup source */ + t_u8 level; +} MLAN_PACK_END MrvlIEtypes_WakeupSourceGPIO_t; + +/** MrvlIEtypes_RobustcoexSourceGPIO_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_RobustcoexSourceGPIO_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** GPIO cfg for external bt request */ + t_u8 enable; + /** GPIO number */ + t_u8 gpio_num; + /** GPIO Polarity */ + t_u8 gpio_polarity; +} MLAN_PACK_END MrvlIEtypes_RobustcoexSourceGPIO_t; + +typedef MLAN_PACK_START struct _MrvlIEtypes_WakeupExtend_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** Events that will be forced ignore **/ + t_u32 event_force_ignore; + /** Events that will use extend gap to inform host*/ + t_u32 event_use_ext_gap; + /** Extend gap*/ + t_u8 ext_gap; + /** GPIO wave level*/ + t_u8 gpio_wave; +} MLAN_PACK_END MrvlIEtypes_WakeupExtend_t; + +#define EVENT_MANAGEMENT_FRAME_WAKEUP 136 +typedef MLAN_PACK_START struct _mgmt_frame_filter { + /** action - bitmap + ** On matching rx'd pkt and filter during NON_HOSTSLEEP mode: + ** Action[1]=0 Discard + ** Action[1]=1 Allow + ** Note that default action on non-match is "Allow". + ** + ** On matching rx'd pkt and filter during HOSTSLEEP mode: + ** Action[1:0]=00 Discard and Not Wake host + ** Action[1:0]=01 Discard and Wake host + ** Action[1:0]=10 Invalid + ** Note that default action on non-match is "Discard and Not Wake host". + **/ + t_u8 action; + /** Frame type(p2p...) + ** type=0: invalid + ** type=1: p2p + ** type=0xff: management frames(assoc req/rsp, probe req/rsp,...) + ** type=others: reserved + **/ + t_u8 type; + /** Frame mask according to each type + ** When type=1 for p2p, frame-mask have following define: + ** Bit Frame + ** 0 GO Negotiation Request + ** 1 GO Negotiation Response + ** 2 GO Negotiation Confirmation + ** 3 P2P Invitation Request + ** 4 P2P Invitation Response + ** 5 Device Discoverability Request + ** 6 Device Discoverability Response + ** 7 Provision Discovery Request + ** 8 Provision Discovery Response + ** 9 Notice of Absence + ** 10 P2P Presence Request + ** 11 P2P Presence Response + ** 12 GO Discoverability Request + ** 13-31 Reserved + ** + ** When type=others, frame-mask is reserved. + **/ + t_u32 frame_mask; +} MLAN_PACK_END mgmt_frame_filter; + +#define MAX_MGMT_FRAME_FILTER 2 +/** MrvlIEtypes_MgmtFrameFilter_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_MgmtFrameFilter_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** management frame filters */ + mgmt_frame_filter filter[MAX_MGMT_FRAME_FILTER]; +} MLAN_PACK_END MrvlIEtypes_MgmtFrameFilter_t; + +/** HostCmd_DS_INACTIVITY_TIMEOUT_EXT */ +typedef MLAN_PACK_START struct _HostCmd_DS_INACTIVITY_TIMEOUT_EXT { + /** ACT_GET/ACT_SET */ + t_u16 action; + /** uS, 0 means 1000uS(1ms) */ + t_u16 timeout_unit; + /** Inactivity timeout for unicast data */ + t_u16 unicast_timeout; + /** Inactivity timeout for multicast data */ + t_u16 mcast_timeout; + /** Timeout for additional RX traffic after Null PM1 packet exchange */ + t_u16 ps_entry_timeout; + /** Reserved to further expansion */ + t_u16 reserved; +} MLAN_PACK_END HostCmd_DS_INACTIVITY_TIMEOUT_EXT; + +/** HostCmd_DS_INDEPENDENT_RESET_CFG */ +typedef MLAN_PACK_START struct _HostCmd_DS_INDEPENDENT_RESET_CFG { + /** ACT_GET/ACT_SET */ + t_u16 action; + /** out band independent reset */ + t_u8 ir_mode; + /** gpio pin */ + t_u8 gpio_pin; +} MLAN_PACK_END HostCmd_DS_INDEPENDENT_RESET_CFG; + +/** HostCmd_DS_802_11_PS_INACTIVITY_TIMEOUT */ +typedef MLAN_PACK_START struct _HostCmd_DS_802_11_PS_INACTIVITY_TIMEOUT { + /** ACT_GET/ACT_SET */ + t_u16 action; + /** ps inactivity timeout value */ + t_u16 inact_tmo; +} MLAN_PACK_END HostCmd_DS_802_11_PS_INACTIVITY_TIMEOUT; + +struct timestamps { + /** t2 time */ + t_u32 t2; + /** t2 error */ + t_u8 t2_err; + /** t3 time */ + t_u32 t3; + /** t3 error */ + t_u8 t3_err; + /** ingress time */ + t_u64 ingress_time; +} __attribute__ ((packed)); + +/** HostCmd_DS_HOST_CLOCK_CFG */ +typedef MLAN_PACK_START struct _HostCmd_DS_HOST_CLOCK_CFG { + /** Action */ + t_u16 action; + /** host time nano secs value */ + t_u64 time; + /** HW time in nano sec value */ + t_u64 hw_time; + /** diff between BBU clock and host clock */ + t_u64 host_bbu_clk_delta; +} MLAN_PACK_END HostCmd_DS_HOST_CLOCK_CFG; + +/** TLV type : STA Mac address */ +#define TLV_TYPE_STA_MAC_ADDRESS (PROPRIETARY_TLV_BASE_ID + 0x20) /* 0x0120 */ + +/** MrvlIEtypes_MacAddr_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_MacAddr_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** mac address */ + t_u8 mac[MLAN_MAC_ADDR_LENGTH]; +} MLAN_PACK_END MrvlIEtypes_MacAddr_t; + +/** Assoc Request */ +#define SUBTYPE_ASSOC_REQUEST 0 +/** ReAssoc Request */ +#define SUBTYPE_REASSOC_REQUEST 2 +/** Probe Resp */ +#define SUBTYPE_PROBE_RESP 5 +/** Disassoc Request */ +#define SUBTYPE_DISASSOC 10 +/** Auth Request */ +#define SUBTYPE_AUTH 11 +/** Deauth Request */ +#define SUBTYPE_DEAUTH 12 +/** Action frame */ +#define SUBTYPE_ACTION 13 +/** beacon */ +#define SUBTYPE_BEACON 8 + +#ifdef UAP_SUPPORT +/** TLV type : AP Channel band Config */ +#define TLV_TYPE_UAP_CHAN_BAND_CONFIG\ + (PROPRIETARY_TLV_BASE_ID + 0x2a) /* 0x012a */ +/** TLV type : AP Mac address */ +#define TLV_TYPE_UAP_MAC_ADDRESS\ + (PROPRIETARY_TLV_BASE_ID + 0x2b) /* 0x012b */ +/** TLV type : AP Beacon period */ +#define TLV_TYPE_UAP_BEACON_PERIOD\ + (PROPRIETARY_TLV_BASE_ID + 0x2c) /* 0x012c */ +/** TLV type : AP DTIM period */ +#define TLV_TYPE_UAP_DTIM_PERIOD\ + (PROPRIETARY_TLV_BASE_ID + 0x2d) /* 0x012d */ +/** TLV type : AP Tx power */ +#define TLV_TYPE_UAP_TX_POWER\ + (PROPRIETARY_TLV_BASE_ID + 0x2f) /* 0x012f */ +/** TLV type : AP SSID broadcast control */ +#define TLV_TYPE_UAP_BCAST_SSID_CTL\ + (PROPRIETARY_TLV_BASE_ID + 0x30) /* 0x0130 */ +/** TLV type : AP Preamble control */ +#define TLV_TYPE_UAP_PREAMBLE_CTL\ + (PROPRIETARY_TLV_BASE_ID + 0x31) /* 0x0131 */ +/** TLV type : AP Antenna control */ +#define TLV_TYPE_UAP_ANTENNA_CTL\ + (PROPRIETARY_TLV_BASE_ID + 0x32) /* 0x0132 */ +/** TLV type : AP RTS threshold */ +#define TLV_TYPE_UAP_RTS_THRESHOLD\ + (PROPRIETARY_TLV_BASE_ID + 0x33) /* 0x0133 */ +/** TLV type : AP Tx data rate */ +#define TLV_TYPE_UAP_TX_DATA_RATE\ + (PROPRIETARY_TLV_BASE_ID + 0x35) /* 0x0135 */ +/** TLV type: AP Packet forwarding control */ +#define TLV_TYPE_UAP_PKT_FWD_CTL\ + (PROPRIETARY_TLV_BASE_ID + 0x36) /* 0x0136 */ +/** TLV type: STA information */ +#define TLV_TYPE_UAP_STA_INFO\ + (PROPRIETARY_TLV_BASE_ID + 0x37) /* 0x0137 */ +/** TLV type: AP STA MAC address filter */ +#define TLV_TYPE_UAP_STA_MAC_ADDR_FILTER\ + (PROPRIETARY_TLV_BASE_ID + 0x38) /* 0x0138 */ +/** TLV type: AP STA ageout timer */ +#define TLV_TYPE_UAP_STA_AGEOUT_TIMER\ + (PROPRIETARY_TLV_BASE_ID + 0x39) /* 0x0139 */ +/** TLV type: AP WEP keys */ +#define TLV_TYPE_UAP_WEP_KEY\ + (PROPRIETARY_TLV_BASE_ID + 0x3b) /* 0x013b */ +/** TLV type: AP WPA passphrase */ +#define TLV_TYPE_UAP_WPA_PASSPHRASE\ + (PROPRIETARY_TLV_BASE_ID + 0x3c) /* 0x013c */ +/** TLV type: AP protocol */ +#define TLV_TYPE_UAP_ENCRYPT_PROTOCOL\ + (PROPRIETARY_TLV_BASE_ID + 0x40) /* 0x0140 */ +/** TLV type: AP AKMP */ +#define TLV_TYPE_UAP_AKMP\ + (PROPRIETARY_TLV_BASE_ID + 0x41) /* 0x0141 */ +/** TLV type: AP Fragment threshold */ +#define TLV_TYPE_UAP_FRAG_THRESHOLD\ + (PROPRIETARY_TLV_BASE_ID + 0x46) /* 0x0146 */ +/** TLV type: AP Group rekey timer */ +#define TLV_TYPE_UAP_GRP_REKEY_TIME\ + (PROPRIETARY_TLV_BASE_ID + 0x47) /* 0x0147 */ +/**TLV type : AP Max Station number */ +#define TLV_TYPE_UAP_MAX_STA_CNT\ + (PROPRIETARY_TLV_BASE_ID + 0x55) /* 0x0155 */ +/**TLV type : AP Retry limit */ +#define TLV_TYPE_UAP_RETRY_LIMIT\ + (PROPRIETARY_TLV_BASE_ID + 0x5d) /* 0x015d */ +/** TLV type : AP MCBC data rate */ +#define TLV_TYPE_UAP_MCBC_DATA_RATE\ + (PROPRIETARY_TLV_BASE_ID + 0x62) /* 0x0162 */ +/**TLV type: AP RSN replay protection */ +#define TLV_TYPE_UAP_RSN_REPLAY_PROTECT\ + (PROPRIETARY_TLV_BASE_ID + 0x64) /* 0x0164 */ +/**TLV type: AP mgmt IE passthru mask */ +#define TLV_TYPE_UAP_MGMT_IE_PASSTHRU_MASK\ + (PROPRIETARY_TLV_BASE_ID + 0x70) /* 0x0170 */ + +/**TLV type: AP pairwise handshake timeout */ +#define TLV_TYPE_UAP_EAPOL_PWK_HSK_TIMEOUT\ + (PROPRIETARY_TLV_BASE_ID + 0x75) /* 0x0175 */ +/**TLV type: AP pairwise handshake retries */ +#define TLV_TYPE_UAP_EAPOL_PWK_HSK_RETRIES\ + (PROPRIETARY_TLV_BASE_ID + 0x76) /* 0x0176 */ +/**TLV type: AP groupwise handshake timeout */ +#define TLV_TYPE_UAP_EAPOL_GWK_HSK_TIMEOUT\ + (PROPRIETARY_TLV_BASE_ID + 0x77) /* 0x0177 */ +/**TLV type: AP groupwise handshake retries */ +#define TLV_TYPE_UAP_EAPOL_GWK_HSK_RETRIES\ + (PROPRIETARY_TLV_BASE_ID + 0x78) /* 0x0178 */ +/** TLV type: AP PS STA ageout timer */ +#define TLV_TYPE_UAP_PS_STA_AGEOUT_TIMER\ + (PROPRIETARY_TLV_BASE_ID + 0x7b) /* 0x017b */ +/** TLV type : Pairwise Cipher */ +#define TLV_TYPE_PWK_CIPHER\ + (PROPRIETARY_TLV_BASE_ID + 0x91) /* 0x0191 */ +/** TLV type : Group Cipher */ +#define TLV_TYPE_GWK_CIPHER\ + (PROPRIETARY_TLV_BASE_ID + 0x92) /* 0x0192 */ +/** TLV type : BSS Status */ +#define TLV_TYPE_BSS_STATUS\ + (PROPRIETARY_TLV_BASE_ID + 0x93) /* 0x0193 */ +/** TLV type : AP WMM params */ +#define TLV_TYPE_AP_WMM_PARAM\ + (PROPRIETARY_TLV_BASE_ID + 0xd0) /* 0x01d0 */ + +/** TLV type : AP Tx beacon rate */ +#define TLV_TYPE_UAP_TX_BEACON_RATE\ + (PROPRIETARY_TLV_BASE_ID + 288) /* 0x0220 */ + +/** MrvlIEtypes_beacon_period_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_beacon_period_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** beacon period */ + t_u16 beacon_period; +} MLAN_PACK_END MrvlIEtypes_beacon_period_t; + +/** MrvlIEtypes_dtim_period_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_dtim_period_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** DTIM period */ + t_u8 dtim_period; +} MLAN_PACK_END MrvlIEtypes_dtim_period_t; + +/** MrvlIEtypes_tx_rate_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_tx_rate_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** tx data rate */ + t_u16 tx_data_rate; +} MLAN_PACK_END MrvlIEtypes_tx_rate_t; + +/** MrvlIEtypes_mcbc_rate_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_mcbc_rate_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** mcbc data rate */ + t_u16 mcbc_data_rate; +} MLAN_PACK_END MrvlIEtypes_mcbc_rate_t; + +/** MrvlIEtypes_tx_power_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_tx_power_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** tx power */ + t_u8 tx_power; +} MLAN_PACK_END MrvlIEtypes_tx_power_t; + +/** MrvlIEtypes_bcast_ssid_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_bcast_ssid_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** bcast ssid control*/ + t_u8 bcast_ssid_ctl; +} MLAN_PACK_END MrvlIEtypes_bcast_ssid_t; + +/** MrvlIEtypes_antenna_mode_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_antenna_mode_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** which antenna */ + t_u8 which_antenna; + /** antenna mode*/ + t_u8 antenna_mode; +} MLAN_PACK_END MrvlIEtypes_antenna_mode_t; + +/** MrvlIEtypes_pkt_forward_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_pkt_forward_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** pkt foward control */ + t_u8 pkt_forward_ctl; +} MLAN_PACK_END MrvlIEtypes_pkt_forward_t; + +/** MrvlIEtypes_max_sta_count_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_max_sta_count_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** max station count */ + t_u16 max_sta_count; +} MLAN_PACK_END MrvlIEtypes_max_sta_count_t; + +/** MrvlIEtypes_sta_ageout_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_sta_ageout_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** station age out timer */ + t_u32 sta_ageout_timer; +} MLAN_PACK_END MrvlIEtypes_sta_ageout_t; + +/** MrvlIEtypes_rts_threshold_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_rts_threshold_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** rts threshold */ + t_u16 rts_threshold; +} MLAN_PACK_END MrvlIEtypes_rts_threshold_t; + +/** MrvlIEtypes_frag_threshold_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_frag_threshold_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** frag threshold */ + t_u16 frag_threshold; +} MLAN_PACK_END MrvlIEtypes_frag_threshold_t; + +/** MrvlIEtypes_retry_limit_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_retry_limit_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** retry limit */ + t_u8 retry_limit; +} MLAN_PACK_END MrvlIEtypes_retry_limit_t; + +/** MrvlIEtypes_eapol_pwk_hsk_timeout_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_eapol_pwk_hsk_timeout_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** pairwise update timeout in milliseconds */ + t_u32 pairwise_update_timeout; +} MLAN_PACK_END MrvlIEtypes_eapol_pwk_hsk_timeout_t; + +/** MrvlIEtypes_eapol_pwk_hsk_retries_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_eapol_pwk_hsk_retries_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** pairwise handshake retries */ + t_u32 pwk_retries; +} MLAN_PACK_END MrvlIEtypes_eapol_pwk_hsk_retries_t; + +/** MrvlIEtypes_eapol_gwk_hsk_timeout_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_eapol_gwk_hsk_timeout_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** groupwise update timeout in milliseconds */ + t_u32 groupwise_update_timeout; +} MLAN_PACK_END MrvlIEtypes_eapol_gwk_hsk_timeout_t; + +/** MrvlIEtypes_eapol_gwk_hsk_retries_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_eapol_gwk_hsk_retries_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** groupwise handshake retries */ + t_u32 gwk_retries; +} MLAN_PACK_END MrvlIEtypes_eapol_gwk_hsk_retries_t; + +/** MrvlIEtypes_mgmt_ie_passthru_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_mgmt_ie_passthru_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** mgmt IE mask value */ + t_u32 mgmt_ie_mask; +} MLAN_PACK_END MrvlIEtypes_mgmt_ie_passthru_t; + +/** MrvlIEtypes_mac_filter_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_mac_filter_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** Filter mode */ + t_u8 filter_mode; + /** Number of STA MACs */ + t_u8 count; + /** STA MAC addresses buffer */ + t_u8 mac_address[1]; +} MLAN_PACK_END MrvlIEtypes_mac_filter_t; + +/** MrvlIEtypes_auth_type_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_auth_type_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** Authentication type */ + t_u8 auth_type; +} MLAN_PACK_END MrvlIEtypes_auth_type_t; + +/** MrvlIEtypes_encrypt_protocol_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_encrypt_protocol_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** encryption protocol */ + t_u16 protocol; +} MLAN_PACK_END MrvlIEtypes_encrypt_protocol_t; + +/** MrvlIEtypes_pwk_cipher_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_pwk_cipher_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** protocol */ + t_u16 protocol; + /** pairwise cipher */ + t_u8 pairwise_cipher; + /** reserved */ + t_u8 reserved; +} MLAN_PACK_END MrvlIEtypes_pwk_cipher_t; + +/** MrvlIEtypes_gwk_cipher_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_gwk_cipher_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** group cipher */ + t_u8 group_cipher; + /** reserved */ + t_u8 reserved; +} MLAN_PACK_END MrvlIEtypes_gwk_cipher_t; + +/** MrvlIEtypes_akmp_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_akmp_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** key management */ + t_u16 key_mgmt; + /** key management operation */ + t_u16 key_mgmt_operation; +} MLAN_PACK_END MrvlIEtypes_akmp_t; + +/** MrvlIEtypes_passphrase_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_passphrase_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** passphrase */ + t_u8 passphrase[1]; +} MLAN_PACK_END MrvlIEtypes_passphrase_t; + +/** MrvlIEtypes_rsn_replay_prot_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_rsn_replay_prot_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** rsn replay proection */ + t_u8 rsn_replay_prot; +} MLAN_PACK_END MrvlIEtypes_rsn_replay_prot_t; + +/** MrvlIEtypes_group_rekey_time_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_group_rekey_time_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** group key rekey time */ + t_u32 gk_rekey_time; +} MLAN_PACK_END MrvlIEtypes_group_rekey_time_t; + +/** MrvlIEtypes_wep_key_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_wep_key_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** key index */ + t_u8 key_index; + /** is default */ + t_u8 is_default; + /** key data */ + t_u8 key[1]; +} MLAN_PACK_END MrvlIEtypes_wep_key_t; + +/** MrvlIEtypes_bss_status_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_bss_status_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** BSS status, READ only */ + t_u16 bss_status; +} MLAN_PACK_END MrvlIEtypes_bss_status_t; + +/** MrvlIEtypes_preamble_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_preamble_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** preamble type, READ only */ + t_u8 preamble_type; +} MLAN_PACK_END MrvlIEtypes_preamble_t; + +/** MrvlIEtypes_wmm_parameter_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_wmm_parameter_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** WMM parameter */ + WmmParameter_t wmm_para; +} MLAN_PACK_END MrvlIEtypes_wmm_parameter_t; + +/** SNMP_MIB_UAP_INDEX */ +typedef enum _SNMP_MIB_UAP_INDEX { + tkip_mic_failures = 0x0b, + ccmp_decrypt_errors = 0x0c, + wep_undecryptable_count = 0x0d, + wep_icv_error_count = 0x0e, + decrypt_failure_count = 0xf, + dot11_failed_count = 0x12, + dot11_retry_count = 0x13, + dot11_multi_retry_count = 0x14, + dot11_frame_dup_count = 0x15, + dot11_rts_success_count = 0x16, + dot11_rts_failure_count = 0x17, + dot11_ack_failure_count = 0x18, + dot11_rx_fragment_count = 0x19, + dot11_mcast_rx_frame_count = 0x1a, + dot11_fcs_error_count = 0x1b, + dot11_tx_frame_count = 0x1c, + dot11_rsna_tkip_cm_invoked = 0x1d, + dot11_rsna_4way_hshk_failures = 0x1e, + dot11_mcast_tx_count = 0x1f, +} SNMP_MIB_UAP_INDEX; + +/** MrvlIEtypes_snmp_oid_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_snmp_oid_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** data */ + t_u32 data; +} MLAN_PACK_END MrvlIEtypes_snmp_oid_t; + +/** HostCmd_SYS_CONFIG */ +typedef MLAN_PACK_START struct _HostCmd_DS_SYS_CONFIG { + /** CMD Action GET/SET*/ + t_u16 action; + /** Tlv buffer */ + t_u8 tlv_buffer[1]; +} MLAN_PACK_END HostCmd_DS_SYS_CONFIG; + +/** HostCmd_SYS_CONFIG */ +typedef MLAN_PACK_START struct _HostCmd_DS_SYS_INFO { + /** sys info */ + t_u8 sys_info[64]; +} MLAN_PACK_END HostCmd_DS_SYS_INFO; + +/** HostCmd_DS_STA_DEAUTH */ +typedef MLAN_PACK_START struct _HostCmd_DS_STA_DEAUTH { + /** mac address */ + t_u8 mac[MLAN_MAC_ADDR_LENGTH]; + /** reason code */ + t_u16 reason; +} MLAN_PACK_END HostCmd_DS_STA_DEAUTH; + +/** HostCmd_UAP_OPER_CTRL */ +typedef MLAN_PACK_START struct _HostCmd_DS_UAP_OPER_CTRL { + /** CMD Action GET/SET*/ + t_u16 action; + /** control*/ + t_u16 ctrl; + /**channel operation*/ + t_u16 chan_opt; + /**channel band tlv*/ + MrvlIEtypes_channel_band_t channel_band; +} MLAN_PACK_END HostCmd_DS_UAP_OPER_CTRL; + +/** Host Command id: POWER_MGMT */ +#define HOST_CMD_POWER_MGMT_EXT 0x00ef +/** TLV type: AP Sleep param */ +#define TLV_TYPE_AP_SLEEP_PARAM\ + (PROPRIETARY_TLV_BASE_ID + 0x6a) /* 0x016a */ +/** TLV type: AP Inactivity Sleep param */ +#define TLV_TYPE_AP_INACT_SLEEP_PARAM\ + (PROPRIETARY_TLV_BASE_ID + 0x6b) /* 0x016b */ + +/** MrvlIEtypes_sleep_param_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_sleep_param_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** control bitmap */ + t_u32 ctrl_bitmap; + /** min_sleep */ + t_u32 min_sleep; + /** max_sleep */ + t_u32 max_sleep; +} MLAN_PACK_END MrvlIEtypes_sleep_param_t; + +/** MrvlIEtypes_inact_sleep_param_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_inact_sleep_param_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** inactivity timeout */ + t_u32 inactivity_to; + + /** min_awake */ + t_u32 min_awake; + /** max_awake */ + t_u32 max_awake; +} MLAN_PACK_END MrvlIEtypes_inact_sleep_param_t; + +/** HostCmd_DS_POWER_MGMT */ +typedef MLAN_PACK_START struct _HostCmd_DS_POWER_MGMT_EXT { + /** CMD Action Get/Set*/ + t_u16 action; + /** power mode */ + t_u16 power_mode; +} MLAN_PACK_END HostCmd_DS_POWER_MGMT_EXT; + +/** MrvlIEtypes_ps_sta_ageout_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_ps_sta_ageout_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** station age out timer */ + t_u32 ps_sta_ageout_timer; +} MLAN_PACK_END MrvlIEtypes_ps_sta_ageout_t; + +/** MrvlIEtypes_sta_info_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_sta_info_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** STA MAC address */ + t_u8 mac_address[MLAN_MAC_ADDR_LENGTH]; + /** Power mfg status */ + t_u8 power_mfg_status; + /** RSSI */ + t_s8 rssi; +} MLAN_PACK_END MrvlIEtypes_sta_info_t; + +/** HostCmd_DS_STA_LIST */ +typedef MLAN_PACK_START struct _HostCmd_DS_STA_LIST { + /** Number of STAs */ + t_u16 sta_count; + /* MrvlIEtypes_sta_info_t sta_info[0]; */ +} MLAN_PACK_END HostCmd_DS_STA_LIST; + +/** TLV ID : WAPI Information */ +#define TLV_TYPE_AP_WAPI_INFO (PROPRIETARY_TLV_BASE_ID + 0x67) /* 0x0167 */ + +/** MrvlIEtypes_sta_info_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_wapi_info_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** Multicast PN */ + t_u8 multicast_PN[16]; +} MLAN_PACK_END MrvlIEtypes_wapi_info_t; +#endif /* UAP_SUPPORT */ + +/** TLV buffer : 2040 coex config */ +typedef MLAN_PACK_START struct _MrvlIEtypes_2040_coex_enable_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** Enable */ + t_u8 enable_2040coex; +} MLAN_PACK_END MrvlIEtypes_2040_coex_enable_t; + +/** HostCmd_DS_REMAIN_ON_CHANNEL */ +typedef MLAN_PACK_START struct _HostCmd_DS_REMAIN_ON_CHANNEL { + /** Action 0-GET, 1-SET, 4 CLEAR*/ + t_u16 action; + /** Not used set to zero */ + t_u8 status; + /** Not used set to zero */ + t_u8 reserved; + /** Band cfg */ + Band_Config_t bandcfg; + /** channel */ + t_u8 channel; + /** remain time: Unit ms*/ + t_u32 remain_period; +} MLAN_PACK_END HostCmd_DS_REMAIN_ON_CHANNEL; + +/**BT coexit scan time setting*/ +typedef MLAN_PACK_START struct _MrvlIEtypes_BtCoexScanTime_t { + /** Header */ + MrvlIEtypesHeader_t header; + /**coex scan state 0: disable 1: enable*/ + t_u8 coex_scan; + /**reserved*/ + t_u8 reserved; + /**min scan time*/ + t_u16 min_scan_time; + /**max scan time*/ + t_u16 max_scan_time; +} MLAN_PACK_END MrvlIEtypes_BtCoexScanTime_t; + +/**BT coexit aggr win size */ +typedef MLAN_PACK_START struct _MrvlIETypes_BtCoexAggrWinSize_t { + /** Header */ + MrvlIEtypesHeader_t header; + /**winsize 0: restore default winsize, 1: use below winsize */ + t_u8 coex_win_size; + /**tx win size*/ + t_u8 tx_win_size; + /**rx win size*/ + t_u8 rx_win_size; + /**reserved*/ + t_u8 reserved; +} MLAN_PACK_END MrvlIETypes_BtCoexAggrWinSize_t; + +/** MrvlIEtypes_eapol_pkt_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_eapol_pkt_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** eapol pkt buf */ + t_u8 pkt_buf[0]; +} MLAN_PACK_END MrvlIEtypes_eapol_pkt_t; + +/** HostCmd_DS_EAPOL_PKT */ +typedef MLAN_PACK_START struct _HostCmd_DS_EAPOL_PKT { + /** Action */ + t_u16 action; + /** TLV buffer */ + MrvlIEtypes_eapol_pkt_t tlv_eapol; +} MLAN_PACK_END HostCmd_DS_EAPOL_PKT; + +#ifdef RX_PACKET_COALESCE +typedef MLAN_PACK_START struct _HostCmd_DS_RX_PKT_COAL_CFG { + /** Action */ + t_u16 action; + /** Packet threshold */ + t_u32 packet_threshold; + /** Timeout */ + t_u16 delay; +} MLAN_PACK_END HostCmd_DS_RX_PKT_COAL_CFG; +#endif + +typedef MLAN_PACK_START struct _MrvlTypes_DrcsTimeSlice_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** Channel Index*/ + t_u16 chan_idx; + /** Channel time (in TU) for chan_idx*/ + t_u8 chantime; + /** Channel swith time (in TU) for chan_idx*/ + t_u8 switchtime; + /** Undoze time (in TU) for chan_idx*/ + t_u8 undozetime; + /** Rx traffic control scheme when channel switch*/ + /** only valid for GC/STA interface*/ + t_u8 mode; +} MLAN_PACK_END MrvlTypes_DrcsTimeSlice_t; +typedef MLAN_PACK_START struct _HostCmd_DS_MULTI_CHAN_CFG { + /** Action */ + t_u16 action; + /** Channel time */ + t_u32 channel_time; + /** Buffer weight */ + t_u8 buffer_weight; + /** TLV buffer */ + t_u8 tlv_buf[0]; + /* t_u8 *tlv_buf; */ +} MLAN_PACK_END HostCmd_DS_MULTI_CHAN_CFG; + +typedef MLAN_PACK_START struct _HostCmd_DS_DRCS_CFG { + /** Action */ + t_u16 action; + /** TLV buffer */ + MrvlTypes_DrcsTimeSlice_t time_slicing; + /** TLV buffer */ + MrvlTypes_DrcsTimeSlice_t drcs_buf[0]; + /* t_u8 *tlv_buf; */ +} MLAN_PACK_END HostCmd_DS_DRCS_CFG; + +typedef MLAN_PACK_START struct _HostCmd_DS_MULTI_CHAN_POLICY { + /** Action */ + t_u16 action; + /** Multi-channel Policy */ + t_u16 policy; +} MLAN_PACK_END HostCmd_DS_MULTI_CHAN_POLICY; + +/** Channel band info */ +typedef MLAN_PACK_START struct _ChannelBandInfo { + /* band config */ + Band_Config_t bandcfg; + /** channel num for specificed band */ + t_u8 chan_num; +} MLAN_PACK_END ChannelBandInfo; + +/** MrvlIETypes_mutli_chan_group_info_t */ +typedef MLAN_PACK_START struct _MrvlIETypes_mutli_chan_group_info_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** channel group id */ + t_u8 chan_group_id; + /** buffer weight for this channel group */ + t_u8 chan_buff_weight; + /** channel number and band information */ + ChannelBandInfo chan_band_info; + /** Max channel time (us) */ + t_u32 channel_time; + /** Reserved */ + t_u32 reserved; + MLAN_PACK_START union { + t_u8 sdio_func_num; + t_u8 usb_epnum; + } MLAN_PACK_END hid_num; + /** interface number in this group */ + t_u8 num_intf; + /** bss_type list */ + t_u8 bss_type_numlist[0]; +} MLAN_PACK_END MrvlIEtypes_multi_chan_group_info_t; + +/** MrvlIEtypes_multi_chan_info_t */ +typedef MLAN_PACK_START struct _MrvlIETypes_mutli_chan_info_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** multi channel operation status */ + t_u16 status; + /** Tlv buffer */ + t_u8 tlv_buffer[0]; +} MLAN_PACK_END MrvlIEtypes_multi_chan_info_t; + +/** TLV buffer : firmware roam keys */ +typedef MLAN_PACK_START struct _MrvlIEtypes_keyParams_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** Tlv buffer */ + t_u8 tlv_buffer[0]; +} MLAN_PACK_END MrvlIEtypes_keyParams_t; +/** TLV buffer : firmware roam enable */ +typedef MLAN_PACK_START struct _MrvlIEtypes_fw_roam_enable_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** Enable */ + t_u8 roam_enable; + /** User set passphrase*/ + t_u8 userset_passphrase; +} MLAN_PACK_END MrvlIEtypes_fw_roam_enable_t; +/** HostCmd_DS_ROAM_OFFLOAD */ +typedef MLAN_PACK_START struct _HostCmd_DS_ROAM_OFFLOAD { + /** Action */ + t_u16 action; + /** tlv */ + t_u8 tlv[0]; +} MLAN_PACK_END HostCmd_DS_ROAM_OFFLOAD; +/** HostCmd_DS_ROAM_OFFLOAD_APLIST */ +typedef MLAN_PACK_START struct _MrvlIEtypes_roam_aplist_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** AP mac addrs**/ + t_u8 ap_mac[][MLAN_MAC_ADDR_LENGTH]; +} MLAN_PACK_END MrvlIEtypes_roam_aplist_t; +/** MrvlIEtypes_fw_roam_trigger_condition_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_fw_roam_trigger_condition_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** Roam offload trigger condition**/ + t_u16 trigger_condition; +} MLAN_PACK_END MrvlIEtypes_fw_roam_trigger_condition_t; +/** MrvlIEtypes_fw_roam_retry_count_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_fw_roam_retry_count_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** Roam offload retry count**/ + t_u16 retry_count; +} MLAN_PACK_END MrvlIEtypes_fw_roam_retry_count_t; +/** MrvlIEtypes_fw_roam_bgscan_setting_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_fw_roam_bgscan_setting_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** Bss type of BG scan during fw roam**/ + t_u8 bss_type; + /** Number of channels scanned during each scan**/ + t_u8 channels_perscan; + /** Interval between consecutive scans**/ + t_u32 scan_interval; + /** Condition to trigger report to host**/ + t_u32 report_condition; +} MLAN_PACK_END MrvlIEtypes_fw_roam_bgscan_setting_t; +/** MrvlIEtypes_para_rssi_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_para_rssi_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** Max value of RSSI threshold**/ + t_u8 max_rssi; + /** Min value of RSSI threshold**/ + t_u8 min_rssi; + /** Adjusting step value of RSSI threshold**/ + t_u8 step_rssi; +} MLAN_PACK_END MrvlIEtypes_para_rssi_t; +/** MrvlIEtypes_band_rssi_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_band_rssi_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** BAND and RSSI gap*/ + mlan_ds_misc_band_rssi band_rssi; +} MLAN_PACK_END MrvlIEtypes_band_rssi_t; +/** MrvlIEtypes_ees_param_set_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_ees_param_set_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** ees params*/ + mlan_ds_misc_ees_cfg ees_cfg; +} MLAN_PACK_END MrvlIEtypes_ees_param_set_t; +/** MrvlIEtypes_roam_blacklist_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_roam_blacklist_t { + /** Header */ + MrvlIEtypesHeader_t header; + /* Black list(BSSID list) */ + mlan_ds_misc_roam_offload_aplist blacklist; +} MLAN_PACK_END MrvlIEtypes_roam_blacklist_t; +/** MrvlIEtypes_beacon_miss_threshold_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_beacon_miss_threshold_t { + /** Header */ + MrvlIEtypesHeader_t header; + /* Beacon miss threshold */ + t_u8 bcn_miss_threshold; +} MLAN_PACK_END MrvlIEtypes_beacon_miss_threshold_t; +/** MrvlIEtypes_pre_beacon_miss_threshold_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_pre_beacon_miss_threshold_t { + /** Header */ + MrvlIEtypesHeader_t header; + /* Pre-Beacon miss threshold */ + t_u8 pre_bcn_miss_threshold; +} MLAN_PACK_END MrvlIEtypes_pre_beacon_miss_threshold_t; + +/** HostCmd_CMD_GET_TSF */ +typedef MLAN_PACK_START struct _HostCmd_DS_TSF { + /** tsf value*/ + t_u64 tsf; +} MLAN_PACK_END HostCmd_DS_TSF; +/* WLAN_GET_TSF*/ + +typedef struct _HostCmd_DS_DFS_REPEATER_MODE { + /** Set or Get */ + t_u16 action; + /** 1 on or 0 off */ + t_u16 mode; +} HostCmd_DS_DFS_REPEATER_MODE; + +/** HostCmd_DS_BOOT_SLEEP */ +typedef MLAN_PACK_START struct _HostCmd_DS_BOOT_SLEEP { + /** Set or Get */ + t_u16 action; + /** 1 on or 0 off */ + t_u16 enable; +} MLAN_PACK_END HostCmd_DS_BOOT_SLEEP; + +/** + * @brief 802.11h Local Power Constraint Marvell extended TLV + */ +typedef MLAN_PACK_START struct { + MrvlIEtypesHeader_t header; + /**< Marvell TLV header: ID/Len */ + t_u8 chan; /**< Channel local constraint applies to */ + + /** Power constraint included in beacons + * and used by fw to offset 11d info + */ + t_u8 constraint; + +} MLAN_PACK_END MrvlIEtypes_LocalPowerConstraint_t; + +/* + * + * Data structures for driver/firmware command processing + * + */ + +/** TPC Info structure sent in CMD_802_11_TPC_INFO command to firmware */ +typedef MLAN_PACK_START struct { + /**< Local constraint */ + MrvlIEtypes_LocalPowerConstraint_t local_constraint; + /**< Power Capability */ + MrvlIEtypes_PowerCapability_t power_cap; + +} MLAN_PACK_END HostCmd_DS_802_11_TPC_INFO; + +/** TPC Request structure sent in CMD_802_11_TPC_ADAPT_REQ + * command to firmware + */ +typedef MLAN_PACK_START struct { + t_u8 dest_mac[MLAN_MAC_ADDR_LENGTH]; /**< Destination STA address */ + t_u16 timeout; /**< Response timeout in ms */ + t_u8 rate_index; /**< IEEE Rate index to send request */ + +} MLAN_PACK_END HostCmd_TpcRequest; + +/** TPC Response structure received from the + * CMD_802_11_TPC_ADAPT_REQ command + */ +typedef MLAN_PACK_START struct { + t_u8 tpc_ret_code; + /**< Firmware command result status code */ + t_s8 tx_power; /**< Reported TX Power from the TPC Report element */ + t_s8 link_margin; + /**< Reported link margin from the TPC Report element */ + t_s8 rssi; /**< RSSI of the received TPC Report frame */ + +} MLAN_PACK_END HostCmd_TpcResponse; + +/** CMD_802_11_TPC_ADAPT_REQ substruct. + * Union of the TPC request and response + */ +typedef MLAN_PACK_START union { + HostCmd_TpcRequest req; + /**< Request struct sent to firmware */ + HostCmd_TpcResponse resp; + /**< Response struct received from firmware */ + +} MLAN_PACK_END HostCmd_DS_802_11_TPC_ADAPT_REQ; + +/** CMD_802_11_CHAN_SW_ANN firmware command substructure */ +typedef MLAN_PACK_START struct { + t_u8 switch_mode; + /**< Set to 1 for a quiet switch request, no STA tx */ + t_u8 new_chan; /**< Requested new channel */ + t_u8 switch_count; + /**< Number of TBTTs until the switch is to occur */ +} MLAN_PACK_END HostCmd_DS_802_11_CHAN_SW_ANN; + +/** + * @brief Enumeration of measurement types, including max supported + * enum for 11h/11k + */ +typedef MLAN_PACK_START enum _MeasType_t { + WLAN_MEAS_BASIC = 0, /**< 11h: Basic */ + WLAN_MEAS_NUM_TYPES, /**< Number of enumerated measurements */ + WLAN_MEAS_11H_MAX_TYPE = WLAN_MEAS_BASIC, /**< Max 11h measurement */ + +} MLAN_PACK_END MeasType_t; + +/** + * @brief Mode octet of the measurement request element (7.3.2.21) + */ +typedef MLAN_PACK_START struct { +#ifdef BIG_ENDIAN_SUPPORT + /**< Reserved */ + t_u8 rsvd5_7:3; + /**< 11k: duration spec. for meas. is mandatory */ + t_u8 duration_mandatory:1; + /**< 11h: en/disable report rcpt. of spec. type */ + t_u8 report:1; + /**< 11h: en/disable requests of specified type */ + t_u8 request:1; + /**< 11h: enable report/request bits */ + t_u8 enable:1; + /**< 11k: series or parallel with previous meas */ + t_u8 parallel:1; +#else + /**< 11k: series or parallel with previous meas */ + t_u8 parallel:1; + /**< 11h: enable report/request bits */ + t_u8 enable:1; + /**< 11h: en/disable requests of specified type */ + t_u8 request:1; + /**< 11h: en/disable report rcpt. of spec. type */ + t_u8 report:1; + /**< 11k: duration spec. for meas. is mandatory */ + t_u8 duration_mandatory:1; + /**< Reserved */ + t_u8 rsvd5_7:3; +#endif /* BIG_ENDIAN_SUPPORT */ + +} MLAN_PACK_END MeasReqMode_t; + +/** + * @brief Common measurement request structure (7.3.2.21.1 to 7.3.2.21.3) + */ +typedef MLAN_PACK_START struct { + t_u8 channel; /**< Channel to measure */ + t_u64 start_time; + /**< TSF Start time of measurement (0 for immediate) */ + t_u16 duration;/**< TU duration of the measurement */ + +} MLAN_PACK_END MeasReqCommonFormat_t; + +/** + * @brief Basic measurement request structure (7.3.2.21.1) + */ +typedef MeasReqCommonFormat_t MeasReqBasic_t; + +/** + * @brief CCA measurement request structure (7.3.2.21.2) + */ +typedef MeasReqCommonFormat_t MeasReqCCA_t; + +/** + * @brief RPI measurement request structure (7.3.2.21.3) + */ +typedef MeasReqCommonFormat_t MeasReqRPI_t; + +/** + * @brief Union of the availble measurement request types. Passed in the + * driver/firmware interface. + */ +typedef union { + MeasReqBasic_t basic; + /**< Basic measurement request */ + MeasReqCCA_t cca; /**< CCA measurement request */ + MeasReqRPI_t rpi; /**< RPI measurement request */ + +} MeasRequest_t; + +/** + * @brief Mode octet of the measurement report element (7.3.2.22) + */ +typedef MLAN_PACK_START struct { +#ifdef BIG_ENDIAN_SUPPORT + t_u8 rsvd3_7:5; /**< Reserved */ + t_u8 refused:1; /**< Measurement refused */ + t_u8 incapable:1; /**< Incapable of performing measurement */ + t_u8 late:1; /**< Start TSF time missed for measurement */ +#else + t_u8 late:1; /**< Start TSF time missed for measurement */ + t_u8 incapable:1; /**< Incapable of performing measurement */ + t_u8 refused:1; /**< Measurement refused */ + t_u8 rsvd3_7:5; /**< Reserved */ +#endif /* BIG_ENDIAN_SUPPORT */ + +} MLAN_PACK_END MeasRptMode_t; + +/** + * @brief Basic measurement report (7.3.2.22.1) + */ +typedef MLAN_PACK_START struct { + t_u8 channel; /**< Channel to measured */ + t_u64 start_time; /**< Start time (TSF) of measurement */ + t_u16 duration; /**< Duration of measurement in TUs */ + MeasRptBasicMap_t map; /**< Basic measurement report */ + +} MLAN_PACK_END MeasRptBasic_t; + +/** + * @brief CCA measurement report (7.3.2.22.2) + */ +typedef MLAN_PACK_START struct { + t_u8 channel; /**< Channel to measured */ + t_u64 start_time; /**< Start time (TSF) of measurement */ + t_u16 duration; /**< Duration of measurement in TUs */ + t_u8 busy_fraction; /**< Fractional duration CCA indicated chan busy */ + +} MLAN_PACK_END MeasRptCCA_t; + +/** + * @brief RPI measurement report (7.3.2.22.3) + */ +typedef MLAN_PACK_START struct { + t_u8 channel; /**< Channel to measured */ + t_u64 start_time; /**< Start time (TSF) of measurement */ + t_u16 duration; /**< Duration of measurement in TUs */ + t_u8 density[8]; /**< RPI Density histogram report */ + +} MLAN_PACK_END MeasRptRPI_t; + +/** + * @brief Union of the availble measurement report types. Passed in the + * driver/firmware interface. + */ +typedef union { + MeasRptBasic_t basic;/**< Basic measurement report */ + MeasRptCCA_t cca; /**< CCA measurement report */ + MeasRptRPI_t rpi; /**< RPI measurement report */ + +} MeasReport_t; + +/** + * @brief Structure passed to firmware to perform a measurement + */ +typedef MLAN_PACK_START struct { + t_u8 mac_addr[MLAN_MAC_ADDR_LENGTH]; /**< Reporting STA address */ + t_u8 dialog_token; /**< Measurement dialog toke */ + MeasReqMode_t req_mode; /**< Report mode */ + MeasType_t meas_type; /**< Measurement type */ + MeasRequest_t req; /**< Measurement request data */ + +} MLAN_PACK_END HostCmd_DS_MEASUREMENT_REQUEST; + +/** + * @brief Structure passed back from firmware with a measurement report, + * also can be to send a measurement report to another STA + */ +typedef MLAN_PACK_START struct { + t_u8 mac_addr[MLAN_MAC_ADDR_LENGTH]; /**< Reporting STA address */ + t_u8 dialog_token; /**< Measurement dialog token */ + MeasRptMode_t rpt_mode; /**< Report mode */ + MeasType_t meas_type; /**< Measurement type */ + MeasReport_t rpt; /**< Measurement report data */ + +} MLAN_PACK_END HostCmd_DS_MEASUREMENT_REPORT; + +typedef MLAN_PACK_START struct { + t_u16 startFreq; + Band_Config_t bandcfg; + t_u8 chanNum; + +} MLAN_PACK_END MrvlChannelDesc_t; + +typedef MLAN_PACK_START struct { + MrvlIEtypesHeader_t Header; /**< Header */ + + MeasRptBasicMap_t map; /**< IEEE 802.11h basic meas report */ +} MLAN_PACK_END MrvlIEtypes_ChanRpt11hBasic_t; + +typedef MLAN_PACK_START struct { + MrvlChannelDesc_t chan_desc; + /**< Channel band, number */ + t_u32 millisec_dwell_time; + /**< Channel dwell time in milliseconds */ +} MLAN_PACK_END HostCmd_DS_CHAN_RPT_REQ; + +typedef MLAN_PACK_START struct { + t_u32 cmd_result; /**< Rpt request command result (0 == SUCCESS) */ + t_u64 start_tsf; /**< TSF Measurement started */ + t_u32 duration; /**< Duration of measurement in microsecs */ + t_u8 tlv_buffer[1]; + /**< TLV Buffer */ +} MLAN_PACK_END HostCmd_DS_CHAN_RPT_RSP; + +/** statistics threshold */ +typedef MLAN_PACK_START struct { + /** Header */ + MrvlIEtypesHeader_t header; + /** value */ + t_u8 value; + /** reporting frequency */ + t_u8 frequency; +} MLAN_PACK_END MrvlIEtypes_BeaconHighRssiThreshold_t, + MrvlIEtypes_BeaconLowRssiThreshold_t, + MrvlIEtypes_BeaconHighSnrThreshold_t, + MrvlIEtypes_BeaconLowSnrThreshold_t, + MrvlIEtypes_FailureCount_t, + MrvlIEtypes_DataLowRssiThreshold_t, + MrvlIEtypes_DataHighRssiThreshold_t, + MrvlIEtypes_DataLowSnrThreshold_t, + MrvlIEtypes_DataHighSnrThreshold_t, + MrvlIETypes_PreBeaconMissed_t, MrvlIEtypes_BeaconsMissed_t; + +/** statistics threshold for LinkQuality */ +typedef MLAN_PACK_START struct { + /** Header */ + MrvlIEtypesHeader_t header; + /** Link SNR threshold (dB) */ + t_u16 link_snr; + /** Link SNR frequency */ + t_u16 link_snr_freq; + /* Second minimum rate value as per the rate table below */ + t_u16 link_rate; + /* Second minimum rate frequency */ + t_u16 link_rate_freq; + /* Tx latency value (us) */ + t_u16 link_tx_latency; + /* Tx latency frequency */ + t_u16 link_tx_lantency_freq; +} MLAN_PACK_END MrvlIEtypes_LinkQualityThreshold_t; + +/** HostCmd_DS_SENSOR_TEMP structure */ +typedef MLAN_PACK_START struct _HostCmd_DS_SENSOR_TEMP { + /** Temperature */ + t_u32 temperature; +} MLAN_PACK_END HostCmd_DS_SENSOR_TEMP; + +#ifdef STA_SUPPORT +/** HostCmd_DS_STA_CONFIGURE structure */ +typedef MLAN_PACK_START struct _HostCmd_DS_STA_CONFIGURE { + /** Action Set or get */ + t_u16 action; + /** Tlv buffer */ + t_u8 tlv_buffer[0]; + /**MrvlIEtypes_channel_band_t band_channel; */ +} MLAN_PACK_END HostCmd_DS_STA_CONFIGURE; +#endif + +/** TLV to indicate firmware only keep probe response while scan */ +#define TLV_TYPE_ONLYPROBERESP (PROPRIETARY_TLV_BASE_ID + 0xE9) /* 0x01E9 */ +typedef MLAN_PACK_START struct _MrvlIEtypes_OnlyProberesp_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** only keep probe response */ + t_u8 proberesp_only; +} MLAN_PACK_END MrvlIEtypes_OnlyProberesp_t; + +/** HostCmd_DS_COMMAND */ +typedef struct MLAN_PACK_START _HostCmd_DS_COMMAND { + /** Command Header : Command */ + t_u16 command; + /** Command Header : Size */ + t_u16 size; + /** Command Header : Sequence number */ + t_u16 seq_num; + /** Command Header : Result */ + t_u16 result; + /** Command Body */ + union { + /** Hardware specifications */ + HostCmd_DS_GET_HW_SPEC hw_spec; + HostCmd_DS_SDIO_SP_RX_AGGR_CFG sdio_rx_aggr; + /** Cfg data */ + HostCmd_DS_802_11_CFG_DATA cfg_data; + /** MAC control */ + HostCmd_DS_MAC_CONTROL mac_ctrl; + /** MAC address */ + HostCmd_DS_802_11_MAC_ADDRESS mac_addr; + /** MAC muticast address */ + HostCmd_DS_MAC_MULTICAST_ADR mc_addr; + /** Get log */ + HostCmd_DS_802_11_GET_LOG get_log; + /** RSSI information */ + HostCmd_DS_802_11_RSSI_INFO_EXT rssi_info_ext; + /** RSSI information */ + HostCmd_DS_802_11_RSSI_INFO rssi_info; + /** RSSI information response */ + HostCmd_DS_802_11_RSSI_INFO_RSP rssi_info_rsp; + /** SNMP MIB */ + HostCmd_DS_802_11_SNMP_MIB smib; + /** Radio control */ + HostCmd_DS_802_11_RADIO_CONTROL radio; + /** RF channel */ + HostCmd_DS_802_11_RF_CHANNEL rf_channel; + /** Tx rate query */ + HostCmd_TX_RATE_QUERY tx_rate; + /** Tx rate configuration */ + HostCmd_DS_TX_RATE_CFG tx_rate_cfg; + /** Tx power configuration */ + HostCmd_DS_TXPWR_CFG txp_cfg; + /** RF Tx power configuration */ + HostCmd_DS_802_11_RF_TX_POWER txp; + + /** RF antenna */ + HostCmd_DS_802_11_RF_ANTENNA antenna; + + /** CW Mode: Tx CW Level control */ + HostCmd_DS_CW_MODE_CTRL cwmode; + /** Enhanced power save command */ + HostCmd_DS_802_11_PS_MODE_ENH psmode_enh; + HostCmd_DS_802_11_HS_CFG_ENH opt_hs_cfg; + HostCmd_DS_802_11_FW_WAKEUP_METHOD fwwakeupmethod; + /** Scan */ + HostCmd_DS_802_11_SCAN scan; + /** Extended Scan */ + HostCmd_DS_802_11_SCAN_EXT ext_scan; + + /** Mgmt frame subtype mask */ + HostCmd_DS_RX_MGMT_IND rx_mgmt_ind; + /** Scan response */ + HostCmd_DS_802_11_SCAN_RSP scan_resp; + + HostCmd_DS_802_11_BG_SCAN_CONFIG bg_scan_config; + HostCmd_DS_802_11_BG_SCAN_QUERY bg_scan_query; + HostCmd_DS_802_11_BG_SCAN_QUERY_RSP bg_scan_query_resp; + HostCmd_DS_SUBSCRIBE_EVENT subscribe_event; + HostCmd_DS_OTP_USER_DATA otp_user_data; + /** Associate */ + HostCmd_DS_802_11_ASSOCIATE associate; + + /** Associate response */ + HostCmd_DS_802_11_ASSOCIATE_RSP associate_rsp; + /** Deauthenticate */ + HostCmd_DS_802_11_DEAUTHENTICATE deauth; + /** Ad-Hoc start */ + HostCmd_DS_802_11_AD_HOC_START adhoc_start; + /** Ad-Hoc start result */ + HostCmd_DS_802_11_AD_HOC_START_RESULT adhoc_start_result; + /** Ad-Hoc join result */ + HostCmd_DS_802_11_AD_HOC_JOIN_RESULT adhoc_join_result; + /** Ad-Hoc join */ + HostCmd_DS_802_11_AD_HOC_JOIN adhoc_join; + /** Domain information */ + HostCmd_DS_802_11D_DOMAIN_INFO domain_info; + /** Domain information response */ + HostCmd_DS_802_11D_DOMAIN_INFO_RSP domain_info_resp; + /** 11K GET NLIST */ + HostCmd_DS_802_11K_GET_NLIST get_nlist; + /** OFFLOAD FEATURE CTRL */ + HostCmd_OFFLOAD_FEATURE_CTRL fctrl; + HostCmd_DS_802_11_TPC_ADAPT_REQ tpc_req; + HostCmd_DS_802_11_TPC_INFO tpc_info; + HostCmd_DS_802_11_CHAN_SW_ANN chan_sw_ann; + HostCmd_DS_CHAN_RPT_REQ chan_rpt_req; + HostCmd_DS_MEASUREMENT_REQUEST meas_req; + HostCmd_DS_MEASUREMENT_REPORT meas_rpt; + /** Add BA request */ + HostCmd_DS_11N_ADDBA_REQ add_ba_req; + /** Add BA response */ + HostCmd_DS_11N_ADDBA_RSP add_ba_rsp; + /** Delete BA entry */ + HostCmd_DS_11N_DELBA del_ba; + /** Tx buffer configuration */ + HostCmd_DS_TXBUF_CFG tx_buf; + /** AMSDU Aggr Ctrl configuration */ + HostCmd_DS_AMSDU_AGGR_CTRL amsdu_aggr_ctrl; + /** 11n configuration */ + HostCmd_DS_11N_CFG htcfg; + /** reject addba req conditions configuration */ + HostCmd_DS_REJECT_ADDBA_REQ rejectaddbareq; + /** 11n configuration */ + HostCmd_DS_TX_BF_CFG tx_bf_cfg; + /** WMM status get */ + HostCmd_DS_WMM_GET_STATUS get_wmm_status; + /** WMM ADDTS */ + HostCmd_DS_WMM_ADDTS_REQ add_ts; + /** WMM DELTS */ + HostCmd_DS_WMM_DELTS_REQ del_ts; + /** WMM set/get queue config */ + HostCmd_DS_WMM_QUEUE_CONFIG queue_config; + /** WMM param config*/ + HostCmd_DS_WMM_PARAM_CONFIG param_config; + /** WMM on/of/get queue statistics */ + HostCmd_DS_WMM_QUEUE_STATS queue_stats; + /** WMM get traffic stream status */ + HostCmd_DS_WMM_TS_STATUS ts_status; + /** Key material */ + HostCmd_DS_802_11_KEY_MATERIAL key_material; + /** GTK Rekey parameters */ + HostCmd_DS_GTK_REKEY_PARAMS gtk_rekey; + /** E-Supplicant PSK */ + HostCmd_DS_802_11_SUPPLICANT_PMK esupplicant_psk; + /** E-Supplicant profile */ + HostCmd_DS_802_11_SUPPLICANT_PROFILE esupplicant_profile; + /** Extended version */ + HostCmd_DS_VERSION_EXT verext; + /** Adhoc Coalescing */ + HostCmd_DS_802_11_IBSS_STATUS ibss_coalescing; + /** Mgmt IE list configuration */ + HostCmd_DS_MGMT_IE_LIST_CFG mgmt_ie_list; + /** TDLS configuration command */ + HostCmd_DS_TDLS_CONFIG tdls_config_data; + /** TDLS operation command */ + HostCmd_DS_TDLS_OPER tdls_oper_data; + /** System clock configuration */ + HostCmd_DS_ECL_SYSTEM_CLOCK_CONFIG sys_clock_cfg; + /** MAC register access */ + HostCmd_DS_MAC_REG_ACCESS mac_reg; + /** BBP register access */ + HostCmd_DS_BBP_REG_ACCESS bbp_reg; + /** RF register access */ + HostCmd_DS_RF_REG_ACCESS rf_reg; + /** EEPROM register access */ + HostCmd_DS_802_11_EEPROM_ACCESS eeprom; + /** Memory access */ + HostCmd_DS_MEM_ACCESS mem; + /** Target device access */ + HostCmd_DS_TARGET_ACCESS target; + + /** Inactivity timeout extend */ + HostCmd_DS_INACTIVITY_TIMEOUT_EXT inactivity_to; +#ifdef UAP_SUPPORT + HostCmd_DS_SYS_CONFIG sys_config; + HostCmd_DS_SYS_INFO sys_info; + HostCmd_DS_STA_DEAUTH sta_deauth; + HostCmd_DS_STA_LIST sta_list; + HostCmd_DS_POWER_MGMT_EXT pm_cfg; + HostCmd_DS_UAP_OPER_CTRL uap_oper_ctrl; +#endif /* UAP_SUPPORT */ + + /** Sleep period command */ + HostCmd_DS_802_11_SLEEP_PERIOD sleep_pd; + /** Sleep params command */ + HostCmd_DS_802_11_SLEEP_PARAMS sleep_param; + + /** SDIO GPIO interrupt config command */ + HostCmd_DS_SDIO_GPIO_INT_CONFIG sdio_gpio_int; + HostCmd_DS_SDIO_PULL_CTRL sdio_pull_ctl; + HostCmd_DS_SET_BSS_MODE bss_mode; + HostCmd_DS_802_11_NET_MONITOR net_mon; + HostCmd_DS_CMD_TX_DATA_PAUSE tx_data_pause; + HostCmd_DS_REMAIN_ON_CHANNEL remain_on_chan; +#ifdef WIFI_DIRECT_SUPPORT + HostCmd_DS_WIFI_DIRECT_MODE wifi_direct_mode; + HostCmd_DS_WIFI_DIRECT_PARAM_CONFIG p2p_params_config; +#endif + HostCmd_DS_COALESCE_CONFIG coalesce_config; + HostCmd_DS_HS_WAKEUP_REASON hs_wakeup_reason; + HostCmd_DS_MULTI_CHAN_CFG multi_chan_cfg; + HostCmd_DS_MULTI_CHAN_POLICY multi_chan_policy; + HostCmd_DS_DRCS_CFG drcs_cfg; + HostCmd_DS_TSF tsf; + HostCmd_DS_DFS_REPEATER_MODE dfs_repeater; +#ifdef RX_PACKET_COALESCE + HostCmd_DS_RX_PKT_COAL_CFG rx_pkt_coal_cfg; +#endif + HostCmd_DS_EAPOL_PKT eapol_pkt; + HostCmd_DS_SENSOR_TEMP temp_sensor; +#ifdef STA_SUPPORT + HostCmd_DS_STA_CONFIGURE sta_cfg; +#endif + /** GPIO Independent reset configure */ + HostCmd_DS_INDEPENDENT_RESET_CFG ind_rst_cfg; + HostCmd_DS_802_11_PS_INACTIVITY_TIMEOUT ps_inact_tmo; + HostCmd_DS_ROAM_OFFLOAD roam_offload; + HostCmd_DS_HOST_CLOCK_CFG host_clock_cfg; + HostCmd_DS_CHAN_REGION_CFG reg_cfg; + HostCmd_DS_802_11_ROBUSTCOEX robustcoexparams; + /** boot sleep configure */ + HostCmd_DS_BOOT_SLEEP boot_sleep; + } params; +} MLAN_PACK_END HostCmd_DS_COMMAND; + +/** PS_CMD_ConfirmSleep */ +typedef MLAN_PACK_START struct _OPT_Confirm_Sleep { + /** Command */ + t_u16 command; + /** Size */ + t_u16 size; + /** Sequence number */ + t_u16 seq_num; + /** Result */ + t_u16 result; + /** Action */ + t_u16 action; + /** Sleep comfirm param definition */ + sleep_confirm_param sleep_cfm; +} MLAN_PACK_END OPT_Confirm_Sleep; + +typedef struct MLAN_PACK_START _opt_sleep_confirm_buffer { + /** Header for interface */ + t_u8 hdr[4]; +#ifdef SPI_SUPPORT + /** Header for interface */ + t_u16 hdr; +#endif + /** New power save command used to send + * sleep confirmation to the firmware */ + OPT_Confirm_Sleep ps_cfm_sleep; +} MLAN_PACK_END opt_sleep_confirm_buffer; + +#ifdef PRAGMA_PACK +#pragma pack(pop) +#endif + +#endif /* !_MLAN_FW_H_ */ diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_ieee.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_ieee.h new file mode 100644 index 000000000000..3d75e1e5758c --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_ieee.h @@ -0,0 +1,1583 @@ +/** @file mlan_ieee.h + * + * @brief This file contains IEEE information element related + * definitions used in MLAN and MOAL module. + * + * Copyright (C) 2008-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 11/03/2008: initial version +******************************************************/ + +#ifndef _MLAN_IEEE_H_ +#define _MLAN_IEEE_H_ + +/** FIX IES size in beacon buffer */ +#define WLAN_802_11_FIXED_IE_SIZE 12 +/** WLAN supported rates */ +#define WLAN_SUPPORTED_RATES 14 + +/** WLAN supported rates extension*/ +#define WLAN_SUPPORTED_RATES_EXT 32 + +/** Enumeration definition*/ +/** WLAN_802_11_NETWORK_TYPE */ +typedef enum _WLAN_802_11_NETWORK_TYPE { + Wlan802_11FH, + Wlan802_11DS, + /* Defined as upper bound */ + Wlan802_11NetworkTypeMax +} WLAN_802_11_NETWORK_TYPE; + +#ifdef BIG_ENDIAN_SUPPORT +/** Frame control: Type Mgmt frame */ +#define IEEE80211_FC_MGMT_FRAME_TYPE_MASK 0x3000 +/** Frame control: SubType Mgmt frame */ +#define IEEE80211_GET_FC_MGMT_FRAME_SUBTYPE(fc) (((fc) & 0xF000) >> 12) +#else +/** Frame control: Type Mgmt frame */ +#define IEEE80211_FC_MGMT_FRAME_TYPE_MASK 0x000C +/** Frame control: SubType Mgmt frame */ +#define IEEE80211_GET_FC_MGMT_FRAME_SUBTYPE(fc) (((fc) & 0x00F0) >> 4) +#endif + +#ifdef PRAGMA_PACK +#pragma pack(push, 1) +#endif + +/* Reason codes */ +#define IEEE_80211_REASONCODE_UNSPECIFIED 1 + +/** IEEE Type definitions */ +typedef MLAN_PACK_START enum _IEEEtypes_ElementId_e { + SSID = 0, + SUPPORTED_RATES = 1, + + FH_PARAM_SET = 2, + DS_PARAM_SET = 3, + CF_PARAM_SET = 4, + + IBSS_PARAM_SET = 6, + + COUNTRY_INFO = 7, + + POWER_CONSTRAINT = 32, + POWER_CAPABILITY = 33, + TPC_REQUEST = 34, + TPC_REPORT = 35, + CHANNEL_SWITCH_ANN = 37, + EXTEND_CHANNEL_SWITCH_ANN = 60, + QUIET = 40, + IBSS_DFS = 41, + SUPPORTED_CHANNELS = 36, + REGULATORY_CLASS = 59, + HT_CAPABILITY = 45, + QOS_INFO = 46, + HT_OPERATION = 61, + BSSCO_2040 = 72, + OVERLAPBSSSCANPARAM = 74, + EXT_CAPABILITY = 127, + LINK_ID = 101, + /*IEEE802.11r */ + MOBILITY_DOMAIN = 54, + FAST_BSS_TRANSITION = 55, + TIMEOUT_INTERVAL = 56, + RIC = 57, + QOS_MAPPING = 110, + + ERP_INFO = 42, + + EXTENDED_SUPPORTED_RATES = 50, + + VENDOR_SPECIFIC_221 = 221, + WMM_IE = VENDOR_SPECIFIC_221, + + WPS_IE = VENDOR_SPECIFIC_221, + + WPA_IE = VENDOR_SPECIFIC_221, + RSN_IE = 48, + VS_IE = VENDOR_SPECIFIC_221, + WAPI_IE = 68, +} MLAN_PACK_END IEEEtypes_ElementId_e; + +/** IEEE IE header */ +typedef MLAN_PACK_START struct _IEEEtypes_Header_t { + /** Element ID */ + t_u8 element_id; + /** Length */ + t_u8 len; +} MLAN_PACK_END IEEEtypes_Header_t, *pIEEEtypes_Header_t; + +/** Vendor specific IE header */ +typedef MLAN_PACK_START struct _IEEEtypes_VendorHeader_t { + /** Element ID */ + t_u8 element_id; + /** Length */ + t_u8 len; + /** OUI */ + t_u8 oui[3]; + /** OUI type */ + t_u8 oui_type; + /** OUI subtype */ + t_u8 oui_subtype; + /** Version */ + t_u8 version; +} MLAN_PACK_END IEEEtypes_VendorHeader_t, *pIEEEtypes_VendorHeader_t; + +/** Vendor specific IE */ +typedef MLAN_PACK_START struct _IEEEtypes_VendorSpecific_t { + /** Vendor specific IE header */ + IEEEtypes_VendorHeader_t vend_hdr; + /** IE Max - size of previous fields */ + t_u8 data[IEEE_MAX_IE_SIZE - sizeof(IEEEtypes_VendorHeader_t)]; +} MLAN_PACK_END IEEEtypes_VendorSpecific_t, *pIEEEtypes_VendorSpecific_t; + +/** IEEE IE */ +typedef MLAN_PACK_START struct _IEEEtypes_Generic_t { + /** Generic IE header */ + IEEEtypes_Header_t ieee_hdr; + /** IE Max - size of previous fields */ + t_u8 data[IEEE_MAX_IE_SIZE - sizeof(IEEEtypes_Header_t)]; +} MLAN_PACK_END IEEEtypes_Generic_t, *pIEEEtypes_Generic_t; + +/**ft capability policy*/ +typedef MLAN_PACK_START struct _IEEEtypes_FtCapPolicy_t { +#ifdef BIG_ENDIAN_SUPPORT + /** Reserved */ + t_u8 reserved:6; + /** RIC support */ + t_u8 ric:1; + /** FT over the DS capable */ + t_u8 ft_over_ds:1; +#else + /** FT over the DS capable */ + t_u8 ft_over_ds:1; + /** RIC support */ + t_u8 ric:1; + /** Reserved */ + t_u8 reserved:6; +#endif +} MLAN_PACK_END IEEEtypes_FtCapPolicy_t; + +/** Mobility domain IE */ +typedef MLAN_PACK_START struct _IEEEtypes_MobilityDomain_t { + /** Generic IE header */ + IEEEtypes_Header_t ieee_hdr; + /** Mobility Domain ID */ + t_u16 mdid; + /** FT Capability policy */ + t_u8 ft_cap; +} MLAN_PACK_END IEEEtypes_MobilityDomain_t; + +/**FT MIC Control*/ +typedef MLAN_PACK_START struct _IEEEtypes_FT_MICControl_t { + /** reserved */ + t_u8 reserved; + /** element count */ + t_u8 element_count; +} MLAN_PACK_END IEEEtypes_FT_MICControl_t; + +/** FTIE MIC LEN */ +#define FTIE_MIC_LEN 16 + +/**FT IE*/ +typedef MLAN_PACK_START struct _IEEEtypes_FastBssTransElement_t { + /** Generic IE header */ + IEEEtypes_Header_t ieee_hdr; + /** mic control */ + IEEEtypes_FT_MICControl_t mic_control; + /** mic */ + t_u8 mic[FTIE_MIC_LEN]; + /** ANonce */ + t_u8 a_nonce[32]; + /** SNonce */ + t_u8 s_nonce[32]; + /** sub element */ + t_u8 sub_element[1]; +} MLAN_PACK_END IEEEtypes_FastBssTransElement_t; + +/** auth frame body*/ +typedef MLAN_PACK_START struct { + /** auth alg */ + t_u16 auth_alg; + /** auth transaction */ + t_u16 auth_transaction; + /** status code */ + t_u16 status_code; + /** variable */ + t_u8 variable[0]; +} MLAN_PACK_END IEEEtypes_Auth_framebody; + +/*Category for FT*/ +#define FT_CATEGORY 6 +/** FT ACTION request */ +#define FT_ACTION_REQUEST 1 +/** FT ACTION response */ +#define FT_ACTION_RESPONSE 2 + +/*FT response and FT ack*/ +typedef MLAN_PACK_START struct { + /** category */ + t_u8 category; + /** action */ + t_u8 action; + /** sta address */ + t_u8 sta_addr[MLAN_MAC_ADDR_LENGTH]; + /** target ap address */ + t_u8 target_ap_addr[MLAN_MAC_ADDR_LENGTH]; + /** status code */ + t_u16 status_code; + /** varible */ + t_u8 variable[0]; +} MLAN_PACK_END IEEEtypes_Ft_action_response; + +/**FT request */ +typedef MLAN_PACK_START struct { + /** category */ + t_u8 category; + /** action */ + t_u8 action; + /** sta address */ + t_u8 sta_addr[MLAN_MAC_ADDR_LENGTH]; + /** target ap address */ + t_u8 target_ap_addr[MLAN_MAC_ADDR_LENGTH]; + /** varible */ + t_u8 variable[0]; +} MLAN_PACK_END IEEEtypes_Ft_action_request; + +/*Mgmt frame*/ +typedef MLAN_PACK_START struct { + /** frame control */ + t_u16 frame_control; + /** duration */ + t_u16 duration; + /** dest address */ + t_u8 da[MLAN_MAC_ADDR_LENGTH]; + /** source address */ + t_u8 sa[MLAN_MAC_ADDR_LENGTH]; + /** bssid */ + t_u8 bssid[MLAN_MAC_ADDR_LENGTH]; + /** seq control */ + t_u16 seq_ctrl; + /** address 4 */ + t_u8 addr4[MLAN_MAC_ADDR_LENGTH]; + union { + IEEEtypes_Auth_framebody auth; + IEEEtypes_Ft_action_response ft_resp; + IEEEtypes_Ft_action_request ft_req; + } u; +} MLAN_PACK_END IEEE80211_MGMT; + +/** TLV header */ +typedef MLAN_PACK_START struct _TLV_Generic_t { + /** Type */ + t_u16 type; + /** Length */ + t_u16 len; +} MLAN_PACK_END TLV_Generic_t, *pTLV_Generic_t; + +/** Capability information mask */ +#define CAPINFO_MASK \ +(~(MBIT(15) | MBIT(14) | MBIT(11) | MBIT(9))) + +/** Capability Bit Map*/ +#ifdef BIG_ENDIAN_SUPPORT +typedef MLAN_PACK_START struct _IEEEtypes_CapInfo_t { + t_u8 rsrvd1:2; + t_u8 dsss_ofdm:1; + t_u8 radio_measurement:1; + t_u8 rsvrd2:1; + t_u8 short_slot_time:1; + t_u8 rsrvd3:1; + t_u8 spectrum_mgmt:1; + t_u8 chan_agility:1; + t_u8 pbcc:1; + t_u8 short_preamble:1; + t_u8 privacy:1; + t_u8 cf_poll_rqst:1; + t_u8 cf_pollable:1; + t_u8 ibss:1; + t_u8 ess:1; +} MLAN_PACK_END IEEEtypes_CapInfo_t, *pIEEEtypes_CapInfo_t; +#else +typedef MLAN_PACK_START struct _IEEEtypes_CapInfo_t { + /** Capability Bit Map : ESS */ + t_u8 ess:1; + /** Capability Bit Map : IBSS */ + t_u8 ibss:1; + /** Capability Bit Map : CF pollable */ + t_u8 cf_pollable:1; + /** Capability Bit Map : CF poll request */ + t_u8 cf_poll_rqst:1; + /** Capability Bit Map : privacy */ + t_u8 privacy:1; + /** Capability Bit Map : Short preamble */ + t_u8 short_preamble:1; + /** Capability Bit Map : PBCC */ + t_u8 pbcc:1; + /** Capability Bit Map : Channel agility */ + t_u8 chan_agility:1; + /** Capability Bit Map : Spectrum management */ + t_u8 spectrum_mgmt:1; + /** Capability Bit Map : Reserved */ + t_u8 rsrvd3:1; + /** Capability Bit Map : Short slot time */ + t_u8 short_slot_time:1; + /** Capability Bit Map : APSD */ + t_u8 Apsd:1; + /** Capability Bit Map : Reserved */ + t_u8 rsvrd2:1; + /** Capability Bit Map : DSS OFDM */ + t_u8 dsss_ofdm:1; + /** Capability Bit Map : Reserved */ + t_u8 rsrvd1:2; +} MLAN_PACK_END IEEEtypes_CapInfo_t, *pIEEEtypes_CapInfo_t; +#endif /* BIG_ENDIAN_SUPPORT */ + +/** IEEEtypes_CfParamSet_t */ +typedef MLAN_PACK_START struct _IEEEtypes_CfParamSet_t { + /** CF peremeter : Element ID */ + t_u8 element_id; + /** CF peremeter : Length */ + t_u8 len; + /** CF peremeter : Count */ + t_u8 cfp_cnt; + /** CF peremeter : Period */ + t_u8 cfp_period; + /** CF peremeter : Maximum duration */ + t_u16 cfp_max_duration; + /** CF peremeter : Remaining duration */ + t_u16 cfp_duration_remaining; +} MLAN_PACK_END IEEEtypes_CfParamSet_t, *pIEEEtypes_CfParamSet_t; + +/** IEEEtypes_IbssParamSet_t */ +typedef MLAN_PACK_START struct _IEEEtypes_IbssParamSet_t { + /** Element ID */ + t_u8 element_id; + /** Length */ + t_u8 len; + /** ATIM window value in milliseconds */ + t_u16 atim_window; +} MLAN_PACK_END IEEEtypes_IbssParamSet_t, *pIEEEtypes_IbssParamSet_t; + +/** IEEEtypes_SsParamSet_t */ +typedef MLAN_PACK_START union _IEEEtypes_SsParamSet_t { + /** SS parameter : CF parameter set */ + IEEEtypes_CfParamSet_t cf_param_set; + /** SS parameter : IBSS parameter set */ + IEEEtypes_IbssParamSet_t ibss_param_set; +} MLAN_PACK_END IEEEtypes_SsParamSet_t, *pIEEEtypes_SsParamSet_t; + +/** IEEEtypes_FhParamSet_t */ +typedef MLAN_PACK_START struct _IEEEtypes_FhParamSet_t { + /** FH parameter : Element ID */ + t_u8 element_id; + /** FH parameter : Length */ + t_u8 len; + /** FH parameter : Dwell time in milliseconds */ + t_u16 dwell_time; + /** FH parameter : Hop set */ + t_u8 hop_set; + /** FH parameter : Hop pattern */ + t_u8 hop_pattern; + /** FH parameter : Hop index */ + t_u8 hop_index; +} MLAN_PACK_END IEEEtypes_FhParamSet_t, *pIEEEtypes_FhParamSet_t; + +/** IEEEtypes_DsParamSet_t */ +typedef MLAN_PACK_START struct _IEEEtypes_DsParamSet_t { + /** DS parameter : Element ID */ + t_u8 element_id; + /** DS parameter : Length */ + t_u8 len; + /** DS parameter : Current channel */ + t_u8 current_chan; +} MLAN_PACK_END IEEEtypes_DsParamSet_t, *pIEEEtypes_DsParamSet_t; + +/** IEEEtypes_PhyParamSet_t */ +typedef MLAN_PACK_START union _IEEEtypes_PhyParamSet_t { + /** FH parameter set */ + IEEEtypes_FhParamSet_t fh_param_set; + /** DS parameter set */ + IEEEtypes_DsParamSet_t ds_param_set; +} MLAN_PACK_END IEEEtypes_PhyParamSet_t, *pIEEEtypes_PhyParamSet_t; + +/** IEEEtypes_ERPInfo_t */ +typedef MLAN_PACK_START struct _IEEEtypes_ERPInfo_t { + /** Element ID */ + t_u8 element_id; + /** Length */ + t_u8 len; + /** ERP flags */ + t_u8 erp_flags; +} MLAN_PACK_END IEEEtypes_ERPInfo_t, *pIEEEtypes_ERPInfo_t; + +/** IEEEtypes_AId_t */ +typedef t_u16 IEEEtypes_AId_t; + +/** IEEEtypes_StatusCode_t */ +typedef t_u16 IEEEtypes_StatusCode_t; + +/** Fixed size in assoc_resp */ +#define ASSOC_RESP_FIXED_SIZE 6 +/** IEEEtypes_AssocRsp_t */ +typedef MLAN_PACK_START struct _IEEEtypes_AssocRsp_t { + /** Capability information */ + IEEEtypes_CapInfo_t capability; + /** Association response status code */ + IEEEtypes_StatusCode_t status_code; + /** Association ID */ + IEEEtypes_AId_t a_id; + /** IE data buffer */ + t_u8 ie_buffer[1]; +} MLAN_PACK_END IEEEtypes_AssocRsp_t, *pIEEEtypes_AssocRsp_t; + +/** 802.11 supported rates */ +typedef t_u8 WLAN_802_11_RATES[WLAN_SUPPORTED_RATES]; + +/** cipher TKIP */ +#define WPA_CIPHER_TKIP 2 +/** cipher AES */ +#define WPA_CIPHER_AES_CCM 4 +/** AKM: 8021x */ +#define RSN_AKM_8021X 1 +/** AKM: PSK */ +#define RSN_AKM_PSK 2 +/** AKM: PSK SHA256 */ +#define RSN_AKM_PSK_SHA256 6 +#if defined(STA_SUPPORT) +/** Pairwise Cipher Suite length */ +#define PAIRWISE_CIPHER_SUITE_LEN 4 +/** AKM Suite length */ +#define AKM_SUITE_LEN 4 +/** MFPC bit in RSN capability */ +#define MFPC_BIT 7 +/** MFPR bit in RSN capability */ +#define MFPR_BIT 6 +/** PMF ORing mask */ +#define PMF_MASK 0x00c0 +#endif + +/** wpa_suite_t */ +typedef MLAN_PACK_START struct _wpa_suite_t { + /** OUI */ + t_u8 oui[3]; + /** tyep */ + t_u8 type; +} MLAN_PACK_END wpa_suite, wpa_suite_mcast_t; + +/** wpa_suite_ucast_t */ +typedef MLAN_PACK_START struct { + /* count */ + t_u16 count; + /** wpa_suite list */ + wpa_suite list[1]; +} MLAN_PACK_END wpa_suite_ucast_t, wpa_suite_auth_key_mgmt_t; + +/** IEEEtypes_Rsn_t */ +typedef MLAN_PACK_START struct _IEEEtypes_Rsn_t { + /** Rsn : Element ID */ + t_u8 element_id; + /** Rsn : Length */ + t_u8 len; + /** Rsn : version */ + t_u16 version; + /** Rsn : group cipher */ + wpa_suite_mcast_t group_cipher; + /** Rsn : pairwise cipher */ + wpa_suite_ucast_t pairwise_cipher; +} MLAN_PACK_END IEEEtypes_Rsn_t, *pIEEEtypes_Rsn_t; + +/** IEEEtypes_Wpa_t */ +typedef MLAN_PACK_START struct _IEEEtypes_Wpa_t { + /** Wpa : Element ID */ + t_u8 element_id; + /** Wpa : Length */ + t_u8 len; + /** Wpa : oui */ + t_u8 oui[4]; + /** version */ + t_u16 version; + /** Wpa : group cipher */ + wpa_suite_mcast_t group_cipher; + /** Wpa : pairwise cipher */ + wpa_suite_ucast_t pairwise_cipher; +} MLAN_PACK_END IEEEtypes_Wpa_t, *pIEEEtypes_Wpa_t; + +/** Data structure of WMM QoS information */ +typedef MLAN_PACK_START struct _IEEEtypes_WmmQosInfo_t { +#ifdef BIG_ENDIAN_SUPPORT + /** QoS UAPSD */ + t_u8 qos_uapsd:1; + /** Reserved */ + t_u8 reserved:3; + /** Parameter set count */ + t_u8 para_set_count:4; +#else + /** Parameter set count */ + t_u8 para_set_count:4; + /** Reserved */ + t_u8 reserved:3; + /** QoS UAPSD */ + t_u8 qos_uapsd:1; +#endif /* BIG_ENDIAN_SUPPORT */ +} MLAN_PACK_END IEEEtypes_WmmQosInfo_t, *pIEEEtypes_WmmQosInfo_t; + +/** Data structure of WMM Aci/Aifsn */ +typedef MLAN_PACK_START struct _IEEEtypes_WmmAciAifsn_t { +#ifdef BIG_ENDIAN_SUPPORT + /** Reserved */ + t_u8 reserved:1; + /** Aci */ + t_u8 aci:2; + /** Acm */ + t_u8 acm:1; + /** Aifsn */ + t_u8 aifsn:4; +#else + /** Aifsn */ + t_u8 aifsn:4; + /** Acm */ + t_u8 acm:1; + /** Aci */ + t_u8 aci:2; + /** Reserved */ + t_u8 reserved:1; +#endif /* BIG_ENDIAN_SUPPORT */ +} MLAN_PACK_END IEEEtypes_WmmAciAifsn_t, *pIEEEtypes_WmmAciAifsn_t; + +/** Data structure of WMM ECW */ +typedef MLAN_PACK_START struct _IEEEtypes_WmmEcw_t { +#ifdef BIG_ENDIAN_SUPPORT + /** Maximum Ecw */ + t_u8 ecw_max:4; + /** Minimum Ecw */ + t_u8 ecw_min:4; +#else + /** Minimum Ecw */ + t_u8 ecw_min:4; + /** Maximum Ecw */ + t_u8 ecw_max:4; +#endif /* BIG_ENDIAN_SUPPORT */ +} MLAN_PACK_END IEEEtypes_WmmEcw_t, *pIEEEtypes_WmmEcw_t; + +/** Data structure of WMM AC parameters */ +typedef MLAN_PACK_START struct _IEEEtypes_WmmAcParameters_t { + IEEEtypes_WmmAciAifsn_t aci_aifsn; /**< AciAifSn */ + IEEEtypes_WmmEcw_t ecw; /**< Ecw */ + t_u16 tx_op_limit; /**< Tx op limit */ +} MLAN_PACK_END IEEEtypes_WmmAcParameters_t, *pIEEEtypes_WmmAcParameters_t; + +/** Data structure of WMM Info IE */ +typedef MLAN_PACK_START struct _IEEEtypes_WmmInfo_t { + + /** + * WMM Info IE - Vendor Specific Header: + * element_id [221/0xdd] + * Len [7] + * Oui [00:50:f2] + * OuiType [2] + * OuiSubType [0] + * Version [1] + */ + IEEEtypes_VendorHeader_t vend_hdr; + + /** QoS information */ + IEEEtypes_WmmQosInfo_t qos_info; + +} MLAN_PACK_END IEEEtypes_WmmInfo_t, *pIEEEtypes_WmmInfo_t; + +/** Data structure of WMM parameter IE */ +typedef MLAN_PACK_START struct _IEEEtypes_WmmParameter_t { + /** + * WMM Parameter IE - Vendor Specific Header: + * element_id [221/0xdd] + * Len [24] + * Oui [00:50:f2] + * OuiType [2] + * OuiSubType [1] + * Version [1] + */ + IEEEtypes_VendorHeader_t vend_hdr; + + /** QoS information */ + IEEEtypes_WmmQosInfo_t qos_info; + /** Reserved */ + t_u8 reserved; + + /** AC Parameters Record WMM_AC_BE, WMM_AC_BK, WMM_AC_VI, WMM_AC_VO */ + IEEEtypes_WmmAcParameters_t ac_params[MAX_AC_QUEUES]; +} MLAN_PACK_END IEEEtypes_WmmParameter_t, *pIEEEtypes_WmmParameter_t; + +/** Enumerator for TSPEC direction */ +typedef MLAN_PACK_START enum _IEEEtypes_WMM_TSPEC_TS_Info_Direction_e { + + TSPEC_DIR_UPLINK = 0, + TSPEC_DIR_DOWNLINK = 1, + /* 2 is a reserved value */ + TSPEC_DIR_BIDIRECT = 3, + +} MLAN_PACK_END IEEEtypes_WMM_TSPEC_TS_Info_Direction_e; + +/** Enumerator for TSPEC PSB */ +typedef MLAN_PACK_START enum _IEEEtypes_WMM_TSPEC_TS_Info_PSB_e { + + TSPEC_PSB_LEGACY = 0, + TSPEC_PSB_TRIG = 1, + +} MLAN_PACK_END IEEEtypes_WMM_TSPEC_TS_Info_PSB_e; + +/** Enumerator for TSPEC Ack Policy */ +typedef MLAN_PACK_START enum _IEEEtypes_WMM_TSPEC_TS_Info_AckPolicy_e { + + TSPEC_ACKPOLICY_NORMAL = 0, + TSPEC_ACKPOLICY_NOACK = 1, + /* 2 is reserved */ + TSPEC_ACKPOLICY_BLOCKACK = 3, + +} MLAN_PACK_END IEEEtypes_WMM_TSPEC_TS_Info_AckPolicy_e; + +/** Enumerator for TSPEC Trafffice type */ +typedef MLAN_PACK_START enum _IEEEtypes_WMM_TSPEC_TS_TRAFFIC_TYPE_e { + + TSPEC_TRAFFIC_APERIODIC = 0, + TSPEC_TRAFFIC_PERIODIC = 1, + +} MLAN_PACK_END IEEEtypes_WMM_TSPEC_TS_TRAFFIC_TYPE_e; + +/** Data structure of WMM TSPEC information */ +typedef MLAN_PACK_START struct { +#ifdef BIG_ENDIAN_SUPPORT + t_u8 Reserved17_23:7; /* ! Reserved */ + t_u8 Schedule:1; + IEEEtypes_WMM_TSPEC_TS_Info_AckPolicy_e AckPolicy:2; + t_u8 UserPri:3; /* ! 802.1d User Priority */ + IEEEtypes_WMM_TSPEC_TS_Info_PSB_e PowerSaveBehavior:1; /* ! Legacy/Trigg */ + t_u8 Aggregation:1; /* ! Reserved */ + t_u8 AccessPolicy2:1; /* ! */ + t_u8 AccessPolicy1:1; /* ! */ + IEEEtypes_WMM_TSPEC_TS_Info_Direction_e Direction:2; + t_u8 TID:4; /* ! Unique identifier */ + IEEEtypes_WMM_TSPEC_TS_TRAFFIC_TYPE_e TrafficType:1; +#else + IEEEtypes_WMM_TSPEC_TS_TRAFFIC_TYPE_e TrafficType:1; + t_u8 TID:4; /* ! Unique identifier */ + IEEEtypes_WMM_TSPEC_TS_Info_Direction_e Direction:2; + t_u8 AccessPolicy1:1; /* ! */ + t_u8 AccessPolicy2:1; /* ! */ + t_u8 Aggregation:1; /* ! Reserved */ + IEEEtypes_WMM_TSPEC_TS_Info_PSB_e PowerSaveBehavior:1; /* ! Legacy/Trigg */ + t_u8 UserPri:3; /* ! 802.1d User Priority */ + IEEEtypes_WMM_TSPEC_TS_Info_AckPolicy_e AckPolicy:2; + t_u8 Schedule:1; + t_u8 Reserved17_23:7; /* ! Reserved */ +#endif +} MLAN_PACK_END IEEEtypes_WMM_TSPEC_TS_Info_t; + +/** Data structure of WMM TSPEC Nominal Size */ +typedef MLAN_PACK_START struct { +#ifdef BIG_ENDIAN_SUPPORT + t_u16 Fixed:1; /* ! 1: Fixed size given in Size, 0: Var, size is nominal */ + t_u16 Size:15; /* ! Nominal size in octets */ +#else + t_u16 Size:15; /* ! Nominal size in octets */ + t_u16 Fixed:1; /* ! 1: Fixed size given in Size, 0: Var, size is nominal */ +#endif +} MLAN_PACK_END IEEEtypes_WMM_TSPEC_NomMSDUSize_t; + +/** Data structure of WMM TSPEC SBWA */ +typedef MLAN_PACK_START struct { +#ifdef BIG_ENDIAN_SUPPORT + t_u16 Whole:3; /* ! Whole portion */ + t_u16 Fractional:13; /* ! Fractional portion */ +#else + t_u16 Fractional:13; /* ! Fractional portion */ + t_u16 Whole:3; /* ! Whole portion */ +#endif +} MLAN_PACK_END IEEEtypes_WMM_TSPEC_SBWA; + +/** Data structure of WMM TSPEC Body */ +typedef MLAN_PACK_START struct { + + /** TS Information */ + IEEEtypes_WMM_TSPEC_TS_Info_t TSInfo; + /** NomMSDU size */ + IEEEtypes_WMM_TSPEC_NomMSDUSize_t NomMSDUSize; + /** MAximum MSDU size */ + t_u16 MaximumMSDUSize; + /** Minimum Service Interval */ + t_u32 MinServiceInterval; + /** Maximum Service Interval */ + t_u32 MaxServiceInterval; + /** Inactivity Interval */ + t_u32 InactivityInterval; + /** Suspension Interval */ + t_u32 SuspensionInterval; + /** Service Start Time */ + t_u32 ServiceStartTime; + /** Minimum Data Rate */ + t_u32 MinimumDataRate; + /** Mean Data Rate */ + t_u32 MeanDataRate; + /** Peak Data Rate */ + t_u32 PeakDataRate; + /** Maximum Burst Size */ + t_u32 MaxBurstSize; + /** Delay Bound */ + t_u32 DelayBound; + /** Minimum Phy Rate */ + t_u32 MinPHYRate; + /** Surplus BA Allowance */ + IEEEtypes_WMM_TSPEC_SBWA SurplusBWAllowance; + /** Medium Time */ + t_u16 MediumTime; +} MLAN_PACK_END IEEEtypes_WMM_TSPEC_Body_t; + +/** Data structure of WMM TSPEC all elements */ +typedef MLAN_PACK_START struct { + /** Element ID */ + t_u8 ElementId; + /** Length */ + t_u8 Len; + /** Oui Type */ + t_u8 OuiType[4]; /* 00:50:f2:02 */ + /** Ouisubtype */ + t_u8 OuiSubType; /* 01 */ + /** Version */ + t_u8 Version; + + /** TspecBody */ + IEEEtypes_WMM_TSPEC_Body_t TspecBody; + +} MLAN_PACK_END IEEEtypes_WMM_TSPEC_t; + +/** WMM Action Category values */ +typedef MLAN_PACK_START enum _IEEEtypes_ActionCategory_e { + + IEEE_MGMT_ACTION_CATEGORY_SPECTRUM_MGMT = 0, + IEEE_MGMT_ACTION_CATEGORY_QOS = 1, + IEEE_MGMT_ACTION_CATEGORY_DLS = 2, + IEEE_MGMT_ACTION_CATEGORY_BLOCK_ACK = 3, + IEEE_MGMT_ACTION_CATEGORY_PUBLIC = 4, + IEEE_MGMT_ACTION_CATEGORY_RADIO_RSRC = 5, + IEEE_MGMT_ACTION_CATEGORY_FAST_BSS_TRANS = 6, + IEEE_MGMT_ACTION_CATEGORY_HT = 7, + + IEEE_MGMT_ACTION_CATEGORY_WNM = 10, + IEEE_MGMT_ACTION_CATEGORY_UNPROTECT_WNM = 11, + + IEEE_MGMT_ACTION_CATEGORY_WMM_TSPEC = 17 +} MLAN_PACK_END IEEEtypes_ActionCategory_e; + +/** WMM TSPEC operations */ +typedef MLAN_PACK_START enum _IEEEtypes_WMM_Tspec_Action_e { + + TSPEC_ACTION_CODE_ADDTS_REQ = 0, + TSPEC_ACTION_CODE_ADDTS_RSP = 1, + TSPEC_ACTION_CODE_DELTS = 2, + +} MLAN_PACK_END IEEEtypes_WMM_Tspec_Action_e; + +/** WMM TSPEC Category Action Base */ +typedef MLAN_PACK_START struct { + + /** Category */ + IEEEtypes_ActionCategory_e category; + /** Action */ + IEEEtypes_WMM_Tspec_Action_e action; + /** Dialog Token */ + t_u8 dialogToken; + +} MLAN_PACK_END IEEEtypes_WMM_Tspec_Action_Base_Tspec_t; + +/** WMM TSPEC AddTS request structure */ +typedef MLAN_PACK_START struct { + + /** Tspec action */ + IEEEtypes_WMM_Tspec_Action_Base_Tspec_t tspecAct; + /** Status Code */ + t_u8 statusCode; + /** tspecIE */ + IEEEtypes_WMM_TSPEC_t tspecIE; + + /* Place holder for additional elements after the TSPEC */ + t_u8 subElem[256]; + +} MLAN_PACK_END IEEEtypes_Action_WMM_AddTsReq_t; + +/** WMM TSPEC AddTS response structure */ +typedef MLAN_PACK_START struct { + IEEEtypes_WMM_Tspec_Action_Base_Tspec_t tspecAct; + t_u8 statusCode; + IEEEtypes_WMM_TSPEC_t tspecIE; + + /* Place holder for additional elements after the TSPEC */ + t_u8 subElem[256]; + +} MLAN_PACK_END IEEEtypes_Action_WMM_AddTsRsp_t; + +/** WMM TSPEC DelTS structure */ +typedef MLAN_PACK_START struct { + /** tspec Action */ + IEEEtypes_WMM_Tspec_Action_Base_Tspec_t tspecAct; + /** Reason Code */ + t_u8 reasonCode; + /** tspecIE */ + IEEEtypes_WMM_TSPEC_t tspecIE; + +} MLAN_PACK_END IEEEtypes_Action_WMM_DelTs_t; + +/** union of WMM TSPEC structures */ +typedef MLAN_PACK_START union { + /** tspec Action */ + IEEEtypes_WMM_Tspec_Action_Base_Tspec_t tspecAct; + + /** add TS request */ + IEEEtypes_Action_WMM_AddTsReq_t addTsReq; + /** add TS response */ + IEEEtypes_Action_WMM_AddTsRsp_t addTsRsp; + /** Delete TS */ + IEEEtypes_Action_WMM_DelTs_t delTs; + +} MLAN_PACK_END IEEEtypes_Action_WMMAC_t; + +/** union of WMM TSPEC & Action category */ +typedef MLAN_PACK_START union { + /** Category */ + IEEEtypes_ActionCategory_e category; + + /** wmmAc */ + IEEEtypes_Action_WMMAC_t wmmAc; + +} MLAN_PACK_END IEEEtypes_ActionFrame_t; + +/** Data structure for subband set */ +typedef MLAN_PACK_START struct _IEEEtypes_SubbandSet_t { + /** First channel */ + t_u8 first_chan; + /** Number of channels */ + t_u8 no_of_chan; + /** Maximum Tx power in dBm */ + t_u8 max_tx_pwr; +} MLAN_PACK_END IEEEtypes_SubbandSet_t, *pIEEEtypes_SubbandSet_t; + +#ifdef STA_SUPPORT +/** Data structure for Country IE */ +typedef MLAN_PACK_START struct _IEEEtypes_CountryInfoSet_t { + /** Element ID */ + t_u8 element_id; + /** Length */ + t_u8 len; + /** Country code */ + t_u8 country_code[COUNTRY_CODE_LEN]; + /** Set of subbands */ + IEEEtypes_SubbandSet_t sub_band[1]; +} MLAN_PACK_END IEEEtypes_CountryInfoSet_t, *pIEEEtypes_CountryInfoSet_t; + +/** Data structure for Country IE full set */ +typedef MLAN_PACK_START struct _IEEEtypes_CountryInfoFullSet_t { + /** Element ID */ + t_u8 element_id; + /** Length */ + t_u8 len; + /** Country code */ + t_u8 country_code[COUNTRY_CODE_LEN]; + /** Set of subbands */ + IEEEtypes_SubbandSet_t sub_band[MRVDRV_MAX_SUBBAND_802_11D]; +} MLAN_PACK_END IEEEtypes_CountryInfoFullSet_t, + *pIEEEtypes_CountryInfoFullSet_t; + +#endif /* STA_SUPPORT */ + +/** Data structure for Link ID */ +typedef MLAN_PACK_START struct _IEEEtypes_LinkIDElement_t { + /** Element ID */ + t_u8 element_id; + /** Length */ + t_u8 len; + /** bssid */ + t_u8 bssid[MLAN_MAC_ADDR_LENGTH]; + /** initial sta address */ + t_u8 init_sta[MLAN_MAC_ADDR_LENGTH]; + /** respose sta address */ + t_u8 resp_sta[MLAN_MAC_ADDR_LENGTH]; +} MLAN_PACK_END IEEEtypes_LinkIDElement_t, *pIEEEtypes_LinkIDElement_t; + +/** HT Capabilities Data */ +typedef struct MLAN_PACK_START _HTCap_t { + /** HT Capabilities Info field */ + t_u16 ht_cap_info; + /** A-MPDU Parameters field */ + t_u8 ampdu_param; + /** Supported MCS Set field */ + t_u8 supported_mcs_set[16]; + /** HT Extended Capabilities field */ + t_u16 ht_ext_cap; + /** Transmit Beamforming Capabilities field */ + t_u32 tx_bf_cap; + /** Antenna Selection Capability field */ + t_u8 asel; +} MLAN_PACK_END HTCap_t, *pHTCap_t; + +/** HT Information Data */ +typedef struct MLAN_PACK_START _HTInfo_t { + /** Primary channel */ + t_u8 pri_chan; + /** Field 2 */ + t_u8 field2; + /** Field 3 */ + t_u16 field3; + /** Field 4 */ + t_u16 field4; + /** Bitmap indicating MCSs supported by all HT STAs in the BSS */ + t_u8 basic_mcs_set[16]; +} MLAN_PACK_END HTInfo_t, *pHTInfo_t; + +/** 20/40 BSS Coexistence Data */ +typedef struct MLAN_PACK_START _BSSCo2040_t { + /** 20/40 BSS Coexistence value */ + t_u8 bss_co_2040_value; +} MLAN_PACK_END BSSCo2040_t, *pBSSCo2040_t; + +#define MAX_DSCP_EXCEPTION_NUM 21 +/** DSCP Range */ +typedef struct MLAN_PACK_START _DSCP_Exception_t { + /* DSCP value 0 to 63 or ff */ + t_u8 dscp_value; + /* user priority 0-7 */ + t_u8 user_priority; +} MLAN_PACK_END DSCP_Exception_t, *pDSCP_Exception_t; + +/** DSCP Range */ +typedef struct MLAN_PACK_START _DSCP_Range_t { + /* DSCP low value */ + t_u8 dscp_low_value; + /* DSCP high value */ + t_u8 dscp_high_value; +} MLAN_PACK_END DSCP_Range_t, *pDSCP_Range_t; + +/** Overlapping BSS Scan Parameters Data */ +typedef struct MLAN_PACK_START _OverlapBSSScanParam_t { + /** OBSS Scan Passive Dwell in milliseconds */ + t_u16 obss_scan_passive_dwell; + /** OBSS Scan Active Dwell in milliseconds */ + t_u16 obss_scan_active_dwell; + /** BSS Channel Width Trigger Scan Interval in seconds */ + t_u16 bss_chan_width_trigger_scan_int; + /** OBSS Scan Passive Total Per Channel */ + t_u16 obss_scan_passive_total; + /** OBSS Scan Active Total Per Channel */ + t_u16 obss_scan_active_total; + /** BSS Width Channel Transition Delay Factor */ + t_u16 bss_width_chan_trans_delay; + /** OBSS Scan Activity Threshold */ + t_u16 obss_scan_active_threshold; +} MLAN_PACK_END OBSSScanParam_t, *pOBSSScanParam_t; + +/** HT Capabilities IE */ +typedef MLAN_PACK_START struct _IEEEtypes_HTCap_t { + /** Generic IE header */ + IEEEtypes_Header_t ieee_hdr; + /** HTCap struct */ + HTCap_t ht_cap; +} MLAN_PACK_END IEEEtypes_HTCap_t, *pIEEEtypes_HTCap_t; + +/** HT Information IE */ +typedef MLAN_PACK_START struct _IEEEtypes_HTInfo_t { + /** Generic IE header */ + IEEEtypes_Header_t ieee_hdr; + /** HTInfo struct */ + HTInfo_t ht_info; +} MLAN_PACK_END IEEEtypes_HTInfo_t, *pIEEEtypes_HTInfo_t; + +/** 20/40 BSS Coexistence IE */ +typedef MLAN_PACK_START struct _IEEEtypes_2040BSSCo_t { + /** Generic IE header */ + IEEEtypes_Header_t ieee_hdr; + /** BSSCo2040_t struct */ + BSSCo2040_t bss_co_2040; +} MLAN_PACK_END IEEEtypes_2040BSSCo_t, *pIEEEtypes_2040BSSCo_t; + +/** Extended Capabilities IE */ +typedef MLAN_PACK_START struct _IEEEtypes_ExtCap_t { + /** Generic IE header */ + IEEEtypes_Header_t ieee_hdr; + /** ExtCap_t struct */ + ExtCap_t ext_cap; +} MLAN_PACK_END IEEEtypes_ExtCap_t, *pIEEEtypes_ExtCap_t; + +/** Overlapping BSS Scan Parameters IE */ +typedef MLAN_PACK_START struct _IEEEtypes_OverlapBSSScanParam_t { + /** Generic IE header */ + IEEEtypes_Header_t ieee_hdr; + /** OBSSScanParam_t struct */ + OBSSScanParam_t obss_scan_param; +} MLAN_PACK_END IEEEtypes_OverlapBSSScanParam_t, + *pIEEEtypes_OverlapBSSScanParam_t; + +/** Maximum number of subbands in the IEEEtypes_SupportedChannels_t structure */ +#define WLAN_11H_MAX_SUBBANDS 5 + +/** Maximum number of DFS channels configured in IEEEtypes_IBSS_DFS_t */ +#define WLAN_11H_MAX_IBSS_DFS_CHANNELS 25 + +/** IEEE Power Constraint element (7.3.2.15) */ +typedef MLAN_PACK_START struct { + t_u8 element_id; /**< IEEE Element ID = 32 */ + t_u8 len; /**< Element length after id and len */ + t_u8 local_constraint; + /**< Local power constraint applied to 11d + chan info */ +} MLAN_PACK_END IEEEtypes_PowerConstraint_t; + +/** IEEE Power Capability element (7.3.2.16) */ +typedef MLAN_PACK_START struct { + t_u8 element_id; /**< IEEE Element ID = 33 */ + t_u8 len; /**< Element length after id and len */ + t_s8 min_tx_power_capability; + /**< Minimum Transmit power (dBm) */ + t_s8 max_tx_power_capability; + /**< Maximum Transmit power (dBm) */ +} MLAN_PACK_END IEEEtypes_PowerCapability_t; + +/** IEEE TPC Report element (7.3.2.18) */ +typedef MLAN_PACK_START struct { + t_u8 element_id;/**< IEEE Element ID = 35 */ + t_u8 len; /**< Element length after id and len */ + t_s8 tx_power; /**< Max power used to transmit the TPC Report frame (dBm) */ + t_s8 link_margin; + /**< Link margin when TPC Request received (dB) */ +} MLAN_PACK_END IEEEtypes_TPCReport_t; + +/* IEEE Supported Channel sub-band description (7.3.2.19) */ +/** + * Sub-band description used in the supported channels element. + */ +typedef MLAN_PACK_START struct { + t_u8 start_chan;/**< Starting channel in the subband */ + t_u8 num_chans; /**< Number of channels in the subband */ + +} MLAN_PACK_END IEEEtypes_SupportChan_Subband_t; + +/* IEEE Supported Channel element (7.3.2.19) */ +/** + * Sent in association requests. Details the sub-bands and number + * of channels supported in each subband + */ +typedef MLAN_PACK_START struct { + t_u8 element_id;/**< IEEE Element ID = 36 */ + t_u8 len; /**< Element length after id and len */ + + /** Configured sub-bands information in the element */ + IEEEtypes_SupportChan_Subband_t subband[WLAN_11H_MAX_SUBBANDS]; + +} MLAN_PACK_END IEEEtypes_SupportedChannels_t; + +/* IEEE Channel Switch Announcement Element (7.3.2.20) */ +/** + * Provided in beacons and probe responses. Used to advertise when + * and to which channel it is changing to. Only starting STAs in + * an IBSS and APs are allowed to originate a chan switch element. + */ +typedef MLAN_PACK_START struct { + t_u8 element_id; /**< IEEE Element ID = 37 */ + t_u8 len; /**< Element length after id and len */ + t_u8 chan_switch_mode; /**< STA should not transmit any frames if 1 */ + t_u8 new_channel_num; /**< Channel # that AP/IBSS is moving to */ + t_u8 chan_switch_count; /**< # of TBTTs before channel switch */ + +} MLAN_PACK_END IEEEtypes_ChanSwitchAnn_t; + +/* IEEE Quiet Period Element (7.3.2.23) */ +/** + * Provided in beacons and probe responses. Indicates times during + * which the STA should not be transmitting data. Only starting STAs in + * an IBSS and APs are allowed to originate a quiet element. + */ +typedef MLAN_PACK_START struct { + t_u8 element_id; /**< IEEE Element ID = 40 */ + t_u8 len; /**< Element length after id and len */ + t_u8 quiet_count; /**< Number of TBTTs until beacon with the quiet period */ + t_u8 quiet_period; /**< Regular quiet period, # of TBTTS between periods */ + t_u16 quiet_duration; + /**< Duration of the quiet period in TUs */ + t_u16 quiet_offset; /**< Offset in TUs from the TBTT for the quiet period */ + +} MLAN_PACK_END IEEEtypes_Quiet_t; + +/** +*** @brief Map octet of the basic measurement report (7.3.2.22.1) +**/ +typedef MLAN_PACK_START struct { +#ifdef BIG_ENDIAN_SUPPORT + /**< Reserved */ + t_u8 rsvd5_7:3; + /**< Channel is unmeasured */ + t_u8 unmeasured:1; + /**< Radar detected on channel */ + t_u8 radar:1; + /**< Unidentified signal found on channel */ + t_u8 unidentified_sig:1; + /**< OFDM preamble detected on channel */ + t_u8 ofdm_preamble:1; + /**< At least one valid MPDU received on channel */ + t_u8 bss:1; +#else + /**< At least one valid MPDU received on channel */ + t_u8 bss:1; + /**< OFDM preamble detected on channel */ + t_u8 ofdm_preamble:1; + /**< Unidentified signal found on channel */ + t_u8 unidentified_sig:1; + /**< Radar detected on channel */ + t_u8 radar:1; + /**< Channel is unmeasured */ + t_u8 unmeasured:1; + /**< Reserved */ + t_u8 rsvd5_7:3; +#endif /* BIG_ENDIAN_SUPPORT */ + +} MLAN_PACK_END MeasRptBasicMap_t; + +/* IEEE DFS Channel Map field (7.3.2.24) */ +/** + * Used to list supported channels and provide a octet "map" field which + * contains a basic measurement report for that channel in the + * IEEEtypes_IBSS_DFS_t element + */ +typedef MLAN_PACK_START struct { + t_u8 channel_number; /**< Channel number */ + MeasRptBasicMap_t rpt_map; + /**< Basic measurement report for the channel */ + +} MLAN_PACK_END IEEEtypes_ChannelMap_t; + +/* IEEE IBSS DFS Element (7.3.2.24) */ +/** + * IBSS DFS element included in ad hoc beacons and probe responses. + * Provides information regarding the IBSS DFS Owner as well as the + * originating STAs supported channels and basic measurement results. + */ +typedef MLAN_PACK_START struct { + t_u8 element_id; /**< IEEE Element ID = 41 */ + t_u8 len; /**< Element length after id and len */ + t_u8 dfs_owner[MLAN_MAC_ADDR_LENGTH]; + /**< DFS Owner STA Address */ + t_u8 dfs_recovery_interval; /**< DFS Recovery time in TBTTs */ + + /** Variable length map field, one Map entry for each supported channel */ + IEEEtypes_ChannelMap_t channel_map[WLAN_11H_MAX_IBSS_DFS_CHANNELS]; + +} MLAN_PACK_END IEEEtypes_IBSS_DFS_t; + +/* 802.11h BSS information kept for each BSSID received in scan results */ +/** + * IEEE BSS information needed from scan results for later processing in + * join commands + */ +typedef struct { + t_u8 sensed_11h; + /**< Capability bit set or 11h IE found in this BSS */ + + IEEEtypes_PowerConstraint_t power_constraint; + /**< Power Constraint IE */ + IEEEtypes_PowerCapability_t power_capability; + /**< Power Capability IE */ + IEEEtypes_TPCReport_t tpc_report; /**< TPC Report IE */ + IEEEtypes_ChanSwitchAnn_t chan_switch_ann;/**< Channel Switch Announcement IE */ + IEEEtypes_Quiet_t quiet; /**< Quiet IE */ + IEEEtypes_IBSS_DFS_t ibss_dfs; /**< IBSS DFS Element IE */ + +} wlan_11h_bss_info_t; + +/** Ethernet packet type for TDLS */ +#define MLAN_ETHER_PKT_TYPE_TDLS_ACTION (0x890D) + +/*802.11z TDLS action frame type and strcuct */ +typedef MLAN_PACK_START struct { + /*link indentifier ie =101 */ + t_u8 element_id; + /*len = 18 */ + t_u8 len; + /** bssid */ + t_u8 bssid[MLAN_MAC_ADDR_LENGTH]; + /** init sta mac address */ + t_u8 init_sta[MLAN_MAC_ADDR_LENGTH]; + /** resp sta mac address */ + t_u8 resp_sta[MLAN_MAC_ADDR_LENGTH]; +} MLAN_PACK_END IEEEtypes_tdls_linkie; + +/** action code for tdls setup request */ +#define TDLS_SETUP_REQUEST 0 +/** action code for tdls setup response */ +#define TDLS_SETUP_RESPONSE 1 +/** action code for tdls setup confirm */ +#define TDLS_SETUP_CONFIRM 2 +/** action code for tdls tear down */ +#define TDLS_TEARDOWN 3 +/** action code for tdls traffic indication */ +#define TDLS_PEER_TRAFFIC_INDICATION 4 +/** action code for tdls channel switch request */ +#define TDLS_CHANNEL_SWITCH_REQUEST 5 +/** action code for tdls channel switch response */ +#define TDLS_CHANNEL_SWITCH_RESPONSE 6 +/** action code for tdls psm request */ +#define TDLS_PEER_PSM_REQUEST 7 +/** action code for tdls psm response */ +#define TDLS_PEER_PSM_RESPONSE 8 +/** action code for tdls traffic response */ +#define TDLS_PEER_TRAFFIC_RESPONSE 9 +/** action code for tdls discovery request */ +#define TDLS_DISCOVERY_REQUEST 10 +/** action code for TDLS discovery response */ +#define TDLS_DISCOVERY_RESPONSE 14 +/** category public */ +#define CATEGORY_PUBLIC 4 + +/** action code for 20/40 BSS Coexsitence Management frame */ +#define BSS_20_40_COEX 0 + +#ifdef STA_SUPPORT +/** Macro for maximum size of scan response buffer */ +#define MAX_SCAN_RSP_BUF (16 * 1024) + +/** Maximum number of channels that can be sent in user scan config */ +#define WLAN_USER_SCAN_CHAN_MAX 50 + +/** Maximum length of SSID list */ +#define MRVDRV_MAX_SSID_LIST_LENGTH 10 + +/** Scan all the channels in specified band */ +#define BAND_SPECIFIED 0x80 + +/** + * IOCTL SSID List sub-structure sent in wlan_ioctl_user_scan_cfg + * + * Used to specify SSID specific filters as well as SSID pattern matching + * filters for scan result processing in firmware. + */ +typedef MLAN_PACK_START struct _wlan_user_scan_ssid { + /** SSID */ + t_u8 ssid[MLAN_MAX_SSID_LENGTH + 1]; + /** Maximum length of SSID */ + t_u8 max_len; +} MLAN_PACK_END wlan_user_scan_ssid; + +/** + * @brief IOCTL channel sub-structure sent in wlan_ioctl_user_scan_cfg + * + * Multiple instances of this structure are included in the IOCTL command + * to configure a instance of a scan on the specific channel. + */ +typedef MLAN_PACK_START struct _wlan_user_scan_chan { + /** Channel Number to scan */ + t_u8 chan_number; + /** Radio type: 'B/G' Band = 0, 'A' Band = 1 */ + t_u8 radio_type; + /** Scan type: Active = 1, Passive = 2 */ + t_u8 scan_type; + /** Reserved */ + t_u8 reserved; + /** Scan duration in milliseconds; if 0 default used */ + t_u32 scan_time; +} MLAN_PACK_END wlan_user_scan_chan; + +/** channel statictics */ +typedef MLAN_PACK_START struct _ChanStatistics_t { + /** channle number */ + t_u8 chan_num; + /** band info */ + Band_Config_t bandcfg; + /** flags */ + t_u8 flags; + /** noise */ + t_s8 noise; + /** total network */ + t_u16 total_networks; + /** scan duration */ + t_u16 cca_scan_duration; + /** busy duration */ + t_u16 cca_busy_duration; +} MLAN_PACK_END ChanStatistics_t; + +/** Enhance ext scan type defination */ +typedef enum _MLAN_EXT_SCAN_TYPE { + EXT_SCAN_DEFAULT, + EXT_SCAN_ENHANCE, + EXT_SCAN_CANCEL, +} MLAN_EXT_SCAN_TYPE; + +/** + * Input structure to configure an immediate scan cmd to firmware + * + * Specifies a number of parameters to be used in general for the scan + * as well as a channel list (wlan_user_scan_chan) for each scan period + * desired. + */ +typedef MLAN_PACK_START struct { + /** + * Flag set to keep the previous scan table intact + * + * If set, the scan results will accumulate, replacing any previous + * matched entries for a BSS with the new scan data + */ + t_u8 keep_previous_scan; + /** + * BSS mode to be sent in the firmware command + * + * Field can be used to restrict the types of networks returned in the + * scan. Valid settings are: + * + * - MLAN_SCAN_MODE_BSS (infrastructure) + * - MLAN_SCAN_MODE_IBSS (adhoc) + * - MLAN_SCAN_MODE_ANY (unrestricted, adhoc and infrastructure) + */ + t_u8 bss_mode; + /** + * Configure the number of probe requests for active chan scans + */ + t_u8 num_probes; + /** + * @brief ssid filter flag + */ + t_u8 ssid_filter; + /** + * @brief BSSID filter sent in the firmware command to limit the results + */ + t_u8 specific_bssid[MLAN_MAC_ADDR_LENGTH]; + /** + * SSID filter list used in the to limit the scan results + */ + wlan_user_scan_ssid ssid_list[MRVDRV_MAX_SSID_LIST_LENGTH]; + /** + * Variable number (fixed maximum) of channels to scan up + */ + wlan_user_scan_chan chan_list[WLAN_USER_SCAN_CHAN_MAX]; + /** scan channel gap */ + t_u16 scan_chan_gap; + /** scan type: 0 legacy, 1: enhance scan*/ + t_u8 ext_scan_type; + /** flag to filer only probe response */ + t_u8 proberesp_only; +} MLAN_PACK_END wlan_user_scan_cfg; + +/** Default scan interval in millisecond*/ +#define DEFAULT_BGSCAN_INTERVAL 30000 + +/** action get all, except pps/uapsd config */ +#define BG_SCAN_ACT_GET 0x0000 +/** action set all, except pps/uapsd config */ +#define BG_SCAN_ACT_SET 0x0001 +/** action get pps/uapsd config */ +#define BG_SCAN_ACT_GET_PPS_UAPSD 0x0100 +/** action set pps/uapsd config */ +#define BG_SCAN_ACT_SET_PPS_UAPSD 0x0101 +/** action set all */ +#define BG_SCAN_ACT_SET_ALL 0xff01 +/** ssid match */ +#define BG_SCAN_SSID_MATCH 0x0001 +/** ssid match and RSSI exceeded */ +#define BG_SCAN_SSID_RSSI_MATCH 0x0004 +/**wait for all channel scan to complete to report scan result*/ +#define BG_SCAN_WAIT_ALL_CHAN_DONE 0x80000000 +/** Maximum number of channels that can be sent in bg scan config */ +#define WLAN_BG_SCAN_CHAN_MAX 38 + +/** + * Input structure to configure bs scan cmd to firmware + */ +typedef MLAN_PACK_START struct { + /** action */ + t_u16 action; + /** enable/disable */ + t_u8 enable; + /** BSS type: + * MLAN_SCAN_MODE_BSS (infrastructure) + * MLAN_SCAN_MODE_IBSS (adhoc) + * MLAN_SCAN_MODE_ANY (unrestricted, adhoc and infrastructure) + */ + t_u8 bss_type; + /** number of channel scanned during each scan */ + t_u8 chan_per_scan; + /** interval between consecutive scan */ + t_u32 scan_interval; + /** bit 0: ssid match bit 1: ssid match and SNR exceeded + * bit 2: ssid match and RSSI exceeded + * bit 31: wait for all channel scan to complete to report scan result + */ + t_u32 report_condition; + /* Configure the number of probe requests for active chan scans */ + t_u8 num_probes; + /** RSSI threshold */ + t_u8 rssi_threshold; + /** SNR threshold */ + t_u8 snr_threshold; + /** repeat count */ + t_u16 repeat_count; + /** start later flag */ + t_u16 start_later; + /** SSID filter list used in the to limit the scan results */ + wlan_user_scan_ssid ssid_list[MRVDRV_MAX_SSID_LIST_LENGTH]; + /** Variable number (fixed maximum) of channels to scan up */ + wlan_user_scan_chan chan_list[WLAN_BG_SCAN_CHAN_MAX]; + /** scan channel gap */ + t_u16 scan_chan_gap; +} MLAN_PACK_END wlan_bgscan_cfg; +#endif /* STA_SUPPORT */ + +#ifdef PRAGMA_PACK +#pragma pack(pop) +#endif + +/** BSSDescriptor_t + * Structure used to store information for beacon/probe response + */ +typedef struct _BSSDescriptor_t { + /** MAC address */ + mlan_802_11_mac_addr mac_address; + + /** SSID */ + mlan_802_11_ssid ssid; + + /** WEP encryption requirement */ + t_u32 privacy; + + /** Receive signal strength in dBm */ + t_s32 rssi; + + /** Channel */ + t_u32 channel; + + /** Freq */ + t_u32 freq; + + /** Beacon period */ + t_u16 beacon_period; + + /** ATIM window */ + t_u32 atim_window; + + /** ERP flags */ + t_u8 erp_flags; + + /** Type of network in use */ + WLAN_802_11_NETWORK_TYPE network_type_use; + + /** Network infrastructure mode */ + t_u32 bss_mode; + + /** Network supported rates */ + WLAN_802_11_RATES supported_rates; + + /** Supported data rates */ + t_u8 data_rates[WLAN_SUPPORTED_RATES]; + + /** Current channel bandwidth + * 0 : 20MHZ + * 1 : 40MHZ + * 2 : 80MHZ + * 3 : 160MHZ + */ + t_u8 curr_bandwidth; + + /** Network band. + * BAND_B(0x01): 'b' band + * BAND_G(0x02): 'g' band + * BAND_A(0X04): 'a' band + */ + t_u16 bss_band; + + /** TSF timestamp from the current firmware TSF */ + t_u64 network_tsf; + + /** TSF value included in the beacon/probe response */ + t_u8 time_stamp[8]; + + /** PHY parameter set */ + IEEEtypes_PhyParamSet_t phy_param_set; + + /** SS parameter set */ + IEEEtypes_SsParamSet_t ss_param_set; + + /** Capability information */ + IEEEtypes_CapInfo_t cap_info; + + /** WMM IE */ + IEEEtypes_WmmParameter_t wmm_ie; + + /** 802.11h BSS information */ + wlan_11h_bss_info_t wlan_11h_bss_info; + + /** Indicate disabling 11n when associate with AP */ + t_u8 disable_11n; + /** 802.11n BSS information */ + /** HT Capabilities IE */ + IEEEtypes_HTCap_t *pht_cap; + /** HT Capabilities Offset */ + t_u16 ht_cap_offset; + /** HT Information IE */ + IEEEtypes_HTInfo_t *pht_info; + /** HT Information Offset */ + t_u16 ht_info_offset; + /** 20/40 BSS Coexistence IE */ + IEEEtypes_2040BSSCo_t *pbss_co_2040; + /** 20/40 BSS Coexistence Offset */ + t_u16 bss_co_2040_offset; + /** Extended Capabilities IE */ + IEEEtypes_ExtCap_t *pext_cap; + /** Extended Capabilities Offset */ + t_u16 ext_cap_offset; + /** Overlapping BSS Scan Parameters IE */ + IEEEtypes_OverlapBSSScanParam_t *poverlap_bss_scan_param; + /** Overlapping BSS Scan Parameters Offset */ + t_u16 overlap_bss_offset; + +#ifdef STA_SUPPORT + /** Country information set */ + IEEEtypes_CountryInfoFullSet_t country_info; +#endif /* STA_SUPPORT */ + + /** WPA IE */ + IEEEtypes_VendorSpecific_t *pwpa_ie; + /** WPA IE offset in the beacon buffer */ + t_u16 wpa_offset; + /** RSN IE */ + IEEEtypes_Generic_t *prsn_ie; + /** RSN IE offset in the beacon buffer */ + t_u16 rsn_offset; +#ifdef STA_SUPPORT + /** WAPI IE */ + IEEEtypes_Generic_t *pwapi_ie; + /** WAPI IE offset in the beacon buffer */ + t_u16 wapi_offset; +#endif + /* Mobility domain IE */ + IEEEtypes_MobilityDomain_t *pmd_ie; + /** Mobility domain IE offset in the beacon buffer */ + t_u16 md_offset; + + /** Pointer to the returned scan response */ + t_u8 *pbeacon_buf; + /** Length of the stored scan response */ + t_u32 beacon_buf_size; + /** Max allocated size for updated scan response */ + t_u32 beacon_buf_size_max; + +} BSSDescriptor_t, *pBSSDescriptor_t; + +#endif /* !_MLAN_IEEE_H_ */ diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_init.c b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_init.c new file mode 100644 index 000000000000..27c79ac2a6ee --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_init.c @@ -0,0 +1,1630 @@ +/** @file mlan_init.c + * + * @brief This file contains the initialization for FW + * and HW. + * + * Copyright (C) 2008-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/******************************************************** +Change log: + 10/13/2008: initial version +********************************************************/ + +#include "mlan.h" +#ifdef STA_SUPPORT +#include "mlan_join.h" +#endif +#include "mlan_util.h" +#include "mlan_fw.h" +#include "mlan_main.h" +#include "mlan_init.h" +#include "mlan_wmm.h" +#include "mlan_11n.h" +#include "mlan_11h.h" +#include "mlan_meas.h" +#include "mlan_sdio.h" +#if defined(DRV_EMBEDDED_AUTHENTICATOR) || defined(DRV_EMBEDDED_SUPPLICANT) +#include "hostsa_init.h" +#endif + +/******************************************************** + Global Variables +********************************************************/ +extern mlan_operations *mlan_ops[]; +/******************************************************* + Local Functions +********************************************************/ + +/** + * @brief This function adds a BSS priority table + * + * @param priv A pointer to mlan_private structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status +wlan_add_bsspriotbl(pmlan_private priv) +{ + pmlan_adapter pmadapter = priv->adapter; + mlan_bssprio_node *pbssprio = MNULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + status = pmadapter->callbacks.moal_malloc(pmadapter->pmoal_handle, + sizeof(mlan_bssprio_node), + MLAN_MEM_DEF, + (t_u8 **)&pbssprio); + if (status) { + PRINTM(MERROR, "Failed to allocate bsspriotbl\n"); + LEAVE(); + return status; + } + + pbssprio->priv = priv; + + util_init_list((pmlan_linked_list)pbssprio); + + if (!pmadapter->bssprio_tbl[priv->bss_priority].bssprio_cur) + pmadapter->bssprio_tbl[priv->bss_priority].bssprio_cur + = pbssprio; + + util_enqueue_list_tail(pmadapter->pmoal_handle, + &pmadapter->bssprio_tbl[priv->bss_priority]. + bssprio_head, (pmlan_linked_list)pbssprio, + pmadapter->callbacks.moal_spin_lock, + pmadapter->callbacks.moal_spin_unlock); + + LEAVE(); + return status; +} + +/** + * @brief This function deletes the BSS priority table + * + * @param priv A pointer to mlan_private structure + * + * @return N/A + */ +static t_void +wlan_delete_bsspriotbl(pmlan_private priv) +{ + int i; + pmlan_adapter pmadapter = priv->adapter; + mlan_bssprio_node *pbssprio_node = MNULL, *ptmp_node = MNULL, **ppcur = + MNULL; + pmlan_list_head phead; + + ENTER(); + + for (i = 0; i < pmadapter->priv_num; ++i) { + phead = &pmadapter->bssprio_tbl[i].bssprio_head; + ppcur = &pmadapter->bssprio_tbl[i].bssprio_cur; + PRINTM(MINFO, + "Delete BSS priority table, index = %d, i = %d, phead = %p, pcur = %p\n", + priv->bss_index, i, phead, *ppcur); + if (*ppcur) { + pbssprio_node = + (mlan_bssprio_node *)util_peek_list(pmadapter-> + pmoal_handle, + phead, + pmadapter-> + callbacks. + moal_spin_lock, + pmadapter-> + callbacks. + moal_spin_unlock); + while (pbssprio_node && + ((pmlan_list_head)pbssprio_node != phead)) { + ptmp_node = pbssprio_node->pnext; + if (pbssprio_node->priv == priv) { + PRINTM(MINFO, + "Delete node, pnode = %p, pnext = %p\n", + pbssprio_node, ptmp_node); + util_unlink_list(pmadapter-> + pmoal_handle, phead, + (pmlan_linked_list) + pbssprio_node, + pmadapter->callbacks. + moal_spin_lock, + pmadapter->callbacks. + moal_spin_unlock); + pmadapter->callbacks. + moal_mfree(pmadapter-> + pmoal_handle, + (t_u8 *) + pbssprio_node); + } + pbssprio_node = ptmp_node; + } + *ppcur = (mlan_bssprio_node *)phead; + } + } + + LEAVE(); +} + +/******************************************************** + Global Functions +********************************************************/ + +/** + * @brief This function allocates buffer for the members of adapter + * structure like command buffer and BSSID list. + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_allocate_adapter(pmlan_adapter pmadapter) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; +#ifdef STA_SUPPORT + t_u32 beacon_buffer_size; + t_u32 buf_size; + BSSDescriptor_t *ptemp_scan_table = MNULL; + t_u8 chan_2g[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 }; + t_u8 chan_5g[] = { + 12, 16, 34, 38, 42, 46, 36, 40, 44, + 48, 52, 56, 60, 64, 100, 104, 108, + 112, 116, 120, 124, 128, 132, 136, + 140, 144, 149, 153, 157, 161, 165 + }; +#endif + t_u32 max_mp_regs = MAX_MP_REGS; +#if defined(SDIO_MULTI_PORT_TX_AGGR) || defined(SDIO_MULTI_PORT_RX_AGGR) + t_u32 mp_tx_aggr_buf_size = SDIO_MP_TX_AGGR_DEF_BUF_SIZE; + t_u32 mp_rx_aggr_buf_size = SDIO_MP_RX_AGGR_DEF_BUF_SIZE; +#endif + + ENTER(); + +#ifdef STA_SUPPORT + /* Allocate buffer to store the BSSID list */ + buf_size = sizeof(BSSDescriptor_t) * MRVDRV_MAX_BSSID_LIST; + if (pmadapter->callbacks.moal_vmalloc && + pmadapter->callbacks.moal_vfree) + ret = pmadapter->callbacks.moal_vmalloc(pmadapter->pmoal_handle, + buf_size, + (t_u8 **) + &ptemp_scan_table); + else + ret = pmadapter->callbacks.moal_malloc(pmadapter->pmoal_handle, + buf_size, MLAN_MEM_DEF, + (t_u8 **) + &ptemp_scan_table); + if (ret != MLAN_STATUS_SUCCESS || !ptemp_scan_table) { + PRINTM(MERROR, "Failed to allocate scan table\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + pmadapter->pscan_table = ptemp_scan_table; + + if (pmadapter->fixed_beacon_buffer) + beacon_buffer_size = MAX_SCAN_BEACON_BUFFER; + else + beacon_buffer_size = DEFAULT_SCAN_BEACON_BUFFER; + ret = pmadapter->callbacks.moal_malloc(pmadapter->pmoal_handle, + beacon_buffer_size, MLAN_MEM_DEF, + (t_u8 **)&pmadapter->bcn_buf); + if (ret != MLAN_STATUS_SUCCESS || !pmadapter->bcn_buf) { + PRINTM(MERROR, "Failed to allocate bcn buf\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + pmadapter->bcn_buf_size = beacon_buffer_size; + + pmadapter->num_in_chan_stats = sizeof(chan_2g); + pmadapter->num_in_chan_stats += sizeof(chan_5g); + buf_size = sizeof(ChanStatistics_t) * pmadapter->num_in_chan_stats; + if (pmadapter->callbacks.moal_vmalloc && + pmadapter->callbacks.moal_vfree) + ret = pmadapter->callbacks.moal_vmalloc(pmadapter->pmoal_handle, + buf_size, + (t_u8 **)&pmadapter-> + pchan_stats); + else + ret = pmadapter->callbacks.moal_malloc(pmadapter->pmoal_handle, + buf_size, MLAN_MEM_DEF, + (t_u8 **)&pmadapter-> + pchan_stats); + if (ret != MLAN_STATUS_SUCCESS || !pmadapter->pchan_stats) { + PRINTM(MERROR, "Failed to allocate channel statistics\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } +#endif + + /* Allocate command buffer */ + ret = wlan_alloc_cmd_buffer(pmadapter); + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "Failed to allocate command buffer\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + ret = pmadapter->callbacks.moal_malloc(pmadapter->pmoal_handle, + max_mp_regs + DMA_ALIGNMENT, + MLAN_MEM_DEF | MLAN_MEM_DMA, + (t_u8 **)&pmadapter-> + mp_regs_buf); + if (ret != MLAN_STATUS_SUCCESS || !pmadapter->mp_regs_buf) { + PRINTM(MERROR, "Failed to allocate mp_regs_buf\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + pmadapter->mp_regs = + (t_u8 *)ALIGN_ADDR(pmadapter->mp_regs_buf, DMA_ALIGNMENT); + +#if defined(SDIO_MULTI_PORT_RX_AGGR) + ret = pmadapter->callbacks.moal_malloc(pmadapter->pmoal_handle, + SDIO_CMD53_MAX_SIZE + + DMA_ALIGNMENT, + MLAN_MEM_DEF | MLAN_MEM_DMA, + (t_u8 **)&pmadapter->rx_buffer); + if (ret != MLAN_STATUS_SUCCESS || !pmadapter->rx_buffer) { + PRINTM(MERROR, "Failed to allocate receive buffer\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + pmadapter->rx_buf = + (t_u8 *)ALIGN_ADDR(pmadapter->rx_buffer, DMA_ALIGNMENT); +#endif + +#if defined(SDIO_MULTI_PORT_TX_AGGR) || defined(SDIO_MULTI_PORT_RX_AGGR) + pmadapter->max_sp_tx_size = MAX_SUPPORT_AMSDU_SIZE; + pmadapter->max_sp_rx_size = MAX_SUPPORT_AMSDU_SIZE; + ret = wlan_alloc_sdio_mpa_buffers(pmadapter, mp_tx_aggr_buf_size, + mp_rx_aggr_buf_size); + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "Failed to allocate sdio mp-a buffers\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } +#ifdef DEBUG_LEVEL1 + if (mlan_drvdbg & MMPA_D) { + pmadapter->mpa_buf_size = + SDIO_MP_DBG_NUM * SDIO_MP_AGGR_DEF_PKT_LIMIT * + MLAN_SDIO_BLOCK_SIZE; + if (pmadapter->callbacks.moal_vmalloc && + pmadapter->callbacks.moal_vfree) + ret = pmadapter->callbacks.moal_vmalloc(pmadapter-> + pmoal_handle, + pmadapter-> + mpa_buf_size, + (t_u8 **) + &pmadapter-> + mpa_buf); + else + ret = pmadapter->callbacks.moal_malloc(pmadapter-> + pmoal_handle, + pmadapter-> + mpa_buf_size, + MLAN_MEM_DEF, + (t_u8 **) + &pmadapter-> + mpa_buf); + if (ret != MLAN_STATUS_SUCCESS || !pmadapter->mpa_buf) { + PRINTM(MERROR, "Failed to allocate mpa buf\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + } +#endif +#endif + + pmadapter->psleep_cfm = + wlan_alloc_mlan_buffer(pmadapter, + sizeof(opt_sleep_confirm_buffer), 0, + MOAL_MALLOC_BUFFER); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function initializes the private structure + * and sets default values to the members of mlan_private. + * + * @param priv A pointer to mlan_private structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_init_priv(pmlan_private priv) +{ + t_u32 i; + pmlan_adapter pmadapter = priv->adapter; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + priv->media_connected = MFALSE; + memset(pmadapter, priv->curr_addr, 0xff, MLAN_MAC_ADDR_LENGTH); + +#ifdef STA_SUPPORT + priv->pkt_tx_ctrl = 0; + priv->bss_mode = MLAN_BSS_MODE_INFRA; + priv->data_rate = 0; /* Initially indicate the rate as auto */ + priv->is_data_rate_auto = MTRUE; + priv->bcn_avg_factor = DEFAULT_BCN_AVG_FACTOR; + priv->data_avg_factor = DEFAULT_DATA_AVG_FACTOR; + + priv->sec_info.wep_status = Wlan802_11WEPDisabled; + priv->sec_info.authentication_mode = MLAN_AUTH_MODE_AUTO; + priv->sec_info.encryption_mode = MLAN_ENCRYPTION_MODE_NONE; + for (i = 0; i < MRVL_NUM_WEP_KEY; i++) + memset(pmadapter, &priv->wep_key[i], 0, sizeof(mrvl_wep_key_t)); + priv->wep_key_curr_index = 0; + priv->ewpa_query = MFALSE; + priv->adhoc_aes_enabled = MFALSE; + priv->curr_pkt_filter = + HostCmd_ACT_MAC_RTS_CTS_ENABLE | + HostCmd_ACT_MAC_RX_ON | HostCmd_ACT_MAC_TX_ON | + HostCmd_ACT_MAC_ETHERNETII_ENABLE; + + priv->beacon_period = MLAN_BEACON_INTERVAL; + priv->pattempted_bss_desc = MNULL; + memset(pmadapter, &priv->gtk_rekey, 0, + sizeof(mlan_ds_misc_gtk_rekey_data)); + memset(pmadapter, &priv->curr_bss_params, 0, + sizeof(priv->curr_bss_params)); + priv->listen_interval = MLAN_DEFAULT_LISTEN_INTERVAL; + + memset(pmadapter, &priv->assoc_rsp_buf, 0, sizeof(priv->assoc_rsp_buf)); + priv->assoc_rsp_size = 0; + + wlan_11d_priv_init(priv); + wlan_11h_priv_init(priv); + priv->enable_11k = MFALSE; + +#ifdef UAP_SUPPORT + priv->uap_bss_started = MFALSE; + priv->uap_host_based = MFALSE; + memset(pmadapter, &priv->uap_state_chan_cb, 0, + sizeof(priv->uap_state_chan_cb)); +#endif +#if defined(UAP_SUPPORT) + priv->num_drop_pkts = 0; +#endif +#if defined(STA_SUPPORT) + priv->adhoc_state_prev = ADHOC_IDLE; + memset(pmadapter, &priv->adhoc_last_start_ssid, 0, + sizeof(priv->adhoc_last_start_ssid)); +#endif + priv->adhoc_channel = DEFAULT_AD_HOC_CHANNEL; + priv->atim_window = 0; + priv->adhoc_state = ADHOC_IDLE; + priv->tx_power_level = 0; + priv->max_tx_power_level = 0; + priv->min_tx_power_level = 0; + priv->tx_rate = 0; + priv->rxpd_rate_info = 0; + priv->rx_pkt_info = MFALSE; + priv->rxpd_rate = 0; + priv->rate_bitmap = 0; + priv->data_rssi_last = 0; + priv->data_rssi_avg = 0; + priv->data_nf_avg = 0; + priv->data_nf_last = 0; + priv->bcn_rssi_last = 0; + priv->bcn_rssi_avg = 0; + priv->bcn_nf_avg = 0; + priv->bcn_nf_last = 0; + + priv->sec_info.ewpa_enabled = MFALSE; + priv->sec_info.wpa_enabled = MFALSE; + priv->sec_info.wpa2_enabled = MFALSE; + memset(pmadapter, &priv->wpa_ie, 0, sizeof(priv->wpa_ie)); + memset(pmadapter, &priv->aes_key, 0, sizeof(priv->aes_key)); + priv->wpa_ie_len = 0; + priv->wpa_is_gtk_set = MFALSE; +#if defined(STA_SUPPORT) + priv->pmfcfg.mfpc = 0; + priv->pmfcfg.mfpr = 0; +#endif + priv->sec_info.wapi_enabled = MFALSE; + priv->wapi_ie_len = 0; + priv->sec_info.wapi_key_on = MFALSE; + + memset(pmadapter, &priv->wps, 0, sizeof(priv->wps)); + memset(pmadapter, &priv->gen_ie_buf, 0, sizeof(priv->gen_ie_buf)); + priv->gen_ie_buf_len = 0; +#endif /* STA_SUPPORT */ + priv->wmm_required = MTRUE; + priv->wmm_enabled = MFALSE; + priv->wmm_qosinfo = 0; + priv->saved_wmm_qosinfo = 0; + priv->host_tdls_cs_support = MTRUE; + priv->host_tdls_uapsd_support = MTRUE; + priv->tdls_cs_channel = 0; + priv->supp_regulatory_class_len = 0; + priv->chan_supp_len = 0; + priv->tdls_idle_time = TDLS_IDLE_TIMEOUT; + priv->txaggrctrl = MTRUE; +#ifdef STA_SUPPORT + priv->pcurr_bcn_buf = MNULL; + priv->curr_bcn_size = 0; + memset(pmadapter, &priv->ext_cap, 0, sizeof(priv->ext_cap)); + + SET_EXTCAP_TDLS(priv->ext_cap); + SET_EXTCAP_QOS_MAP(priv->ext_cap); + /* Save default Extended Capability */ + memcpy(priv->adapter, &priv->def_ext_cap, &priv->ext_cap, + sizeof(priv->ext_cap)); +#endif /* STA_SUPPORT */ + + for (i = 0; i < MAX_NUM_TID; i++) + priv->addba_reject[i] = ADDBA_RSP_STATUS_ACCEPT; + priv->addba_reject[6] = ADDBA_RSP_STATUS_REJECT; + priv->addba_reject[7] = ADDBA_RSP_STATUS_REJECT; + memcpy(priv->adapter, priv->ibss_addba_reject, priv->addba_reject, + sizeof(priv->addba_reject)); + priv->max_amsdu = 0; +#ifdef STA_SUPPORT + if (priv->bss_type == MLAN_BSS_TYPE_STA) { + priv->add_ba_param.tx_win_size = MLAN_STA_AMPDU_DEF_TXWINSIZE; + priv->add_ba_param.rx_win_size = MLAN_STA_AMPDU_DEF_RXWINSIZE; + } +#endif +#ifdef WIFI_DIRECT_SUPPORT + if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT) { + priv->add_ba_param.tx_win_size = MLAN_WFD_AMPDU_DEF_TXRXWINSIZE; + priv->add_ba_param.rx_win_size = MLAN_WFD_AMPDU_DEF_TXRXWINSIZE; + } +#endif +#ifdef UAP_SUPPORT + if (priv->bss_type == MLAN_BSS_TYPE_UAP) { + priv->add_ba_param.tx_win_size = MLAN_UAP_AMPDU_DEF_TXWINSIZE; + priv->add_ba_param.rx_win_size = MLAN_UAP_AMPDU_DEF_RXWINSIZE; + priv->aggr_prio_tbl[6].ampdu_user = + priv->aggr_prio_tbl[7].ampdu_user = + BA_STREAM_NOT_ALLOWED; + } +#endif + priv->user_rxwinsize = priv->add_ba_param.rx_win_size; + + priv->port_ctrl_mode = MTRUE; + + priv->port_open = MFALSE; + + priv->intf_hr_len = INTF_HEADER_LEN; + ret = wlan_add_bsspriotbl(priv); +#if defined(DRV_EMBEDDED_AUTHENTICATOR) || defined(DRV_EMBEDDED_SUPPLICANT) + hostsa_init(priv); +#endif + + LEAVE(); + return ret; +} + +/** + * @brief This function initializes the adapter structure + * and sets default values to the members of adapter. + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return N/A + */ +t_void +wlan_init_adapter(pmlan_adapter pmadapter) +{ + opt_sleep_confirm_buffer *sleep_cfm_buf = MNULL; + + ENTER(); + + if (pmadapter->psleep_cfm) { + sleep_cfm_buf = + (opt_sleep_confirm_buffer *)(pmadapter->psleep_cfm-> + pbuf + + pmadapter->psleep_cfm-> + data_offset); + } +#ifdef MFG_CMD_SUPPORT + if (pmadapter->init_para.mfg_mode == MLAN_INIT_PARA_DISABLED) + pmadapter->mfg_mode = MFALSE; + else + pmadapter->mfg_mode = pmadapter->init_para.mfg_mode; +#endif + + pmadapter->int_mode = pmadapter->init_para.int_mode; + pmadapter->gpio_pin = pmadapter->init_para.gpio_pin; + +#if defined(STA_SUPPORT) + pmadapter->pwarm_reset_ioctl_req = MNULL; +#endif + pmadapter->cmd_sent = MFALSE; + pmadapter->data_sent = MTRUE; + pmadapter->mp_rd_bitmap = 0; + pmadapter->mp_wr_bitmap = 0; + pmadapter->curr_rd_port = 0; + pmadapter->curr_wr_port = 0; + pmadapter->mp_data_port_mask = DATA_PORT_MASK; + pmadapter->mp_invalid_update = 0; + memset(pmadapter, pmadapter->mp_update, 0, + sizeof(pmadapter->mp_update)); +#ifdef SDIO_MULTI_PORT_TX_AGGR + pmadapter->mpa_tx.buf_len = 0; + pmadapter->mpa_tx.pkt_cnt = 0; + pmadapter->mpa_tx.start_port = 0; + + if (!pmadapter->init_para.mpa_tx_cfg) + pmadapter->mpa_tx.enabled = MFALSE; + else if (pmadapter->init_para.mpa_tx_cfg == MLAN_INIT_PARA_DISABLED) + pmadapter->mpa_tx.enabled = MFALSE; + else + pmadapter->mpa_tx.enabled = MTRUE; + pmadapter->mpa_tx.pkt_aggr_limit = SDIO_MP_AGGR_DEF_PKT_LIMIT; +#endif /* SDIO_MULTI_PORT_TX_AGGR */ + +#ifdef SDIO_MULTI_PORT_RX_AGGR + pmadapter->mpa_rx.buf_len = 0; + pmadapter->mpa_rx.pkt_cnt = 0; + pmadapter->mpa_rx.start_port = 0; + + if (!pmadapter->init_para.mpa_rx_cfg) + pmadapter->mpa_rx.enabled = MFALSE; + else if (pmadapter->init_para.mpa_rx_cfg == MLAN_INIT_PARA_DISABLED) + pmadapter->mpa_rx.enabled = MFALSE; + else + pmadapter->mpa_rx.enabled = MTRUE; + pmadapter->mpa_rx.pkt_aggr_limit = SDIO_MP_AGGR_DEF_PKT_LIMIT; + +#endif /* SDIO_MULTI_PORT_RX_AGGR */ + + pmadapter->rx_pkts_queued = 0; + pmadapter->cmd_resp_received = MFALSE; + pmadapter->event_received = MFALSE; + pmadapter->data_received = MFALSE; + + pmadapter->cmd_timer_is_set = MFALSE; + + /* PnP and power profile */ + pmadapter->surprise_removed = MFALSE; + + if (!pmadapter->init_para.ps_mode) { + pmadapter->ps_mode = DEFAULT_PS_MODE; + } else if (pmadapter->init_para.ps_mode == MLAN_INIT_PARA_DISABLED) + pmadapter->ps_mode = Wlan802_11PowerModeCAM; + else + pmadapter->ps_mode = Wlan802_11PowerModePSP; + pmadapter->ps_state = PS_STATE_AWAKE; + pmadapter->need_to_wakeup = MFALSE; + +#ifdef STA_SUPPORT + pmadapter->scan_block = MFALSE; + /* Scan type */ + pmadapter->scan_type = MLAN_SCAN_TYPE_ACTIVE; + /* Scan mode */ + pmadapter->scan_mode = HostCmd_BSS_MODE_ANY; + /* Scan time */ + pmadapter->specific_scan_time = MRVDRV_SPECIFIC_SCAN_CHAN_TIME; + pmadapter->active_scan_time = MRVDRV_ACTIVE_SCAN_CHAN_TIME; + pmadapter->passive_scan_time = MRVDRV_PASSIVE_SCAN_CHAN_TIME; + + pmadapter->num_in_scan_table = 0; + memset(pmadapter, pmadapter->pscan_table, 0, + (sizeof(BSSDescriptor_t) * MRVDRV_MAX_BSSID_LIST)); + pmadapter->active_scan_triggered = MFALSE; + pmadapter->ext_scan = MTRUE; + pmadapter->scan_probes = DEFAULT_PROBES; + + memset(pmadapter, pmadapter->bcn_buf, 0, pmadapter->bcn_buf_size); + pmadapter->pbcn_buf_end = pmadapter->bcn_buf; + + pmadapter->radio_on = RADIO_ON; + if (!pmadapter->multiple_dtim) + pmadapter->multiple_dtim = MRVDRV_DEFAULT_MULTIPLE_DTIM; + + pmadapter->local_listen_interval = 0; /* default value in firmware will be used */ +#endif /* STA_SUPPORT */ + pmadapter->fw_wakeup_method = WAKEUP_FW_UNCHANGED; + pmadapter->fw_wakeup_gpio_pin = DEF_WAKEUP_FW_GPIO; + + pmadapter->is_deep_sleep = MFALSE; + pmadapter->idle_time = DEEP_SLEEP_IDLE_TIME; + if (!pmadapter->init_para.auto_ds) + pmadapter->init_auto_ds = DEFAULT_AUTO_DS_MODE; + else if (pmadapter->init_para.auto_ds == MLAN_INIT_PARA_DISABLED) + pmadapter->init_auto_ds = MFALSE; + else + pmadapter->init_auto_ds = MTRUE; + + pmadapter->delay_null_pkt = MFALSE; + pmadapter->delay_to_ps = DELAY_TO_PS_DEFAULT; + pmadapter->enhanced_ps_mode = PS_MODE_AUTO; + + pmadapter->gen_null_pkt = MFALSE; /* Disable NULL Pkt generation-default */ + pmadapter->pps_uapsd_mode = MFALSE; /* Disable pps/uapsd mode -default */ + + pmadapter->pm_wakeup_card_req = MFALSE; + + pmadapter->pm_wakeup_fw_try = MFALSE; + + if (!pmadapter->init_para.max_tx_buf) + pmadapter->max_tx_buf_size = MLAN_TX_DATA_BUF_SIZE_2K; + else + pmadapter->max_tx_buf_size = + (t_u16)pmadapter->init_para.max_tx_buf; + pmadapter->tx_buf_size = MLAN_TX_DATA_BUF_SIZE_2K; + pmadapter->curr_tx_buf_size = MLAN_TX_DATA_BUF_SIZE_2K; + + pmadapter->is_hs_configured = MFALSE; + pmadapter->hs_cfg.conditions = HOST_SLEEP_DEF_COND; + pmadapter->hs_cfg.gpio = HOST_SLEEP_DEF_GPIO; + pmadapter->hs_cfg.gap = HOST_SLEEP_DEF_GAP; + pmadapter->hs_activated = MFALSE; + pmadapter->min_wake_holdoff = HOST_SLEEP_DEF_WAKE_HOLDOFF; + pmadapter->hs_inactivity_timeout = HOST_SLEEP_DEF_INACTIVITY_TIMEOUT; + + memset(pmadapter, pmadapter->event_body, 0, + sizeof(pmadapter->event_body)); + pmadapter->hw_dot_11n_dev_cap = 0; + pmadapter->hw_dev_mcs_support = 0; + pmadapter->coex_rx_winsize = 1; +#ifdef STA_SUPPORT + pmadapter->chan_bandwidth = 0; + pmadapter->adhoc_11n_enabled = MFALSE; + pmadapter->tdls_status = TDLS_NOT_SETUP; +#endif /* STA_SUPPORT */ + + pmadapter->max_sta_conn = 0; + /* Initialize 802.11d */ + wlan_11d_init(pmadapter); + + wlan_11h_init(pmadapter); + + wlan_wmm_init(pmadapter); + wlan_init_wmm_param(pmadapter); + pmadapter->bypass_pkt_count = 0; + if (pmadapter->psleep_cfm) { + pmadapter->psleep_cfm->buf_type = MLAN_BUF_TYPE_CMD; + pmadapter->psleep_cfm->data_len = sizeof(OPT_Confirm_Sleep); + memset(pmadapter, &sleep_cfm_buf->ps_cfm_sleep, 0, + sizeof(OPT_Confirm_Sleep)); + sleep_cfm_buf->ps_cfm_sleep.command = + wlan_cpu_to_le16(HostCmd_CMD_802_11_PS_MODE_ENH); + sleep_cfm_buf->ps_cfm_sleep.size = + wlan_cpu_to_le16(sizeof(OPT_Confirm_Sleep)); + sleep_cfm_buf->ps_cfm_sleep.result = 0; + sleep_cfm_buf->ps_cfm_sleep.action = + wlan_cpu_to_le16(SLEEP_CONFIRM); + sleep_cfm_buf->ps_cfm_sleep.sleep_cfm.resp_ctrl = + wlan_cpu_to_le16(RESP_NEEDED); + } + memset(pmadapter, &pmadapter->sleep_params, 0, + sizeof(pmadapter->sleep_params)); + memset(pmadapter, &pmadapter->sleep_period, 0, + sizeof(pmadapter->sleep_period)); + memset(pmadapter, &pmadapter->saved_sleep_period, 0, + sizeof(pmadapter->saved_sleep_period)); + pmadapter->tx_lock_flag = MFALSE; + pmadapter->null_pkt_interval = 0; + pmadapter->fw_bands = 0; + pmadapter->config_bands = 0; + pmadapter->adhoc_start_band = 0; + pmadapter->pscan_channels = MNULL; + pmadapter->fw_release_number = 0; + pmadapter->fw_cap_info = 0; + memset(pmadapter, &pmadapter->upld_buf, 0, sizeof(pmadapter->upld_buf)); + pmadapter->upld_len = 0; + pmadapter->event_cause = 0; + pmadapter->pmlan_buffer_event = MNULL; + memset(pmadapter, &pmadapter->region_channel, 0, + sizeof(pmadapter->region_channel)); + pmadapter->region_code = 0; + memcpy(pmadapter, pmadapter->country_code, MRVDRV_DEFAULT_COUNTRY_CODE, + COUNTRY_CODE_LEN); + pmadapter->bcn_miss_time_out = DEFAULT_BCN_MISS_TIMEOUT; + pmadapter->adhoc_awake_period = 0; +#ifdef STA_SUPPORT + memset(pmadapter, &pmadapter->arp_filter, 0, + sizeof(pmadapter->arp_filter)); + pmadapter->arp_filter_size = 0; +#endif /* STA_SUPPORT */ + + pmadapter->mc_status = MFALSE; + + LEAVE(); + return; +} + +/** + * @brief This function intializes the lock variables and + * the list heads for interface + * + * @param pmadapter A pointer to a mlan_adapter structure + * @param start_index start index of mlan private + * + * @return MLAN_STATUS_SUCCESS -- on success, + * otherwise MLAN_STATUS_FAILURE + * + */ +mlan_status +wlan_init_priv_lock_list(IN pmlan_adapter pmadapter, t_u8 start_index) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_private priv = MNULL; + pmlan_callbacks pcb = &pmadapter->callbacks; + t_s32 i = 0; + t_u32 j = 0; + for (i = start_index; i < pmadapter->priv_num; i++) { + if (pmadapter->priv[i]) { + priv = pmadapter->priv[i]; + if (pcb-> + moal_init_lock(pmadapter->pmoal_handle, + &priv->rx_pkt_lock) + != MLAN_STATUS_SUCCESS) { + ret = MLAN_STATUS_FAILURE; + goto error; + } + if (pcb-> + moal_init_lock(pmadapter->pmoal_handle, + &priv->wmm.ra_list_spinlock) + != MLAN_STATUS_SUCCESS) { + ret = MLAN_STATUS_FAILURE; + goto error; + } +#ifdef STA_SUPPORT + if (pcb-> + moal_init_lock(pmadapter->pmoal_handle, + &priv->curr_bcn_buf_lock) + != MLAN_STATUS_SUCCESS) { + ret = MLAN_STATUS_FAILURE; + goto error; + } +#endif + } + } + for (i = start_index; i < pmadapter->priv_num; ++i) { + util_init_list_head((t_void *)pmadapter->pmoal_handle, + &pmadapter->bssprio_tbl[i].bssprio_head, + MTRUE, pmadapter->callbacks.moal_init_lock); + pmadapter->bssprio_tbl[i].bssprio_cur = MNULL; + } + + for (i = start_index; i < pmadapter->priv_num; i++) { + if (pmadapter->priv[i]) { + priv = pmadapter->priv[i]; + for (j = 0; j < MAX_NUM_TID; ++j) { + util_init_list_head((t_void *)pmadapter-> + pmoal_handle, + &priv->wmm.tid_tbl_ptr[j]. + ra_list, MTRUE, + priv->adapter->callbacks. + moal_init_lock); + } + util_init_list_head((t_void *)pmadapter->pmoal_handle, + &priv->tx_ba_stream_tbl_ptr, MTRUE, + pmadapter->callbacks. + moal_init_lock); + util_init_list_head((t_void *)pmadapter->pmoal_handle, + &priv->rx_reorder_tbl_ptr, MTRUE, + pmadapter->callbacks. + moal_init_lock); + util_scalar_init((t_void *)pmadapter->pmoal_handle, + &priv->wmm.tx_pkts_queued, 0, + priv->wmm.ra_list_spinlock, + pmadapter->callbacks.moal_init_lock); + util_scalar_init((t_void *)pmadapter->pmoal_handle, + &priv->wmm.highest_queued_prio, + HIGH_PRIO_TID, + priv->wmm.ra_list_spinlock, + pmadapter->callbacks.moal_init_lock); + util_init_list_head((t_void *)pmadapter->pmoal_handle, + &priv->sta_list, MTRUE, + pmadapter->callbacks. + moal_init_lock); + /* Initialize tdls_pending_txq */ + util_init_list_head((t_void *)pmadapter->pmoal_handle, + &priv->tdls_pending_txq, MTRUE, + pmadapter->callbacks. + moal_init_lock); + /* Initialize bypass_txq */ + util_init_list_head((t_void *)pmadapter->pmoal_handle, + &priv->bypass_txq, MTRUE, + pmadapter->callbacks. + moal_init_lock); + } + } +error: + LEAVE(); + return ret; +} + +/** + * @brief This function intializes the lock variables and + * the list heads. + * + * @param pmadapter A pointer to a mlan_adapter structure + * + * @return MLAN_STATUS_SUCCESS -- on success, + * otherwise MLAN_STATUS_FAILURE + * + */ +mlan_status +wlan_init_lock_list(IN pmlan_adapter pmadapter) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_callbacks pcb = &pmadapter->callbacks; + ENTER(); + + if (pcb->moal_init_lock(pmadapter->pmoal_handle, &pmadapter->pmlan_lock) + != MLAN_STATUS_SUCCESS) { + ret = MLAN_STATUS_FAILURE; + goto error; + } + if (pcb->moal_init_lock(pmadapter->pmoal_handle, &pmadapter->pint_lock) + != MLAN_STATUS_SUCCESS) { + ret = MLAN_STATUS_FAILURE; + goto error; + } + if (pcb-> + moal_init_lock(pmadapter->pmoal_handle, &pmadapter->pmain_proc_lock) + != MLAN_STATUS_SUCCESS) { + ret = MLAN_STATUS_FAILURE; + goto error; + } + if (pcb-> + moal_init_lock(pmadapter->pmoal_handle, &pmadapter->prx_proc_lock) + != MLAN_STATUS_SUCCESS) { + ret = MLAN_STATUS_FAILURE; + goto error; + } + if (pcb-> + moal_init_lock(pmadapter->pmoal_handle, &pmadapter->pmlan_cmd_lock) + != MLAN_STATUS_SUCCESS) { + ret = MLAN_STATUS_FAILURE; + goto error; + } + + util_init_list_head((t_void *)pmadapter->pmoal_handle, + &pmadapter->rx_data_queue, MTRUE, + pmadapter->callbacks.moal_init_lock); + util_scalar_init((t_void *)pmadapter->pmoal_handle, + &pmadapter->pending_bridge_pkts, 0, + MNULL, pmadapter->callbacks.moal_init_lock); + /* Initialize cmd_free_q */ + util_init_list_head((t_void *)pmadapter->pmoal_handle, + &pmadapter->cmd_free_q, MTRUE, + pmadapter->callbacks.moal_init_lock); + /* Initialize cmd_pending_q */ + util_init_list_head((t_void *)pmadapter->pmoal_handle, + &pmadapter->cmd_pending_q, MTRUE, + pmadapter->callbacks.moal_init_lock); + /* Initialize scan_pending_q */ + util_init_list_head((t_void *)pmadapter->pmoal_handle, + &pmadapter->scan_pending_q, MTRUE, + pmadapter->callbacks.moal_init_lock); + + /* Initialize ioctl_pending_q */ + util_init_list_head((t_void *)pmadapter->pmoal_handle, + &pmadapter->ioctl_pending_q, MTRUE, + pmadapter->callbacks.moal_init_lock); + +error: + LEAVE(); + return ret; +} + +/** + * @brief This function releases the lock variables + * + * @param pmadapter A pointer to a mlan_adapter structure + * + * @return None + * + */ +t_void +wlan_free_lock_list(IN pmlan_adapter pmadapter) +{ + pmlan_private priv = MNULL; + pmlan_callbacks pcb = &pmadapter->callbacks; + t_s32 i = 0; + t_s32 j = 0; + + ENTER(); + + if (pmadapter->pmlan_lock) + pcb->moal_free_lock(pmadapter->pmoal_handle, + pmadapter->pmlan_lock); + if (pmadapter->pint_lock) + pcb->moal_free_lock(pmadapter->pmoal_handle, + pmadapter->pint_lock); + if (pmadapter->prx_proc_lock) + pcb->moal_free_lock(pmadapter->pmoal_handle, + pmadapter->prx_proc_lock); + if (pmadapter->pmain_proc_lock) + pcb->moal_free_lock(pmadapter->pmoal_handle, + pmadapter->pmain_proc_lock); + if (pmadapter->pmlan_cmd_lock) + pcb->moal_free_lock(pmadapter->pmoal_handle, + pmadapter->pmlan_cmd_lock); + + for (i = 0; i < pmadapter->priv_num; i++) { + if (pmadapter->priv[i]) { + priv = pmadapter->priv[i]; + if (priv->rx_pkt_lock) + pcb->moal_free_lock(pmadapter->pmoal_handle, + priv->rx_pkt_lock); + if (priv->wmm.ra_list_spinlock) + pcb->moal_free_lock(pmadapter->pmoal_handle, + priv->wmm.ra_list_spinlock); +#ifdef STA_SUPPORT + if (priv->curr_bcn_buf_lock) + pcb->moal_free_lock(pmadapter->pmoal_handle, + priv->curr_bcn_buf_lock); +#endif + } + } + + /* Free lists */ + util_free_list_head((t_void *)pmadapter->pmoal_handle, + &pmadapter->rx_data_queue, pcb->moal_free_lock); + + util_scalar_free((t_void *)pmadapter->pmoal_handle, + &pmadapter->pending_bridge_pkts, pcb->moal_free_lock); + util_free_list_head((t_void *)pmadapter->pmoal_handle, + &pmadapter->cmd_free_q, + pmadapter->callbacks.moal_free_lock); + + util_free_list_head((t_void *)pmadapter->pmoal_handle, + &pmadapter->cmd_pending_q, + pmadapter->callbacks.moal_free_lock); + + util_free_list_head((t_void *)pmadapter->pmoal_handle, + &pmadapter->scan_pending_q, + pmadapter->callbacks.moal_free_lock); + + util_free_list_head((t_void *)pmadapter->pmoal_handle, + &pmadapter->ioctl_pending_q, + pmadapter->callbacks.moal_free_lock); + + for (i = 0; i < pmadapter->priv_num; i++) + util_free_list_head((t_void *)pmadapter->pmoal_handle, + &pmadapter->bssprio_tbl[i].bssprio_head, + pcb->moal_free_lock); + + for (i = 0; i < pmadapter->priv_num; i++) { + if (pmadapter->priv[i]) { + priv = pmadapter->priv[i]; + util_free_list_head((t_void *)pmadapter->pmoal_handle, + &priv->sta_list, + priv->adapter->callbacks. + moal_free_lock); + util_free_list_head((t_void *)pmadapter->pmoal_handle, + &priv->tdls_pending_txq, + pmadapter->callbacks. + moal_free_lock); + util_free_list_head((t_void *)pmadapter->pmoal_handle, + &priv->bypass_txq, + pmadapter->callbacks. + moal_free_lock); + + for (j = 0; j < MAX_NUM_TID; ++j) + util_free_list_head((t_void *)priv->adapter-> + pmoal_handle, + &priv->wmm.tid_tbl_ptr[j]. + ra_list, + priv->adapter->callbacks. + moal_free_lock); + util_free_list_head((t_void *)priv->adapter-> + pmoal_handle, + &priv->tx_ba_stream_tbl_ptr, + priv->adapter->callbacks. + moal_free_lock); + util_free_list_head((t_void *)priv->adapter-> + pmoal_handle, + &priv->rx_reorder_tbl_ptr, + priv->adapter->callbacks. + moal_free_lock); + util_scalar_free((t_void *)priv->adapter->pmoal_handle, + &priv->wmm.tx_pkts_queued, + priv->adapter->callbacks. + moal_free_lock); + util_scalar_free((t_void *)priv->adapter->pmoal_handle, + &priv->wmm.highest_queued_prio, + priv->adapter->callbacks. + moal_free_lock); + } + } + + LEAVE(); + return; +} + +/** + * @brief This function intializes the timers + * + * @param pmadapter A pointer to a mlan_adapter structure + * + * @return MLAN_STATUS_SUCCESS -- on success, + * otherwise MLAN_STATUS_FAILURE + * + */ +mlan_status +wlan_init_timer(IN pmlan_adapter pmadapter) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_callbacks pcb = &pmadapter->callbacks; + ENTER(); + + if (pcb-> + moal_init_timer(pmadapter->pmoal_handle, + &pmadapter->pmlan_cmd_timer, wlan_cmd_timeout_func, + pmadapter) + != MLAN_STATUS_SUCCESS) { + ret = MLAN_STATUS_FAILURE; + goto error; + } +error: + LEAVE(); + return ret; +} + +/** + * @brief This function releases the timers + * + * @param pmadapter A pointer to a mlan_adapter structure + * + * @return None + * + */ +t_void +wlan_free_timer(IN pmlan_adapter pmadapter) +{ + pmlan_callbacks pcb = &pmadapter->callbacks; + ENTER(); + + if (pmadapter->pmlan_cmd_timer) + pcb->moal_free_timer(pmadapter->pmoal_handle, + pmadapter->pmlan_cmd_timer); + + LEAVE(); + return; +} + +/** + * @brief This function initializes firmware + * + * @param pmadapter A pointer to mlan_adapter + * + * @return MLAN_STATUS_SUCCESS, MLAN_STATUS_PENDING or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_init_fw(IN pmlan_adapter pmadapter) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + ENTER(); + /* Initialize adapter structure */ + wlan_init_adapter(pmadapter); +#ifdef MFG_CMD_SUPPORT + if (pmadapter->mfg_mode != MTRUE) { +#endif + wlan_adapter_get_hw_spec(pmadapter); +#ifdef MFG_CMD_SUPPORT + } +#endif /* MFG_CMD_SUPPORT */ + if (wlan_is_cmd_pending(pmadapter)) { + /* Send the first command in queue and return */ + if (mlan_main_process(pmadapter) == MLAN_STATUS_FAILURE) + ret = MLAN_STATUS_FAILURE; + else + ret = MLAN_STATUS_PENDING; + } +#ifdef MFG_CMD_SUPPORT + if (pmadapter->mfg_mode == MTRUE) { + pmadapter->hw_status = WlanHardwareStatusInitializing; + ret = wlan_get_hw_spec_complete(pmadapter); + } +#endif + LEAVE(); + return ret; +} + +/** + * @brief This function udpate hw spec info to each interface + * + * @param pmadapter A pointer to mlan_adapter + * + * @return MLAN_STATUS_SUCCESS, MLAN_STATUS_PENDING or MLAN_STATUS_FAILURE + */ +void +wlan_update_hw_spec(IN pmlan_adapter pmadapter) +{ + t_u32 i; + + ENTER(); + +#ifdef STA_SUPPORT + if (IS_SUPPORT_MULTI_BANDS(pmadapter)) + pmadapter->fw_bands = (t_u8)GET_FW_DEFAULT_BANDS(pmadapter); + else + pmadapter->fw_bands = BAND_B; + + pmadapter->config_bands = pmadapter->fw_bands; + for (i = 0; i < pmadapter->priv_num; i++) { + if (pmadapter->priv[i]) { + pmadapter->priv[i]->config_bands = pmadapter->fw_bands; + } + } + + if (pmadapter->fw_bands & BAND_A) { + if (pmadapter->fw_bands & BAND_GN) { + pmadapter->config_bands |= BAND_AN; + for (i = 0; i < pmadapter->priv_num; i++) { + if (pmadapter->priv[i]) + pmadapter->priv[i]->config_bands |= + BAND_AN; + } + + pmadapter->fw_bands |= BAND_AN; + } + if ((pmadapter->fw_bands & BAND_AN) + ) { + pmadapter->adhoc_start_band = BAND_A | BAND_AN; + pmadapter->adhoc_11n_enabled = MTRUE; + } else + pmadapter->adhoc_start_band = BAND_A; + for (i = 0; i < pmadapter->priv_num; i++) { + if (pmadapter->priv[i]) + pmadapter->priv[i]->adhoc_channel = + DEFAULT_AD_HOC_CHANNEL_A; + } + + } else if ((pmadapter->fw_bands & BAND_GN) + ) { + pmadapter->adhoc_start_band = BAND_G | BAND_B | BAND_GN; + for (i = 0; i < pmadapter->priv_num; i++) { + if (pmadapter->priv[i]) + pmadapter->priv[i]->adhoc_channel = + DEFAULT_AD_HOC_CHANNEL; + } + pmadapter->adhoc_11n_enabled = MTRUE; + } else if (pmadapter->fw_bands & BAND_G) { + pmadapter->adhoc_start_band = BAND_G | BAND_B; + for (i = 0; i < pmadapter->priv_num; i++) { + if (pmadapter->priv[i]) + pmadapter->priv[i]->adhoc_channel = + DEFAULT_AD_HOC_CHANNEL; + } + } else if (pmadapter->fw_bands & BAND_B) { + pmadapter->adhoc_start_band = BAND_B; + for (i = 0; i < pmadapter->priv_num; i++) { + if (pmadapter->priv[i]) + pmadapter->priv[i]->adhoc_channel = + DEFAULT_AD_HOC_CHANNEL; + } + } +#endif /* STA_SUPPORT */ + + for (i = 0; i < pmadapter->priv_num; i++) { + if (pmadapter->priv[i]->curr_addr[0] == 0xff) + memmove(pmadapter, pmadapter->priv[i]->curr_addr, + pmadapter->permanent_addr, + MLAN_MAC_ADDR_LENGTH); + } + + for (i = 0; i < pmadapter->priv_num; i++) { + if (pmadapter->priv[i]) + wlan_update_11n_cap(pmadapter->priv[i]); + } + if (ISSUPP_BEAMFORMING(pmadapter->hw_dot_11n_dev_cap)) { + PRINTM(MCMND, "Enable Beamforming\n"); + for (i = 0; i < pmadapter->priv_num; i++) { + if (pmadapter->priv[i]) + pmadapter->priv[i]->tx_bf_cap = + DEFAULT_11N_TX_BF_CAP; + } + } + + LEAVE(); + return; +} + +/** + * @brief This function initializes firmware for interface + * + * @param pmadapter A pointer to mlan_adapter + * + * @return MLAN_STATUS_SUCCESS, MLAN_STATUS_PENDING or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_init_priv_fw(IN pmlan_adapter pmadapter) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_private priv = MNULL; + t_u8 i = 0; + + ENTER(); + + wlan_init_priv_lock_list(pmadapter, 1); + for (i = 0; i < pmadapter->priv_num; i++) { + if (pmadapter->priv[i]) { + priv = pmadapter->priv[i]; + + /* Initialize private structure */ + ret = wlan_init_priv(priv); + if (ret) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + } + } +#ifdef MFG_CMD_SUPPORT + if (pmadapter->mfg_mode != MTRUE) { +#endif + wlan_update_hw_spec(pmadapter); + /* Issue firmware initialize commands for first BSS, + * for other interfaces it will be called after getting + * the last init command response of previous interface + */ + priv = wlan_get_priv(pmadapter, MLAN_BSS_ROLE_ANY); + if (!priv) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + ret = priv->ops.init_cmd(priv, MTRUE); + if (ret == MLAN_STATUS_FAILURE) + goto done; +#ifdef MFG_CMD_SUPPORT + } +#endif /* MFG_CMD_SUPPORT */ + + if (wlan_is_cmd_pending(pmadapter)) { + /* Send the first command in queue and return */ + if (mlan_main_process(pmadapter) == MLAN_STATUS_FAILURE) + ret = MLAN_STATUS_FAILURE; + else + ret = MLAN_STATUS_PENDING; + } else { + pmadapter->hw_status = WlanHardwareStatusReady; + } +done: + LEAVE(); + return ret; +} + +/** + * @brief This function frees the structure of adapter + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return N/A + */ +t_void +wlan_free_adapter(pmlan_adapter pmadapter) +{ + mlan_callbacks *pcb = (mlan_callbacks *)&pmadapter->callbacks; + ENTER(); + + if (!pmadapter) { + PRINTM(MERROR, "The adapter is NULL\n"); + LEAVE(); + return; + } + + wlan_cancel_all_pending_cmd(pmadapter); + /* Free command buffer */ + PRINTM(MINFO, "Free Command buffer\n"); + wlan_free_cmd_buffer(pmadapter); + + if (pmadapter->cmd_timer_is_set) { + /* Cancel command timeout timer */ + pcb->moal_stop_timer(pmadapter->pmoal_handle, + pmadapter->pmlan_cmd_timer); + pmadapter->cmd_timer_is_set = MFALSE; + } + wlan_free_fw_cfp_tables(pmadapter); +#ifdef STA_SUPPORT + PRINTM(MINFO, "Free ScanTable\n"); + if (pmadapter->pscan_table) { + if (pcb->moal_vmalloc && pcb->moal_vfree) + pcb->moal_vfree(pmadapter->pmoal_handle, + (t_u8 *)pmadapter->pscan_table); + else + pcb->moal_mfree(pmadapter->pmoal_handle, + (t_u8 *)pmadapter->pscan_table); + pmadapter->pscan_table = MNULL; + } + if (pmadapter->pchan_stats) { + if (pcb->moal_vmalloc && pcb->moal_vfree) + pcb->moal_vfree(pmadapter->pmoal_handle, + (t_u8 *)pmadapter->pchan_stats); + else + pcb->moal_mfree(pmadapter->pmoal_handle, + (t_u8 *)pmadapter->pchan_stats); + pmadapter->pchan_stats = MNULL; + } + if (pmadapter->bcn_buf) { + pcb->moal_mfree(pmadapter->pmoal_handle, + (t_u8 *)pmadapter->bcn_buf); + pmadapter->bcn_buf = MNULL; + } +#endif + + wlan_11h_cleanup(pmadapter); + + if (pmadapter->mp_regs_buf) { + pcb->moal_mfree(pmadapter->pmoal_handle, + (t_u8 *)pmadapter->mp_regs_buf); + pmadapter->mp_regs_buf = MNULL; + pmadapter->mp_regs = MNULL; + } +#if defined(SDIO_MULTI_PORT_RX_AGGR) + if (pmadapter->rx_buffer) { + pcb->moal_mfree(pmadapter->pmoal_handle, + (t_u8 *)pmadapter->rx_buffer); + pmadapter->rx_buffer = MNULL; + pmadapter->rx_buf = MNULL; + } +#endif +#if defined(SDIO_MULTI_PORT_TX_AGGR) || defined(SDIO_MULTI_PORT_RX_AGGR) + wlan_free_sdio_mpa_buffers(pmadapter); +#ifdef DEBUG_LEVEL1 + if (pmadapter->mpa_buf) { + if (pcb->moal_vmalloc && pcb->moal_vfree) + pcb->moal_vfree(pmadapter->pmoal_handle, + (t_u8 *)pmadapter->mpa_buf); + else + pcb->moal_mfree(pmadapter->pmoal_handle, + (t_u8 *)pmadapter->mpa_buf); + pmadapter->mpa_buf = MNULL; + pmadapter->mpa_buf_size = 0; + } +#endif +#endif + wlan_free_mlan_buffer(pmadapter, pmadapter->psleep_cfm); + pmadapter->psleep_cfm = MNULL; + + LEAVE(); + return; +} + +/** + * @brief This function frees the structure of priv + * + * @param pmpriv A pointer to mlan_private structure + * + * @return N/A + */ +t_void +wlan_free_priv(mlan_private *pmpriv) +{ + ENTER(); + wlan_clean_txrx(pmpriv); + wlan_delete_bsspriotbl(pmpriv); + +#ifdef STA_SUPPORT + wlan_free_curr_bcn(pmpriv); +#endif /* STA_SUPPORT */ + +#if defined(DRV_EMBEDDED_AUTHENTICATOR) || defined(DRV_EMBEDDED_SUPPLICANT) + hostsa_cleanup(pmpriv); +#endif /*EMBEDDED AUTHENTICATOR */ + + wlan_delete_station_list(pmpriv); + LEAVE(); +} + +/** + * @brief This function init interface based on pmadapter's bss_attr table + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return N/A + */ +mlan_status +wlan_init_interface(IN pmlan_adapter pmadapter) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_callbacks pcb = MNULL; + t_u8 i = 0; + t_u32 j = 0; + + ENTER(); + + pcb = &pmadapter->callbacks; + for (i = 0; i < MLAN_MAX_BSS_NUM; i++) { + if (pmadapter->bss_attr[i].active == MTRUE) { + if (!pmadapter->priv[i]) { + /* For valid bss_attr, allocate memory for private structure */ + if (pcb->moal_vmalloc && pcb->moal_vfree) + ret = pcb->moal_vmalloc(pmadapter-> + pmoal_handle, + sizeof + (mlan_private), + (t_u8 **) + &pmadapter-> + priv[i]); + else + ret = pcb->moal_malloc(pmadapter-> + pmoal_handle, + sizeof + (mlan_private), + MLAN_MEM_DEF, + (t_u8 **) + &pmadapter-> + priv[i]); + if (ret != MLAN_STATUS_SUCCESS || + !pmadapter->priv[i]) { + ret = MLAN_STATUS_FAILURE; + goto error; + } + + pmadapter->priv_num++; + memset(pmadapter, pmadapter->priv[i], 0, + sizeof(mlan_private)); + } + pmadapter->priv[i]->adapter = pmadapter; + + /* Save bss_type, frame_type & bss_priority */ + pmadapter->priv[i]->bss_type = + (t_u8)pmadapter->bss_attr[i].bss_type; + pmadapter->priv[i]->frame_type = + (t_u8)pmadapter->bss_attr[i].frame_type; + pmadapter->priv[i]->bss_priority = + (t_u8)pmadapter->bss_attr[i].bss_priority; + if (pmadapter->bss_attr[i].bss_type == + MLAN_BSS_TYPE_STA) + pmadapter->priv[i]->bss_role = + MLAN_BSS_ROLE_STA; + else if (pmadapter->bss_attr[i].bss_type == + MLAN_BSS_TYPE_UAP) + pmadapter->priv[i]->bss_role = + MLAN_BSS_ROLE_UAP; +#ifdef WIFI_DIRECT_SUPPORT + else if (pmadapter->bss_attr[i].bss_type == + MLAN_BSS_TYPE_WIFIDIRECT) { + pmadapter->priv[i]->bss_role = + MLAN_BSS_ROLE_STA; + if (pmadapter->bss_attr[i].bss_virtual) + pmadapter->priv[i]->bss_virtual = MTRUE; + } +#endif + /* Save bss_index and bss_num */ + pmadapter->priv[i]->bss_index = i; + pmadapter->priv[i]->bss_num = + (t_u8)pmadapter->bss_attr[i].bss_num; + + /* init function table */ + for (j = 0; mlan_ops[j]; j++) { + if (mlan_ops[j]->bss_role == + GET_BSS_ROLE(pmadapter->priv[i])) { + memcpy(pmadapter, + &pmadapter->priv[i]->ops, + mlan_ops[j], + sizeof(mlan_operations)); + } + } + } + } + /*wmm init */ + wlan_wmm_init(pmadapter); + /* Initialize firmware, may return PENDING */ + ret = wlan_init_priv_fw(pmadapter); + PRINTM(MINFO, "wlan_init_priv_fw returned ret=0x%x\n", ret); +error: + LEAVE(); + return ret; +} + +/** + * @brief The cmdresp handler calls this function for init_fw_complete callback + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return MLAN_STATUS_SUCCESS + * The firmware initialization callback succeeded. + */ +mlan_status +wlan_get_hw_spec_complete(IN pmlan_adapter pmadapter) +{ + mlan_status status = MLAN_STATUS_SUCCESS; + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_callbacks pcb = &pmadapter->callbacks; + mlan_hw_info info; + mlan_bss_tbl bss_tbl; + + ENTER(); +#ifdef MFG_CMD_SUPPORT + if (pmadapter->mfg_mode != MTRUE) { +#endif + /* Check if hardware is ready */ + if (pmadapter->hw_status != WlanHardwareStatusInitializing) + status = MLAN_STATUS_FAILURE; + else { + memset(pmadapter, &info, 0, sizeof(info)); + info.fw_cap = pmadapter->fw_cap_info; + memset(pmadapter, &bss_tbl, 0, sizeof(bss_tbl)); + memcpy(pmadapter, bss_tbl.bss_attr, pmadapter->bss_attr, + sizeof(mlan_bss_tbl)); + } + /* Invoke callback */ + ret = pcb->moal_get_hw_spec_complete(pmadapter->pmoal_handle, + status, &info, &bss_tbl); + if (ret == MLAN_STATUS_SUCCESS && status == MLAN_STATUS_SUCCESS) + memcpy(pmadapter, pmadapter->bss_attr, bss_tbl.bss_attr, + sizeof(mlan_bss_tbl)); +#ifdef MFG_CMD_SUPPORT + } +#endif + if (pmadapter->hw_status == WlanHardwareStatusInitializing) + ret = wlan_init_interface(pmadapter); + LEAVE(); + return ret; +} + +/** + * @brief The cmdresp handler calls this function for init_fw_complete callback + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return MLAN_STATUS_SUCCESS + * The firmware initialization callback succeeded. + */ +mlan_status +wlan_init_fw_complete(IN pmlan_adapter pmadapter) +{ + mlan_status status = MLAN_STATUS_SUCCESS; + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_callbacks pcb = &pmadapter->callbacks; + + ENTER(); + + /* Check if hardware is ready */ + if (pmadapter->hw_status != WlanHardwareStatusReady) + status = MLAN_STATUS_FAILURE; + + /* Reconfigure wmm parameter */ + if (status == MLAN_STATUS_SUCCESS) { + wlan_prepare_cmd(wlan_get_priv(pmadapter, MLAN_BSS_ROLE_STA), + HostCmd_CMD_WMM_PARAM_CONFIG, + HostCmd_ACT_GEN_SET, 0, MNULL, + &pmadapter->ac_params); + } + /* Invoke callback */ + ret = pcb->moal_init_fw_complete(pmadapter->pmoal_handle, status); + LEAVE(); + return ret; +} + +/** + * @brief The cmdresp handler calls this function + * for shutdown_fw_complete callback + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return MLAN_STATUS_SUCCESS + * The firmware shutdown callback succeeded. + */ +mlan_status +wlan_shutdown_fw_complete(IN pmlan_adapter pmadapter) +{ + pmlan_callbacks pcb = &pmadapter->callbacks; + mlan_status status = MLAN_STATUS_SUCCESS; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + pmadapter->hw_status = WlanHardwareStatusNotReady; + /* Invoke callback */ + ret = pcb->moal_shutdown_fw_complete(pmadapter->pmoal_handle, status); + LEAVE(); + return ret; +} diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_init.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_init.h new file mode 100644 index 000000000000..5cebb24c9a82 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_init.h @@ -0,0 +1,90 @@ +/** @file mlan_init.h + * + * @brief This file defines the FW initialization data + * structures. + * + * Copyright (C) 2008-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 10/13/2008: initial version +******************************************************/ + +#ifndef _MLAN_INIT_H_ +#define _MLAN_INIT_H_ + +/** Tx buffer size for firmware download*/ +#define FW_DNLD_TX_BUF_SIZE 620 +/** Rx buffer size for firmware download*/ +#define FW_DNLD_RX_BUF_SIZE 2048 +/** Max firmware retry */ +#define MAX_FW_RETRY 3 + +/** Firmware has last block */ +#define FW_HAS_LAST_BLOCK 0x00000004 +/** CMD id for CMD7 */ +#define FW_CMD_7 0x00000007 + +/** Firmware data transmit size */ +#define FW_DATA_XMIT_SIZE (sizeof(FWHeader) + DataLength + sizeof(t_u32)) + +/** FWHeader */ +typedef MLAN_PACK_START struct _FWHeader { + /** FW download command */ + t_u32 dnld_cmd; + /** FW base address */ + t_u32 base_addr; + /** FW data length */ + t_u32 data_length; + /** FW CRC */ + t_u32 crc; +} MLAN_PACK_END FWHeader; + +/** FWData */ +typedef MLAN_PACK_START struct _FWData { + /** FW data header */ + FWHeader fw_header; + /** FW data sequence number */ + t_u32 seq_num; + /** FW data buffer */ + t_u8 data[1]; +} MLAN_PACK_END FWData; + +/** FWSyncHeader */ +typedef MLAN_PACK_START struct _FWSyncHeader { + /** FW sync header command */ + t_u32 cmd; + /** FW sync header sequence number */ + t_u32 seq_num; +} MLAN_PACK_END FWSyncHeader; + +#ifdef BIG_ENDIAN_SUPPORT +/** Convert sequence number and command fields + * of fwheader to correct endian format + */ +#define endian_convert_syncfwheader(x) { \ + (x)->cmd = wlan_le32_to_cpu((x)->cmd); \ + (x)->seq_num = wlan_le32_to_cpu((x)->seq_num); \ + } +#else +/** Convert sequence number and command fields + * of fwheader to correct endian format + */ +#define endian_convert_syncfwheader(x) +#endif /* BIG_ENDIAN_SUPPORT */ + +#endif /* _MLAN_INIT_H_ */ diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_ioctl.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_ioctl.h new file mode 100644 index 000000000000..e5be9ac0776c --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_ioctl.h @@ -0,0 +1,4403 @@ +/** @file mlan_ioctl.h + * + * @brief This file declares the IOCTL data structures and APIs. + * + * Copyright (C) 2008-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 11/07/2008: initial version +******************************************************/ + +#ifndef _MLAN_IOCTL_H_ +#define _MLAN_IOCTL_H_ + +/** Enumeration for IOCTL request ID */ +enum _mlan_ioctl_req_id { + /* Scan Group */ + MLAN_IOCTL_SCAN = 0x00010000, + MLAN_OID_SCAN_NORMAL = 0x00010001, + MLAN_OID_SCAN_SPECIFIC_SSID = 0x00010002, + MLAN_OID_SCAN_USER_CONFIG = 0x00010003, + MLAN_OID_SCAN_CONFIG = 0x00010004, + MLAN_OID_SCAN_GET_CURRENT_BSS = 0x00010005, + MLAN_OID_SCAN_CANCEL = 0x00010006, + MLAN_OID_SCAN_TABLE_FLUSH = 0x0001000A, + MLAN_OID_SCAN_BGSCAN_CONFIG = 0x0001000B, + /* BSS Configuration Group */ + MLAN_IOCTL_BSS = 0x00020000, + MLAN_OID_BSS_START = 0x00020001, + MLAN_OID_BSS_STOP = 0x00020002, + MLAN_OID_BSS_MODE = 0x00020003, + MLAN_OID_BSS_CHANNEL = 0x00020004, + MLAN_OID_BSS_CHANNEL_LIST = 0x00020005, + MLAN_OID_BSS_MAC_ADDR = 0x00020006, + MLAN_OID_BSS_MULTICAST_LIST = 0x00020007, + MLAN_OID_BSS_FIND_BSS = 0x00020008, + MLAN_OID_IBSS_BCN_INTERVAL = 0x00020009, + MLAN_OID_IBSS_ATIM_WINDOW = 0x0002000A, + MLAN_OID_IBSS_CHANNEL = 0x0002000B, +#ifdef UAP_SUPPORT + MLAN_OID_UAP_BSS_CONFIG = 0x0002000C, + MLAN_OID_UAP_DEAUTH_STA = 0x0002000D, + MLAN_OID_UAP_BSS_RESET = 0x0002000E, +#endif +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) + MLAN_OID_BSS_ROLE = 0x0002000F, +#endif +#ifdef WIFI_DIRECT_SUPPORT + MLAN_OID_WIFI_DIRECT_MODE = 0x00020010, +#endif +#ifdef STA_SUPPORT + MLAN_OID_BSS_LISTEN_INTERVAL = 0x00020011, +#endif + MLAN_OID_BSS_REMOVE = 0x00020014, +#ifdef UAP_SUPPORT + MLAN_OID_UAP_CFG_WMM_PARAM = 0x00020015, +#endif + MLAN_OID_BSS_11D_CHECK_CHANNEL = 0x00020016, +#ifdef UAP_SUPPORT + MLAN_OID_UAP_SCAN_CHANNELS = 0x00020018, + MLAN_OID_UAP_CHANNEL = 0x00020019, + MLAN_OID_UAP_OPER_CTRL = 0x0002001A, +#endif +#ifdef STA_SUPPORT + MLAN_OID_BSS_CHAN_INFO = 0x0002001B, +#endif + + /* Radio Configuration Group */ + MLAN_IOCTL_RADIO_CFG = 0x00030000, + MLAN_OID_RADIO_CTRL = 0x00030001, + MLAN_OID_BAND_CFG = 0x00030002, + MLAN_OID_ANT_CFG = 0x00030003, + MLAN_OID_REMAIN_CHAN_CFG = 0x00030004, + + /* SNMP MIB Group */ + MLAN_IOCTL_SNMP_MIB = 0x00040000, + MLAN_OID_SNMP_MIB_RTS_THRESHOLD = 0x00040001, + MLAN_OID_SNMP_MIB_FRAG_THRESHOLD = 0x00040002, + MLAN_OID_SNMP_MIB_RETRY_COUNT = 0x00040003, + MLAN_OID_SNMP_MIB_DOT11D = 0x00040004, +#if defined(UAP_SUPPORT) + MLAN_OID_SNMP_MIB_DOT11H = 0x00040005, +#endif + MLAN_OID_SNMP_MIB_DTIM_PERIOD = 0x00040006, + MLAN_OID_SNMP_MIB_SIGNALEXT_ENABLE = 0x00040007, + MLAN_OID_SNMP_MIB_CTRL_DEAUTH = 0x00040008, + + /* Status Information Group */ + MLAN_IOCTL_GET_INFO = 0x00050000, + MLAN_OID_GET_STATS = 0x00050001, + MLAN_OID_GET_SIGNAL = 0x00050002, + MLAN_OID_GET_FW_INFO = 0x00050003, + MLAN_OID_GET_VER_EXT = 0x00050004, + MLAN_OID_GET_BSS_INFO = 0x00050005, + MLAN_OID_GET_DEBUG_INFO = 0x00050006, +#ifdef UAP_SUPPORT + MLAN_OID_UAP_STA_LIST = 0x00050007, +#endif + MLAN_OID_GET_SIGNAL_EXT = 0x00050008, + MLAN_OID_GET_UAP_STATS_LOG = 0x0005000A, + /* Security Configuration Group */ + MLAN_IOCTL_SEC_CFG = 0x00060000, + MLAN_OID_SEC_CFG_AUTH_MODE = 0x00060001, + MLAN_OID_SEC_CFG_ENCRYPT_MODE = 0x00060002, + MLAN_OID_SEC_CFG_WPA_ENABLED = 0x00060003, + MLAN_OID_SEC_CFG_ENCRYPT_KEY = 0x00060004, + MLAN_OID_SEC_CFG_PASSPHRASE = 0x00060005, + MLAN_OID_SEC_CFG_EWPA_ENABLED = 0x00060006, + MLAN_OID_SEC_CFG_ESUPP_MODE = 0x00060007, + MLAN_OID_SEC_CFG_WAPI_ENABLED = 0x00060009, + MLAN_OID_SEC_CFG_PORT_CTRL_ENABLED = 0x0006000A, + + /* Rate Group */ + MLAN_IOCTL_RATE = 0x00070000, + MLAN_OID_RATE_CFG = 0x00070001, + MLAN_OID_GET_DATA_RATE = 0x00070002, + MLAN_OID_SUPPORTED_RATES = 0x00070003, + + /* Power Configuration Group */ + MLAN_IOCTL_POWER_CFG = 0x00080000, + MLAN_OID_POWER_CFG = 0x00080001, + MLAN_OID_POWER_CFG_EXT = 0x00080002, + + /* Power Management Configuration Group */ + MLAN_IOCTL_PM_CFG = 0x00090000, + MLAN_OID_PM_CFG_IEEE_PS = 0x00090001, + MLAN_OID_PM_CFG_HS_CFG = 0x00090002, + MLAN_OID_PM_CFG_INACTIVITY_TO = 0x00090003, + MLAN_OID_PM_CFG_DEEP_SLEEP = 0x00090004, + MLAN_OID_PM_CFG_SLEEP_PD = 0x00090005, + MLAN_OID_PM_CFG_PS_CFG = 0x00090006, + MLAN_OID_PM_CFG_FW_WAKEUP_METHOD = 0x00090007, + MLAN_OID_PM_CFG_SLEEP_PARAMS = 0x00090008, +#ifdef UAP_SUPPORT + MLAN_OID_PM_CFG_PS_MODE = 0x00090009, +#endif /* UAP_SUPPORT */ + MLAN_OID_PM_INFO = 0x0009000A, + MLAN_OID_PM_HS_WAKEUP_REASON = 0x0009000B, + MLAN_OID_PM_MGMT_FILTER = 0x0009000C, + MLAN_OID_PM_CFG_BCN_TIMEOUT = 0x0009000D, + + /* WMM Configuration Group */ + MLAN_IOCTL_WMM_CFG = 0x000A0000, + MLAN_OID_WMM_CFG_ENABLE = 0x000A0001, + MLAN_OID_WMM_CFG_QOS = 0x000A0002, + MLAN_OID_WMM_CFG_ADDTS = 0x000A0003, + MLAN_OID_WMM_CFG_DELTS = 0x000A0004, + MLAN_OID_WMM_CFG_QUEUE_CONFIG = 0x000A0005, + MLAN_OID_WMM_CFG_QUEUE_STATS = 0x000A0006, + MLAN_OID_WMM_CFG_QUEUE_STATUS = 0x000A0007, + MLAN_OID_WMM_CFG_TS_STATUS = 0x000A0008, + + /* WPS Configuration Group */ + MLAN_IOCTL_WPS_CFG = 0x000B0000, + MLAN_OID_WPS_CFG_SESSION = 0x000B0001, + + /* 802.11n Configuration Group */ + MLAN_IOCTL_11N_CFG = 0x000C0000, + MLAN_OID_11N_CFG_TX = 0x000C0001, + MLAN_OID_11N_HTCAP_CFG = 0x000C0002, + MLAN_OID_11N_CFG_ADDBA_REJECT = 0x000C0003, + MLAN_OID_11N_CFG_AGGR_PRIO_TBL = 0x000C0004, + MLAN_OID_11N_CFG_ADDBA_PARAM = 0x000C0005, + MLAN_OID_11N_CFG_MAX_TX_BUF_SIZE = 0x000C0006, + MLAN_OID_11N_CFG_AMSDU_AGGR_CTRL = 0x000C0007, + MLAN_OID_11N_CFG_SUPPORTED_MCS_SET = 0x000C0008, + MLAN_OID_11N_CFG_TX_BF_CAP = 0x000C0009, + MLAN_OID_11N_CFG_TX_BF_CFG = 0x000C000A, + MLAN_OID_11N_CFG_DELBA = 0x000C000C, + MLAN_OID_11N_CFG_REJECT_ADDBA_REQ = 0x000C000D, + MLAN_OID_11N_CFG_COEX_RX_WINSIZE = 0x000C000E, + MLAN_OID_11N_CFG_TX_AGGR_CTRL = 0x000C000F, + MLAN_OID_11N_CFG_IBSS_AMPDU_PARAM = 0x000C0010, + + /* 802.11d Configuration Group */ + MLAN_IOCTL_11D_CFG = 0x000D0000, +#ifdef STA_SUPPORT + MLAN_OID_11D_CFG_ENABLE = 0x000D0001, + MLAN_OID_11D_CLR_CHAN_TABLE = 0x000D0002, +#endif /* STA_SUPPORT */ + MLAN_OID_11D_DOMAIN_INFO = 0x000D0003, + + /* Register Memory Access Group */ + MLAN_IOCTL_REG_MEM = 0x000E0000, + MLAN_OID_REG_RW = 0x000E0001, + MLAN_OID_EEPROM_RD = 0x000E0002, + MLAN_OID_MEM_RW = 0x000E0003, + + /* Multi-Radio Configuration Group */ + MLAN_IOCTL_MFR_CFG = 0x00100000, + + /* 802.11h Configuration Group */ + MLAN_IOCTL_11H_CFG = 0x00110000, + MLAN_OID_11H_CHANNEL_CHECK = 0x00110001, + MLAN_OID_11H_LOCAL_POWER_CONSTRAINT = 0x00110002, +#if defined(DFS_TESTING_SUPPORT) + MLAN_OID_11H_DFS_TESTING = 0x00110003, +#endif + MLAN_OID_11H_CHAN_REPORT_REQUEST = 0x00110004, + MLAN_OID_11H_CHAN_SWITCH_COUNT = 0x00110005, +#ifdef DFS_TESTING_SUPPORT + MLAN_OID_11H_CHAN_NOP_INFO = 0x00110006, +#endif + + MLAN_IOCTL_11K_CFG = 0x00130000, + MLAN_OID_11K_CFG_ENABLE = 0x00130001, + MLAN_OID_11K_GET_NLIST = 0x00130002, + + /* Miscellaneous Configuration Group */ + MLAN_IOCTL_MISC_CFG = 0x00200000, + MLAN_OID_MISC_GEN_IE = 0x00200001, + MLAN_OID_MISC_REGION = 0x00200002, + MLAN_OID_MISC_WARM_RESET = 0x00200003, +#if defined(SDIO_MULTI_PORT_TX_AGGR) || defined(SDIO_MULTI_PORT_RX_AGGR) + MLAN_OID_MISC_SDIO_MPA_CTRL = 0x00200006, +#endif + MLAN_OID_MISC_HOST_CMD = 0x00200007, + MLAN_OID_MISC_SYS_CLOCK = 0x00200009, + MLAN_OID_MISC_SOFT_RESET = 0x0020000A, + MLAN_OID_MISC_WWS = 0x0020000B, + MLAN_OID_MISC_ASSOC_RSP = 0x0020000C, + MLAN_OID_MISC_INIT_SHUTDOWN = 0x0020000D, + MLAN_OID_MISC_COALESCING_STATUS = 0x0020000E, + MLAN_OID_MISC_CUSTOM_IE = 0x0020000F, + MLAN_OID_MISC_TDLS_CONFIG = 0x00200010, + MLAN_OID_MISC_NET_MONITOR = 0x00200011, + MLAN_OID_MISC_TX_DATAPAUSE = 0x00200012, + MLAN_OID_MISC_IP_ADDR = 0x00200013, + MLAN_OID_MISC_MAC_CONTROL = 0x00200014, + MLAN_OID_MISC_MEF_CFG = 0x00200015, + MLAN_OID_MISC_CFP_CODE = 0x00200016, + MLAN_OID_MISC_COUNTRY_CODE = 0x00200017, + MLAN_OID_MISC_THERMAL = 0x00200018, + MLAN_OID_MISC_RX_MGMT_IND = 0x00200019, + MLAN_OID_MISC_SUBSCRIBE_EVENT = 0x0020001A, +#ifdef DEBUG_LEVEL1 + MLAN_OID_MISC_DRVDBG = 0x0020001B, +#endif + MLAN_OID_MISC_OTP_USER_DATA = 0x0020001D, + MLAN_OID_MISC_TXCONTROL = 0x00200020, +#ifdef STA_SUPPORT + MLAN_OID_MISC_EXT_CAP_CFG = 0x00200021, +#endif +#if defined(STA_SUPPORT) + MLAN_OID_MISC_PMFCFG = 0x00200022, +#endif + MLAN_OID_MISC_MULTI_CHAN_CFG = 0x00200023, + MLAN_OID_MISC_MULTI_CHAN_POLICY = 0x00200024, +#ifdef WIFI_DIRECT_SUPPORT + MLAN_OID_MISC_WIFI_DIRECT_CONFIG = 0x00200025, +#endif + MLAN_OID_MISC_TDLS_OPER = 0x00200026, + MLAN_OID_MISC_GET_TDLS_IES = 0x00200027, + MLAN_OID_MISC_DFS_REAPTER_MODE = 0x0020002B, +#ifdef RX_PACKET_COALESCE + MLAN_OID_MISC_RX_PACKET_COALESCE = 0x0020002C, +#endif + MLAN_OID_MISC_TDLS_CS_CHANNEL = 0x0020002D, + MLAN_OID_MISC_COALESCE_CFG = 0x0020002E, + MLAN_OID_MISC_TDLS_IDLE_TIME = 0x0020002F, + MLAN_OID_MISC_GET_SENSOR_TEMP = 0x00200030, + MLAN_OID_MISC_GTK_REKEY_OFFLOAD = 0x00200037, + MLAN_OID_MISC_OPER_CLASS = 0x00200038, + MLAN_OID_MISC_PMIC_CFG = 0x00200039, + MLAN_OID_MISC_IND_RST_CFG = 0x00200040, + MLAN_OID_MISC_ROAM_OFFLOAD = 0x00200042, + MLAN_OID_MISC_ROAM_OFFLOAD_APLIST = 0x00200043, + MLAN_OID_MISC_GET_TSF = 0x00200045, + MLAN_OID_MISC_GET_CHAN_REGION_CFG = 0x00200046, + MLAN_OID_MISC_OPER_CLASS_CHECK = 0x00200049, + MLAN_OID_MISC_DRCS_CFG = 0x00200050, + + MLAN_OID_MISC_CWMODE_CTRL = 0x00200051, + MLAN_OID_MISC_FW_DUMP_EVENT = 0x00200054, + MLAN_OID_MISC_PER_PKT_CFG = 0x00200055, + MLAN_OID_MISC_ROBUSTCOEX = 0x00200056, + MLAN_OID_MISC_GET_CORRELATED_TIME = 0x00200058, + MLAN_OID_MISC_CFP_INFO = 0x00200060, + MLAN_OID_MISC_BOOT_SLEEP = 0x00200061, +}; + +/** Sub command size */ +#define MLAN_SUB_COMMAND_SIZE 4 + +/** Enumeration for the action of IOCTL request */ +enum _mlan_act_ioctl { + MLAN_ACT_SET = 1, + MLAN_ACT_GET, + MLAN_ACT_CANCEL, + MLAN_ACT_CLEAR, + MLAN_ACT_RESET +}; + +/** Enumeration for generic enable/disable */ +enum _mlan_act_generic { + MLAN_ACT_DISABLE = 0, + MLAN_ACT_ENABLE = 1 +}; + +/** Enumeration for scan mode */ +enum _mlan_scan_mode { + MLAN_SCAN_MODE_UNCHANGED = 0, + MLAN_SCAN_MODE_BSS, + MLAN_SCAN_MODE_IBSS, + MLAN_SCAN_MODE_ANY +}; + +/** Enumeration for scan type */ +enum _mlan_scan_type { + MLAN_SCAN_TYPE_UNCHANGED = 0, + MLAN_SCAN_TYPE_ACTIVE, + MLAN_SCAN_TYPE_PASSIVE +}; + +/** Max number of supported rates */ +#define MLAN_SUPPORTED_RATES 32 + +/** RSSI scan */ +#define SCAN_RSSI(RSSI) (0x100 - ((t_u8)(RSSI))) + +/** Max passive scan time for each channel in milliseconds */ +#define MRVDRV_MAX_PASSIVE_SCAN_CHAN_TIME 2000 + +/** Max active scan time for each channel in milliseconds */ +#define MRVDRV_MAX_ACTIVE_SCAN_CHAN_TIME 500 + +/** Maximum number of probes to send on each channel */ +#define MAX_PROBES 4 + +/** Default number of probes to send on each channel */ +#define DEFAULT_PROBES 4 + +/** + * @brief Sub-structure passed in wlan_ioctl_get_scan_table_entry for each BSS + * + * Fixed field information returned for the scan response in the IOCTL + * response. + */ +typedef struct _wlan_get_scan_table_fixed { + /** BSSID of this network */ + t_u8 bssid[MLAN_MAC_ADDR_LENGTH]; + /** Channel this beacon/probe response was detected */ + t_u8 channel; + /** RSSI for the received packet */ + t_u8 rssi; + /** TSF value in microseconds from the firmware at packet reception */ + t_u64 network_tsf; +} wlan_get_scan_table_fixed; + +/** mlan_802_11_ssid data structure */ +typedef struct _mlan_802_11_ssid { + /** SSID Length */ + t_u32 ssid_len; + /** SSID information field */ + t_u8 ssid[MLAN_MAX_SSID_LENGTH]; +} mlan_802_11_ssid, *pmlan_802_11_ssid; + +/** tx status event structure */ +typedef MLAN_PACK_START struct _tx_status_event { + /** packet type */ + t_u8 packet_type; + /** tx_token_id */ + t_u8 tx_token_id; + /** 0--success, 1--fail, 2--watchdogtimeout */ + t_u8 status; + /** t1 time stamp */ + t_u64 t1_tstamp; + /** t4 time stamp */ + t_u64 t4_tstamp; + /** t1 error */ + t_u64 t1_error; + /** t4 error */ + t_u64 t4_error; + /** egress time */ + t_u64 egress_time; +} MLAN_PACK_END tx_status_event; + +/** + * Sructure to retrieve the scan table + */ +typedef struct { + /** + * - Zero based scan entry to start retrieval in command request + * - Number of scans entries returned in command response + */ + t_u32 scan_number; + /** + * Buffer marker for multiple wlan_ioctl_get_scan_table_entry structures. + * Each struct is padded to the nearest 32 bit boundary. + */ + t_u8 scan_table_entry_buf[1]; +} wlan_ioctl_get_scan_table_info; + +/** + * Structure passed in the wlan_ioctl_get_scan_table_info for each + * BSS returned in the WLAN_GET_SCAN_RESP IOCTL + */ +typedef struct _wlan_ioctl_get_scan_table_entry { + /** + * Fixed field length included in the response. + * + * Length value is included so future fixed fields can be added to the + * response without breaking backwards compatibility. Use the length + * to find the offset for the bssInfoLength field, not a sizeof() calc. + */ + t_u32 fixed_field_length; + + /** + * Length of the BSS Information (probe resp or beacon) that + * follows after the fixed_field_length + */ + t_u32 bss_info_length; + + /** + * Always present, fixed length data fields for the BSS + */ + wlan_get_scan_table_fixed fixed_fields; + + /* + * Probe response or beacon scanned for the BSS. + * + * Field layout: + * - TSF 8 octets + * - Beacon Interval 2 octets + * - Capability Info 2 octets + * + * - IEEE Infomation Elements; variable number & length per 802.11 spec + */ + /* t_u8 bss_info_buffer[0]; */ +} wlan_ioctl_get_scan_table_entry; + +/** Type definition of mlan_scan_time_params */ +typedef struct _mlan_scan_time_params { + /** Scan channel time for specific scan in milliseconds */ + t_u32 specific_scan_time; + /** Scan channel time for active scan in milliseconds */ + t_u32 active_scan_time; + /** Scan channel time for passive scan in milliseconds */ + t_u32 passive_scan_time; +} mlan_scan_time_params, *pmlan_scan_time_params; + +/** Type definition of mlan_user_scan */ +typedef struct _mlan_user_scan { + /** Length of scan_cfg_buf */ + t_u32 scan_cfg_len; + /** Buffer of scan config */ + t_u8 scan_cfg_buf[1]; +} mlan_user_scan, *pmlan_user_scan; + +/** Type definition of mlan_scan_req */ +typedef struct _mlan_scan_req { + /** BSS mode for scanning */ + t_u32 scan_mode; + /** Scan type */ + t_u32 scan_type; + /** SSID */ + mlan_802_11_ssid scan_ssid; + /** Scan time parameters */ + mlan_scan_time_params scan_time; + /** Scan config parameters in user scan */ + mlan_user_scan user_scan; +} mlan_scan_req, *pmlan_scan_req; + +/** Type defnition of mlan_scan_resp */ +typedef struct _mlan_scan_resp { + /** Number of scan result */ + t_u32 num_in_scan_table; + /** Scan table */ + t_u8 *pscan_table; + /* Age in seconds */ + t_u32 age_in_secs; + /** channel statstics */ + t_u8 *pchan_stats; + /** Number of records in the chan_stats */ + t_u32 num_in_chan_stats; +} mlan_scan_resp, *pmlan_scan_resp; + +#define EXT_SCAN_TYPE_ENH 2 +/** Type definition of mlan_scan_cfg */ +typedef struct _mlan_scan_cfg { + /** Scan type */ + t_u32 scan_type; + /** BSS mode for scanning */ + t_u32 scan_mode; + /** Scan probe */ + t_u32 scan_probe; + /** Scan time parameters */ + mlan_scan_time_params scan_time; + /** Ext_scan: 0 disable, 1: enable, 2: enhance scan*/ + t_u32 ext_scan; +} mlan_scan_cfg, *pmlan_scan_cfg; + +/** Type defnition of mlan_ds_scan for MLAN_IOCTL_SCAN */ +typedef struct _mlan_ds_scan { + /** Sub-command */ + t_u32 sub_command; + /** Scan request/response */ + union { + /** Scan request */ + mlan_scan_req scan_req; + /** Scan response */ + mlan_scan_resp scan_resp; + /** Scan config parameters in user scan */ + mlan_user_scan user_scan; + /** Scan config parameters */ + mlan_scan_cfg scan_cfg; + } param; +} mlan_ds_scan, *pmlan_ds_scan; + +/*-----------------------------------------------------------------*/ +/** BSS Configuration Group */ +/*-----------------------------------------------------------------*/ +/** Enumeration for BSS mode */ +enum _mlan_bss_mode { + MLAN_BSS_MODE_INFRA = 1, + MLAN_BSS_MODE_IBSS, + MLAN_BSS_MODE_AUTO +}; + +/** Maximum key length */ +#define MLAN_MAX_KEY_LENGTH 32 +/** Maximum PMK R0 NAME key length */ +#define MLAN_MAX_PMKR0_NAME_LENGTH 16 + +/** Maximum atim window in milliseconds */ +#define MLAN_MAX_ATIM_WINDOW 50 + +/** Minimum beacon interval */ +#define MLAN_MIN_BEACON_INTERVAL 20 +/** Maximum beacon interval */ +#define MLAN_MAX_BEACON_INTERVAL 1000 +/** Default beacon interval */ +#define MLAN_BEACON_INTERVAL 100 + +/** Receive all packets */ +#define MLAN_PROMISC_MODE 1 +/** Receive multicast packets in multicast list */ +#define MLAN_MULTICAST_MODE 2 +/** Receive all multicast packets */ +#define MLAN_ALL_MULTI_MODE 4 + +/** Maximum size of multicast list */ +#define MLAN_MAX_MULTICAST_LIST_SIZE 32 + +/** mlan_multicast_list data structure for MLAN_OID_BSS_MULTICAST_LIST */ +typedef struct _mlan_multicast_list { + /** Multicast mode */ + t_u32 mode; + /** Number of multicast addresses in the list */ + t_u32 num_multicast_addr; + /** Multicast address list */ + mlan_802_11_mac_addr mac_list[MLAN_MAX_MULTICAST_LIST_SIZE]; +} mlan_multicast_list, *pmlan_multicast_list; + +/** Max channel */ +#define MLAN_MAX_CHANNEL 165 +/** Maximum number of channels in table */ +#define MLAN_MAX_CHANNEL_NUM 128 + +/** Channel/frequence for MLAN_OID_BSS_CHANNEL */ +typedef struct _chan_freq { + /** Channel Number */ + t_u32 channel; + /** Frequency of this Channel */ + t_u32 freq; +} chan_freq; + +/** mlan_chan_list data structure for MLAN_OID_BSS_CHANNEL_LIST */ +typedef struct _mlan_chan_list { + /** Number of channel */ + t_u32 num_of_chan; + /** Channel-Frequency table */ + chan_freq cf[MLAN_MAX_CHANNEL_NUM]; +} mlan_chan_list; + +/* This channel is disabled.*/ +#define CHAN_FLAGS_DISABLED MBIT(0) +/* do not initiate radiation, this includes sending probe requests or beaconing */ +#define CHAN_FLAGS_NO_IR MBIT(1) +/* Radar detection is required on this channel */ +#define CHAN_FLAGS_RADAR MBIT(3) +/* extension channel above this channel is not permitted */ +#define CHAN_FLAGS_NO_HT40PLUS MBIT(4) +/* extension channel below this channel is not permitted */ +#define CHAN_FLAGS_NO_HT40MINUS MBIT(5) +/* OFDM is not allowed on this channel */ +#define CHAN_FLAGS_NO_OFDM MBIT(6) +/** 80Mhz can not used on this channel */ +#define CHAN_FLAGS_NO_80MHZ MBIT(7) +/** 180Mhz can not used on this channel */ +#define CHAN_FLAGS_NO_160MHZ MBIT(8) +/* Only indoor use is permitted on this channel */ +#define CHAN_FLAGS_INDOOR_ONLY MBIT(9) +/* IR operation is allowed on this channel if it's + * connected concurrently to a BSS on the same channel on + * the 2 GHz band or to a channel in the same UNII band (on the 5 GHz + * band), and IEEE80211_CHAN_RADAR is not set */ +#define CHAN_FLAGS_IR_CONCURRENT MBIT(10) +/* 20 MHz operation is not allowed on this channel */ +#define CHAN_FLAGS_20MHZ MBIT(11) +/* 10 MHz operation is not allowed on this channel */ +#define CHAN_FLAGS_NO_10MHZ MBIT(12) + +/** mlan_ssid_bssid data structure for + * MLAN_OID_BSS_START and MLAN_OID_BSS_FIND_BSS + */ +typedef struct _mlan_ssid_bssid { + /** SSID */ + mlan_802_11_ssid ssid; + /** BSSID */ + mlan_802_11_mac_addr bssid; + /** index in BSSID list, start from 1 */ + t_u32 idx; + /** Receive signal strength in dBm */ + t_s32 rssi; + /**channel*/ + t_u16 channel; + /**mobility domain value*/ + t_u16 ft_md; + /**ft capability*/ + t_u8 ft_cap; + /**band*/ + t_u16 bss_band; + t_u32 channel_flags; +} mlan_ssid_bssid; + +/** Data structure of WMM ECW */ +typedef struct _wmm_ecw_t { +#ifdef BIG_ENDIAN_SUPPORT + /** Maximum Ecw */ + t_u8 ecw_max:4; + /** Minimum Ecw */ + t_u8 ecw_min:4; +#else + /** Minimum Ecw */ + t_u8 ecw_min:4; + /** Maximum Ecw */ + t_u8 ecw_max:4; +#endif /* BIG_ENDIAN_SUPPORT */ +} wmm_ecw_t, *pwmm_ecw_t; + +/** Data structure of WMM Aci/Aifsn */ +typedef struct _wmm_aci_aifsn_t { +#ifdef BIG_ENDIAN_SUPPORT + /** Reserved */ + t_u8 reserved:1; + /** Aci */ + t_u8 aci:2; + /** Acm */ + t_u8 acm:1; + /** Aifsn */ + t_u8 aifsn:4; +#else + /** Aifsn */ + t_u8 aifsn:4; + /** Acm */ + t_u8 acm:1; + /** Aci */ + t_u8 aci:2; + /** Reserved */ + t_u8 reserved:1; +#endif /* BIG_ENDIAN_SUPPORT */ +} wmm_aci_aifsn_t, *pwmm_aci_aifsn_t; + +/** Data structure of WMM AC parameters */ +typedef struct _wmm_ac_parameters_t { + wmm_aci_aifsn_t aci_aifsn; /**< AciAifSn */ + wmm_ecw_t ecw; /**< Ecw */ + t_u16 tx_op_limit; /**< Tx op limit */ +} wmm_ac_parameters_t, *pwmm_ac_parameters_t; + +/** mlan_deauth_param */ +typedef struct _mlan_deauth_param { + /** STA mac addr */ + t_u8 mac_addr[MLAN_MAC_ADDR_LENGTH]; + /** deauth reason */ + t_u16 reason_code; +} mlan_deauth_param; + +#ifdef UAP_SUPPORT +/** Maximum packet forward control value */ +#define MAX_PKT_FWD_CTRL 15 +/** Maximum BEACON period */ +#define MAX_BEACON_PERIOD 4000 +/** Minimum BEACON period */ +#define MIN_BEACON_PERIOD 50 +/** Maximum DTIM period */ +#define MAX_DTIM_PERIOD 100 +/** Minimum DTIM period */ +#define MIN_DTIM_PERIOD 1 +/** Maximum TX Power Limit */ +#define MAX_TX_POWER 20 +/** Minimum TX Power Limit */ +#define MIN_TX_POWER 0 +/** MAX station count */ +#define MAX_STA_COUNT 10 +/** Maximum RTS threshold */ +#define MAX_RTS_THRESHOLD 2347 +/** Maximum fragmentation threshold */ +#define MAX_FRAG_THRESHOLD 2346 +/** Minimum fragmentation threshold */ +#define MIN_FRAG_THRESHOLD 256 +/** data rate 54 M */ +#define DATA_RATE_54M 108 +/** Maximum value of bcast_ssid_ctl */ +#define MAX_BCAST_SSID_CTL 2 +/** antenna A */ +#define ANTENNA_MODE_A 0 +/** antenna B */ +#define ANTENNA_MODE_B 1 +/** transmit antenna */ +#define TX_ANTENNA 1 +/** receive antenna */ +#define RX_ANTENNA 0 +/** Maximum stage out time */ +#define MAX_STAGE_OUT_TIME 864000 +/** Minimum stage out time */ +#define MIN_STAGE_OUT_TIME 300 +/** Maximum Retry Limit */ +#define MAX_RETRY_LIMIT 14 + +/** Maximum group key timer in seconds */ +#define MAX_GRP_TIMER 86400 + +/** Maximum value of 4 byte configuration */ +#define MAX_VALID_DWORD 0x7FFFFFFF /* (1 << 31) - 1 */ + +/** default UAP BAND 2.4G */ +#define DEFAULT_UAP_BAND 0 +/** default UAP channel 6 */ +#define DEFAULT_UAP_CHANNEL 6 + +/** Maximum data rates */ +#define MAX_DATA_RATES 14 + +/** auto data rate */ +#define DATA_RATE_AUTO 0 + +/**filter mode: disable */ +#define MAC_FILTER_MODE_DISABLE 0 +/**filter mode: block mac address */ +#define MAC_FILTER_MODE_ALLOW_MAC 1 +/**filter mode: block mac address */ +#define MAC_FILTER_MODE_BLOCK_MAC 2 +/** Maximum mac filter num */ +#define MAX_MAC_FILTER_NUM 16 + +/* Bitmap for protocol to use */ +/** No security */ +#define PROTOCOL_NO_SECURITY 0x01 +/** Static WEP */ +#define PROTOCOL_STATIC_WEP 0x02 +/** WPA */ +#define PROTOCOL_WPA 0x08 +/** WPA2 */ +#define PROTOCOL_WPA2 0x20 +/** WP2 Mixed */ +#define PROTOCOL_WPA2_MIXED 0x28 +/** EAP */ +#define PROTOCOL_EAP 0x40 +/** WAPI */ +#define PROTOCOL_WAPI 0x80 + +/** Key_mgmt_psk */ +#define KEY_MGMT_NONE 0x04 +/** Key_mgmt_none */ +#define KEY_MGMT_PSK 0x02 +/** Key_mgmt_eap */ +#define KEY_MGMT_EAP 0x01 +/** Key_mgmt_psk_sha256 */ +#define KEY_MGMT_PSK_SHA256 0x100 + +/** TKIP */ +#define CIPHER_TKIP 0x04 +/** AES CCMP */ +#define CIPHER_AES_CCMP 0x08 + +/** Valid cipher bitmap */ +#define VALID_CIPHER_BITMAP 0x0c + +/** Packet forwarding to be done by FW or host */ +#define PKT_FWD_FW_BIT 0x01 +/** Intra-BSS broadcast packet forwarding allow bit */ +#define PKT_FWD_INTRA_BCAST 0x02 +/** Intra-BSS unicast packet forwarding allow bit */ +#define PKT_FWD_INTRA_UCAST 0x04 +/** Inter-BSS unicast packet forwarding allow bit */ +#define PKT_FWD_INTER_UCAST 0x08 +/** Intra-BSS unicast packet */ +#define PKT_INTRA_UCAST 0x01 +/** Inter-BSS unicast packet */ +#define PKT_INTER_UCAST 0x02 +/** Enable Host PKT forwarding */ +#define PKT_FWD_ENABLE_BIT 0x01 + +/** Channel List Entry */ +typedef struct _channel_list { + /** Channel Number */ + t_u8 chan_number; + /** Band Config */ + Band_Config_t bandcfg; +} scan_chan_list; + +/** mac_filter data structure */ +typedef struct _mac_filter { + /** mac filter mode */ + t_u16 filter_mode; + /** mac adress count */ + t_u16 mac_count; + /** mac address list */ + mlan_802_11_mac_addr mac_list[MAX_MAC_FILTER_NUM]; +} mac_filter; + +/** wpa parameter */ +typedef struct _wpa_param { + /** Pairwise cipher WPA */ + t_u8 pairwise_cipher_wpa; + /** Pairwise cipher WPA2 */ + t_u8 pairwise_cipher_wpa2; + /** group cipher */ + t_u8 group_cipher; + /** RSN replay protection */ + t_u8 rsn_protection; + /** passphrase length */ + t_u32 length; + /** passphrase */ + t_u8 passphrase[64]; + /**group key rekey time in seconds */ + t_u32 gk_rekey_time; +} wpa_param; + +/** wep key */ +typedef struct _wep_key { + /** key index 0-3 */ + t_u8 key_index; + /** is default */ + t_u8 is_default; + /** length */ + t_u16 length; + /** key data */ + t_u8 key[26]; +} wep_key; + +/** wep param */ +typedef struct _wep_param { + /** key 0 */ + wep_key key0; + /** key 1 */ + wep_key key1; + /** key 2 */ + wep_key key2; + /** key 3 */ + wep_key key3; +} wep_param; + +/** Data structure of WMM QoS information */ +typedef struct _wmm_qos_info_t { +#ifdef BIG_ENDIAN_SUPPORT + /** QoS UAPSD */ + t_u8 qos_uapsd:1; + /** Reserved */ + t_u8 reserved:3; + /** Parameter set count */ + t_u8 para_set_count:4; +#else + /** Parameter set count */ + t_u8 para_set_count:4; + /** Reserved */ + t_u8 reserved:3; + /** QoS UAPSD */ + t_u8 qos_uapsd:1; +#endif /* BIG_ENDIAN_SUPPORT */ +} wmm_qos_info_t, *pwmm_qos_info_t; + +/** Data structure of WMM parameter IE */ +typedef struct _wmm_parameter_t { + /** OuiType: 00:50:f2:02 */ + t_u8 ouitype[4]; + /** Oui subtype: 01 */ + t_u8 ouisubtype; + /** version: 01 */ + t_u8 version; + /** QoS information */ + t_u8 qos_info; + /** Reserved */ + t_u8 reserved; + /** AC Parameters Record WMM_AC_BE, WMM_AC_BK, WMM_AC_VI, WMM_AC_VO */ + wmm_ac_parameters_t ac_params[MAX_AC_QUEUES]; +} wmm_parameter_t, *pwmm_parameter_t; + +/** 5G band */ +#define BAND_CONFIG_5G 0x01 +/** 2.4 G band */ +#define BAND_CONFIG_2G 0x00 +/** MAX BG channel */ +#define MAX_BG_CHANNEL 14 +/** mlan_bss_param + * Note: For each entry you must enter an invalid value + * in the MOAL function woal_set_sys_config_invalid_data(). + * Otherwise for a valid data an unwanted TLV will be + * added to that command. + */ +typedef struct _mlan_uap_bss_param { + /** AP mac addr */ + mlan_802_11_mac_addr mac_addr; + /** SSID */ + mlan_802_11_ssid ssid; + /** Broadcast ssid control */ + t_u8 bcast_ssid_ctl; + /** Radio control: on/off */ + t_u8 radio_ctl; + /** dtim period */ + t_u8 dtim_period; + /** beacon period */ + t_u16 beacon_period; + /** rates */ + t_u8 rates[MAX_DATA_RATES]; + /** Tx data rate */ + t_u16 tx_data_rate; + /** Tx beacon rate */ + t_u16 tx_beacon_rate; + /** multicast/broadcast data rate */ + t_u16 mcbc_data_rate; + /** Tx power level in dBm */ + t_u8 tx_power_level; + /** Tx antenna */ + t_u8 tx_antenna; + /** Rx antenna */ + t_u8 rx_antenna; + /** packet forward control */ + t_u8 pkt_forward_ctl; + /** max station count */ + t_u16 max_sta_count; + /** mac filter */ + mac_filter filter; + /** station ageout timer in unit of 100ms */ + t_u32 sta_ageout_timer; + /** PS station ageout timer in unit of 100ms */ + t_u32 ps_sta_ageout_timer; + /** RTS threshold */ + t_u16 rts_threshold; + /** fragmentation threshold */ + t_u16 frag_threshold; + /** retry_limit */ + t_u16 retry_limit; + /** pairwise update timeout in milliseconds */ + t_u32 pairwise_update_timeout; + /** pairwise handshake retries */ + t_u32 pwk_retries; + /** groupwise update timeout in milliseconds */ + t_u32 groupwise_update_timeout; + /** groupwise handshake retries */ + t_u32 gwk_retries; + /** preamble type */ + t_u8 preamble_type; + /** band cfg */ + Band_Config_t bandcfg; + /** channel */ + t_u8 channel; + /** auth mode */ + t_u16 auth_mode; + /** encryption protocol */ + t_u16 protocol; + /** key managment type */ + t_u16 key_mgmt; + /** wep param */ + wep_param wep_cfg; + /** wpa param */ + wpa_param wpa_cfg; + /** Mgmt IE passthru mask */ + t_u32 mgmt_ie_passthru_mask; + /* + * 11n HT Cap HTCap_t ht_cap + */ + /** HT Capabilities Info field */ + t_u16 ht_cap_info; + /** A-MPDU Parameters field */ + t_u8 ampdu_param; + /** Supported MCS Set field */ + t_u8 supported_mcs_set[16]; + /** HT Extended Capabilities field */ + t_u16 ht_ext_cap; + /** Transmit Beamforming Capabilities field */ + t_u32 tx_bf_cap; + /** Antenna Selection Capability field */ + t_u8 asel; + /** Enable 2040 Coex */ + t_u8 enable_2040coex; + /** key management operation */ + t_u16 key_mgmt_operation; + /** BSS status */ + t_u16 bss_status; +#ifdef WIFI_DIRECT_SUPPORT + /* pre shared key */ + t_u8 psk[MLAN_MAX_KEY_LENGTH]; +#endif /* WIFI_DIRECT_SUPPORT */ + /** Number of channels in scan_channel_list */ + t_u32 num_of_chan; + /** scan channel list in ACS mode */ + scan_chan_list chan_list[MLAN_MAX_CHANNEL]; + /** Wmm parameters */ + wmm_parameter_t wmm_para; + +} mlan_uap_bss_param; + +/** mlan_uap_scan_channels */ +typedef struct _mlan_uap_scan_channels { + /** flag for remove nop channel*/ + t_u8 remove_nop_channel; + /** num of removed channel */ + t_u8 num_remvoed_channel; + /** Number of channels in scan_channel_list */ + t_u32 num_of_chan; + /** scan channel list in ACS mode */ + scan_chan_list chan_list[MLAN_MAX_CHANNEL]; +} mlan_uap_scan_channels; + +/** mlan_uap_oper_ctrl */ +typedef struct _mlan_uap_oper_ctrl { + /** control value + * 0: do nothing, + * 2: uap stops and restarts automaticaly + */ + t_u16 ctrl_value; + /** channel opt + * 1: uap restart on default 2.4G/channel 6 + * 2: uap restart on the band/channel configured by driver previously + * 3: uap restart on the band/channel specified by band_cfg and channel + */ + t_u16 chan_opt; + /** band cfg 0 + * 0: 20Mhz 2: 40 Mhz 3: 80Mhz + */ + t_u8 band_cfg; + /** channel */ + t_u8 channel; +} mlan_uap_oper_ctrl; + +#endif + +#ifdef WIFI_DIRECT_SUPPORT +/** mode: disable wifi direct */ +#define WIFI_DIRECT_MODE_DISABLE 0 +/** mode: listen */ +#define WIFI_DIRECT_MODE_LISTEN 1 +/** mode: GO */ +#define WIFI_DIRECT_MODE_GO 2 +/** mode: client */ +#define WIFI_DIRECT_MODE_CLIENT 3 +/** mode: find */ +#define WIFI_DIRECT_MODE_FIND 4 +/** mode: stop find */ +#define WIFI_DIRECT_MODE_STOP_FIND 5 +#endif + +/** Type definition of mlan_ds_bss for MLAN_IOCTL_BSS */ +typedef struct _mlan_ds_bss { + /** Sub-command */ + t_u32 sub_command; + /** BSS parameter */ + union { + /** SSID-BSSID for MLAN_OID_BSS_START */ + mlan_ssid_bssid ssid_bssid; + /** BSSID for MLAN_OID_BSS_STOP */ + mlan_802_11_mac_addr bssid; + /** BSS mode for MLAN_OID_BSS_MODE */ + t_u32 bss_mode; + /** BSS channel/frequency for MLAN_OID_BSS_CHANNEL */ + chan_freq bss_chan; + /** BSS channel list for MLAN_OID_BSS_CHANNEL_LIST */ + mlan_chan_list chanlist; + /** MAC address for MLAN_OID_BSS_MAC_ADDR */ + mlan_802_11_mac_addr mac_addr; + /** Multicast list for MLAN_OID_BSS_MULTICAST_LIST */ + mlan_multicast_list multicast_list; + /** Beacon interval for MLAN_OID_IBSS_BCN_INTERVAL */ + t_u32 bcn_interval; + /** ATIM window for MLAN_OID_IBSS_ATIM_WINDOW */ + t_u32 atim_window; + /** deauth param for MLAN_OID_BSS_STOP & MLAN_OID_UAP_DEAUTH_STA */ + mlan_deauth_param deauth_param; +#ifdef UAP_SUPPORT + /** host based uap flag for MLAN_OID_BSS_START */ + t_u8 host_based; + /** BSS param for AP mode for MLAN_OID_UAP_BSS_CONFIG */ + mlan_uap_bss_param bss_config; + /** AP Wmm parameters for MLAN_OID_UAP_CFG_WMM_PARAM */ + wmm_parameter_t ap_wmm_para; + /** ap scan channels for MLAN_OID_UAP_SCAN_CHANNELS*/ + mlan_uap_scan_channels ap_scan_channels; + /** ap channel for MLAN_OID_UAP_CHANNEL*/ + chan_band_info ap_channel; + /** ap operation control for MLAN_OID_UAP_OPER_CTRL*/ + mlan_uap_oper_ctrl ap_oper_ctrl; +#endif +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) + /** BSS role for MLAN_OID_BSS_ROLE */ + t_u8 bss_role; +#endif +#ifdef WIFI_DIRECT_SUPPORT + /** wifi direct mode for MLAN_OID_WIFI_DIRECT_MODE */ + t_u16 wfd_mode; +#endif +#ifdef STA_SUPPORT + /** Listen interval for MLAN_OID_BSS_LISTEN_INTERVAL */ + t_u16 listen_interval; + /** STA channel info for MLAN_OID_BSS_CHAN_INFO */ + chan_band_info sta_channel; +#endif + } param; +} mlan_ds_bss, *pmlan_ds_bss; + +/** Type definition of mlan_ds_custom_reg_domain */ +typedef struct _mlan_ds_custom_reg_domain { + t_u8 cfg_len; + t_u8 cfg_buf[0]; +} mlan_ds_custom_reg_domain; +/*-----------------------------------------------------------------*/ +/** Radio Control Group */ +/*-----------------------------------------------------------------*/ +/** Enumeration for band */ +enum _mlan_band_def { + BAND_B = 1, + BAND_G = 2, + BAND_A = 4, + BAND_GN = 8, + BAND_AN = 16, +}; + +/** Channel bandwidth */ +#define CHANNEL_BW_20MHZ 0 +#define CHANNEL_BW_40MHZ_ABOVE 1 +#define CHANNEL_BW_40MHZ_BELOW 3 + +/** RF antenna selection */ +#define RF_ANTENNA_MASK(n) ((1<<(n))-1) +/** RF antenna auto select */ +#define RF_ANTENNA_AUTO 0xFFFF + +/** Type definition of mlan_ds_band_cfg for MLAN_OID_BAND_CFG */ +typedef struct _mlan_ds_band_cfg { + /** Infra band */ + t_u32 config_bands; + /** Ad-hoc start band */ + t_u32 adhoc_start_band; + /** Ad-hoc start channel */ + t_u32 adhoc_channel; + /** Ad-hoc channel bandwidth */ + t_u32 adhoc_chan_bandwidth; + /** fw supported band */ + t_u32 fw_bands; +} mlan_ds_band_cfg; + +/** Type definition of mlan_ds_ant_cfg_1x1 for MLAN_OID_ANT_CFG */ +typedef struct _mlan_ds_ant_cfg_1x1 { + /** Antenna mode */ + t_u32 antenna; + /** Evaluate time */ + t_u16 evaluate_time; + /** Current antenna */ + t_u16 current_antenna; +} mlan_ds_ant_cfg_1x1, *pmlan_ds_ant_cfg_1x1; + +/** Type definition of mlan_ds_remain_chan for MLAN_OID_REMAIN_CHAN_CFG */ +typedef struct _mlan_ds_remain_chan { + /** remove flag */ + t_u16 remove; + /** status */ + t_u8 status; + /** Band cfg */ + Band_Config_t bandcfg; + /** channel */ + t_u8 channel; + /** remain time: Unit ms*/ + t_u32 remain_period; +} mlan_ds_remain_chan, *pmlan_ds_remain_chan; + +/** Type definition of mlan_ds_radio_cfg for MLAN_IOCTL_RADIO_CFG */ +typedef struct _mlan_ds_radio_cfg { + /** Sub-command */ + t_u32 sub_command; + /** Radio control parameter */ + union { + /** Radio on/off for MLAN_OID_RADIO_CTRL */ + t_u32 radio_on_off; + /** Band info for MLAN_OID_BAND_CFG */ + mlan_ds_band_cfg band_cfg; + /** Antenna info for MLAN_OID_ANT_CFG */ + mlan_ds_ant_cfg_1x1 ant_cfg_1x1; + /** remain on channel for MLAN_OID_REMAIN_CHAN_CFG */ + mlan_ds_remain_chan remain_chan; + } param; +} mlan_ds_radio_cfg, *pmlan_ds_radio_cfg; + +enum COALESCE_OPERATION { + RECV_FILTER_MATCH_TYPE_EQ = 0x80, + RECV_FILTER_MATCH_TYPE_NE, +}; + +enum COALESCE_PACKET_TYPE { + PACKET_TYPE_UNICAST = 1, + PACKET_TYPE_MULTICAST = 2, + PACKET_TYPE_BROADCAST = 3 +}; + +#define COALESCE_MAX_RULES 8 +#define COALESCE_MAX_BYTESEQ 4 /* non-adjustable */ +#define COALESCE_MAX_FILTERS 4 +#define MAX_COALESCING_DELAY 100 /* in msecs */ +#define MAX_PATTERN_LEN 20 +#define MAX_OFFSET_LEN 100 + +/** filt field param structure */ +struct filt_field_param { + /** Operation */ + t_u8 operation; + /** Operand len */ + t_u8 operand_len; + /** offset */ + t_u16 offset; + /** Operand byte stream */ + t_u8 operand_byte_stream[COALESCE_MAX_BYTESEQ]; +}; + +/** coalesce rule structure */ +struct coalesce_rule { + /** max coalescing delay */ + t_u16 max_coalescing_delay; + /** number of fields */ + t_u8 num_of_fields; + /** packet type */ + t_u8 pkt_type; + struct filt_field_param params[COALESCE_MAX_FILTERS]; +}; + +/** coalesce configuration structure */ +typedef struct _mlan_ds_coalesce_cfg { + t_u16 num_of_rules; + struct coalesce_rule rule[COALESCE_MAX_RULES]; +} mlan_ds_coalesce_cfg; + +/*-----------------------------------------------------------------*/ +/** SNMP MIB Group */ +/*-----------------------------------------------------------------*/ +/** Type definition of mlan_ds_snmp_mib for MLAN_IOCTL_SNMP_MIB */ +typedef struct _mlan_ds_snmp_mib { + /** Sub-command */ + t_u32 sub_command; + /** SNMP MIB parameter */ + union { + /** RTS threshold for MLAN_OID_SNMP_MIB_RTS_THRESHOLD */ + t_u32 rts_threshold; + /** Fragment threshold for MLAN_OID_SNMP_MIB_FRAG_THRESHOLD */ + t_u32 frag_threshold; + /** Retry count for MLAN_OID_SNMP_MIB_RETRY_COUNT */ + t_u32 retry_count; + /** OID value for MLAN_OID_SNMP_MIB_DOT11D/H */ + t_u32 oid_value; + /** DTIM period for MLAN_OID_SNMP_MIB_DTIM_PERIOD */ + t_u32 dtim_period; + /** Singal_ext Enable for MLAN_OID_SNMP_MIB_SIGNALEXT_ENABLE */ + t_u8 signalext_enable; + /** Control deauth when uap switch channel */ + t_u8 deauthctrl; + } param; +} mlan_ds_snmp_mib, *pmlan_ds_snmp_mib; + +/*-----------------------------------------------------------------*/ +/** Status Information Group */ +/*-----------------------------------------------------------------*/ +/** Enumeration for ad-hoc status */ +enum _mlan_adhoc_status { + ADHOC_IDLE, + ADHOC_STARTED, + ADHOC_JOINED, + ADHOC_COALESCED, ADHOC_STARTING +}; + +/** Get stats org structure */ +typedef struct _mlan_ds_get_stats_org { + /** Statistics counter */ + /** Multicast transmitted frame count */ + t_u32 mcast_tx_frame; + /** Failure count */ + t_u32 failed; + /** Retry count */ + t_u32 retry; + /** Multi entry count */ + t_u32 multi_retry; + /** Duplicate frame count */ + t_u32 frame_dup; + /** RTS success count */ + t_u32 rts_success; + /** RTS failure count */ + t_u32 rts_failure; + /** Ack failure count */ + t_u32 ack_failure; + /** Rx fragmentation count */ + t_u32 rx_frag; + /** Multicast Tx frame count */ + t_u32 mcast_rx_frame; + /** FCS error count */ + t_u32 fcs_error; + /** Tx frame count */ + t_u32 tx_frame; + /** WEP ICV error count */ + t_u32 wep_icv_error[4]; + /** beacon recv count */ + t_u32 bcn_rcv_cnt; + /** beacon miss count */ + t_u32 bcn_miss_cnt; + /** received amsdu count*/ + t_u32 amsdu_rx_cnt; + /** received msdu count in amsdu*/ + t_u32 msdu_in_rx_amsdu_cnt; + /** tx amsdu count*/ + t_u32 amsdu_tx_cnt; + /** tx msdu count in amsdu*/ + t_u32 msdu_in_tx_amsdu_cnt; +} mlan_ds_get_stats_org; + +/** Type definition of mlan_ds_get_stats for MLAN_OID_GET_STATS */ +typedef struct _mlan_ds_get_stats { + /** Statistics counter */ + /** Multicast transmitted frame count */ + t_u32 mcast_tx_frame; + /** Failure count */ + t_u32 failed; + /** Retry count */ + t_u32 retry; + /** Multi entry count */ + t_u32 multi_retry; + /** Duplicate frame count */ + t_u32 frame_dup; + /** RTS success count */ + t_u32 rts_success; + /** RTS failure count */ + t_u32 rts_failure; + /** Ack failure count */ + t_u32 ack_failure; + /** Rx fragmentation count */ + t_u32 rx_frag; + /** Multicast Tx frame count */ + t_u32 mcast_rx_frame; + /** FCS error count */ + t_u32 fcs_error; + /** Tx frame count */ + t_u32 tx_frame; + /** WEP ICV error count */ + t_u32 wep_icv_error[4]; + /** beacon recv count */ + t_u32 bcn_rcv_cnt; + /** beacon miss count */ + t_u32 bcn_miss_cnt; + /** received amsdu count*/ + t_u32 amsdu_rx_cnt; + /** received msdu count in amsdu*/ + t_u32 msdu_in_rx_amsdu_cnt; + /** tx amsdu count*/ + t_u32 amsdu_tx_cnt; + /** tx msdu count in amsdu*/ + t_u32 msdu_in_tx_amsdu_cnt; + + /** Tx frag count */ + t_u32 tx_frag_cnt; + /** Qos Tx frag count */ + t_u32 qos_tx_frag_cnt[8]; + /** Qos failed count */ + t_u32 qos_failed_cnt[8]; + /** Qos retry count */ + t_u32 qos_retry_cnt[8]; + /** Qos multi retry count */ + t_u32 qos_multi_retry_cnt[8]; + /** Qos frame dup count */ + t_u32 qos_frm_dup_cnt[8]; + /** Qos rts success count */ + t_u32 qos_rts_suc_cnt[8]; + /** Qos rts failure count */ + t_u32 qos_rts_failure_cnt[8]; + /** Qos ack failure count */ + t_u32 qos_ack_failure_cnt[8]; + /** Qos Rx frag count */ + t_u32 qos_rx_frag_cnt[8]; + /** Qos Tx frame count */ + t_u32 qos_tx_frm_cnt[8]; + /** Qos discarded frame count */ + t_u32 qos_discarded_frm_cnt[8]; + /** Qos mpdus Rx count */ + t_u32 qos_mpdus_rx_cnt[8]; + /** Qos retry rx count */ + t_u32 qos_retries_rx_cnt[8]; + /** CMAC ICV errors count */ + t_u32 cmacicv_errors; + /** CMAC replays count */ + t_u32 cmac_replays; + /** mgmt CCMP replays count */ + t_u32 mgmt_ccmp_replays; + /** TKIP ICV errors count */ + t_u32 tkipicv_errors; + /** TKIP replays count */ + t_u32 tkip_replays; + /** CCMP decrypt errors count */ + t_u32 ccmp_decrypt_errors; + /** CCMP replays count */ + t_u32 ccmp_replays; + /** Tx amsdu count */ + t_u32 tx_amsdu_cnt; + /** failed amsdu count */ + t_u32 failed_amsdu_cnt; + /** retry amsdu count */ + t_u32 retry_amsdu_cnt; + /** multi-retry amsdu count */ + t_u32 multi_retry_amsdu_cnt; + /** Tx octets in amsdu count */ + t_u64 tx_octets_in_amsdu_cnt; + /** amsdu ack failure count */ + t_u32 amsdu_ack_failure_cnt; + /** Rx amsdu count */ + t_u32 rx_amsdu_cnt; + /** Rx octets in amsdu count */ + t_u64 rx_octets_in_amsdu_cnt; + /** Tx ampdu count */ + t_u32 tx_ampdu_cnt; + /** tx mpdus in ampdu count */ + t_u32 tx_mpdus_in_ampdu_cnt; + /** tx octets in ampdu count */ + t_u64 tx_octets_in_ampdu_cnt; + /** ampdu Rx count */ + t_u32 ampdu_rx_cnt; + /** mpdu in Rx ampdu count */ + t_u32 mpdu_in_rx_ampdu_cnt; + /** Rx octets ampdu count */ + t_u64 rx_octets_in_ampdu_cnt; + /** ampdu delimiter CRC error count */ + t_u32 ampdu_delimiter_crc_error_cnt; +} mlan_ds_get_stats, *pmlan_ds_get_stats; + +/** Type definition of mlan_ds_get_stats for MLAN_OID_GET_STATS */ +typedef struct _mlan_ds_get_correlated_time { + /** System time */ + t_u64 sys_time; + /** FW time */ + t_u64 hw_time; +} mlan_ds_get_correlated_time, *pmlan_ds_get_correlated_time; + +/** Type definition of mlan_ds_uap_stats for MLAN_OID_GET_STATS */ +typedef struct _mlan_ds_uap_stats { + /** tkip mic failures */ + t_u32 tkip_mic_failures; + /** ccmp decrypt errors */ + t_u32 ccmp_decrypt_errors; + /** wep undecryptable count */ + t_u32 wep_undecryptable_count; + /** wep icv error count */ + t_u32 wep_icv_error_count; + /** decrypt failure count */ + t_u32 decrypt_failure_count; + /** dot11 multicast tx count */ + t_u32 mcast_tx_count; + /** dot11 failed count */ + t_u32 failed_count; + /** dot11 retry count */ + t_u32 retry_count; + /** dot11 multi retry count */ + t_u32 multi_retry_count; + /** dot11 frame duplicate count */ + t_u32 frame_dup_count; + /** dot11 rts success count */ + t_u32 rts_success_count; + /** dot11 rts failure count */ + t_u32 rts_failure_count; + /** dot11 ack failure count */ + t_u32 ack_failure_count; + /** dot11 rx ragment count */ + t_u32 rx_fragment_count; + /** dot11 mcast rx frame count */ + t_u32 mcast_rx_frame_count; + /** dot11 fcs error count */ + t_u32 fcs_error_count; + /** dot11 tx frame count */ + t_u32 tx_frame_count; + /** dot11 rsna tkip cm invoked */ + t_u32 rsna_tkip_cm_invoked; + /** dot11 rsna 4way handshake failures */ + t_u32 rsna_4way_hshk_failures; +} mlan_ds_uap_stats, *pmlan_ds_uap_stats; + +/** Mask of last beacon RSSI */ +#define BCN_RSSI_LAST_MASK 0x00000001 +/** Mask of average beacon RSSI */ +#define BCN_RSSI_AVG_MASK 0x00000002 +/** Mask of last data RSSI */ +#define DATA_RSSI_LAST_MASK 0x00000004 +/** Mask of average data RSSI */ +#define DATA_RSSI_AVG_MASK 0x00000008 +/** Mask of last beacon SNR */ +#define BCN_SNR_LAST_MASK 0x00000010 +/** Mask of average beacon SNR */ +#define BCN_SNR_AVG_MASK 0x00000020 +/** Mask of last data SNR */ +#define DATA_SNR_LAST_MASK 0x00000040 +/** Mask of average data SNR */ +#define DATA_SNR_AVG_MASK 0x00000080 +/** Mask of last beacon NF */ +#define BCN_NF_LAST_MASK 0x00000100 +/** Mask of average beacon NF */ +#define BCN_NF_AVG_MASK 0x00000200 +/** Mask of last data NF */ +#define DATA_NF_LAST_MASK 0x00000400 +/** Mask of average data NF */ +#define DATA_NF_AVG_MASK 0x00000800 +/** Mask of all RSSI_INFO */ +#define ALL_RSSI_INFO_MASK 0x00000fff +#define MAX_PATH_NUM 1 +/** path A */ +#define PATH_A 0x01 +/** path B */ +#define PATH_B 0x02 +/** path AB */ +#define PATH_AB 0x03 +/** ALL the path */ +#define PATH_ALL 0 +/** Type definition of mlan_ds_get_signal for MLAN_OID_GET_SIGNAL */ +typedef struct _mlan_ds_get_signal { + /** Selector of get operation */ + /* + * Bit0: Last Beacon RSSI, Bit1: Average Beacon RSSI, + * Bit2: Last Data RSSI, Bit3: Average Data RSSI, + * Bit4: Last Beacon SNR, Bit5: Average Beacon SNR, + * Bit6: Last Data SNR, Bit7: Average Data SNR, + * Bit8: Last Beacon NF, Bit9: Average Beacon NF, + * Bit10: Last Data NF, Bit11: Average Data NF + * + * Bit0: PATH A + * Bit1: PATH B + */ + t_u16 selector; + + /** RSSI */ + /** RSSI of last beacon */ + t_s16 bcn_rssi_last; + /** RSSI of beacon average */ + t_s16 bcn_rssi_avg; + /** RSSI of last data packet */ + t_s16 data_rssi_last; + /** RSSI of data packet average */ + t_s16 data_rssi_avg; + + /** SNR */ + /** SNR of last beacon */ + t_s16 bcn_snr_last; + /** SNR of beacon average */ + t_s16 bcn_snr_avg; + /** SNR of last data packet */ + t_s16 data_snr_last; + /** SNR of data packet average */ + t_s16 data_snr_avg; + + /** NF */ + /** NF of last beacon */ + t_s16 bcn_nf_last; + /** NF of beacon average */ + t_s16 bcn_nf_avg; + /** NF of last data packet */ + t_s16 data_nf_last; + /** NF of data packet average */ + t_s16 data_nf_avg; +} mlan_ds_get_signal, *pmlan_ds_get_signal; + +/** bit for 2.4 G antenna diversity */ +#define ANT_DIVERSITY_2G MBIT(3) +/** bit for 5 G antenna diversity */ +#define ANT_DIVERSITY_5G MBIT(7) + +/** mlan_fw_info data structure for MLAN_OID_GET_FW_INFO */ +typedef struct _mlan_fw_info { + /** Firmware version */ + t_u32 fw_ver; + /** MAC address */ + mlan_802_11_mac_addr mac_addr; + /** 802.11n device capabilities */ + t_u32 hw_dot_11n_dev_cap; + /** Device support for MIMO abstraction of MCSs */ + t_u8 hw_dev_mcs_support; + /** user's MCS setting */ + t_u8 usr_dev_mcs_support; + /** fw supported band */ + t_u8 fw_bands; + /** region code */ + t_u16 region_code; + /** ECSA support */ + t_u8 ecsa_enable; + /** Get log support */ + t_u8 getlog_enable; + /** FW support for embedded supplicant */ + t_u8 fw_supplicant_support; + /** ant info */ + t_u8 antinfo; + /** max AP associated sta count supported by fw */ + t_u8 max_ap_assoc_sta; + /** FW support roaming offload */ + t_u8 fw_roaming_support; +} mlan_fw_info, *pmlan_fw_info; + +/** Version string buffer length */ +#define MLAN_MAX_VER_STR_LEN 128 + +/** mlan_ver_ext data structure for MLAN_OID_GET_VER_EXT */ +typedef struct _mlan_ver_ext { + /** Selected version string */ + t_u32 version_str_sel; + /** Version string */ + char version_str[MLAN_MAX_VER_STR_LEN]; +} mlan_ver_ext, *pmlan_ver_ext; + +#ifdef BIG_ENDIAN_SUPPORT +/** Extended Capabilities Data */ +typedef struct MLAN_PACK_START _ExtCap_t { + /** Extended Capabilities value */ + t_u8 rsvdBit79:1; /* bit 79 */ + t_u8 rsvdBit78:1; /* bit 78 */ + t_u8 rsvdBit77:1; /* bit 77 */ + t_u8 rsvdBit76:1; /* bit 76 */ + t_u8 rsvdBit75:1; /* bit 75 */ + t_u8 rsvdBit74:1; /* bit 74 */ + t_u8 rsvdBit73:1; /* bit 73 */ + t_u8 FILS:1; /* bit 72 */ + t_u8 FTMI:1; /* bit 71 */ + t_u8 FTMR:1; /* bit 70 */ + t_u8 CAQ:1; /* bit 69 */ + t_u8 rsvdBit68:1; /* bit 68 */ + t_u8 NCC:1; /* bit 67 */ + t_u8 rsvdBit66:1; /* bit 66 */ + t_u8 chanSchedMgnt:1; /* bit 65 */ + t_u8 MaxAMSDU:2; /* bit 63-bit 64 */ + t_u8 OperModeNtf:1; /* bit 62 */ + t_u8 TDLSWildBandwidth:1; /* bit 61 */ + t_u8 rsvdBit60:1; /* bit 60 */ + t_u8 rsvdBit59:1; /* bit 59 */ + t_u8 rsvdBit58:1; /* bit 58 */ + t_u8 rsvdBit57:1; /* bit 57 */ + t_u8 rsvdBit56:1; /* bit 56 */ + t_u8 rsvdBit55:1; /* bit 55 */ + t_u8 rsvdBit54:1; /* bit 54 */ + t_u8 rsvdBit53:1; /* bit 53 */ + t_u8 rsvdBit52:1; /* bit 52 */ + t_u8 rsvdBit51:1; /* bit 51 */ + t_u8 rsvdBit50:1; /* bit 50 */ + t_u8 rsvdBit49:1; /* bit 49 */ + t_u8 rsvdBit48:1; /* bit 48 */ + t_u8 rsvdBit47:1; /* bit 47 */ + t_u8 rsvdBit46:1; /* bit 46 */ + t_u8 rsvdBit45:1; /* bit 45 */ + t_u8 rsvdBit44:1; /* bit 44 */ + t_u8 rsvdBit43:1; /* bit 43 */ + t_u8 rsvdBit42:1; /* bit 42 */ + t_u8 rsvdBit41:1; /* bit 41 */ + t_u8 rsvdBit40:1; /* bit 40 */ + t_u8 TDLSChlSwitchProhib:1; /* bit 39 */ + t_u8 TDLSProhibited:1; /* bit 38 */ + t_u8 TDLSSupport:1; /* bit 37 */ + t_u8 MSGCF_Capa:1; /* bit 36 */ + t_u8 Reserved35:1; /* bit 35 */ + t_u8 SSPN_Interface:1; /* bit 34 */ + t_u8 EBR:1; /* bit 33 */ + t_u8 Qos_Map:1; /* bit 32 */ + t_u8 Interworking:1; /* bit 31 */ + t_u8 TDLSChannelSwitching:1; /* bit 30 */ + t_u8 TDLSPeerPSMSupport:1; /* bit 29 */ + t_u8 TDLSPeerUAPSDSupport:1; /* bit 28 */ + t_u8 UTC:1; /* bit 27 */ + t_u8 DMS:1; /* bit 26 */ + t_u8 SSID_List:1; /* bit 25 */ + t_u8 ChannelUsage:1; /* bit 24 */ + t_u8 TimingMeasurement:1; /* bit 23 */ + t_u8 MultipleBSSID:1; /* bit 22 */ + t_u8 AC_StationCount:1; /* bit 21 */ + t_u8 QoSTrafficCap:1; /* bit 20 */ + t_u8 BSS_Transition:1; /* bit 19 */ + t_u8 TIM_Broadcast:1; /* bit 18 */ + t_u8 WNM_Sleep:1; /* bit 17 */ + t_u8 TFS:1; /* bit 16 */ + t_u8 GeospatialLocation:1; /* bit 15 */ + t_u8 CivicLocation:1; /* bit 14 */ + t_u8 CollocatedIntf:1; /* bit 13 */ + t_u8 ProxyARPService:1; /* bit 12 */ + t_u8 FMS:1; /* bit 11 */ + t_u8 LocationTracking:1; /* bit 10 */ + t_u8 MulticastDiagnostics:1; /* bit 9 */ + t_u8 Diagnostics:1; /* bit 8 */ + t_u8 Event:1; /* bit 7 */ + t_u8 SPSMP_Support:1; /* bit 6 */ + t_u8 Reserved5:1; /* bit 5 */ + t_u8 PSMP_Capable:1; /* bit 4 */ + t_u8 RejectUnadmFrame:1; /* bit 3 */ + t_u8 ExtChanSwitching:1; /* bit 2 */ + t_u8 Reserved1:1; /* bit 1 */ + t_u8 BSS_CoexistSupport:1; /* bit 0 */ +} MLAN_PACK_END ExtCap_t, *pExtCap_t; +#else +/** Extended Capabilities Data */ +typedef struct MLAN_PACK_START _ExtCap_t { + /** Extended Capabilities value */ + t_u8 BSS_CoexistSupport:1; /* bit 0 */ + t_u8 Reserved1:1; /* bit 1 */ + t_u8 ExtChanSwitching:1; /* bit 2 */ + t_u8 RejectUnadmFrame:1; /* bit 3 */ + t_u8 PSMP_Capable:1; /* bit 4 */ + t_u8 Reserved5:1; /* bit 5 */ + t_u8 SPSMP_Support:1; /* bit 6 */ + t_u8 Event:1; /* bit 7 */ + t_u8 Diagnostics:1; /* bit 8 */ + t_u8 MulticastDiagnostics:1; /* bit 9 */ + t_u8 LocationTracking:1; /* bit 10 */ + t_u8 FMS:1; /* bit 11 */ + t_u8 ProxyARPService:1; /* bit 12 */ + t_u8 CollocatedIntf:1; /* bit 13 */ + t_u8 CivicLocation:1; /* bit 14 */ + t_u8 GeospatialLocation:1; /* bit 15 */ + t_u8 TFS:1; /* bit 16 */ + t_u8 WNM_Sleep:1; /* bit 17 */ + t_u8 TIM_Broadcast:1; /* bit 18 */ + t_u8 BSS_Transition:1; /* bit 19 */ + t_u8 QoSTrafficCap:1; /* bit 20 */ + t_u8 AC_StationCount:1; /* bit 21 */ + t_u8 MultipleBSSID:1; /* bit 22 */ + t_u8 TimingMeasurement:1; /* bit 23 */ + t_u8 ChannelUsage:1; /* bit 24 */ + t_u8 SSID_List:1; /* bit 25 */ + t_u8 DMS:1; /* bit 26 */ + t_u8 UTC:1; /* bit 27 */ + t_u8 TDLSPeerUAPSDSupport:1; /* bit 28 */ + t_u8 TDLSPeerPSMSupport:1; /* bit 29 */ + t_u8 TDLSChannelSwitching:1; /* bit 30 */ + t_u8 Interworking:1; /* bit 31 */ + t_u8 Qos_Map:1; /* bit 32 */ + t_u8 EBR:1; /* bit 33 */ + t_u8 SSPN_Interface:1; /* bit 34 */ + t_u8 Reserved35:1; /* bit 35 */ + t_u8 MSGCF_Capa:1; /* bit 36 */ + t_u8 TDLSSupport:1; /* bit 37 */ + t_u8 TDLSProhibited:1; /* bit 38 */ + t_u8 TDLSChlSwitchProhib:1; /* bit 39 */ + t_u8 rsvdBit40:1; /* bit 40 */ + t_u8 rsvdBit41:1; /* bit 41 */ + t_u8 rsvdBit42:1; /* bit 42 */ + t_u8 rsvdBit43:1; /* bit 43 */ + t_u8 rsvdBit44:1; /* bit 44 */ + t_u8 rsvdBit45:1; /* bit 45 */ + t_u8 rsvdBit46:1; /* bit 46 */ + t_u8 rsvdBit47:1; /* bit 47 */ + t_u8 rsvdBit48:1; /* bit 48 */ + t_u8 rsvdBit49:1; /* bit 49 */ + t_u8 rsvdBit50:1; /* bit 50 */ + t_u8 rsvdBit51:1; /* bit 51 */ + t_u8 rsvdBit52:1; /* bit 52 */ + t_u8 rsvdBit53:1; /* bit 53 */ + t_u8 rsvdBit54:1; /* bit 54 */ + t_u8 rsvdBit55:1; /* bit 55 */ + t_u8 rsvdBit56:1; /* bit 56 */ + t_u8 rsvdBit57:1; /* bit 57 */ + t_u8 rsvdBit58:1; /* bit 58 */ + t_u8 rsvdBit59:1; /* bit 59 */ + t_u8 rsvdBit60:1; /* bit 60 */ + t_u8 TDLSWildBandwidth:1; /* bit 61 */ + t_u8 OperModeNtf:1; /* bit 62 */ + t_u8 MaxAMSDU:2; /* bit 63-bit 64 */ + t_u8 chanSchedMgnt:1; /* bit 65 */ + t_u8 rsvdBit66:1; /* bit 66 */ + t_u8 NCC:1; /* bit 67 */ + t_u8 rsvdBit68:1; /* bit 68 */ + t_u8 CAQ:1; /* bit 69 */ + t_u8 FTMR:1; /* bit 70 */ + t_u8 FTMI:1; /* bit 71 */ + t_u8 FILS:1; /* bit 72 */ + t_u8 rsvdBit73:1; /* bit 73 */ + t_u8 rsvdBit74:1; /* bit 74 */ + t_u8 rsvdBit75:1; /* bit 75 */ + t_u8 rsvdBit76:1; /* bit 76 */ + t_u8 rsvdBit77:1; /* bit 77 */ + t_u8 rsvdBit78:1; /* bit 78 */ + t_u8 rsvdBit79:1; /* bit 79 */ +} MLAN_PACK_END ExtCap_t, *pExtCap_t; +#endif + +/** ExtCap : TDLS prohibited */ +#define IS_EXTCAP_TDLS_PROHIBITED(ext_cap) (ext_cap.TDLSProhibited) +/** ExtCap : TDLS channel switch prohibited */ +#define IS_EXTCAP_TDLS_CHLSWITCHPROHIB(ext_cap) (ext_cap.TDLSChlSwitchProhib) + +/** mlan_bss_info data structure for MLAN_OID_GET_BSS_INFO */ +typedef struct _mlan_bss_info { + /** BSS mode */ + t_u32 bss_mode; + /** SSID */ + mlan_802_11_ssid ssid; + /** Table index */ + t_u32 scan_table_idx; + /** Channel */ + t_u32 bss_chan; + /** Band */ + t_u8 bss_band; + /** Region code */ + t_u32 region_code; + /** Connection status */ + t_u32 media_connected; + /** Radio on */ + t_u32 radio_on; + /** Max power level in dBm */ + t_s32 max_power_level; + /** Min power level in dBm */ + t_s32 min_power_level; + /** Adhoc state */ + t_u32 adhoc_state; + /** NF of last beacon */ + t_s32 bcn_nf_last; + /** wep status */ + t_u32 wep_status; + /** scan block status */ + t_u8 scan_block; + /** Host Sleep configured flag */ + t_u32 is_hs_configured; + /** Deep Sleep flag */ + t_u32 is_deep_sleep; + /** BSSID */ + mlan_802_11_mac_addr bssid; +#ifdef STA_SUPPORT + /** Capability Info */ + t_u16 capability_info; + /** Beacon Interval */ + t_u16 beacon_interval; + /** Listen Interval */ + t_u16 listen_interval; + /** Association Id */ + t_u16 assoc_id; + /** AP/Peer supported rates */ + t_u8 peer_supp_rates[MLAN_SUPPORTED_RATES]; + /** extend capability for AP */ + ExtCap_t ext_cap; +#endif /* STA_SUPPORT */ + /** Mobility Domain ID */ + t_u16 mdid; + /** FT Capability policy */ + t_u8 ft_cap; +} mlan_bss_info, *pmlan_bss_info; + +/** MAXIMUM number of TID */ +#define MAX_NUM_TID 8 + +/** Max RX Win size */ +#define MAX_RX_WINSIZE 64 + +/** rx_reorder_tbl */ +typedef struct { + /** TID */ + t_u16 tid; + /** TA */ + t_u8 ta[MLAN_MAC_ADDR_LENGTH]; + /** Start window */ + t_u32 start_win; + /** Window size */ + t_u32 win_size; + /** amsdu flag */ + t_u8 amsdu; + /** buffer status */ + t_u32 buffer[MAX_RX_WINSIZE]; +} rx_reorder_tbl; + +/** tx_ba_stream_tbl */ +typedef struct { + /** TID */ + t_u16 tid; + /** RA */ + t_u8 ra[MLAN_MAC_ADDR_LENGTH]; + /** amsdu flag */ + t_u8 amsdu; +} tx_ba_stream_tbl; + +/** Debug command number */ +#define DBG_CMD_NUM 10 + +#ifdef SDIO_MULTI_PORT_TX_AGGR +/** sdio mp debug number */ +#define SDIO_MP_DBG_NUM 10 +#endif + +/** Maximum size of IEEE Information Elements */ +#define IEEE_MAX_IE_SIZE 256 + +/** support up to 8 TDLS peer */ +#define MLAN_MAX_TDLS_PEER_SUPPORTED 8 +/** TDLS peer info */ +typedef struct _tdls_peer_info { + /** station mac address */ + t_u8 mac_addr[MLAN_MAC_ADDR_LENGTH]; + /** SNR */ + t_s8 snr; + /** Noise Floor */ + t_s8 nf; + /** Extended Capabilities IE */ + t_u8 ext_cap[IEEE_MAX_IE_SIZE]; + /** HT Capabilities IE */ + t_u8 ht_cap[IEEE_MAX_IE_SIZE]; +} tdls_peer_info; + +/** max ralist num */ +#define MLAN_MAX_RALIST_NUM 8 +/** ralist info */ +typedef struct _ralist_info { + /** RA list buffer */ + t_u8 ra[MLAN_MAC_ADDR_LENGTH]; + /** total packets in RA list */ + t_u16 total_pkts; + /** tid num */ + t_u8 tid; + /** tx_pause flag */ + t_u8 tx_pause; +} ralist_info; + +/** mlan_debug_info data structure for MLAN_OID_GET_DEBUG_INFO */ +typedef struct _mlan_debug_info { + /* WMM AC_BK count */ + t_u32 wmm_ac_bk; + /* WMM AC_BE count */ + t_u32 wmm_ac_be; + /* WMM AC_VI count */ + t_u32 wmm_ac_vi; + /* WMM AC_VO count */ + t_u32 wmm_ac_vo; + /** Corresponds to max_tx_buf_size member of mlan_adapter*/ + t_u32 max_tx_buf_size; + /** Corresponds to tx_buf_size member of mlan_adapter*/ + t_u32 tx_buf_size; + /** Corresponds to curr_tx_buf_size member of mlan_adapter*/ + t_u32 curr_tx_buf_size; + /** Tx table num */ + t_u32 tx_tbl_num; + /** Tx ba stream table */ + tx_ba_stream_tbl tx_tbl[MLAN_MAX_TX_BASTREAM_SUPPORTED]; + /** Rx table num */ + t_u32 rx_tbl_num; + /** Rx reorder table*/ + rx_reorder_tbl rx_tbl[MLAN_MAX_RX_BASTREAM_SUPPORTED]; + /** TDLS peer number */ + t_u32 tdls_peer_num; + /** TDLS peer list*/ + tdls_peer_info tdls_peer_list[MLAN_MAX_TDLS_PEER_SUPPORTED]; + /** ralist num */ + t_u32 ralist_num; + /** ralist info */ + ralist_info ralist[MLAN_MAX_RALIST_NUM]; + /** Corresponds to ps_mode member of mlan_adapter */ + t_u16 ps_mode; + /** Corresponds to ps_state member of mlan_adapter */ + t_u32 ps_state; +#ifdef STA_SUPPORT + /** Corresponds to is_deep_sleep member of mlan_adapter */ + t_u8 is_deep_sleep; +#endif /** STA_SUPPORT */ + /** Corresponds to pm_wakeup_card_req member of mlan_adapter */ + t_u8 pm_wakeup_card_req; + /** Corresponds to pm_wakeup_fw_try member of mlan_adapter */ + t_u32 pm_wakeup_fw_try; + /** time stamp when host try to wake up firmware */ + t_u32 pm_wakeup_in_secs; + /** Corresponds to is_hs_configured member of mlan_adapter */ + t_u8 is_hs_configured; + /** Corresponds to hs_activated member of mlan_adapter */ + t_u8 hs_activated; + /** Corresponds to pps_uapsd_mode member of mlan_adapter */ + t_u16 pps_uapsd_mode; + /** Corresponds to sleep_period.period member of mlan_adapter */ + t_u16 sleep_pd; + /** Corresponds to wmm_qosinfo member of mlan_private */ + t_u8 qos_cfg; + /** Corresponds to tx_lock_flag member of mlan_adapter */ + t_u8 tx_lock_flag; + /** Corresponds to port_open member of mlan_private */ + t_u8 port_open; + /** bypass pkt count */ + t_u16 bypass_pkt_count; + /** Corresponds to scan_processing member of mlan_adapter */ + t_u32 scan_processing; + /** Corresponds to mlan_processing member of mlan_adapter */ + t_u32 mlan_processing; + /** Corresponds to main_lock_flag member of mlan_adapter */ + t_u32 main_lock_flag; + /** Corresponds to main_process_cnt member of mlan_adapter */ + t_u32 main_process_cnt; + /** Corresponds to delay_task_flag member of mlan_adapter */ + t_u32 delay_task_flag; + /** mlan_rx_processing */ + t_u32 mlan_rx_processing; + /** rx pkts queued */ + t_u32 rx_pkts_queued; + /** Number of host to card command failures */ + t_u32 num_cmd_host_to_card_failure; + /** Number of host to card sleep confirm failures */ + t_u32 num_cmd_sleep_cfm_host_to_card_failure; + /** Number of host to card Tx failures */ + t_u32 num_tx_host_to_card_failure; + /** Number of allocate buffer failure */ + t_u32 num_alloc_buffer_failure; + /** Number of pkt dropped */ + t_u32 num_pkt_dropped; + /** Number of card to host command/event failures */ + t_u32 num_cmdevt_card_to_host_failure; + /** Number of card to host Rx failures */ + t_u32 num_rx_card_to_host_failure; + /** Number of interrupt read failures */ + t_u32 num_int_read_failure; + /** Last interrupt status */ + t_u32 last_int_status; + /** number of interrupt receive */ + t_u32 num_of_irq; + /** flag for sdio rx aggr */ + t_u8 sdio_rx_aggr; + /** FW update port number */ + t_u32 mp_update[SDIO_MP_AGGR_DEF_PKT_LIMIT * 2]; + /** Invalid port update count */ + t_u32 mp_invalid_update; +#ifdef SDIO_MULTI_PORT_TX_AGGR + /** Number of packets tx aggr */ + t_u32 mpa_tx_count[SDIO_MP_AGGR_DEF_PKT_LIMIT]; + /** no more packets count*/ + t_u32 mpa_sent_last_pkt; + /** no write_ports count */ + t_u32 mpa_sent_no_ports; + /** last recv wr_bitmap */ + t_u32 last_recv_wr_bitmap; + /** last mp_wr_bitmap */ + t_u32 last_mp_wr_bitmap[SDIO_MP_DBG_NUM]; + /** last ports for cmd53 write data */ + t_u32 last_mp_wr_ports[SDIO_MP_DBG_NUM]; + /** last len for cmd53 write data */ + t_u32 last_mp_wr_len[SDIO_MP_DBG_NUM]; + /** last curr_wr_port */ + t_u8 last_curr_wr_port[SDIO_MP_DBG_NUM]; + /** length info for cmd53 write data */ + t_u16 last_mp_wr_info[SDIO_MP_DBG_NUM * SDIO_MP_AGGR_DEF_PKT_LIMIT]; + /** last mp_index */ + t_u8 last_mp_index; + /** buffer for mp debug */ + t_u8 *mpa_buf; + /** length info for mp buf size */ + t_u32 mpa_buf_size; +#endif +#ifdef SDIO_MULTI_PORT_RX_AGGR + /** Number of packets rx aggr */ + t_u32 mpa_rx_count[SDIO_MP_AGGR_DEF_PKT_LIMIT]; +#endif + /** Number of deauthentication events */ + t_u32 num_event_deauth; + /** Number of disassosiation events */ + t_u32 num_event_disassoc; + /** Number of link lost events */ + t_u32 num_event_link_lost; + /** Number of deauthentication commands */ + t_u32 num_cmd_deauth; + /** Number of association comamnd successes */ + t_u32 num_cmd_assoc_success; + /** Number of association command failures */ + t_u32 num_cmd_assoc_failure; + /** Number of Tx timeouts */ + t_u32 num_tx_timeout; + /** Number of command timeouts */ + t_u32 num_cmd_timeout; + /** Number of command timeouts */ + t_u32 dbg_num_cmd_timeout; + /** Timeout command ID */ + t_u16 timeout_cmd_id; + /** Timeout command action */ + t_u16 timeout_cmd_act; + /** List of last command IDs */ + t_u16 last_cmd_id[DBG_CMD_NUM]; + /** List of last command actions */ + t_u16 last_cmd_act[DBG_CMD_NUM]; + /** Last command index */ + t_u16 last_cmd_index; + /** List of last command response IDs */ + t_u16 last_cmd_resp_id[DBG_CMD_NUM]; + /** Last command response index */ + t_u16 last_cmd_resp_index; + /** List of last events */ + t_u16 last_event[DBG_CMD_NUM]; + /** Last event index */ + t_u16 last_event_index; + /** Number of no free command node */ + t_u16 num_no_cmd_node; + /** pending command id */ + t_u16 pending_cmd; + /** time stamp for dnld last cmd */ + t_u32 dnld_cmd_in_secs; + /** Corresponds to data_sent member of mlan_adapter */ + t_u8 data_sent; + /** Corresponds to cmd_sent member of mlan_adapter */ + t_u8 cmd_sent; + /** SDIO multiple port read bitmap */ + t_u32 mp_rd_bitmap; + /** SDIO multiple port write bitmap */ + t_u32 mp_wr_bitmap; + /** Current available port for read */ + t_u8 curr_rd_port; + /** Current available port for write */ + t_u8 curr_wr_port; + /** Corresponds to cmdresp_received member of mlan_adapter */ + t_u8 cmd_resp_received; + /** Corresponds to event_received member of mlan_adapter */ + t_u8 event_received; + /** pendig tx pkts */ + t_u32 tx_pkts_queued; +#ifdef UAP_SUPPORT + /** pending bridge pkts */ + t_u16 num_bridge_pkts; + /** dropped pkts */ + t_u32 num_drop_pkts; +#endif + /** mlan_adapter pointer */ + t_void *mlan_adapter; + /** mlan_adapter_size */ + t_u32 mlan_adapter_size; + /** mlan_priv vector */ + t_void *mlan_priv[MLAN_MAX_BSS_NUM]; + /** mlan_priv_size */ + t_u32 mlan_priv_size[MLAN_MAX_BSS_NUM]; + /** mlan_priv_num */ + t_u8 mlan_priv_num; +} mlan_debug_info, *pmlan_debug_info; + +#ifdef UAP_SUPPORT +/** Maximum number of clients supported by AP */ +#define MAX_NUM_CLIENTS MAX_STA_COUNT + +/** station info */ +typedef struct _sta_info { + /** STA MAC address */ + t_u8 mac_address[MLAN_MAC_ADDR_LENGTH]; + /** Power mfg status */ + t_u8 power_mfg_status; + /** RSSI */ + t_s8 rssi; + /** station bandmode */ + t_u8 bandmode; +} sta_info; + +/** mlan_ds_sta_list structure for MLAN_OID_UAP_STA_LIST */ +typedef struct _mlan_ds_sta_list { + /** station count */ + t_u16 sta_count; + /** station list */ + sta_info info[MAX_NUM_CLIENTS]; +} mlan_ds_sta_list, *pmlan_ds_sta_list; +#endif + +/** Type definition of mlan_ds_get_info for MLAN_IOCTL_GET_INFO */ +typedef struct _mlan_ds_get_info { + /** Sub-command */ + t_u32 sub_command; + + /** Status information parameter */ + union { + /** Signal information for MLAN_OID_GET_SIGNAL */ + mlan_ds_get_signal signal; + /** Signal path id for MLAN_OID_GET_SIGNAL_EXT */ + t_u16 path_id; + /** Signal information for MLAN_OID_GET_SIGNAL_EXT */ + mlan_ds_get_signal signal_ext[MAX_PATH_NUM]; + /** Statistics information for MLAN_OID_GET_STATS */ + mlan_ds_get_stats stats; + /** Firmware information for MLAN_OID_GET_FW_INFO */ + mlan_fw_info fw_info; + /** Extended version information for MLAN_OID_GET_VER_EXT */ + mlan_ver_ext ver_ext; + /** BSS information for MLAN_OID_GET_BSS_INFO */ + mlan_bss_info bss_info; + /** Debug information for MLAN_OID_GET_DEBUG_INFO */ + t_u8 debug_info[1]; +#ifdef UAP_SUPPORT + /** UAP Statistics information for MLAN_OID_GET_STATS */ + mlan_ds_uap_stats ustats; + /** UAP station list for MLAN_OID_UAP_STA_LIST */ + mlan_ds_sta_list sta_list; +#endif + } param; +} mlan_ds_get_info, *pmlan_ds_get_info; + +/*-----------------------------------------------------------------*/ +/** Security Configuration Group */ +/*-----------------------------------------------------------------*/ +/** Enumeration for authentication mode */ +enum _mlan_auth_mode { + MLAN_AUTH_MODE_OPEN = 0x00, + MLAN_AUTH_MODE_SHARED = 0x01, + MLAN_AUTH_MODE_FT = 0x02, + MLAN_AUTH_MODE_NETWORKEAP = 0x80, + MLAN_AUTH_MODE_AUTO = 0xFF, +}; + +/**Enumeration for AssocAgent authentication mode, sync from FW.*/ +typedef enum { + AssocAgentAuth_Open, + AssocAgentAuth_Shared, + AssocAgentAuth_FastBss, + AssocAgentAuth_FastBss_Skip, + AssocAgentAuth_Network_EAP, + AssocAgentAuth_Auto, +} AssocAgentAuthType_e; + +/** Enumeration for encryption mode */ +enum _mlan_encryption_mode { + MLAN_ENCRYPTION_MODE_NONE = 0, + MLAN_ENCRYPTION_MODE_WEP40 = 1, + MLAN_ENCRYPTION_MODE_TKIP = 2, + MLAN_ENCRYPTION_MODE_CCMP = 3, + MLAN_ENCRYPTION_MODE_WEP104 = 4, +}; + +/** Enumeration for PSK */ +enum _mlan_psk_type { + MLAN_PSK_PASSPHRASE = 1, + MLAN_PSK_PMK, + MLAN_PSK_CLEAR, + MLAN_PSK_QUERY, +}; + +/** The bit to indicate the key is for unicast */ +#define MLAN_KEY_INDEX_UNICAST 0x40000000 +/** The key index to indicate default key */ +#define MLAN_KEY_INDEX_DEFAULT 0x000000ff +/** Maximum key length */ +/* #define MLAN_MAX_KEY_LENGTH 32 */ +/** Minimum passphrase length */ +#define MLAN_MIN_PASSPHRASE_LENGTH 8 +/** Maximum passphrase length */ +#define MLAN_MAX_PASSPHRASE_LENGTH 63 +/** PMK length */ +#define MLAN_PMK_HEXSTR_LENGTH 64 +/* A few details needed for WEP (Wireless Equivalent Privacy) */ +/** 104 bits */ +#define MAX_WEP_KEY_SIZE 13 +/** 40 bits RC4 - WEP */ +#define MIN_WEP_KEY_SIZE 5 +/** packet number size */ +#define PN_SIZE 16 +/** max seq size of wpa/wpa2 key */ +#define SEQ_MAX_SIZE 8 + +/** key flag for tx_seq */ +#define KEY_FLAG_TX_SEQ_VALID 0x00000001 +/** key flag for rx_seq */ +#define KEY_FLAG_RX_SEQ_VALID 0x00000002 +/** key flag for group key */ +#define KEY_FLAG_GROUP_KEY 0x00000004 +/** key flag for tx */ +#define KEY_FLAG_SET_TX_KEY 0x00000008 +/** key flag for mcast IGTK */ +#define KEY_FLAG_AES_MCAST_IGTK 0x00000010 +/** key flag for remove key */ +#define KEY_FLAG_REMOVE_KEY 0x80000000 + +/** Type definition of mlan_ds_encrypt_key for MLAN_OID_SEC_CFG_ENCRYPT_KEY */ +typedef struct _mlan_ds_encrypt_key { + /** Key disabled, all other fields will be + * ignore when this flag set to MTRUE + */ + t_u32 key_disable; + /** key removed flag, when this flag is set + * to MTRUE, only key_index will be check + */ + t_u32 key_remove; + /** Key index, used as current tx key index + * when is_current_wep_key is set to MTRUE + */ + t_u32 key_index; + /** Current Tx key flag */ + t_u32 is_current_wep_key; + /** Key length */ + t_u32 key_len; + /** Key */ + t_u8 key_material[MLAN_MAX_KEY_LENGTH]; + /** mac address */ + t_u8 mac_addr[MLAN_MAC_ADDR_LENGTH]; + /** wapi key flag */ + t_u32 is_wapi_key; + /** Initial packet number */ + t_u8 pn[PN_SIZE]; + /** key flags */ + t_u32 key_flags; +} mlan_ds_encrypt_key, *pmlan_ds_encrypt_key; + +/** Type definition of mlan_passphrase_t */ +typedef struct _mlan_passphrase_t { + /** Length of passphrase */ + t_u32 passphrase_len; + /** Passphrase */ + t_u8 passphrase[MLAN_MAX_PASSPHRASE_LENGTH]; +} mlan_passphrase_t; + +/** Type defnition of mlan_pmk_t */ +typedef struct _mlan_pmk_t { + /** PMK */ + t_u8 pmk[MLAN_MAX_KEY_LENGTH]; + t_u8 pmk_r0[MLAN_MAX_KEY_LENGTH]; + t_u8 pmk_r0_name[MLAN_MAX_PMKR0_NAME_LENGTH]; +} mlan_pmk_t; + +/** Embedded supplicant RSN type: No RSN */ +#define RSN_TYPE_NO_RSN MBIT(0) +/** Embedded supplicant RSN type: WPA */ +#define RSN_TYPE_WPA MBIT(3) +/** Embedded supplicant RSN type: WPA-NONE */ +#define RSN_TYPE_WPANONE MBIT(4) +/** Embedded supplicant RSN type: WPA2 */ +#define RSN_TYPE_WPA2 MBIT(5) +/** Embedded supplicant RSN type: RFU */ +#define RSN_TYPE_VALID_BITS (RSN_TYPE_NO_RSN | RSN_TYPE_WPA | RSN_TYPE_WPANONE | RSN_TYPE_WPA2) + +/** Embedded supplicant cipher type: TKIP */ +#define EMBED_CIPHER_TKIP MBIT(2) +/** Embedded supplicant cipher type: AES */ +#define EMBED_CIPHER_AES MBIT(3) +/** Embedded supplicant cipher type: RFU */ +#define EMBED_CIPHER_VALID_BITS (EMBED_CIPHER_TKIP | EMBED_CIPHER_AES) + +/** Type definition of mlan_ds_passphrase for MLAN_OID_SEC_CFG_PASSPHRASE */ +typedef struct _mlan_ds_passphrase { + /** SSID may be used */ + mlan_802_11_ssid ssid; + /** BSSID may be used */ + mlan_802_11_mac_addr bssid; + /** Flag for passphrase or pmk used */ + t_u16 psk_type; + /** Passphrase or PMK */ + union { + /** Passphrase */ + mlan_passphrase_t passphrase; + /** PMK */ + mlan_pmk_t pmk; + } psk; +} mlan_ds_passphrase, *pmlan_ds_passphrase; + +/** Type definition of mlan_ds_esupp_mode for MLAN_OID_SEC_CFG_ESUPP_MODE */ +typedef struct _mlan_ds_ewpa_mode { + /** RSN mode */ + t_u32 rsn_mode; + /** Active pairwise cipher */ + t_u32 act_paircipher; + /** Active pairwise cipher */ + t_u32 act_groupcipher; +} mlan_ds_esupp_mode, *pmlan_ds_esupp_mode; + +/* Security SSID MAX number support by firmware*/ +#define MAX_SEC_SSID_NUM 6 + +/** Type definition of mlan_ds_sec_cfg for MLAN_IOCTL_SEC_CFG */ +typedef struct _mlan_ds_sec_cfg { + /** Sub-command */ + t_u32 sub_command; + /** Flag to extend some structures to support multiple values. + ** For example, mlan_ds_passphrase can only contain one value, + ** if need use mlan_ds_passphrase[N], just set this flag and + ** use mlan_ds_passphrase[] instead to avoid modify + ** more already exist code. + */ + t_u8 multi_passphrase; + /** Security configuration parameter */ + union { + /** Authentication mode for MLAN_OID_SEC_CFG_AUTH_MODE */ + t_u32 auth_mode; + /** Encryption mode for MLAN_OID_SEC_CFG_ENCRYPT_MODE */ + t_u32 encrypt_mode; + /** WPA enabled flag for MLAN_OID_SEC_CFG_WPA_ENABLED */ + t_u32 wpa_enabled; + /** WAPI enabled flag for MLAN_OID_SEC_CFG_WAPI_ENABLED */ + t_u32 wapi_enabled; + /** Port Control enabled flag for MLAN_OID_SEC_CFG_PORT_CTRL */ + t_u32 port_ctrl_enabled; + /** Encryption key for MLAN_OID_SEC_CFG_ENCRYPT_KEY */ + mlan_ds_encrypt_key encrypt_key; + /** Passphrase for MLAN_OID_SEC_CFG_PASSPHRASE */ + mlan_ds_passphrase passphrase; + /** Embedded supplicant WPA enabled flag for + * MLAN_OID_SEC_CFG_EWPA_ENABLED + */ + t_u32 ewpa_enabled; + /** Embedded supplicant mode for MLAN_OID_SEC_CFG_ESUPP_MODE */ + mlan_ds_esupp_mode esupp_mode; + mlan_ds_passphrase roam_passphrase[MAX_SEC_SSID_NUM]; + } param; +} mlan_ds_sec_cfg, *pmlan_ds_sec_cfg; + +/*-----------------------------------------------------------------*/ +/** Rate Configuration Group */ +/*-----------------------------------------------------------------*/ +/** Enumeration for rate type */ +enum _mlan_rate_type { + MLAN_RATE_INDEX, + MLAN_RATE_VALUE, + MLAN_RATE_BITMAP +}; + +/** Enumeration for rate format */ +enum _mlan_rate_format { + MLAN_RATE_FORMAT_LG = 0, + MLAN_RATE_FORMAT_HT, + MLAN_RATE_FORMAT_AUTO = 0xFF, +}; +/** Max bitmap rates size */ +#define MAX_BITMAP_RATES_SIZE 18 + +/** Type definition of mlan_rate_cfg_t for MLAN_OID_RATE_CFG */ +typedef struct _mlan_rate_cfg_t { + /** Fixed rate: 0, auto rate: 1 */ + t_u32 is_rate_auto; + /** Rate type. 0: index; 1: value; 2: bitmap */ + t_u32 rate_type; + /** Rate/MCS index or rate value if fixed rate */ + t_u32 rate; + /** Rate Bitmap */ + t_u16 bitmap_rates[MAX_BITMAP_RATES_SIZE]; + /* LG rate: 0, HT rate: 1, VHT rate: 2 */ + t_u32 rate_format; +} mlan_rate_cfg_t; + +/** HT channel bandwidth */ +typedef enum _mlan_ht_bw { + MLAN_HT_BW20, + MLAN_HT_BW40, +} mlan_ht_bw; + +/** HT guard interval */ +typedef enum _mlan_ht_gi { + MLAN_HT_LGI, + MLAN_HT_SGI, +} mlan_ht_gi; + +/** Band and BSS mode */ +typedef struct _mlan_band_data_rate { + /** Band configuration */ + t_u8 config_bands; + /** BSS mode (Infra or IBSS) */ + t_u8 bss_mode; +} mlan_band_data_rate; + +/** Type definition of mlan_data_rate for MLAN_OID_GET_DATA_RATE */ +typedef struct _mlan_data_rate { + /** Tx data rate */ + t_u32 tx_data_rate; + /** Rx data rate */ + t_u32 rx_data_rate; + + /** Tx channel bandwidth */ + t_u32 tx_ht_bw; + /** Tx guard interval */ + t_u32 tx_ht_gi; + /** Rx channel bandwidth */ + t_u32 rx_ht_bw; + /** Rx guard interval */ + t_u32 rx_ht_gi; + /** MCS index */ + t_u32 tx_mcs_index; + t_u32 rx_mcs_index; + /* LG rate: 0, HT rate: 1, VHT rate: 2 */ + t_u32 tx_rate_format; + t_u32 rx_rate_format; +} mlan_data_rate; + +/** Type definition of mlan_ds_rate for MLAN_IOCTL_RATE */ +typedef struct _mlan_ds_rate { + /** Sub-command */ + t_u32 sub_command; + /** Rate configuration parameter */ + union { + /** Rate configuration for MLAN_OID_RATE_CFG */ + mlan_rate_cfg_t rate_cfg; + /** Data rate for MLAN_OID_GET_DATA_RATE */ + mlan_data_rate data_rate; + /** Supported rates for MLAN_OID_SUPPORTED_RATES */ + t_u8 rates[MLAN_SUPPORTED_RATES]; + /** Band/BSS mode for getting supported rates */ + mlan_band_data_rate rate_band_cfg; + } param; +} mlan_ds_rate, *pmlan_ds_rate; + +/*-----------------------------------------------------------------*/ +/** Power Configuration Group */ +/*-----------------------------------------------------------------*/ + +/** Type definition of mlan_power_cfg_t for MLAN_OID_POWER_CFG */ +typedef struct _mlan_power_cfg_t { + /** Is power auto */ + t_u32 is_power_auto; + /** Power level in dBm */ + t_s32 power_level; +} mlan_power_cfg_t; + +/** max power table size */ +#define MAX_POWER_TABLE_SIZE 128 +#define TX_PWR_CFG_AUTO_CTRL_OFF 0xFF +#define MAX_POWER_GROUP 64 +/** Type definition of mlan_power group info */ +typedef struct mlan_power_group { + + /** rate format (LG: 0, HT: 1, VHT: 2, no auto ctrl: 0xFF) */ + t_u32 rate_format; + /** bandwidth (LG: 20 MHz, HT: 20/40 MHz, VHT: 80/160/80+80 MHz) */ + t_u8 bandwidth; + /** LG: first rate index, HT/VHT: first MCS */ + t_u8 first_rate_ind; + /** LG: last rate index, HT/VHT: last MCS */ + t_u8 last_rate_ind; + /** minmum tx power (dBm) */ + t_s8 power_min; + /** maximum tx power (dBm) */ + t_s8 power_max; + /** tx power step (dB) */ + t_s8 power_step; +} mlan_power_group; + +/** Type definition of mlan_power_cfg_ext for MLAN_OID_POWER_CFG_EXT */ +typedef struct _mlan_power_cfg_ext { + /** number of power_groups */ + t_u32 num_pwr_grp; + /** array of power groups */ + mlan_power_group power_group[MAX_POWER_GROUP]; +} mlan_power_cfg_ext; + +/** Type definition of mlan_ds_power_cfg for MLAN_IOCTL_POWER_CFG */ +typedef struct _mlan_ds_power_cfg { + /** Sub-command */ + t_u32 sub_command; + /** Power configuration parameter */ + union { + /** Power configuration for MLAN_OID_POWER_CFG */ + mlan_power_cfg_t power_cfg; + /** Extended power configuration for MLAN_OID_POWER_CFG_EXT */ + mlan_power_cfg_ext power_ext; + } param; +} mlan_ds_power_cfg, *pmlan_ds_power_cfg; + +/*-----------------------------------------------------------------*/ +/** Power Management Configuration Group */ +/*-----------------------------------------------------------------*/ +/** Host sleep config conditions : Cancel */ +#define HOST_SLEEP_CFG_CANCEL 0xffffffff + +/** Host sleep config condition: broadcast data */ +#define HOST_SLEEP_COND_BROADCAST_DATA MBIT(0) +/** Host sleep config condition: unicast data */ +#define HOST_SLEEP_COND_UNICAST_DATA MBIT(1) +/** Host sleep config condition: mac event */ +#define HOST_SLEEP_COND_MAC_EVENT MBIT(2) +/** Host sleep config condition: multicast data */ +#define HOST_SLEEP_COND_MULTICAST_DATA MBIT(3) +/** Host sleep config condition: IPV6 packet */ +#define HOST_SLEEP_COND_IPV6_PACKET MBIT(31) + +/** Host sleep config conditions: Default */ +#define HOST_SLEEP_DEF_COND (HOST_SLEEP_COND_BROADCAST_DATA | HOST_SLEEP_COND_UNICAST_DATA | HOST_SLEEP_COND_MAC_EVENT) + +/** Host sleep config GPIO : Default */ +#define HOST_SLEEP_DEF_GPIO 0xff +/** Host sleep config gap : Default */ +#define HOST_SLEEP_DEF_GAP 200 +/** Host sleep config min wake holdoff */ +#define HOST_SLEEP_DEF_WAKE_HOLDOFF 0; +/** Host sleep config inactivity timeout */ +#define HOST_SLEEP_DEF_INACTIVITY_TIMEOUT 10; + +/** Type definition of mlan_ds_hs_cfg for MLAN_OID_PM_CFG_HS_CFG */ +typedef struct _mlan_ds_hs_cfg { + /** MTRUE to invoke the HostCmd, MFALSE otherwise */ + t_u32 is_invoke_hostcmd; + /** Host sleep config condition */ + /** Bit0: broadcast data + * Bit1: unicast data + * Bit2: mac event + * Bit3: multicast data + */ + t_u32 conditions; + /** GPIO pin or 0xff for interface */ + t_u32 gpio; + /** Gap in milliseconds or or 0xff for special + * setting when GPIO is used to wakeup host + */ + t_u32 gap; + /** Host sleep wake interval */ + t_u32 hs_wake_interval; + /** Parameter type for indication gpio*/ + t_u8 param_type_ind; + /** GPIO pin for indication wakeup source */ + t_u32 ind_gpio; + /** Level on ind_gpio pin for indication normal wakeup source */ + t_u32 level; + /** Parameter type for extend hscfg*/ + t_u8 param_type_ext; + /** Events that will be forced ignore*/ + t_u32 event_force_ignore; + /** Events that will use extend gap to inform host*/ + t_u32 event_use_ext_gap; + /** Ext gap*/ + t_u8 ext_gap; + /** GPIO wave level for extend hscfg*/ + t_u8 gpio_wave; +} mlan_ds_hs_cfg, *pmlan_ds_hs_cfg; + +#define MAX_MGMT_FRAME_FILTER 2 +typedef struct _mlan_mgmt_frame_wakeup { + /** action - bitmap + ** On matching rx'd pkt and filter during NON_HOSTSLEEP mode: + ** Action[1]=0 Discard + ** Action[1]=1 Allow + ** Note that default action on non-match is "Allow". + ** + ** On matching rx'd pkt and filter during HOSTSLEEP mode: + ** Action[1:0]=00 Discard and Not Wake host + ** Action[1:0]=01 Discard and Wake host + ** Action[1:0]=10 Invalid + ** Note that default action on non-match is "Discard and Not Wake host". + **/ + t_u32 action; + /** Frame type(p2p, tdls...) + ** type=0: invalid + ** type=1: p2p + ** type=others: reserved + **/ + t_u32 type; + /** Frame mask according to each type + ** When type=1 for p2p, frame-mask have following define: + ** Bit Frame + ** 0 GO Negotiation Request + ** 1 GO Negotiation Response + ** 2 GO Negotiation Confirmation + ** 3 P2P Invitation Request + ** 4 P2P Invitation Response + ** 5 Device Discoverability Request + ** 6 Device Discoverability Response + ** 7 Provision Discovery Request + ** 8 Provision Discovery Response + ** 9 Notice of Absence + ** 10 P2P Presence Request + ** 11 P2P Presence Response + ** 12 GO Discoverability Request + ** 13-31 Reserved + ** + ** When type=others, frame-mask is reserved. + **/ + t_u32 frame_mask; +} mlan_mgmt_frame_wakeup, *pmlan_mgmt_frame_wakeup; + +/** Enable deep sleep mode */ +#define DEEP_SLEEP_ON 1 +/** Disable deep sleep mode */ +#define DEEP_SLEEP_OFF 0 + +/** Default idle time in milliseconds for auto deep sleep */ +#define DEEP_SLEEP_IDLE_TIME 100 + +typedef struct _mlan_ds_auto_ds { + /** auto ds mode, 0 - disable, 1 - enable */ + t_u16 auto_ds; + /** auto ds idle time in milliseconds */ + t_u16 idletime; +} mlan_ds_auto_ds; + +/** Type definition of mlan_ds_inactivity_to + * for MLAN_OID_PM_CFG_INACTIVITY_TO + */ +typedef struct _mlan_ds_inactivity_to { + /** Timeout unit in microsecond, 0 means 1000us (1ms) */ + t_u32 timeout_unit; + /** Inactivity timeout for unicast data */ + t_u32 unicast_timeout; + /** Inactivity timeout for multicast data */ + t_u32 mcast_timeout; + /** Timeout for additional Rx traffic after Null PM1 packet exchange */ + t_u32 ps_entry_timeout; +} mlan_ds_inactivity_to, *pmlan_ds_inactivity_to; + +/** Minimum sleep period in milliseconds */ +#define MIN_SLEEP_PERIOD 10 +/** Maximum sleep period in milliseconds */ +#define MAX_SLEEP_PERIOD 60 +/** Special setting for UPSD certification tests */ +#define SLEEP_PERIOD_RESERVED_FF 0xFF + +/** PS null interval disable */ +#define PS_NULL_DISABLE (-1) + +/** Local listen interval disable */ +#define MRVDRV_LISTEN_INTERVAL_DISABLE (-1) +/** Minimum listen interval */ +#define MRVDRV_MIN_LISTEN_INTERVAL 0 + +/** Minimum multiple DTIM */ +#define MRVDRV_MIN_MULTIPLE_DTIM 0 +/** Maximum multiple DTIM */ +#define MRVDRV_MAX_MULTIPLE_DTIM 5 +/** Ignore multiple DTIM */ +#define MRVDRV_IGNORE_MULTIPLE_DTIM 0xfffe +/** Match listen interval to closest DTIM */ +#define MRVDRV_MATCH_CLOSEST_DTIM 0xfffd + +/** Minimum adhoc awake period */ +#define MIN_ADHOC_AWAKE_PD 0 +/** Maximum adhoc awake period */ +#define MAX_ADHOC_AWAKE_PD 31 +/** Special adhoc awake period */ +#define SPECIAL_ADHOC_AWAKE_PD 255 + +/** Minimum beacon miss timeout in milliseconds */ +#define MIN_BCN_MISS_TO 0 +/** Maximum beacon miss timeout in milliseconds */ +#define MAX_BCN_MISS_TO 50 +/** Disable beacon miss timeout */ +#define DISABLE_BCN_MISS_TO 65535 + +/** Minimum delay to PS in milliseconds */ +#define MIN_DELAY_TO_PS 0 +/** Maximum delay to PS in milliseconds */ +#define MAX_DELAY_TO_PS 65535 +/** Delay to PS unchanged */ +#define DELAY_TO_PS_UNCHANGED (-1) +/** Default delay to PS in milliseconds */ +#define DELAY_TO_PS_DEFAULT 1000 + +/** PS mode: Unchanged */ +#define PS_MODE_UNCHANGED 0 +/** PS mode: Auto */ +#define PS_MODE_AUTO 1 +/** PS mode: Poll */ +#define PS_MODE_POLL 2 +/** PS mode: Null */ +#define PS_MODE_NULL 3 + +/** Type definition of mlan_ds_ps_cfg for MLAN_OID_PM_CFG_PS_CFG */ +typedef struct _mlan_ds_ps_cfg { + /** PS null interval in seconds */ + t_u32 ps_null_interval; + /** Multiple DTIM interval */ + t_u32 multiple_dtim_interval; + /** Listen interval */ + t_u32 listen_interval; + /** Adhoc awake period */ + t_u32 adhoc_awake_period; + /** Beacon miss timeout in milliseconds */ + t_u32 bcn_miss_timeout; + /** Delay to PS in milliseconds */ + t_s32 delay_to_ps; + /** PS mode */ + t_u32 ps_mode; +} mlan_ds_ps_cfg, *pmlan_ds_ps_cfg; + +/** Type definition of mlan_ds_sleep_params for MLAN_OID_PM_CFG_SLEEP_PARAMS */ +typedef struct _mlan_ds_sleep_params { + /** Error */ + t_u32 error; + /** Offset in microseconds */ + t_u32 offset; + /** Stable time in microseconds */ + t_u32 stable_time; + /** Calibration control */ + t_u32 cal_control; + /** External sleep clock */ + t_u32 ext_sleep_clk; + /** Reserved */ + t_u32 reserved; +} mlan_ds_sleep_params, *pmlan_ds_sleep_params; + +/** sleep_param */ +typedef struct _ps_sleep_param { + /** control bitmap */ + t_u32 ctrl_bitmap; + /** minimum sleep period (micro second) */ + t_u32 min_sleep; + /** maximum sleep period (micro second) */ + t_u32 max_sleep; +} ps_sleep_param; + +/** inactivity sleep_param */ +typedef struct _inact_sleep_param { + /** inactivity timeout (micro second) */ + t_u32 inactivity_to; + /** miniumu awake period (micro second) */ + t_u32 min_awake; + /** maximum awake period (micro second) */ + t_u32 max_awake; +} inact_sleep_param; + +/** flag for ps mode */ +#define PS_FLAG_PS_MODE 1 +/** flag for sleep param */ +#define PS_FLAG_SLEEP_PARAM 2 +/** flag for inactivity sleep param */ +#define PS_FLAG_INACT_SLEEP_PARAM 4 + +/** Enable Robust Coex mode */ +#define ROBUSTCOEX_GPIOCFG_ENABLE 1 +/** Disable Robust Coex mode */ +#define ROBUSTCOEX_GPIOCFG_DISABLE 0 + +/** Disable power mode */ +#define PS_MODE_DISABLE 0 +/** Enable periodic dtim ps */ +#define PS_MODE_PERIODIC_DTIM 1 +/** Enable inactivity ps */ +#define PS_MODE_INACTIVITY 2 +/** FW wake up method interface */ +#define FW_WAKEUP_METHOD_INTERFACE 1 +/** FW wake up method gpio */ +#define FW_WAKEUP_METHOD_GPIO 2 +/** mlan_ds_ps_mgmt */ +typedef struct _mlan_ds_ps_mgmt { + /** flags for valid field */ + t_u16 flags; + /** power mode */ + t_u16 ps_mode; + /** sleep param */ + ps_sleep_param sleep_param; + /** inactivity sleep param */ + inact_sleep_param inact_param; +} mlan_ds_ps_mgmt; + +/** mlan_ds_ps_info */ +typedef struct _mlan_ds_ps_info { + /** suspend allowed flag */ + t_u32 is_suspend_allowed; +} mlan_ds_ps_info; + +/** Type definition of mlan_ds_wakeup_reason for MLAN_OID_PM_HS_WAKEUP_REASON */ +typedef struct _mlan_ds_hs_wakeup_reason { + t_u16 hs_wakeup_reason; +} mlan_ds_hs_wakeup_reason; + +/** Type definition of mlan_fw_wakeup_params for MLAN_OID_PM_CFG_FW_WAKEUP_METHOD */ +typedef struct _mlan_fw_wakeup_params { + /** FW wakeup method */ + t_u16 method; + /** GPIO pin NO.*/ + t_u8 gpio_pin; +} mlan_fw_wakeup_params, *pmlan_fw_wakeup_params; + +/** Type definition of mlan_ds_ps_cfg for MLAN_OID_PM_CFG_PS_CFG */ +typedef struct _mlan_ds_bcn_timeout { + /** Beacon miss timeout period window */ + t_u16 bcn_miss_tmo_window; + /** Beacon miss timeout period */ + t_u16 bcn_miss_tmo_period; + /** Beacon reacquire timeout period window */ + t_u16 bcn_rq_tmo_window; + /** Beacon reacquire timeout period */ + t_u16 bcn_rq_tmo_period; +} mlan_ds_bcn_timeout, *pmlan_ds_bcn_timeout; + +/** Type definition of mlan_ds_pm_cfg for MLAN_IOCTL_PM_CFG */ +typedef struct _mlan_ds_pm_cfg { + /** Sub-command */ + t_u32 sub_command; + /** Power management parameter */ + union { + /** Power saving mode for MLAN_OID_PM_CFG_IEEE_PS */ + t_u32 ps_mode; + /** Host Sleep configuration for MLAN_OID_PM_CFG_HS_CFG */ + mlan_ds_hs_cfg hs_cfg; + /** Deep sleep mode for MLAN_OID_PM_CFG_DEEP_SLEEP */ + mlan_ds_auto_ds auto_deep_sleep; + /** Inactivity timeout for MLAN_OID_PM_CFG_INACTIVITY_TO */ + mlan_ds_inactivity_to inactivity_to; + /** Sleep period for MLAN_OID_PM_CFG_SLEEP_PD */ + t_u32 sleep_period; + /** PS configuration parameters for MLAN_OID_PM_CFG_PS_CFG */ + mlan_ds_ps_cfg ps_cfg; + /** FW wakeup method for MLAN_OID_PM_CFG_FW_WAKEUP_METHOD */ + mlan_fw_wakeup_params fw_wakeup_params; + /** PS configuration parameters for MLAN_OID_PM_CFG_SLEEP_PARAMS */ + mlan_ds_sleep_params sleep_params; + /** PS configuration parameters for MLAN_OID_PM_CFG_PS_MODE */ + mlan_ds_ps_mgmt ps_mgmt; + /** power info for MLAN_OID_PM_INFO */ + mlan_ds_ps_info ps_info; + /** hs wakeup reason for MLAN_OID_PM_HS_WAKEUP_REASON */ + mlan_ds_hs_wakeup_reason wakeup_reason; + /** config manamgement frame for hs wakeup */ + mlan_mgmt_frame_wakeup mgmt_filter[MAX_MGMT_FRAME_FILTER]; + /** Beacon timout parameters for MLAN_OID_PM_CFG_BCN_TIMEOUT */ + mlan_ds_bcn_timeout bcn_timeout; + } param; +} mlan_ds_pm_cfg, *pmlan_ds_pm_cfg; + +#ifdef RX_PACKET_COALESCE +typedef struct { + mlan_cmd_result_e cmd_result; /**< Firmware execution result */ + + t_u32 pkt_threshold; + /** Packet threshold */ + t_u16 delay; + /** Timeout value in milliseconds */ +} wlan_ioctl_rx_pkt_coalesce_config_t; +#endif + +/*-----------------------------------------------------------------*/ +/** WMM Configuration Group */ +/*-----------------------------------------------------------------*/ + +/** WMM TSpec size */ +#define MLAN_WMM_TSPEC_SIZE 63 +/** WMM Add TS extra IE bytes */ +#define MLAN_WMM_ADDTS_EXTRA_IE_BYTES 256 +/** WMM statistics for packets hist bins */ +#define MLAN_WMM_STATS_PKTS_HIST_BINS 7 +/** Maximum number of AC QOS queues available */ +#define MLAN_WMM_MAX_AC_QUEUES 4 + +/** + * @brief IOCTL structure to send an ADDTS request and retrieve the response. + * + * IOCTL structure from the application layer relayed to firmware to + * instigate an ADDTS management frame with an appropriate TSPEC IE as well + * as any additional IEs appended in the ADDTS Action frame. + * + * @sa woal_wmm_addts_req_ioctl + */ +typedef struct { + mlan_cmd_result_e cmd_result; /**< Firmware execution result */ + + t_u32 timeout_ms; /**< Timeout value in milliseconds */ + t_u8 ieee_status_code; /**< IEEE status code */ + + t_u32 ie_data_len; /**< Length of ie block in ie_data */ + t_u8 ie_data[MLAN_WMM_TSPEC_SIZE + /**< TSPEC to send in the ADDTS */ + + MLAN_WMM_ADDTS_EXTRA_IE_BYTES]; + /**< Extra IE buf*/ +} wlan_ioctl_wmm_addts_req_t; + +/** + * @brief IOCTL structure to send a DELTS request. + * + * IOCTL structure from the application layer relayed to firmware to + * instigate an DELTS management frame with an appropriate TSPEC IE. + * + * @sa woal_wmm_delts_req_ioctl + */ +typedef struct { + mlan_cmd_result_e cmd_result; + /**< Firmware execution result */ + t_u8 ieee_reason_code; /**< IEEE reason code sent, unused for WMM */ + t_u32 ie_data_len; /**< Length of ie block in ie_data */ + t_u8 ie_data[MLAN_WMM_TSPEC_SIZE]; + /**< TSPEC to send in the DELTS */ +} wlan_ioctl_wmm_delts_req_t; + +/** + * @brief IOCTL structure to configure a specific AC Queue's parameters + * + * IOCTL structure from the application layer relayed to firmware to + * get, set, or default the WMM AC queue parameters. + * + * - msdu_lifetime_expiry is ignored if set to 0 on a set command + * + * @sa woal_wmm_queue_config_ioctl + */ +typedef struct { + mlan_wmm_queue_config_action_e action;/**< Set, Get, or Default */ + mlan_wmm_ac_e access_category; /**< WMM_AC_BK(0) to WMM_AC_VO(3) */ + t_u16 msdu_lifetime_expiry; /**< lifetime expiry in TUs */ + t_u8 supported_rates[10]; /**< Not supported yet */ +} wlan_ioctl_wmm_queue_config_t; + +/** + * @brief IOCTL structure to start, stop, and get statistics for a WMM AC + * + * IOCTL structure from the application layer relayed to firmware to + * start or stop statistical collection for a given AC. Also used to + * retrieve and clear the collected stats on a given AC. + * + * @sa woal_wmm_queue_stats_ioctl + */ +typedef struct { + /** Action of Queue Config : Start, Stop, or Get */ + mlan_wmm_queue_stats_action_e action; + /** User Priority */ + t_u8 user_priority; + /** Number of successful packets transmitted */ + t_u16 pkt_count; + /** Packets lost; not included in pkt_count */ + t_u16 pkt_loss; + /** Average Queue delay in microseconds */ + t_u32 avg_queue_delay; + /** Average Transmission delay in microseconds */ + t_u32 avg_tx_delay; + /** Calculated used time in units of 32 microseconds */ + t_u16 used_time; + /** Calculated policed time in units of 32 microseconds */ + t_u16 policed_time; + /** Queue Delay Histogram; number of packets per queue delay range + * + * [0] - 0ms <= delay < 5ms + * [1] - 5ms <= delay < 10ms + * [2] - 10ms <= delay < 20ms + * [3] - 20ms <= delay < 30ms + * [4] - 30ms <= delay < 40ms + * [5] - 40ms <= delay < 50ms + * [6] - 50ms <= delay < msduLifetime (TUs) + */ + t_u16 delay_histogram[MLAN_WMM_STATS_PKTS_HIST_BINS]; +} wlan_ioctl_wmm_queue_stats_t, +/** Type definition of mlan_ds_wmm_queue_stats + * for MLAN_OID_WMM_CFG_QUEUE_STATS + */ +mlan_ds_wmm_queue_stats, *pmlan_ds_wmm_queue_stats; + +/** + * @brief IOCTL sub structure for a specific WMM AC Status + */ +typedef struct { + /** WMM Acm */ + t_u8 wmm_acm; + /** Flow required flag */ + t_u8 flow_required; + /** Flow created flag */ + t_u8 flow_created; + /** Disabled flag */ + t_u8 disabled; +} wlan_ioctl_wmm_queue_status_ac_t; + +/** + * @brief IOCTL structure to retrieve the WMM AC Queue status + * + * IOCTL structure from the application layer to retrieve: + * - ACM bit setting for the AC + * - Firmware status (flow required, flow created, flow disabled) + * + * @sa woal_wmm_queue_status_ioctl + */ +typedef struct { + /** WMM AC queue status */ + wlan_ioctl_wmm_queue_status_ac_t ac_status[MLAN_WMM_MAX_AC_QUEUES]; +} wlan_ioctl_wmm_queue_status_t, +/** Type definition of mlan_ds_wmm_queue_status + * for MLAN_OID_WMM_CFG_QUEUE_STATUS + */ +mlan_ds_wmm_queue_status, *pmlan_ds_wmm_queue_status; + +/** Type definition of mlan_ds_wmm_addts for MLAN_OID_WMM_CFG_ADDTS */ +typedef struct _mlan_ds_wmm_addts { + /** Result of ADDTS request */ + mlan_cmd_result_e result; + /** Timeout value in milliseconds */ + t_u32 timeout; + /** IEEE status code */ + t_u32 status_code; + /** Dialog token */ + t_u8 dialog_tok; + /** TSPEC data length */ + t_u32 ie_data_len; + /** TSPEC to send in the ADDTS + buffering for any extra IEs */ + t_u8 ie_data[MLAN_WMM_TSPEC_SIZE + MLAN_WMM_ADDTS_EXTRA_IE_BYTES]; +} mlan_ds_wmm_addts, *pmlan_ds_wmm_addts; + +/** Type definition of mlan_ds_wmm_delts for MLAN_OID_WMM_CFG_DELTS */ +typedef struct _mlan_ds_wmm_delts { + /** Result of DELTS request */ + mlan_cmd_result_e result; + /** IEEE status code */ + t_u32 status_code; + /** TSPEC data length */ + t_u8 ie_data_len; + /** TSPEC to send in the DELTS */ + t_u8 ie_data[MLAN_WMM_TSPEC_SIZE]; +} mlan_ds_wmm_delts, *pmlan_ds_wmm_delts; + +/** Type definition of mlan_ds_wmm_queue_config + * for MLAN_OID_WMM_CFG_QUEUE_CONFIG + */ +typedef struct _mlan_ds_wmm_queue_config { + /** Action of Queue Config : Set, Get, or Default */ + mlan_wmm_queue_config_action_e action; + /** WMM Access Category: WMM_AC_BK(0) to WMM_AC_VO(3) */ + mlan_wmm_ac_e access_category; + /** Lifetime expiry in TUs */ + t_u16 msdu_lifetime_expiry; + /** Reserve for future use */ + t_u8 reserved[10]; +} mlan_ds_wmm_queue_config, *pmlan_ds_wmm_queue_config; + +/** Type definition of mlan_ds_wmm_cfg for MLAN_IOCTL_WMM_CFG */ +typedef struct _mlan_ds_wmm_cfg { + /** Sub-command */ + t_u32 sub_command; + /** WMM configuration parameter */ + union { + /** WMM enable for MLAN_OID_WMM_CFG_ENABLE */ + t_u32 wmm_enable; + /** QoS configuration for MLAN_OID_WMM_CFG_QOS */ + t_u8 qos_cfg; + /** WMM add TS for MLAN_OID_WMM_CFG_ADDTS */ + mlan_ds_wmm_addts addts; + /** WMM delete TS for MLAN_OID_WMM_CFG_DELTS */ + mlan_ds_wmm_delts delts; + /** WMM queue configuration for MLAN_OID_WMM_CFG_QUEUE_CONFIG */ + mlan_ds_wmm_queue_config q_cfg; + /** AC Parameters Record WMM_AC_BE, WMM_AC_BK, WMM_AC_VI, WMM_AC_VO */ + wmm_ac_parameters_t ac_params[MAX_AC_QUEUES]; + /** WMM queue status for MLAN_OID_WMM_CFG_QUEUE_STATS */ + mlan_ds_wmm_queue_stats q_stats; + /** WMM queue status for MLAN_OID_WMM_CFG_QUEUE_STATUS */ + mlan_ds_wmm_queue_status q_status; + /** WMM TS status for MLAN_OID_WMM_CFG_TS_STATUS */ + mlan_ds_wmm_ts_status ts_status; + } param; +} mlan_ds_wmm_cfg, *pmlan_ds_wmm_cfg; + +/*-----------------------------------------------------------------*/ +/** WPS Configuration Group */ +/*-----------------------------------------------------------------*/ +/** Enumeration for WPS session */ +enum _mlan_wps_status { + MLAN_WPS_CFG_SESSION_START = 1, + MLAN_WPS_CFG_SESSION_END = 0 +}; + +/** Type definition of mlan_ds_wps_cfg for MLAN_IOCTL_WPS_CFG */ +typedef struct _mlan_ds_wps_cfg { + /** Sub-command */ + t_u32 sub_command; + /** WPS configuration parameter */ + union { + /** WPS session for MLAN_OID_WPS_CFG_SESSION */ + t_u32 wps_session; + } param; +} mlan_ds_wps_cfg, *pmlan_ds_wps_cfg; + +/*-----------------------------------------------------------------*/ +/** 802.11n Configuration Group */ +/*-----------------------------------------------------------------*/ +/** Maximum MCS */ +#define NUM_MCS_FIELD 16 + +/* Both 2.4G and 5G band selected */ +#define BAND_SELECT_BOTH 0 +/* Band 2.4G selected */ +#define BAND_SELECT_BG 1 +/* Band 5G selected */ +#define BAND_SELECT_A 2 + +/** Type definition of mlan_ds_11n_htcap_cfg for MLAN_OID_11N_HTCAP_CFG */ +typedef struct _mlan_ds_11n_htcap_cfg { + /** HT Capability information */ + t_u32 htcap; + /** Band selection */ + t_u32 misc_cfg; + /** Hardware HT cap information required */ + t_u32 hw_cap_req; +} mlan_ds_11n_htcap_cfg, *pmlan_ds_11n_htcap_cfg; + +/** Type definition of mlan_ds_11n_addba_param + * for MLAN_OID_11N_CFG_ADDBA_PARAM + */ +typedef struct _mlan_ds_11n_addba_param { + /** Timeout */ + t_u32 timeout; + /** Buffer size for ADDBA request */ + t_u32 txwinsize; + /** Buffer size for ADDBA response */ + t_u32 rxwinsize; + /** amsdu for ADDBA request */ + t_u8 txamsdu; + /** amsdu for ADDBA response */ + t_u8 rxamsdu; +} mlan_ds_11n_addba_param, *pmlan_ds_11n_addba_param; + +/** Type definition of mlan_ds_11n_tx_cfg for MLAN_OID_11N_CFG_TX */ +typedef struct _mlan_ds_11n_tx_cfg { + /** HTTxCap */ + t_u16 httxcap; + /** HTTxInfo */ + t_u16 httxinfo; + /** Band selection */ + t_u32 misc_cfg; +} mlan_ds_11n_tx_cfg, *pmlan_ds_11n_tx_cfg; + +/** BF Global Configuration */ +#define BF_GLOBAL_CONFIGURATION 0x00 +/** Performs NDP sounding for PEER specified */ +#define TRIGGER_SOUNDING_FOR_PEER 0x01 +/** TX BF interval for channel sounding */ +#define SET_GET_BF_PERIODICITY 0x02 +/** Tell FW not to perform any sounding for peer */ +#define TX_BF_FOR_PEER_ENBL 0x03 +/** TX BF SNR threshold for peer */ +#define SET_SNR_THR_PEER 0x04 +/** TX Sounding*/ +#define TX_SOUNDING_CFG 0x05 + +/* Maximum number of peer MAC and status/SNR tuples */ +#define MAX_PEER_MAC_TUPLES 10 + +/** Any new subcommand structure should be declare here */ + +/** bf global cfg args */ +typedef struct _mlan_bf_global_cfg_args { + /** Global enable/disable bf */ + t_u8 bf_enbl; + /** Global enable/disable sounding */ + t_u8 sounding_enbl; + /** FB Type */ + t_u8 fb_type; + /** SNR Threshold */ + t_u8 snr_threshold; + /** Sounding interval in milliseconds */ + t_u16 sounding_interval; + /** BF mode */ + t_u8 bf_mode; + /** Reserved */ + t_u8 reserved; +} mlan_bf_global_cfg_args; + +/** trigger sounding args */ +typedef struct _mlan_trigger_sound_args { + /** Peer MAC address */ + t_u8 peer_mac[MLAN_MAC_ADDR_LENGTH]; + /** Status */ + t_u8 status; +} mlan_trigger_sound_args; + +/** bf periodicity args */ +typedef struct _mlan_bf_periodicity_args { + /** Peer MAC address */ + t_u8 peer_mac[MLAN_MAC_ADDR_LENGTH]; + /** Current Tx BF Interval in milliseconds */ + t_u16 interval; + /** Status */ + t_u8 status; +} mlan_bf_periodicity_args; + +/** tx bf peer args */ +typedef struct _mlan_tx_bf_peer_args { + /** Peer MAC address */ + t_u8 peer_mac[MLAN_MAC_ADDR_LENGTH]; + /** Reserved */ + t_u16 reserved; + /** Enable/Disable Beamforming */ + t_u8 bf_enbl; + /** Enable/Disable sounding */ + t_u8 sounding_enbl; + /** FB Type */ + t_u8 fb_type; +} mlan_tx_bf_peer_args; + +/** SNR threshold args */ +typedef struct _mlan_snr_thr_args { + /** Peer MAC address */ + t_u8 peer_mac[MLAN_MAC_ADDR_LENGTH]; + /** SNR for peer */ + t_u8 snr; +} mlan_snr_thr_args; + +/** Type definition of mlan_ds_11n_tx_bf_cfg for MLAN_OID_11N_CFG_TX_BF_CFG */ +typedef struct _mlan_ds_11n_tx_bf_cfg { + /** BF Action */ + t_u16 bf_action; + /** Action */ + t_u16 action; + /** Number of peers */ + t_u32 no_of_peers; + union { + mlan_bf_global_cfg_args bf_global_cfg; + mlan_trigger_sound_args bf_sound[MAX_PEER_MAC_TUPLES]; + mlan_bf_periodicity_args bf_periodicity[MAX_PEER_MAC_TUPLES]; + mlan_tx_bf_peer_args tx_bf_peer[MAX_PEER_MAC_TUPLES]; + mlan_snr_thr_args bf_snr[MAX_PEER_MAC_TUPLES]; + } body; +} mlan_ds_11n_tx_bf_cfg, *pmlan_ds_11n_tx_bf_cfg; + +/** Type definition of mlan_ds_11n_amsdu_aggr_ctrl for + * MLAN_OID_11N_AMSDU_AGGR_CTRL*/ +typedef struct _mlan_ds_11n_amsdu_aggr_ctrl { + /** Enable/Disable */ + t_u16 enable; + /** Current AMSDU size valid */ + t_u16 curr_buf_size; +} mlan_ds_11n_amsdu_aggr_ctrl, *pmlan_ds_11n_amsdu_aggr_ctrl; + +/** Type definition of mlan_ds_11n_aggr_prio_tbl + * for MLAN_OID_11N_CFG_AGGR_PRIO_TBL + */ +typedef struct _mlan_ds_11n_aggr_prio_tbl { + /** ampdu priority table */ + t_u8 ampdu[MAX_NUM_TID]; + /** amsdu priority table */ + t_u8 amsdu[MAX_NUM_TID]; +} mlan_ds_11n_aggr_prio_tbl, *pmlan_ds_11n_aggr_prio_tbl; + +/** DelBA All TIDs */ +#define DELBA_ALL_TIDS 0xff +/** DelBA Tx */ +#define DELBA_TX MBIT(0) +/** DelBA Rx */ +#define DELBA_RX MBIT(1) + +/** Type definition of mlan_ds_11n_delba for MLAN_OID_11N_CFG_DELBA */ +typedef struct _mlan_ds_11n_delba { + /** TID */ + t_u8 tid; + /** Peer MAC address */ + t_u8 peer_mac_addr[MLAN_MAC_ADDR_LENGTH]; + /** Direction (Tx: bit 0, Rx: bit 1) */ + t_u8 direction; +} mlan_ds_11n_delba, *pmlan_ds_11n_delba; + +/** Type definition of mlan_ds_delba for MLAN_OID_11N_CFG_REJECT_ADDBA_REQ */ +typedef struct _mlan_ds_reject_addba_req { + /** Bit0 : host sleep activated + * Bit1 : auto reconnect enabled + * Others : reserved + */ + t_u32 conditions; +} mlan_ds_reject_addba_req, *pmlan_ds_reject_addba_req; + +/** Type definition of mlan_ds_ibss_ampdu_param */ +typedef struct _mlan_ds_ibss_ampdu_param { + /** ampdu priority table */ + t_u8 ampdu[MAX_NUM_TID]; + /** rx amdpdu setting */ + t_u8 addba_reject[MAX_NUM_TID]; +} mlan_ds_ibss_ampdu_param, *pmlan_ds_ibss_ampdu_param; + +/** Type definition of mlan_ds_11n_cfg for MLAN_IOCTL_11N_CFG */ +typedef struct _mlan_ds_11n_cfg { + /** Sub-command */ + t_u32 sub_command; + /** 802.11n configuration parameter */ + union { + /** Tx param for 11n for MLAN_OID_11N_CFG_TX */ + mlan_ds_11n_tx_cfg tx_cfg; + /** Aggr priority table for MLAN_OID_11N_CFG_AGGR_PRIO_TBL */ + mlan_ds_11n_aggr_prio_tbl aggr_prio_tbl; + /** Add BA param for MLAN_OID_11N_CFG_ADDBA_PARAM */ + mlan_ds_11n_addba_param addba_param; + /** Add BA Reject paramters for MLAN_OID_11N_CFG_ADDBA_REJECT */ + t_u8 addba_reject[MAX_NUM_TID]; + /** Tx buf size for MLAN_OID_11N_CFG_MAX_TX_BUF_SIZE */ + t_u32 tx_buf_size; + /** HT cap info configuration for MLAN_OID_11N_HTCAP_CFG */ + mlan_ds_11n_htcap_cfg htcap_cfg; + /** Tx param for 11n for MLAN_OID_11N_AMSDU_AGGR_CTRL */ + mlan_ds_11n_amsdu_aggr_ctrl amsdu_aggr_ctrl; + /** Supported MCS Set field */ + t_u8 supported_mcs_set[NUM_MCS_FIELD]; + /** Transmit Beamforming Capabilities field */ + t_u32 tx_bf_cap; + /** Transmit Beamforming configuration */ + mlan_ds_11n_tx_bf_cfg tx_bf; + /** DelBA for MLAN_OID_11N_CFG_DELBA */ + mlan_ds_11n_delba del_ba; + /** Reject Addba Req for MLAN_OID_11N_CFG_REJECT_ADDBA_REQ */ + mlan_ds_reject_addba_req reject_addba_req; + /** Control coex RX window size configuration */ + t_u32 coex_rx_winsize; + /** Control TX AMPDU configuration */ + t_u32 txaggrctrl; + /** aggrprirotity table for MLAN_OID_11N_CFG_IBSS_AMPDU_PARAM */ + mlan_ds_ibss_ampdu_param ibss_ampdu; + } param; +} mlan_ds_11n_cfg, *pmlan_ds_11n_cfg; + +/** Country code length */ +#define COUNTRY_CODE_LEN 3 + +/*-----------------------------------------------------------------*/ +/** 802.11d Configuration Group */ +/*-----------------------------------------------------------------*/ +/** Maximum subbands for 11d */ +#define MRVDRV_MAX_SUBBAND_802_11D 83 + +#ifdef STA_SUPPORT +/** Data structure for subband set */ +typedef struct _mlan_ds_subband_set_t { + /** First channel */ + t_u8 first_chan; + /** Number of channels */ + t_u8 no_of_chan; + /** Maximum Tx power in dBm */ + t_u8 max_tx_pwr; +} mlan_ds_subband_set_t; + +/** Domain regulatory information */ +typedef struct _mlan_ds_11d_domain_info { + /** Country Code */ + t_u8 country_code[COUNTRY_CODE_LEN]; + /** Band that channels in sub_band belong to */ + t_u8 band; + /** No. of subband in below */ + t_u8 no_of_sub_band; + /** Subband data to send/last sent */ + mlan_ds_subband_set_t sub_band[MRVDRV_MAX_SUBBAND_802_11D]; +} mlan_ds_11d_domain_info; +#endif + +/** Type definition of mlan_ds_11d_cfg for MLAN_IOCTL_11D_CFG */ +typedef struct _mlan_ds_11d_cfg { + /** Sub-command */ + t_u32 sub_command; + /** 802.11d configuration parameter */ + union { +#ifdef STA_SUPPORT + /** Enable for MLAN_OID_11D_CFG_ENABLE */ + t_u32 enable_11d; + /** Domain info for MLAN_OID_11D_DOMAIN_INFO */ + mlan_ds_11d_domain_info domain_info; +#endif /* STA_SUPPORT */ +#ifdef UAP_SUPPORT + /** tlv data for MLAN_OID_11D_DOMAIN_INFO */ + t_u8 domain_tlv[MAX_IE_SIZE]; +#endif /* UAP_SUPPORT */ + } param; +} mlan_ds_11d_cfg, *pmlan_ds_11d_cfg; + +typedef struct _mlan_ds_11k_cfg { + /** Sub-command */ + t_u32 sub_command; + union { + t_u32 enable_11k; + } param; +} mlan_ds_11k_cfg; + +/*-----------------------------------------------------------------*/ +/** Register Memory Access Group */ +/*-----------------------------------------------------------------*/ +/** Enumeration for CSU target device type */ +enum _mlan_csu_target_type { + MLAN_CSU_TARGET_CAU = 1, + MLAN_CSU_TARGET_PSU, +}; + +/** Enumeration for register type */ +enum _mlan_reg_type { + MLAN_REG_MAC = 1, + MLAN_REG_BBP, + MLAN_REG_RF, + MLAN_REG_CAU = 5, + MLAN_REG_PSU = 6, +}; + +/** Type definition of mlan_ds_reg_rw for MLAN_OID_REG_RW */ +typedef struct _mlan_ds_reg_rw { + /** Register type */ + t_u32 type; + /** Offset */ + t_u32 offset; + /** Value */ + t_u32 value; +} mlan_ds_reg_rw; + +/** Maximum EEPROM data */ +#define MAX_EEPROM_DATA 256 + +/** Type definition of mlan_ds_read_eeprom for MLAN_OID_EEPROM_RD */ +typedef struct _mlan_ds_read_eeprom { + /** Multiples of 4 */ + t_u16 offset; + /** Number of bytes */ + t_u16 byte_count; + /** Value */ + t_u8 value[MAX_EEPROM_DATA]; +} mlan_ds_read_eeprom; + +/** Type definition of mlan_ds_mem_rw for MLAN_OID_MEM_RW */ +typedef struct _mlan_ds_mem_rw { + /** Address */ + t_u32 addr; + /** Value */ + t_u32 value; +} mlan_ds_mem_rw; + +/** Type definition of mlan_ds_reg_mem for MLAN_IOCTL_REG_MEM */ +typedef struct _mlan_ds_reg_mem { + /** Sub-command */ + t_u32 sub_command; + /** Register memory access parameter */ + union { + /** Register access for MLAN_OID_REG_RW */ + mlan_ds_reg_rw reg_rw; + /** EEPROM access for MLAN_OID_EEPROM_RD */ + mlan_ds_read_eeprom rd_eeprom; + /** Memory access for MLAN_OID_MEM_RW */ + mlan_ds_mem_rw mem_rw; + } param; +} mlan_ds_reg_mem, *pmlan_ds_reg_mem; + +/*-----------------------------------------------------------------*/ +/** Multi-Radio Configuration Group */ +/*-----------------------------------------------------------------*/ + +/*-----------------------------------------------------------------*/ +/** 802.11h Configuration Group */ +/*-----------------------------------------------------------------*/ +#if defined(DFS_TESTING_SUPPORT) +/** Type definition of mlan_ds_11h_dfs_testing for MLAN_OID_11H_DFS_TESTING */ +typedef struct _mlan_ds_11h_dfs_testing { + /** User-configured CAC period in milliseconds, 0 to use default */ + t_u32 usr_cac_period_msec; + /** User-configured NOP period in seconds, 0 to use default */ + t_u16 usr_nop_period_sec; + /** User-configured skip channel change, 0 to disable */ + t_u8 usr_no_chan_change; + /** User-configured fixed channel to change to, 0 to use random channel */ + t_u8 usr_fixed_new_chan; +} mlan_ds_11h_dfs_testing, *pmlan_ds_11h_dfs_testing; + +/** Type definition of mlan_ds_11h_dfs_testing for MLAN_OID_11H_CHAN_NOP_INFO */ +typedef struct _mlan_ds_11h_chan_nop_info { + /** current channel */ + t_u8 curr_chan; + /** channel_width */ + t_u8 chan_width; + /** flag for chan under nop */ + t_bool chan_under_nop; + /** chan_ban_info for new channel */ + chan_band_info new_chan; +} mlan_ds_11h_chan_nop_info; +#endif + +typedef struct _mlan_ds_11h_chan_rep_req { + t_u16 startFreq; + Band_Config_t bandcfg; + t_u8 chanNum; + t_u32 millisec_dwell_time; + /**< Channel dwell time in milliseconds */ + t_u8 host_based; +} mlan_ds_11h_chan_rep_req; + +/** Type definition of mlan_ds_11h_cfg for MLAN_IOCTL_11H_CFG */ +typedef struct _mlan_ds_11h_cfg { + /** Sub-command */ + t_u32 sub_command; + union { + /** Local power constraint for MLAN_OID_11H_LOCAL_POWER_CONSTRAINT */ + t_s8 usr_local_power_constraint; +#if defined(DFS_TESTING_SUPPORT) + /** User-configuation for MLAN_OID_11H_DFS_TESTING */ + mlan_ds_11h_dfs_testing dfs_testing; + /** channel NOP information for MLAN_OID_11H_CHAN_NOP_INFO */ + mlan_ds_11h_chan_nop_info ch_nop_info; +#endif + mlan_ds_11h_chan_rep_req chan_rpt_req; + t_s8 cs_count; + } param; +} mlan_ds_11h_cfg, *pmlan_ds_11h_cfg; + +/*-----------------------------------------------------------------*/ +/** Miscellaneous Configuration Group */ +/*-----------------------------------------------------------------*/ + +/** CMD buffer size */ +#define MLAN_SIZE_OF_CMD_BUFFER 2048 + +/** LDO Internal */ +#define LDO_INTERNAL 0 +/** LDO External */ +#define LDO_EXTERNAL 1 + +/** Enumeration for IE type */ +enum _mlan_ie_type { + MLAN_IE_TYPE_GEN_IE = 0, +#ifdef STA_SUPPORT + MLAN_IE_TYPE_ARP_FILTER, +#endif /* STA_SUPPORT */ +}; + +/** Type definition of mlan_ds_misc_gen_ie for MLAN_OID_MISC_GEN_IE */ +typedef struct _mlan_ds_misc_gen_ie { + /** IE type */ + t_u32 type; + /** IE length */ + t_u32 len; + /** IE buffer */ + t_u8 ie_data[MAX_IE_SIZE]; +} mlan_ds_misc_gen_ie; + +#if defined(SDIO_MULTI_PORT_TX_AGGR) || defined(SDIO_MULTI_PORT_RX_AGGR) +/** Type definition of mlan_ds_misc_sdio_mpa_ctrl + * for MLAN_OID_MISC_SDIO_MPA_CTRL + */ +typedef struct _mlan_ds_misc_sdio_mpa_ctrl { + /** SDIO MP-A TX enable/disable */ + t_u16 tx_enable; + /** SDIO MP-A RX enable/disable */ + t_u16 rx_enable; + /** SDIO MP-A TX buf size */ + t_u16 tx_buf_size; + /** SDIO MP-A RX buf size */ + t_u16 rx_buf_size; + /** SDIO MP-A TX Max Ports */ + t_u16 tx_max_ports; + /** SDIO MP-A RX Max Ports */ + t_u16 rx_max_ports; +} mlan_ds_misc_sdio_mpa_ctrl; +#endif + +/** Type definition of mlan_ds_misc_cmd for MLAN_OID_MISC_HOST_CMD */ +typedef struct _mlan_ds_misc_cmd { + /** Command length */ + t_u32 len; + /** Command buffer */ + t_u8 cmd[MLAN_SIZE_OF_CMD_BUFFER]; +} mlan_ds_misc_cmd; + +/** Maximum number of system clocks */ +#define MLAN_MAX_CLK_NUM 16 + +/** Clock type : Configurable */ +#define MLAN_CLK_CONFIGURABLE 0 +/** Clock type : Supported */ +#define MLAN_CLK_SUPPORTED 1 + +/** Type definition of mlan_ds_misc_sys_clock for MLAN_OID_MISC_SYS_CLOCK */ +typedef struct _mlan_ds_misc_sys_clock { + /** Current system clock */ + t_u16 cur_sys_clk; + /** Clock type */ + t_u16 sys_clk_type; + /** Number of clocks */ + t_u16 sys_clk_num; + /** System clocks */ + t_u16 sys_clk[MLAN_MAX_CLK_NUM]; +} mlan_ds_misc_sys_clock; + +/** Maximum response buffer length */ +#define ASSOC_RSP_BUF_SIZE 500 + +/** Type definition of mlan_ds_misc_assoc_rsp for MLAN_OID_MISC_ASSOC_RSP */ +typedef struct _mlan_ds_misc_assoc_rsp { + /** Associate response buffer */ + t_u8 assoc_resp_buf[ASSOC_RSP_BUF_SIZE]; + /** Response buffer length */ + t_u32 assoc_resp_len; +} mlan_ds_misc_assoc_rsp; + +/** Enumeration for function init/shutdown */ +enum _mlan_func_cmd { + MLAN_FUNC_INIT = 1, + MLAN_FUNC_SHUTDOWN, +}; + +/** Enumeration for Coalescing status */ +enum _mlan_coal_status { + MLAN_MISC_COALESCING_ENABLE = 1, + MLAN_MISC_COALESCING_DISABLE = 0 +}; + +/** Net monitor filter: management frame */ +#define MLAN_NETMON_MANAGEMENT_FRAME MBIT(0) +/** Net monitor filter: control frame */ +#define MLAN_NETMON_CONTROL_FRAME MBIT(1) +/** Net monitor filter: data frame */ +#define MLAN_NETMON_DATA_FRAME MBIT(2) + +typedef struct _mlan_ds_misc_net_monitor { + /** Enable/disable network monitor */ + t_u32 enable_net_mon; + /** Set net monitor filer flag */ + t_u32 filter_flag; + /** Radio type */ + t_u32 band; + /** Channel */ + t_u32 channel; + /** Secondary channel bandwidth */ + t_u32 chan_bandwidth; +} mlan_ds_misc_net_monitor; + +/** Type definition of mlan_ds_misc_tx_datapause + * for MLAN_OID_MISC_TX_DATAPAUSE + */ +typedef struct _mlan_ds_misc_tx_datapause { + /** Tx data pause flag */ + t_u16 tx_pause; + /** Max number of Tx buffers for all PS clients */ + t_u16 tx_buf_cnt; +} mlan_ds_misc_tx_datapause; + +/** IP address length */ +#define IPADDR_LEN (16) +/** Max number of ip */ +#define MAX_IPADDR (4) +/** IP address type - NONE*/ +#define IPADDR_TYPE_NONE (0) +/** IP address type - IPv4*/ +#define IPADDR_TYPE_IPV4 (1) +/** IP operation remove */ +#define MLAN_IPADDR_OP_IP_REMOVE (0) +/** IP operation ARP filter */ +#define MLAN_IPADDR_OP_ARP_FILTER MBIT(0) +/** IP operation ARP response */ +#define MLAN_IPADDR_OP_AUTO_ARP_RESP MBIT(1) + +/** Type definition of mlan_ds_misc_ipaddr_cfg for MLAN_OID_MISC_IP_ADDR */ +typedef struct _mlan_ds_misc_ipaddr_cfg { + /** Operation code */ + t_u32 op_code; + /** IP address type */ + t_u32 ip_addr_type; + /** Number of IP */ + t_u32 ip_addr_num; + /** IP address */ + t_u8 ip_addr[MAX_IPADDR][IPADDR_LEN]; +} mlan_ds_misc_ipaddr_cfg; + +/* MEF configuration disable */ +#define MEF_CFG_DISABLE 0 +/* MEF configuration Rx filter enable */ +#define MEF_CFG_RX_FILTER_ENABLE 1 +/* MEF configuration auto ARP response */ +#define MEF_CFG_AUTO_ARP_RESP 2 +/* MEF configuration host command */ +#define MEF_CFG_HOSTCMD 0xFFFF + +/** Type definition of mlan_ds_misc_mef_cfg for MLAN_OID_MISC_MEF_CFG */ +typedef struct _mlan_ds_misc_mef_cfg { + /** Sub-ID for operation */ + t_u32 sub_id; + /** Parameter according to sub-ID */ + union { + /** MEF command buffer for MEF_CFG_HOSTCMD */ + mlan_ds_misc_cmd cmd_buf; + } param; +} mlan_ds_misc_mef_cfg; + +/** Type definition of mlan_ds_misc_cfp_code for MLAN_OID_MISC_CFP_CODE */ +typedef struct _mlan_ds_misc_cfp_code { + /** CFP table code for 2.4GHz */ + t_u32 cfp_code_bg; + /** CFP table code for 5GHz */ + t_u32 cfp_code_a; +} mlan_ds_misc_cfp_code; + +/** Type definition of mlan_ds_misc_country_code + * for MLAN_OID_MISC_COUNTRY_CODE + */ +typedef struct _mlan_ds_misc_country_code { + /** Country Code */ + t_u8 country_code[COUNTRY_CODE_LEN]; +} mlan_ds_misc_country_code; + +/** Type definition of mlan_ds_host_clock */ +typedef struct _mlan_ds_host_clock { + /** host time in secs */ + t_u64 time; + /** fw time */ + t_u64 fw_time; + /** host-bbu clock delta */ + t_u64 host_bbu_clk_delta; +} mlan_ds_host_clock; + +/** action for set */ +#define SUBSCRIBE_EVT_ACT_BITWISE_SET 0x0002 +/** action for clear */ +#define SUBSCRIBE_EVT_ACT_BITWISE_CLR 0x0003 +/** BITMAP for subscribe event rssi low */ +#define SUBSCRIBE_EVT_RSSI_LOW MBIT(0) +/** BITMAP for subscribe event snr low */ +#define SUBSCRIBE_EVT_SNR_LOW MBIT(1) +/** BITMAP for subscribe event max fail */ +#define SUBSCRIBE_EVT_MAX_FAIL MBIT(2) +/** BITMAP for subscribe event beacon missed */ +#define SUBSCRIBE_EVT_BEACON_MISSED MBIT(3) +/** BITMAP for subscribe event rssi high */ +#define SUBSCRIBE_EVT_RSSI_HIGH MBIT(4) +/** BITMAP for subscribe event snr high */ +#define SUBSCRIBE_EVT_SNR_HIGH MBIT(5) +/** BITMAP for subscribe event data rssi low */ +#define SUBSCRIBE_EVT_DATA_RSSI_LOW MBIT(6) +/** BITMAP for subscribe event data snr low */ +#define SUBSCRIBE_EVT_DATA_SNR_LOW MBIT(7) +/** BITMAP for subscribe event data rssi high */ +#define SUBSCRIBE_EVT_DATA_RSSI_HIGH MBIT(8) +/** BITMAP for subscribe event data snr high */ +#define SUBSCRIBE_EVT_DATA_SNR_HIGH MBIT(9) +/** BITMAP for subscribe event link quality */ +#define SUBSCRIBE_EVT_LINK_QUALITY MBIT(10) +/** BITMAP for subscribe event pre_beacon_lost */ +#define SUBSCRIBE_EVT_PRE_BEACON_LOST MBIT(11) +/** default PRE_BEACON_MISS_COUNT */ +#define DEFAULT_PRE_BEACON_MISS 30 + +/** Type definition of mlan_ds_subscribe_evt for MLAN_OID_MISC_CFP_CODE */ +typedef struct _mlan_ds_subscribe_evt { + /** evt action */ + t_u16 evt_action; + /** bitmap for subscribe event */ + t_u16 evt_bitmap; + /** Absolute value of RSSI threshold value (dBm) */ + t_u8 low_rssi; + /** 0--report once, 1--report everytime happen, + * N -- report only happend > N consecutive times + */ + t_u8 low_rssi_freq; + /** SNR threshold value (dB) */ + t_u8 low_snr; + /** 0--report once, 1--report everytime happen, + * N -- report only happend > N consecutive times + */ + t_u8 low_snr_freq; + /** Failure count threshold */ + t_u8 failure_count; + /** 0--report once, 1--report everytime happen, + * N -- report only happend > N consecutive times + */ + t_u8 failure_count_freq; + /** num of missed beacons */ + t_u8 beacon_miss; + /** 0--report once, 1--report everytime happen, + * N -- report only happend > N consecutive times + */ + t_u8 beacon_miss_freq; + /** Absolute value of RSSI threshold value (dBm) */ + t_u8 high_rssi; + /** 0--report once, 1--report everytime happen, + * N -- report only happend > N consecutive times + */ + t_u8 high_rssi_freq; + /** SNR threshold value (dB) */ + t_u8 high_snr; + /** 0--report once, 1--report everytime happen, + * N -- report only happend > N consecutive times + */ + t_u8 high_snr_freq; + /** Absolute value of data RSSI threshold value (dBm) */ + t_u8 data_low_rssi; + /** 0--report once, 1--report everytime happen, + * N -- report only happend > N consecutive times + */ + t_u8 data_low_rssi_freq; + /** Absolute value of data SNR threshold value (dBm) */ + t_u8 data_low_snr; + /** 0--report once, 1--report everytime happen, + * N -- report only happend > N consecutive times + */ + t_u8 data_low_snr_freq; + /** Absolute value of data RSSI threshold value (dBm) */ + t_u8 data_high_rssi; + /** 0--report once, 1--report everytime happen, + * N -- report only happend > N consecutive times + */ + t_u8 data_high_rssi_freq; + /** Absolute value of data SNR threshold value (dBm) */ + t_u8 data_high_snr; + /** 0--report once, 1--report everytime happen, + * N -- report only happend > N consecutive times + */ + t_u8 data_high_snr_freq; + /* Link SNR threshold (dB) */ + t_u16 link_snr; + /* Link SNR frequency */ + t_u16 link_snr_freq; + /* Second minimum rate value as per the rate table below */ + t_u16 link_rate; + /* Second minimum rate frequency */ + t_u16 link_rate_freq; + /* Tx latency value (us) */ + t_u16 link_tx_latency; + /* Tx latency frequency */ + t_u16 link_tx_lantency_freq; + /* Number of pre missed beacons */ + t_u8 pre_beacon_miss; +} mlan_ds_subscribe_evt; + +/** Max OTP user data length */ +#define MAX_OTP_USER_DATA_LEN 252 + +/** Type definition of mlan_ds_misc_otp_user_data + * for MLAN_OID_MISC_OTP_USER_DATA + */ +typedef struct _mlan_ds_misc_otp_user_data { + /** Reserved */ + t_u16 reserved; + /** OTP user data length */ + t_u16 user_data_length; + /** User data buffer */ + t_u8 user_data[MAX_OTP_USER_DATA_LEN]; +} mlan_ds_misc_otp_user_data; + +#ifdef WIFI_DIRECT_SUPPORT +/** flag for NOA */ +#define WIFI_DIRECT_NOA 1 +/** flag for OPP_PS */ +#define WIFI_DIRECT_OPP_PS 2 +/** Type definition of mlan_ds_wifi_direct_config + * for MLAN_OID_MISC_WIFI_DIRECT_CONFIG + */ +typedef struct _mlan_ds_wifi_direct_config { + /** flags for NOA/OPP_PS */ + t_u8 flags; + /** NoA enable/disable */ + t_u8 noa_enable; + /** index */ + t_u16 index; + /** NoA count */ + t_u8 noa_count; + /** NoA duration */ + t_u32 noa_duration; + /** NoA interval */ + t_u32 noa_interval; + /** opp ps enable/disable */ + t_u8 opp_ps_enable; + /** CT window value */ + t_u8 ct_window; +} mlan_ds_wifi_direct_config; +#endif + +#if defined(STA_SUPPORT) +/** mlan_ds_misc_pmfcfg structure */ +typedef struct _mlan_ds_misc_pmfcfg { + /** Management Frame Protection Capable */ + t_u8 mfpc; + /** Management Frame Protection Required */ + t_u8 mfpr; +} mlan_ds_misc_pmfcfg; +#endif + +/** mlan_ds_multi_chan_cfg structure */ +typedef MLAN_PACK_START struct _mlan_ds_multi_chan_cfg { + /** Channel Time */ + t_u32 channel_time; + /** Buffer Weight */ + t_u8 buffer_weight; + /** tlv len */ + t_u16 tlv_len; + /** TLV buffer */ + t_u8 tlv_buf[0]; +} MLAN_PACK_END mlan_ds_multi_chan_cfg; + +/** mlan_ds_drcs_cfg structure */ +typedef MLAN_PACK_START struct _mlan_ds_drcs_cfg { + /** Channel Index*/ + t_u16 chan_idx; + /** Channel time (in TU) for chan_idx */ + t_u8 chantime; + /** Channel swith time (in TU) for chan_idx */ + t_u8 switchtime; + /** Undoze time (in TU) for chan_idx */ + t_u8 undozetime; + /** Rx traffic control scheme when channel switch*/ + /** only valid for GC/STA interface*/ + t_u8 mode; +} MLAN_PACK_END mlan_ds_drcs_cfg; + +#define MAX_SSID_NUM 16 +#define MAX_AP_LIST 8 +#define RETRY_UNLIMITED_TIME 0xFF + +#define FW_ROAM_ENABLE MBIT(0) +#define FW_ROAM_TRIGGER_COND MBIT(1) +#define FW_ROAM_BSSID MBIT(2) +#define FW_ROAM_SSID MBIT(3) +#define FW_ROAM_RETRY_COUNT MBIT(4) +#define FW_ROAM_RSSI_PARA MBIT(5) +#define FW_ROAM_BAND_RSSI MBIT(6) +#define FW_ROAM_BGSCAN_PARAM MBIT(7) +#define FW_ROAM_EES_PARAM MBIT(8) +#define FW_ROAM_BCN_MISS_THRESHOLD MBIT(9) +#define FW_ROAM_PRE_BCN_MISS_THRESHOLD MBIT(10) +#define FW_ROAM_BLACKLIST MBIT(11) +#define FW_ROAM_REPEAT_CNT MBIT(12) + +/*Roam offload configuration for auto reconnection when suspend and resume*/ +typedef enum _roam_offload_config_mode { + ROAM_OFFLOAD_ENABLE = 1, + ROAM_OFFLOAD_SUSPEND_CFG, + ROAM_OFFLOAD_RESUME_CFG, + ROAM_OFFLOAD_PARAM_CFG, +} roam_offload_config_mode; + +typedef enum _roam_offload_set_mode { + ROAM_OFFLOAD_DISABLE = 0, + ROAM_OFFLOAD_WITH_APLIST, + ROAM_OFFLOAD_WITHOUT_APLIST, + ROAM_OFFLOAD_WITH_BSSID, + ROAM_OFFLOAD_WITH_SSID, + AUTO_RECONNECT, +} roam_offload_set_mode; + +typedef enum _roam_offload_trigger_mode { + NO_TRIGGER = 0x00, + RSSI_LOW_TRIGGER = 0x01, + PRE_BEACON_LOST_TRIGGER = 0x02, + LINK_LOST_TRIGGER = 0x04, + DEAUTH_WITH_EXT_AP_TRIGGER = 0x08, +} roam_offload_trigger_mode; + +/** mlan_ds_misc_ees_cfg structure */ +typedef MLAN_PACK_START struct _mlan_ds_misc_ees_cfg { + /* EES mode */ + t_u16 ees_mode; + /* EES report condition */ + t_u16 ees_rpt_condition; + /* High scan period(milliseconds) */ + t_u16 high_scan_period; + /* High scan count */ + t_u16 high_scan_count; + /* Middle scan period(milliseconds) */ + t_u16 mid_scan_period; + /* Middle scan count */ + t_u16 mid_scan_count; + /* Low scan period(milliseconds) */ + t_u16 low_scan_period; + /* Low scan count */ + t_u16 low_scan_count; +} MLAN_PACK_END mlan_ds_misc_ees_cfg; + +/** mlan_ds_misc_bgscan_cfg structure */ +typedef MLAN_PACK_START struct _mlan_ds_misc_bgscan_cfg { + /* BSS Type 0x1-bss independent, 0x2-bss infrastructure, 0x3-bss any */ + t_u8 bss_type; + /* Number of channels scanned for each scan */ + t_u8 channels_per_scan; + /* Interval between consective scans */ + t_u32 scan_interval; + /* Conditons to trigger report to host */ + t_u32 bg_rpt_condition; +} MLAN_PACK_END mlan_ds_misc_bgscan_cfg; + +/** mlan_ds_misc_band_rssi structure */ +typedef MLAN_PACK_START struct _mlan_ds_misc_band_rssi { + /* RSSI hysteresis */ + t_u8 rssi_hysteresis; + /* Preferred channel band for fw roaming + * 0:2.4G band; 1: 5G band; 2:4G band; 0xFF:band not set(invalid) + */ + t_u8 band_preferred; +} MLAN_PACK_END mlan_ds_misc_band_rssi; + +/** mlan_ds_misc_ssid_list structure */ +typedef MLAN_PACK_START struct _mlan_ds_misc_ssid_list { + /* SSID number */ + t_u8 ssid_num; + /* SSID for fw roaming/auto_reconnect */ + mlan_802_11_ssid ssids[MAX_SSID_NUM]; +} MLAN_PACK_END mlan_ds_misc_ssid_list; + +/** mlan_ds_misc_roam_offload_aplist structure */ +typedef MLAN_PACK_START struct _mlan_ds_misc_roam_offload_aplist { + /** Number of AP**/ + t_u8 ap_num; + /** AP mac addrs**/ + t_u8 ap_mac[MAX_AP_LIST][MLAN_MAC_ADDR_LENGTH]; +} MLAN_PACK_END mlan_ds_misc_roam_offload_aplist; + +/** _mlan_ds_misc_roam_offload_para_rssi structure */ +typedef MLAN_PACK_START struct _mlan_ds_misc_roam_offload_para_rssi { + /** Setting flag**/ + t_u8 set_flag; + /** Max value of RSSI threshold**/ + t_u8 max_rssi; + /** Min value of RSSI threshold**/ + t_u8 min_rssi; + /** Adjusting step value of RSSI threshold**/ + t_u8 step_rssi; +} MLAN_PACK_END mlan_ds_misc_roam_offload_para_rssi; + +/** mlan_ds_misc_roam_offload structure */ +typedef MLAN_PACK_START struct _mlan_ds_misc_roam_offload { + /** Enable roam offload**/ + t_u8 enable; + /** User set passphrase**/ + t_u8 userset_passphrase; + /* Condition to trigger roaming + * Bit0 : RSSI low trigger + * Bit1 : Pre-beacon lost trigger + * Bit2 : Link Lost trigger + * Bit3 : Deauth by ext-AP trigger + * Bit4 ~ Bit15 : Reserved + * value 0 : no trigger + * value 0xff : invalid + */ + t_u16 trigger_condition; + /** AP list**/ + mlan_ds_misc_roam_offload_aplist aplist; + /*Roam offload configuration mode for auto connection when suspend and resume */ + roam_offload_config_mode config_mode; + /** Retry count**/ + t_u8 retry_count; + /** RSSI para**/ + mlan_ds_misc_roam_offload_para_rssi para_rssi; + /** BSSID of reconnection**/ + mlan_802_11_mac_addr bssid_reconnect; + /* SSID List(White list) */ + mlan_ds_misc_ssid_list ssid_list; + /* Black list(BSSID list) */ + mlan_ds_misc_roam_offload_aplist black_list; + /* BAND and RSSI_HYSTERESIS set flag */ + t_u8 band_rssi_flag; + mlan_ds_misc_band_rssi band_rssi; + + /* BGSCAN params set flag */ + t_u8 bgscan_set_flag; + mlan_ds_misc_bgscan_cfg bgscan_cfg; + + /* EES mode params set flag */ + t_u8 ees_param_set_flag; + mlan_ds_misc_ees_cfg ees_cfg; + + /* Beacon miss threshold */ + t_u8 bcn_miss_threshold; + + /* Beacon miss threshold */ + t_u8 pre_bcn_miss_threshold; + + /* Scan repeat count */ + t_u16 repeat_count; +} MLAN_PACK_END mlan_ds_misc_roam_offload; + +/**Action ID for TDLS disable link*/ +#define WLAN_TDLS_DISABLE_LINK 0x00 +/**Action ID for TDLS enable link*/ +#define WLAN_TDLS_ENABLE_LINK 0x01 +/**Action ID for TDLS create link*/ +#define WLAN_TDLS_CREATE_LINK 0x02 +/**Action ID for TDLS config link*/ +#define WLAN_TDLS_CONFIG_LINK 0x03 +/*reason code*/ +#define MLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED 26 +/** TDLS operation buffer */ +typedef struct _mlan_ds_misc_tdls_oper { + /** TDLS Action */ + t_u16 tdls_action; + /** TDLS peer address */ + t_u8 peer_mac[MLAN_MAC_ADDR_LENGTH]; + /** peer capability */ + t_u16 capability; + /** peer qos info */ + t_u8 qos_info; + /** peer extend capability */ + t_u8 *ext_capab; + /** extend capability len */ + t_u8 ext_capab_len; + /** support rates */ + t_u8 *supported_rates; + /** supported rates len */ + t_u8 supported_rates_len; + /** peer ht_cap */ + t_u8 *ht_capa; +} mlan_ds_misc_tdls_oper; + +/** flag for TDLS extcap */ +#define TDLS_IE_FLAGS_EXTCAP 0x0001 +/** flag for TDLS HTCAP */ +#define TDLS_IE_FLAGS_HTCAP 0x0002 +/** flag for TDLS HTINFO */ +#define TDLS_IE_FLAGS_HTINFO 0x0004 +/** flag for TDLS Supported channels and regulatory class IE*/ +#define TDLS_IE_FLAGS_SUPP_CS_IE 0x0040 +/** flag for TDLS Qos info */ +#define TDLS_IE_FLAGS_QOS_INFO 0x0080 +/** flag for TDLS SETUP */ +#define TDLS_IE_FLAGS_SETUP 0x0100 + +/** TDLS ie buffer */ +typedef struct _mlan_ds_misc_tdls_ies { + /** TDLS peer address */ + t_u8 peer_mac[MLAN_MAC_ADDR_LENGTH]; + /** flags for request IEs */ + t_u16 flags; + /** Qos info */ + t_u8 QosInfo; + /** Extended Capabilities IE */ + t_u8 ext_cap[IEEE_MAX_IE_SIZE]; + /** HT Capabilities IE */ + t_u8 ht_cap[IEEE_MAX_IE_SIZE]; + /** HT Information IE */ + t_u8 ht_info[IEEE_MAX_IE_SIZE]; + /** supported channels */ + t_u8 supp_chan[IEEE_MAX_IE_SIZE]; + /** supported regulatory class */ + t_u8 regulatory_class[IEEE_MAX_IE_SIZE]; +} mlan_ds_misc_tdls_ies; + +#ifdef RX_PACKET_COALESCE +typedef struct _mlan_ds_misc_rx_packet_coalesce { + /** packet threshold */ + t_u32 packet_threshold; + /** timeout value */ + t_u16 delay; +} mlan_ds_misc_rx_packet_coalesce; +#endif + +/** mlan_ds_misc_dfs_repeater structure */ +typedef struct _mlan_ds_misc_dfs_repeater { + /** Set or Get */ + t_u16 action; + /** 1 on or 0 off */ + t_u16 mode; +} mlan_ds_misc_dfs_repeater; + +#define WOWLAN_MAX_PATTERN_LEN 20 +#define WOWLAN_MAX_OFFSET_LEN 50 +#define MAX_NUM_FILTERS 10 + +/** Temperature Sensor structure */ +typedef struct _mlan_ds_sensor_temp { + /** Temperature */ + t_u32 temperature; +} mlan_ds_sensor_temp; + +#define MLAN_KCK_LEN 16 +#define MLAN_KEK_LEN 16 +#define MLAN_REPLAY_CTR_LEN 8 +/** mlan_ds_misc_gtk_rekey_data */ +typedef struct _mlan_ds_misc_gtk_rekey_data { + /** key encryption key */ + t_u8 kek[MLAN_KEK_LEN]; + /** key confirmation key */ + t_u8 kck[MLAN_KCK_LEN]; + /** replay counter */ + t_u8 replay_ctr[MLAN_REPLAY_CTR_LEN]; +} mlan_ds_misc_gtk_rekey_data; +typedef struct _mlan_ds_bw_chan_oper { + /* bandwidth 20:20M 40:40M 80:80M */ + t_u8 bandwidth; + /* channel number */ + t_u8 channel; + /* Non-global operating class */ + t_u8 oper_class; +} mlan_ds_bw_chan_oper; + +typedef struct _mlan_ds_ind_rst_cfg { + /** Set or Get */ + t_u16 action; + /** oob mode enable/ disable */ + t_u8 ir_mode; + /** gpio pin */ + t_u8 gpio_pin; +} mlan_ds_ind_rst_cfg; + +/** mlan_ds_cw_mode_ctrl structure */ +typedef MLAN_PACK_START struct _mlan_ds_cw_mode_ctrl { + /** Mode of Operation 0: Disable 1: Tx Continuous Packet 2: Tx Continuous Wave */ + t_u8 mode; + /*channel */ + t_u8 channel; + /* channel info */ + t_u8 chanInfo; + /** Tx Power level in dBm */ + t_u16 txPower; + /** Packet Length */ + t_u16 pktLength; + /** bit rate Info */ + t_u32 rateInfo; +} MLAN_PACK_END mlan_ds_cw_mode_ctrl; + +#define RX_PKT_INFO MBIT(1) +/** Struct for per-packet configuration */ +typedef struct _mlan_per_pkt_cfg { + /** Type ID*/ + t_u16 type; + /** Length of payload*/ + t_u16 len; + /** Tx/Rx per-packet control */ + t_u8 tx_rx_control; + /** Number of ethernet types in ether_type array */ + t_u8 proto_type_num; + /** Array of ether_type for per-packet control */ + t_u16 ether_type[0]; +} mlan_per_pkt_cfg; + +/** Type definition of mlan_ds_misc_robustcoex_params for MLAN_IOCTL_MISC_CFG */ +typedef struct _mlan_ds_misc_robustcoex_params { + t_u16 method; + /** enable/disable robustcoex gpio cfg */ + t_u8 enable; + /** Number of GPIO */ + t_u8 gpio_num; + /** Polarity of GPIO */ + t_u8 gpio_polarity; +} mlan_ds_misc_robustcoex_params; + +/** Type definition of mlan_ds_misc_cfg for MLAN_IOCTL_MISC_CFG */ +typedef struct _mlan_ds_misc_cfg { + /** Sub-command */ + t_u32 sub_command; + /** Miscellaneous configuration parameter */ + union { + /** Generic IE for MLAN_OID_MISC_GEN_IE */ + mlan_ds_misc_gen_ie gen_ie; + /** Region code for MLAN_OID_MISC_REGION */ + t_u32 region_code; +#if defined(SDIO_MULTI_PORT_TX_AGGR) || defined(SDIO_MULTI_PORT_RX_AGGR) + /** SDIO MP-A Ctrl command for MLAN_OID_MISC_SDIO_MPA_CTRL */ + mlan_ds_misc_sdio_mpa_ctrl mpa_ctrl; +#endif + /** Hostcmd for MLAN_OID_MISC_HOST_CMD */ + mlan_ds_misc_cmd hostcmd; + /** System clock for MLAN_OID_MISC_SYS_CLOCK */ + mlan_ds_misc_sys_clock sys_clock; + /** WWS set/get for MLAN_OID_MISC_WWS */ + t_u32 wws_cfg; + /** Get associate response for MLAN_OID_MISC_ASSOC_RSP */ + mlan_ds_misc_assoc_rsp assoc_resp; + /** Function init/shutdown for MLAN_OID_MISC_INIT_SHUTDOWN */ + t_u32 func_init_shutdown; + /** Coalescing status for MLAN_OID_MISC_COALESCING_STATUS */ + t_u16 coalescing_status; + /** Custom IE for MLAN_OID_MISC_CUSTOM_IE */ + mlan_ds_misc_custom_ie cust_ie; + t_u16 tdls_idle_time; + /** TDLS configuration for MLAN_OID_MISC_TDLS_CONFIG */ + mlan_ds_misc_tdls_config tdls_config; + /** TDLS operation for MLAN_OID_MISC_TDLS_OPER */ + mlan_ds_misc_tdls_oper tdls_oper; + /** TDLS ies for MLAN_OID_MISC_GET_TDLS_IES */ + mlan_ds_misc_tdls_ies tdls_ies; + /**tdls cs off channel*/ + t_u8 tdls_cs_channel; + /** Net monitor for MLAN_OID_MISC_NET_MONITOR */ + mlan_ds_misc_net_monitor net_mon; + /** Tx data pause for MLAN_OID_MISC_TX_DATAPAUSE */ + mlan_ds_misc_tx_datapause tx_datapause; + /** IP address configuration */ + mlan_ds_misc_ipaddr_cfg ipaddr_cfg; + /** MAC control for MLAN_OID_MISC_MAC_CONTROL */ + t_u32 mac_ctrl; + /** MEF configuration for MLAN_OID_MISC_MEF_CFG */ + mlan_ds_misc_mef_cfg mef_cfg; + /** CFP code for MLAN_OID_MISC_CFP_CODE */ + mlan_ds_misc_cfp_code cfp_code; + /** Country code for MLAN_OID_MISC_COUNTRY_CODE */ + mlan_ds_misc_country_code country_code; + /** Thermal reading for MLAN_OID_MISC_THERMAL */ + t_u32 thermal; + /** Mgmt subtype mask for MLAN_OID_MISC_RX_MGMT_IND */ + t_u32 mgmt_subtype_mask; + /** subscribe event for MLAN_OID_MISC_SUBSCRIBE_EVENT */ + mlan_ds_subscribe_evt subscribe_event; +#ifdef DEBUG_LEVEL1 + /** Driver debug bit masks */ + t_u32 drvdbg; +#endif +#ifdef STA_SUPPORT + ExtCap_t ext_cap; +#endif + mlan_ds_misc_otp_user_data otp_user_data; + /** Tx control */ + t_u32 tx_control; +#if defined(STA_SUPPORT) + mlan_ds_misc_pmfcfg pmfcfg; +#endif + /** Multi-channel config for MLAN_OID_MISC_MULTI_CHAN_CFG */ + mlan_ds_multi_chan_cfg multi_chan_cfg; + /** Multi-channel policy for MLAN_OID_MISC_MULTI_CHAN_POLICY */ + t_u16 multi_chan_policy; + /** channel drcs time slicing config for MLAN_OID_MISC_DRCS_CFG */ + mlan_ds_drcs_cfg drcs_cfg[2]; +#ifdef WIFI_DIRECT_SUPPORT + mlan_ds_wifi_direct_config p2p_config; +#endif + mlan_ds_coalesce_cfg coalesce_cfg; + mlan_ds_misc_dfs_repeater dfs_repeater; +#ifdef RX_PACKET_COALESCE + mlan_ds_misc_rx_packet_coalesce rx_coalesce; +#endif + /** FW reload flag */ + t_u8 fw_reload; + /** Sensor temperature */ + mlan_ds_sensor_temp sensor_temp; + /** GTK rekey data */ + mlan_ds_misc_gtk_rekey_data gtk_rekey; + /** Bandwidth Channel operation */ + mlan_ds_bw_chan_oper bw_chan_oper; + /** Independent Reset Configuration */ + mlan_ds_ind_rst_cfg ind_rst_cfg; + /** Roam offload */ + mlan_ds_misc_roam_offload roam_offload; + /** misc tsf */ + t_u64 misc_tsf; + /** Custom regulatory domain */ + mlan_ds_custom_reg_domain custom_reg_domain; + /** cwmmode */ + mlan_ds_cw_mode_ctrl cwmode; + /** Tx/Rx per-packet control */ + t_u8 txrx_pkt_ctrl; + mlan_ds_misc_robustcoex_params robustcoexparams; + mlan_ds_host_clock host_clock; + /** boot sleep enable or disable */ + t_u16 boot_sleep; + } param; +} mlan_ds_misc_cfg, *pmlan_ds_misc_cfg; + +#endif /* !_MLAN_IOCTL_H_ */ diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_join.c b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_join.c new file mode 100644 index 000000000000..012abb222569 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_join.c @@ -0,0 +1,2399 @@ +/** @file mlan_join.c + * + * @brief Functions implementing wlan infrastructure and adhoc join routines + * + * IOCTL handlers as well as command preparation and response routines + * for sending adhoc start, adhoc join, and association commands + * to the firmware. + * + * Copyright (C) 2008-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 10/30/2008: initial version +******************************************************/ + +#include "mlan.h" +#include "mlan_join.h" +#include "mlan_util.h" +#include "mlan_fw.h" +#include "mlan_main.h" +#include "mlan_wmm.h" +#include "mlan_11n.h" +#include "mlan_11h.h" +#ifdef DRV_EMBEDDED_SUPPLICANT +#include "authenticator_api.h" +#endif +/******************************************************** + Local Constants +********************************************************/ + +/******************************************************** + Local Variables +********************************************************/ + +/******************************************************** + Global Variables +********************************************************/ + +/******************************************************** + Local Functions +********************************************************/ +/** + * @brief Append a generic IE as a pass through TLV to a TLV buffer. + * + * This function is called from the network join command prep. routine. + * If the IE buffer has been setup by the application, this routine appends + * the buffer as a pass through TLV type to the request. + * + * @param priv A pointer to mlan_private structure + * @param ppbuffer pointer to command buffer pointer + * + * @return bytes added to the buffer + */ +static int +wlan_cmd_append_generic_ie(mlan_private *priv, t_u8 **ppbuffer) +{ + int ret_len = 0; + MrvlIEtypesHeader_t ie_header; + + ENTER(); + + /* Null Checks */ + if (ppbuffer == MNULL) { + LEAVE(); + return 0; + } + if (*ppbuffer == MNULL) { + LEAVE(); + return 0; + } + + /* + * If there is a generic ie buffer setup, append it to the return + * parameter buffer pointer. + */ + if (priv->gen_ie_buf_len) { + PRINTM(MINFO, "append generic IE %d to %p\n", + priv->gen_ie_buf_len, *ppbuffer); + + /* Wrap the generic IE buffer with a pass through TLV type */ + ie_header.type = wlan_cpu_to_le16(TLV_TYPE_PASSTHROUGH); + ie_header.len = wlan_cpu_to_le16(priv->gen_ie_buf_len); + memcpy(priv->adapter, *ppbuffer, &ie_header, sizeof(ie_header)); + + /* Increment the return size and the return buffer pointer param */ + *ppbuffer += sizeof(ie_header); + ret_len += sizeof(ie_header); + + /* Copy the generic IE buffer to the output buffer, advance pointer */ + memcpy(priv->adapter, *ppbuffer, priv->gen_ie_buf, + priv->gen_ie_buf_len); + + /* Increment the return size and the return buffer pointer param */ + *ppbuffer += priv->gen_ie_buf_len; + ret_len += priv->gen_ie_buf_len; + + /* Reset the generic IE buffer */ + priv->gen_ie_buf_len = 0; + } + + /* return the length appended to the buffer */ + LEAVE(); + return ret_len; +} + +/** + * @brief Append IE as a pass through TLV to a TLV buffer. + * + * This routine appends IE as a pass through TLV type to the request. + * + * @param priv A pointer to mlan_private structure + * @param ie A pointer to IE buffer + * @param ppbuffer pointer to command buffer pointer + * + * @return bytes added to the buffer + */ +static int +wlan_cmd_append_pass_through_ie(mlan_private *priv, IEEEtypes_Generic_t *ie, + t_u8 **ppbuffer) +{ + int ret_len = 0; + MrvlIEtypesHeader_t ie_header; + + ENTER(); + + /* Null Checks */ + if (ppbuffer == MNULL) { + LEAVE(); + return 0; + } + if (*ppbuffer == MNULL) { + LEAVE(); + return 0; + } + if (ie->ieee_hdr.len) { + PRINTM(MINFO, "append generic IE %d to %p\n", ie->ieee_hdr.len, + *ppbuffer); + + /* Wrap the generic IE buffer with a pass through TLV type */ + ie_header.type = wlan_cpu_to_le16(TLV_TYPE_PASSTHROUGH); + ie_header.len = + wlan_cpu_to_le16(ie->ieee_hdr.len + + sizeof(MrvlIEtypesHeader_t)); + memcpy(priv->adapter, *ppbuffer, &ie_header, sizeof(ie_header)); + + /* Increment the return size and the return buffer pointer param */ + *ppbuffer += sizeof(ie_header); + ret_len += sizeof(ie_header); + + /* Copy the generic IE buffer to the output buffer, advance pointer */ + memcpy(priv->adapter, *ppbuffer, ie, + ie->ieee_hdr.len + sizeof(MrvlIEtypesHeader_t)); + + /* Increment the return size and the return buffer pointer param */ + *ppbuffer += ie->ieee_hdr.len + sizeof(MrvlIEtypesHeader_t); + ret_len += ie->ieee_hdr.len + sizeof(MrvlIEtypesHeader_t); + } + /* return the length appended to the buffer */ + LEAVE(); + return ret_len; +} + +/** + * @brief Append TSF tracking info from the scan table for the target AP + * + * This function is called from the network join command prep. routine. + * The TSF table TSF sent to the firmware contains two TSF values: + * - the TSF of the target AP from its previous beacon/probe response + * - the TSF timestamp of our local MAC at the time we observed the + * beacon/probe response. + * + * The firmware uses the timestamp values to set an initial TSF value + * in the MAC for the new association after a reassociation attempt. + * + * @param pmpriv A pointer to mlan_private structure + * @param ppbuffer A pointer to command buffer pointer + * @param pbss_desc A pointer to the BSS Descriptor from the scan table of + * the AP we are trying to join + * + * @return bytes added to the buffer + */ +static int +wlan_cmd_append_tsf_tlv(mlan_private *pmriv, t_u8 **ppbuffer, + BSSDescriptor_t *pbss_desc) +{ + MrvlIEtypes_TsfTimestamp_t tsf_tlv; + t_u64 tsf_val; + + ENTER(); + + /* Null Checks */ + if (ppbuffer == MNULL) { + LEAVE(); + return 0; + } + if (*ppbuffer == MNULL) { + LEAVE(); + return 0; + } + + memset(pmriv->adapter, &tsf_tlv, 0x00, + sizeof(MrvlIEtypes_TsfTimestamp_t)); + + tsf_tlv.header.type = wlan_cpu_to_le16(TLV_TYPE_TSFTIMESTAMP); + tsf_tlv.header.len = wlan_cpu_to_le16(2 * sizeof(tsf_val)); + + memcpy(pmriv->adapter, *ppbuffer, &tsf_tlv, sizeof(tsf_tlv.header)); + *ppbuffer += sizeof(tsf_tlv.header); + + /* TSF timestamp from the firmware TSF when the bcn/prb rsp was received */ + tsf_val = wlan_cpu_to_le64(pbss_desc->network_tsf); + memcpy(pmriv->adapter, *ppbuffer, &tsf_val, sizeof(tsf_val)); + *ppbuffer += sizeof(tsf_val); + + memcpy(pmriv->adapter, &tsf_val, pbss_desc->time_stamp, + sizeof(tsf_val)); + + PRINTM(MINFO, "ASSOC: TSF offset calc: %016llx - %016llx\n", + tsf_val, pbss_desc->network_tsf); + + memcpy(pmriv->adapter, *ppbuffer, &tsf_val, sizeof(tsf_val)); + *ppbuffer += sizeof(tsf_val); + + LEAVE(); + return sizeof(tsf_tlv.header) + (2 * sizeof(tsf_val)); +} + +/** + * @brief This function finds out the common rates between rate1 and rate2. + * + * It will fill common rates in rate1 as output if found. + * + * NOTE: Setting the MSB of the basic rates needs to be taken + * care of, either before or after calling this function + * + * @param pmpriv A pointer to mlan_private structure + * @param rate1 the buffer which keeps input and output + * @param rate1_size the size of rate1 buffer + * @param rate2 the buffer which keeps rate2 + * @param rate2_size the size of rate2 buffer. + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status +wlan_get_common_rates(IN mlan_private *pmpriv, + IN t_u8 *rate1, + IN t_u32 rate1_size, IN t_u8 *rate2, IN t_u32 rate2_size) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_callbacks *pcb = (mlan_callbacks *)&pmpriv->adapter->callbacks; + t_u8 *ptr = rate1; + t_u8 *tmp = MNULL; + t_u32 i, j; + + ENTER(); + + ret = pcb->moal_malloc(pmpriv->adapter->pmoal_handle, rate1_size, + MLAN_MEM_DEF, &tmp); + if (ret != MLAN_STATUS_SUCCESS || !tmp) { + PRINTM(MERROR, "Failed to allocate buffer\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + memcpy(pmpriv->adapter, tmp, rate1, rate1_size); + memset(pmpriv->adapter, rate1, 0, rate1_size); + + for (i = 0; rate2[i] && i < rate2_size; i++) { + for (j = 0; tmp[j] && j < rate1_size; j++) { + /* Check common rate, excluding the bit + * for basic rate */ + if ((rate2[i] & 0x7F) == (tmp[j] & 0x7F)) { + *rate1++ = tmp[j]; + break; + } + } + } + + HEXDUMP("rate1 (AP) Rates", tmp, rate1_size); + HEXDUMP("rate2 (Card) Rates", rate2, rate2_size); + HEXDUMP("Common Rates", ptr, rate1 - ptr); + PRINTM(MINFO, "Tx DataRate is set to 0x%X\n", pmpriv->data_rate); + + if (!pmpriv->is_data_rate_auto) { + while (*ptr) { + if ((*ptr & 0x7f) == pmpriv->data_rate) { + ret = MLAN_STATUS_SUCCESS; + goto done; + } + ptr++; + } + PRINTM(MMSG, "Previously set fixed data rate %#x is not " + "compatible with the network\n", pmpriv->data_rate); + + ret = MLAN_STATUS_FAILURE; + goto done; + } + + ret = MLAN_STATUS_SUCCESS; +done: + if (tmp) + pcb->moal_mfree(pmpriv->adapter->pmoal_handle, tmp); + + LEAVE(); + return ret; +} + +/** + * @brief Create the intersection of the rates supported by a target BSS and + * our pmadapter settings for use in an assoc/join command. + * + * @param pmpriv A pointer to mlan_private structure + * @param pbss_desc BSS Descriptor whose rates are used in the setup + * @param pout_rates Output: Octet array of rates common between the BSS + * and the pmadapter supported rates settings + * @param pout_rates_size Output: Number of rates/octets set in pout_rates + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status +wlan_setup_rates_from_bssdesc(IN mlan_private *pmpriv, + IN BSSDescriptor_t *pbss_desc, + OUT t_u8 *pout_rates, OUT t_u32 *pout_rates_size) +{ + t_u8 card_rates[WLAN_SUPPORTED_RATES]; + t_u32 card_rates_size = 0; + ENTER(); + /* Copy AP supported rates */ + memcpy(pmpriv->adapter, pout_rates, pbss_desc->supported_rates, + WLAN_SUPPORTED_RATES); + + if ((pmpriv->adapter->region_code == COUNTRY_CODE_JP_40 || + pmpriv->adapter->region_code == COUNTRY_CODE_JP_FF) + && (pbss_desc->phy_param_set.ds_param_set.current_chan == 14)) { + /* Special Case: For Japan, 11G rates on CH14 are not allowed */ + card_rates_size = + wlan_get_supported_rates(pmpriv, pmpriv->bss_mode, + BAND_B, card_rates); + } else { + /* Get the STA supported rates */ + card_rates_size = + wlan_get_supported_rates(pmpriv, pmpriv->bss_mode, + pmpriv->config_bands, + card_rates); + } + /* Get the common rates between AP and STA supported rates */ + if (wlan_get_common_rates(pmpriv, pout_rates, WLAN_SUPPORTED_RATES, + card_rates, card_rates_size)) { + *pout_rates_size = 0; + PRINTM(MERROR, "wlan_get_common_rates failed\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + *pout_rates_size = + MIN(wlan_strlen((char *)pout_rates), WLAN_SUPPORTED_RATES); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Update the scan entry TSF timestamps to reflect a new association + * + * @param pmpriv A pointer to mlan_private structure + * @param pnew_bss_desc A pointer to the newly associated AP's scan table entry + * + * @return N/A + */ +static t_void +wlan_update_tsf_timestamps(IN mlan_private *pmpriv, + IN BSSDescriptor_t *pnew_bss_desc) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + t_u32 table_idx; + t_u64 new_tsf_base; + t_s64 tsf_delta; + + ENTER(); + + memcpy(pmpriv->adapter, &new_tsf_base, pnew_bss_desc->time_stamp, + sizeof(new_tsf_base)); + + tsf_delta = new_tsf_base - pnew_bss_desc->network_tsf; + + PRINTM(MINFO, "TSF: Update TSF timestamps, 0x%016llx -> 0x%016llx\n", + pnew_bss_desc->network_tsf, new_tsf_base); + + for (table_idx = 0; table_idx < pmadapter->num_in_scan_table; + table_idx++) { + pmadapter->pscan_table[table_idx].network_tsf += tsf_delta; + } + + LEAVE(); +} + +/** + * @brief Append a wapi IE + * + * This function is called from the network join command prep. routine. + * If the IE buffer has been setup by the application, this routine appends + * the buffer as a wapi TLV type to the request. + * + * @param priv A pointer to mlan_private structure + * @param ppbuffer pointer to command buffer pointer + * + * @return bytes added to the buffer + */ +static int +wlan_cmd_append_wapi_ie(mlan_private *priv, t_u8 **ppbuffer) +{ + int retlen = 0; + MrvlIEtypesHeader_t ie_header; + + ENTER(); + + /* Null Checks */ + if (ppbuffer == MNULL) { + LEAVE(); + return 0; + } + if (*ppbuffer == MNULL) { + LEAVE(); + return 0; + } + + /* + * If there is a wapi ie buffer setup, append it to the return + * parameter buffer pointer. + */ + if (priv->wapi_ie_len) { + PRINTM(MCMND, "append wapi ie %d to %p\n", priv->wapi_ie_len, + *ppbuffer); + + /* Wrap the generic IE buffer with a pass through TLV type */ + ie_header.type = wlan_cpu_to_le16(TLV_TYPE_WAPI_IE); + ie_header.len = wlan_cpu_to_le16(priv->wapi_ie_len); + memcpy(priv->adapter, *ppbuffer, &ie_header, sizeof(ie_header)); + + /* Increment the return size and the return buffer pointer param */ + *ppbuffer += sizeof(ie_header); + retlen += sizeof(ie_header); + + /* Copy the wapi IE buffer to the output buffer, advance pointer */ + memcpy(priv->adapter, *ppbuffer, priv->wapi_ie, + priv->wapi_ie_len); + + /* Increment the return size and the return buffer pointer param */ + *ppbuffer += priv->wapi_ie_len; + retlen += priv->wapi_ie_len; + + } + /* return the length appended to the buffer */ + LEAVE(); + return retlen; +} + +/******************************************************** + Global Functions +********************************************************/ +/** + * @brief This function updates RSN IE in the association request. + * + * @param pmpriv A pointer to mlan_private structure + * + * @param ptlv_rsn_ie A pointer to rsn_ie TLV + */ +int +wlan_update_rsn_ie(mlan_private *pmpriv, MrvlIEtypes_RsnParamSet_t *ptlv_rsn_ie) +{ + t_u16 *prsn_cap; + t_u16 *ptr; + t_u16 *akm_suite_count_ptr; + t_u16 pmf_mask = 0x00; + t_u8 *temp; + t_u16 pairwise_cipher_count = 0; + t_u16 akm_suite_count = 0; + t_u16 temp_akm_suite_count = 0; + int found = 0; + t_u8 sha_256_oui[4] = { 0x00, 0x0f, 0xac, 0x06 }; + mlan_adapter *pmadapter = pmpriv->adapter; + + int ap_mfpc = 0, ap_mfpr = 0, ret = MLAN_STATUS_SUCCESS; + + pmf_mask = + (((pmpriv->pmfcfg.mfpc << MFPC_BIT) | (pmpriv->pmfcfg. + mfpr << MFPR_BIT)) | + (~PMF_MASK)); + /* prsn_cap = prsn_ie->rsn_ie + 2 bytes version + 4 bytes group_cipher_suite + + * 2 bytes pairwise_cipher_count + pairwise_cipher_count * PAIRWISE_CIPHER_SUITE_LEN + + * 2 bytes akm_suite_count + akm_suite_count * AKM_SUITE_LEN + */ + ptr = (t_u16 *)(ptlv_rsn_ie->rsn_ie + sizeof(t_u16) + 4 * sizeof(t_u8)); + pairwise_cipher_count = wlan_le16_to_cpu(*ptr); + ptr = (t_u16 *)(ptlv_rsn_ie->rsn_ie + sizeof(t_u16) + 4 * sizeof(t_u8) + + sizeof(t_u16) + + pairwise_cipher_count * PAIRWISE_CIPHER_SUITE_LEN); + temp_akm_suite_count = wlan_le16_to_cpu(*ptr); + akm_suite_count = wlan_le16_to_cpu(*ptr); + /* Save pointer to akm_suite_count in RSN IE to update it later */ + akm_suite_count_ptr = ptr; + temp = ptlv_rsn_ie->rsn_ie + sizeof(t_u16) + 4 * sizeof(t_u8) + + sizeof(t_u16) + pairwise_cipher_count + * PAIRWISE_CIPHER_SUITE_LEN + sizeof(t_u16); + /* ptr now points to the 1st AKM suite */ + if (temp_akm_suite_count > 1) { + while (temp_akm_suite_count) { + if (!memcmp + (pmadapter, temp, sha_256_oui, AKM_SUITE_LEN)) { + found = 1; + break; + } + temp += AKM_SUITE_LEN; + temp_akm_suite_count--; + } + if (found) { + /* Copy SHA256 as AKM suite */ + memcpy(pmadapter, + ptlv_rsn_ie->rsn_ie + (sizeof(t_u16) + + 4 * sizeof(t_u8) + + sizeof(t_u16) + + pairwise_cipher_count * + PAIRWISE_CIPHER_SUITE_LEN + + sizeof(t_u16)), + sha_256_oui, AKM_SUITE_LEN); + /* Shift remaining bytes of RSN IE after this */ + memmove(pmadapter, + ptlv_rsn_ie->rsn_ie + (sizeof(t_u16) + + 4 * sizeof(t_u8) + + sizeof(t_u16) + + pairwise_cipher_count * + PAIRWISE_CIPHER_SUITE_LEN + + sizeof(t_u16) + + AKM_SUITE_LEN), + ptlv_rsn_ie->rsn_ie + (sizeof(t_u16) + + 4 * sizeof(t_u8) + + sizeof(t_u16) + + pairwise_cipher_count * + PAIRWISE_CIPHER_SUITE_LEN + + sizeof(t_u16) + + akm_suite_count * + AKM_SUITE_LEN), + ptlv_rsn_ie->header.len - (sizeof(t_u16) + + 4 * sizeof(t_u8) + + sizeof(t_u16) + + pairwise_cipher_count + * + PAIRWISE_CIPHER_SUITE_LEN + + sizeof(t_u16) + + akm_suite_count * + AKM_SUITE_LEN)); + ptlv_rsn_ie->header.len = + ptlv_rsn_ie->header.len - (akm_suite_count - + 1) * AKM_SUITE_LEN; + /* Update akm suite count */ + akm_suite_count = 1; + *akm_suite_count_ptr = akm_suite_count; + } + } + ptr = (t_u16 *)(ptlv_rsn_ie->rsn_ie + sizeof(t_u16) + 4 * sizeof(t_u8) + + sizeof(t_u16) + + pairwise_cipher_count * PAIRWISE_CIPHER_SUITE_LEN + + sizeof(t_u16) + akm_suite_count * AKM_SUITE_LEN); + prsn_cap = ptr; + + ap_mfpc = ((*prsn_cap & (0x1 << MFPC_BIT)) == (0x1 << MFPC_BIT)); + ap_mfpr = ((*prsn_cap & (0x1 << MFPR_BIT)) == (0x1 << MFPR_BIT)); + + if ((!ap_mfpc && !ap_mfpr && pmpriv->pmfcfg.mfpr) + || ((!ap_mfpc) && ap_mfpr) + || (ap_mfpc && ap_mfpr && (!pmpriv->pmfcfg.mfpc)) + ) { + PRINTM(MERROR, + "Mismatch in PMF config of STA and AP, can't associate to AP\n"); + return MLAN_STATUS_FAILURE; + } + /* If PMF is required by AP, just leave the same value with AP */ + if (!(*prsn_cap & (0x1 << MFPR_BIT))) + *prsn_cap &= pmf_mask; + + return ret; +} + +/** + * @brief This function is to find FT AKM in RSN. + * + * @param pmpriv A pointer to mlan_private structure + * + * @param rsn_ie A pointer to rsn_ie + * + */ +t_u8 +wlan_ft_akm_is_used(mlan_private *pmpriv, t_u8 *rsn_ie) +{ + t_u8 *temp; + t_u16 count; + t_u16 pairwise_cipher_count = 0; + t_u16 akm_suite_count = 0; + t_u8 found = 0; + t_u8 rsn_ft_1x_oui[4] = { 0x00, 0x0f, 0xac, 0x03 }; + t_u8 rsn_ft_psk_oui[4] = { 0x00, 0x0f, 0xac, 0x04 }; + t_u8 rsn_ft_sae_oui[4] = { 0x00, 0x0f, 0xac, 0x09 }; + mlan_adapter *pmadapter = pmpriv->adapter; + + ENTER(); + + if (!rsn_ie) + goto done; + + if (rsn_ie[0] != RSN_IE) + goto done; + + /* 2 bytes header + 2 bytes version + 4 bytes group_cipher_suite + + * 2 bytes pairwise_cipher_count + pairwise_cipher_count * PAIRWISE_CIPHER_SUITE_LEN (4) + + * 2 bytes akm_suite_count + akm_suite_count * AKM_SUITE_LEN (4) + */ + count = *(t_u16 *)(rsn_ie + 2 + 2 + 4 * sizeof(t_u8)); + pairwise_cipher_count = wlan_le16_to_cpu(count); + count = *(t_u16 *)(rsn_ie + 2 + 2 + 4 * sizeof(t_u8) + + sizeof(t_u16) + pairwise_cipher_count * 4); + akm_suite_count = wlan_le16_to_cpu(count); + temp = (t_u8 *)(rsn_ie + 2 + sizeof(t_u16) + 4 * sizeof(t_u8) + + sizeof(t_u16) + pairwise_cipher_count + * 4 + sizeof(t_u16)); + + while (akm_suite_count) { + if (!memcmp + (pmadapter, temp, rsn_ft_1x_oui, sizeof(rsn_ft_1x_oui)) || + !memcmp(pmadapter, temp, rsn_ft_psk_oui, + sizeof(rsn_ft_psk_oui)) || + !memcmp(pmadapter, temp, rsn_ft_sae_oui, + sizeof(rsn_ft_sae_oui))) { + found = 1; + break; + } + temp += 4; + akm_suite_count--; + } + +done: + LEAVE(); + return found; +} + +/** + * @brief This function prepares command of association. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param pdata_buf A pointer cast of BSSDescriptor_t from the + * scan table to assoc + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_cmd_802_11_associate(IN mlan_private *pmpriv, + IN HostCmd_DS_COMMAND *cmd, IN t_void *pdata_buf) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_adapter *pmadapter = pmpriv->adapter; + HostCmd_DS_802_11_ASSOCIATE *passo = &cmd->params.associate; + BSSDescriptor_t *pbss_desc; + MrvlIEtypes_SsIdParamSet_t *pssid_tlv; + MrvlIEtypes_PhyParamSet_t *pphy_tlv; + MrvlIEtypes_SsParamSet_t *pss_tlv; + MrvlIEtypes_RatesParamSet_t *prates_tlv; + MrvlIEtypes_AuthType_t *pauth_tlv; + MrvlIEtypes_RsnParamSet_t *prsn_ie_tlv = MNULL; + MrvlIEtypes_ChanListParamSet_t *pchan_tlv; + WLAN_802_11_RATES rates; + t_u32 rates_size; + t_u16 tmp_cap; + t_u8 *pos; +#ifdef DRV_EMBEDDED_SUPPLICANT + void *rsn_wpa_ie_tmp = MNULL; +#endif + t_u8 ft_akm = 0; + t_u8 oper_class; + + ENTER(); + + pbss_desc = (BSSDescriptor_t *)pdata_buf; + pos = (t_u8 *)passo; + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_802_11_ASSOCIATE); + + /* Save so we know which BSS Desc to use in the response handler */ + pmpriv->pattempted_bss_desc = pbss_desc; + + memcpy(pmadapter, passo->peer_sta_addr, + pbss_desc->mac_address, sizeof(passo->peer_sta_addr)); + pos += sizeof(passo->peer_sta_addr); + + /* Set the listen interval */ + passo->listen_interval = wlan_cpu_to_le16(pmpriv->listen_interval); + /* Set the beacon period */ + passo->beacon_period = wlan_cpu_to_le16(pbss_desc->beacon_period); + + pos += sizeof(passo->cap_info); + pos += sizeof(passo->listen_interval); + pos += sizeof(passo->beacon_period); + pos += sizeof(passo->dtim_period); + + pssid_tlv = (MrvlIEtypes_SsIdParamSet_t *)pos; + pssid_tlv->header.type = wlan_cpu_to_le16(TLV_TYPE_SSID); + pssid_tlv->header.len = (t_u16)pbss_desc->ssid.ssid_len; + memcpy(pmadapter, pssid_tlv->ssid, pbss_desc->ssid.ssid, + pssid_tlv->header.len); + pos += sizeof(pssid_tlv->header) + pssid_tlv->header.len; + pssid_tlv->header.len = wlan_cpu_to_le16(pssid_tlv->header.len); + + pphy_tlv = (MrvlIEtypes_PhyParamSet_t *)pos; + pphy_tlv->header.type = wlan_cpu_to_le16(TLV_TYPE_PHY_DS); + pphy_tlv->header.len = sizeof(pphy_tlv->fh_ds.ds_param_set); + memcpy(pmadapter, &pphy_tlv->fh_ds.ds_param_set, + &pbss_desc->phy_param_set.ds_param_set.current_chan, + sizeof(pphy_tlv->fh_ds.ds_param_set)); + pos += sizeof(pphy_tlv->header) + pphy_tlv->header.len; + pphy_tlv->header.len = wlan_cpu_to_le16(pphy_tlv->header.len); + + pss_tlv = (MrvlIEtypes_SsParamSet_t *)pos; + pss_tlv->header.type = wlan_cpu_to_le16(TLV_TYPE_CF); + pss_tlv->header.len = sizeof(pss_tlv->cf_ibss.cf_param_set); + pos += sizeof(pss_tlv->header) + pss_tlv->header.len; + pss_tlv->header.len = wlan_cpu_to_le16(pss_tlv->header.len); + + /* Get the common rates supported between the driver and the BSS Desc */ + if (wlan_setup_rates_from_bssdesc + (pmpriv, pbss_desc, rates, &rates_size)) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* Save the data rates into Current BSS state structure */ + pmpriv->curr_bss_params.num_of_rates = rates_size; + memcpy(pmadapter, &pmpriv->curr_bss_params.data_rates, rates, + rates_size); + + /* Setup the Rates TLV in the association command */ + prates_tlv = (MrvlIEtypes_RatesParamSet_t *)pos; + prates_tlv->header.type = wlan_cpu_to_le16(TLV_TYPE_RATES); + prates_tlv->header.len = wlan_cpu_to_le16((t_u16)rates_size); + memcpy(pmadapter, prates_tlv->rates, rates, rates_size); + pos += sizeof(prates_tlv->header) + rates_size; + PRINTM(MINFO, "ASSOC_CMD: Rates size = %d\n", rates_size); + + /* Add the Authentication type to be used for Auth frames if needed */ + if (pmpriv->sec_info.authentication_mode != MLAN_AUTH_MODE_AUTO) { + pauth_tlv = (MrvlIEtypes_AuthType_t *)pos; + pauth_tlv->header.type = wlan_cpu_to_le16(TLV_TYPE_AUTH_TYPE); + pauth_tlv->header.len = sizeof(pauth_tlv->auth_type); + if ((pmpriv->sec_info.wep_status == Wlan802_11WEPEnabled) + || (pmpriv->sec_info.authentication_mode == + MLAN_AUTH_MODE_NETWORKEAP)) + pauth_tlv->auth_type = + wlan_cpu_to_le16((t_u16)pmpriv->sec_info. + authentication_mode); + else if (pmpriv->sec_info.authentication_mode == + MLAN_AUTH_MODE_FT) + pauth_tlv->auth_type = + wlan_cpu_to_le16(AssocAgentAuth_FastBss_Skip); + else + pauth_tlv->auth_type = + wlan_cpu_to_le16(MLAN_AUTH_MODE_OPEN); + pos += sizeof(pauth_tlv->header) + pauth_tlv->header.len; + pauth_tlv->header.len = wlan_cpu_to_le16(pauth_tlv->header.len); + } + + if (IS_SUPPORT_MULTI_BANDS(pmadapter) + && (pbss_desc->bss_band & pmpriv->config_bands) + && !(ISSUPP_11NENABLED(pmadapter->fw_cap_info) + && (!pbss_desc->disable_11n) + && (pmpriv->config_bands & BAND_GN + || pmpriv->config_bands & BAND_AN) + && (pbss_desc->pht_cap) + ) + ) { + /* Append a channel TLV for the channel the attempted AP was found on */ + pchan_tlv = (MrvlIEtypes_ChanListParamSet_t *)pos; + pchan_tlv->header.type = wlan_cpu_to_le16(TLV_TYPE_CHANLIST); + pchan_tlv->header.len = + wlan_cpu_to_le16(sizeof(ChanScanParamSet_t)); + + memset(pmadapter, pchan_tlv->chan_scan_param, 0x00, + sizeof(ChanScanParamSet_t)); + pchan_tlv->chan_scan_param[0].chan_number = + (pbss_desc->phy_param_set.ds_param_set.current_chan); + PRINTM(MINFO, "Assoc: TLV Chan = %d\n", + pchan_tlv->chan_scan_param[0].chan_number); + + pchan_tlv->chan_scan_param[0].bandcfg.chanBand = + wlan_band_to_radio_type((t_u8)pbss_desc->bss_band); + + PRINTM(MINFO, "Assoc: TLV Bandcfg = %x\n", + pchan_tlv->chan_scan_param[0].bandcfg); + pos += sizeof(pchan_tlv->header) + sizeof(ChanScanParamSet_t); + } + if (!pmpriv->wps.session_enable) { + if ((pmpriv->sec_info.wpa_enabled + || pmpriv->sec_info.wpa2_enabled)) { + prsn_ie_tlv = (MrvlIEtypes_RsnParamSet_t *)pos; + /* WPA_IE or RSN_IE */ + prsn_ie_tlv->header.type = (t_u16)pmpriv->wpa_ie[0]; + prsn_ie_tlv->header.type = + prsn_ie_tlv->header.type & 0x00FF; + prsn_ie_tlv->header.type = + wlan_cpu_to_le16(prsn_ie_tlv->header.type); + prsn_ie_tlv->header.len = (t_u16)pmpriv->wpa_ie[1]; + prsn_ie_tlv->header.len = + prsn_ie_tlv->header.len & 0x00FF; + if (prsn_ie_tlv->header.len <= + (sizeof(pmpriv->wpa_ie) - 2)) + memcpy(pmadapter, prsn_ie_tlv->rsn_ie, + &pmpriv->wpa_ie[2], + prsn_ie_tlv->header.len); + else { + ret = MLAN_STATUS_FAILURE; + goto done; + } + HEXDUMP("ASSOC_CMD: RSN IE", (t_u8 *)prsn_ie_tlv, + sizeof(prsn_ie_tlv->header) + + prsn_ie_tlv->header.len); + pos += sizeof(prsn_ie_tlv->header) + + prsn_ie_tlv->header.len; + prsn_ie_tlv->header.len = + wlan_cpu_to_le16(prsn_ie_tlv->header.len); + /** parse rsn ie to find whether ft akm is used*/ + ft_akm = wlan_ft_akm_is_used(pmpriv, pmpriv->wpa_ie); + } +#ifdef DRV_EMBEDDED_SUPPLICANT + else if (supplicantIsEnabled(pmpriv->psapriv)) { + supplicantClrEncryptKey(pmpriv->psapriv); + + if (pbss_desc->prsn_ie) + rsn_wpa_ie_tmp = pbss_desc->prsn_ie; + else if (pbss_desc->pwpa_ie) + rsn_wpa_ie_tmp = pbss_desc->pwpa_ie; + prsn_ie_tlv = (MrvlIEtypes_RsnParamSet_t *)pos; + pos += supplicantFormatRsnWpaTlv(pmpriv->psapriv, + rsn_wpa_ie_tmp, + prsn_ie_tlv); + } +#endif + else if (pmpriv->sec_info.ewpa_enabled) { + prsn_ie_tlv = (MrvlIEtypes_RsnParamSet_t *)pos; + if (pbss_desc->pwpa_ie) { + prsn_ie_tlv->header.type = + (t_u16)(*(pbss_desc->pwpa_ie)).vend_hdr. + element_id; + prsn_ie_tlv->header.type = + prsn_ie_tlv->header.type & 0x00FF; + prsn_ie_tlv->header.type = + wlan_cpu_to_le16(prsn_ie_tlv->header. + type); + prsn_ie_tlv->header.len = + (t_u16)(*(pbss_desc->pwpa_ie)).vend_hdr. + len; + prsn_ie_tlv->header.len = + prsn_ie_tlv->header.len & 0x00FF; + if (prsn_ie_tlv->header.len <= + (sizeof(pmpriv->wpa_ie))) { + memcpy(pmadapter, prsn_ie_tlv->rsn_ie, + &((*(pbss_desc->pwpa_ie)). + vend_hdr.oui[0]), + prsn_ie_tlv->header.len); + } else { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + HEXDUMP("ASSOC_CMD: RSN IE", + (t_u8 *)prsn_ie_tlv, + sizeof(prsn_ie_tlv->header) + + prsn_ie_tlv->header.len); + pos += sizeof(prsn_ie_tlv->header) + + prsn_ie_tlv->header.len; + prsn_ie_tlv->header.len = + wlan_cpu_to_le16(prsn_ie_tlv->header. + len); + } + if (pbss_desc->prsn_ie) { + prsn_ie_tlv = (MrvlIEtypes_RsnParamSet_t *)pos; + prsn_ie_tlv->header.type = + (t_u16)(*(pbss_desc->prsn_ie)).ieee_hdr. + element_id; + prsn_ie_tlv->header.type = + prsn_ie_tlv->header.type & 0x00FF; + prsn_ie_tlv->header.type = + wlan_cpu_to_le16(prsn_ie_tlv->header. + type); + prsn_ie_tlv->header.len = + (t_u16)(*(pbss_desc->prsn_ie)).ieee_hdr. + len; + prsn_ie_tlv->header.len = + prsn_ie_tlv->header.len & 0x00FF; + if (prsn_ie_tlv->header.len <= + (sizeof(pmpriv->wpa_ie))) { + memcpy(pmadapter, prsn_ie_tlv->rsn_ie, + &((*(pbss_desc->prsn_ie)). + data[0]) + , prsn_ie_tlv->header.len); + ret = wlan_update_rsn_ie(pmpriv, + prsn_ie_tlv); + if (ret != MLAN_STATUS_SUCCESS) { + goto done; + } + } else { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + HEXDUMP("ASSOC_CMD: RSN IE", + (t_u8 *)prsn_ie_tlv, + sizeof(prsn_ie_tlv->header) + + prsn_ie_tlv->header.len); + pos += sizeof(prsn_ie_tlv->header) + + prsn_ie_tlv->header.len; + prsn_ie_tlv->header.len = + wlan_cpu_to_le16(prsn_ie_tlv->header. + len); + } + } + } + + if (ISSUPP_11NENABLED(pmadapter->fw_cap_info) + && (!pbss_desc->disable_11n) + && wlan_11n_bandconfig_allowed(pmpriv, pbss_desc->bss_band)) + wlan_cmd_append_11n_tlv(pmpriv, pbss_desc, &pos); + if (pmpriv->adapter->ecsa_enable) { + if (MLAN_STATUS_SUCCESS == + wlan_get_curr_oper_class(pmpriv, + pbss_desc->phy_param_set. + ds_param_set.current_chan, + pbss_desc->curr_bandwidth, + &oper_class)) + wlan_add_supported_oper_class_ie(pmpriv, &pos, + oper_class); + } + + wlan_wmm_process_association_req(pmpriv, &pos, &pbss_desc->wmm_ie, + pbss_desc->pht_cap); + if (pmpriv->sec_info.wapi_enabled && pmpriv->wapi_ie_len) + wlan_cmd_append_wapi_ie(pmpriv, &pos); + + wlan_cmd_append_generic_ie(pmpriv, &pos); + + if (ft_akm && pbss_desc->pmd_ie) + wlan_cmd_append_pass_through_ie(pmpriv, + (IEEEtypes_Generic_t *) + pbss_desc->pmd_ie, &pos); + wlan_cmd_append_tsf_tlv(pmpriv, &pos, pbss_desc); + + if (wlan_11d_create_dnld_countryinfo(pmpriv, (t_u8)pbss_desc->bss_band)) { + PRINTM(MERROR, "Dnld_countryinfo_11d failed\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + if (wlan_11d_parse_dnld_countryinfo + (pmpriv, pmpriv->pattempted_bss_desc)) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* + * Call 11h join API after capability bits are set so adhoc/infra 11h + * behavior can be properly triggered. pos modified if data is appended + */ + wlan_11h_process_join(pmpriv, &pos, &passo->cap_info, + (t_u8)pbss_desc->bss_band, + pbss_desc->phy_param_set.ds_param_set. + current_chan, &pbss_desc->wlan_11h_bss_info); + + cmd->size = wlan_cpu_to_le16((t_u16)(pos - (t_u8 *)passo) + S_DS_GEN); + + /* Set the Capability info at last */ + memcpy(pmadapter, &tmp_cap, &pbss_desc->cap_info, + sizeof(passo->cap_info)); + + if (pmpriv->config_bands == BAND_B) + SHORT_SLOT_TIME_DISABLED(tmp_cap); + + /* set SpectrumMgmt(BIT8) and RadioMeasurement(BIT12) if 11K is enabled */ + if (pmpriv->enable_11k) { + SPECTRUM_MGMT_ENABLED(tmp_cap); + RADIO_MEASUREMENT_ENABLED(tmp_cap); + } + + RADIO_MEASUREMENT_ENABLED(tmp_cap); + + tmp_cap &= CAPINFO_MASK; + PRINTM(MINFO, "ASSOC_CMD: tmp_cap=%4X CAPINFO_MASK=%4lX\n", + tmp_cap, CAPINFO_MASK); + tmp_cap = wlan_cpu_to_le16(tmp_cap); + memcpy(pmadapter, &passo->cap_info, &tmp_cap, sizeof(passo->cap_info)); + +done: + LEAVE(); + return ret; +} + +/** + * @brief Association firmware command response handler + * + * The response buffer for the association command has the following + * memory layout. + * + * For cases where an association response was not received (indicated + * by the CapInfo and AId field): + * + * .------------------------------------------------------------. + * | Header(4 * sizeof(t_u16)): Standard command response hdr | + * .------------------------------------------------------------. + * | cap_info/Error Return(t_u16): | + * | 0xFFFF(-1): Internal error for association | + * | 0xFFFE(-2): Authentication unhandled message | + * | 0xFFFD(-3): Authentication refused | + * | 0xFFFC(-4): Timeout waiting for AP response | + * | 0xFFFB(-5): Internal error for authentication | + * .------------------------------------------------------------. + * | status_code(t_u16): | + * | If cap_info is -1: | + * | An internal firmware failure prevented the | + * | command from being processed. The status code | + * | is 6 if associate response parameter invlaid, | + * | 1 otherwise. | + * | | + * | If cap_info is -2: | + * | An authentication frame was received but was | + * | not handled by the firmware. IEEE Status code | + * | for the failure is returned. | + * | | + * | If cap_info is -3: | + * | An authentication frame was received and the | + * | status_code is the IEEE Status reported in the | + * | response. | + * | | + * | If cap_info is -4: | + * | (1) Association response timeout | + * | (2) Authentication response timeout | + * | | + * | If cap_info is -5: | + * | An internal firmware failure prevented the | + * | command from being processed. The status code | + * | is 6 if authentication parameter invlaid, | + * | 1 otherwise. | + * .------------------------------------------------------------. + * | a_id(t_u16): 0xFFFF | + * .------------------------------------------------------------. + * + * + * For cases where an association response was received, the IEEE + * standard association response frame is returned: + * + * .------------------------------------------------------------. + * | Header(4 * sizeof(t_u16)): Standard command response hdr | + * .------------------------------------------------------------. + * | cap_info(t_u16): IEEE Capability | + * .------------------------------------------------------------. + * | status_code(t_u16): IEEE Status Code | + * .------------------------------------------------------------. + * | a_id(t_u16): IEEE Association ID | + * .------------------------------------------------------------. + * | IEEE IEs(variable): Any received IEs comprising the | + * | remaining portion of a received | + * | association response frame. | + * .------------------------------------------------------------. + * + * For simplistic handling, the status_code field can be used to determine + * an association success (0) or failure (non-zero). + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_ret_802_11_associate(IN mlan_private *pmpriv, + IN HostCmd_DS_COMMAND *resp, IN t_void *pioctl_buf) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ioctl_req *pioctl_req = (mlan_ioctl_req *)pioctl_buf; + IEEEtypes_AssocRsp_t *passoc_rsp; + BSSDescriptor_t *pbss_desc; + t_u8 enable_data = MTRUE; + t_u8 event_buf[100]; + mlan_event *pevent = (mlan_event *)event_buf; + t_u8 cur_mac[MLAN_MAC_ADDR_LENGTH]; + t_u8 media_connected = pmpriv->media_connected; + ENTER(); + + passoc_rsp = (IEEEtypes_AssocRsp_t *)&resp->params; + passoc_rsp->status_code = wlan_le16_to_cpu(passoc_rsp->status_code); + if (pmpriv->media_connected == MTRUE) + memcpy(pmpriv->adapter, cur_mac, + pmpriv->curr_bss_params.bss_descriptor.mac_address, + MLAN_MAC_ADDR_LENGTH); + + HEXDUMP("ASSOC_RESP:", (t_u8 *)&resp->params, (resp->size - S_DS_GEN)); + + pmpriv->assoc_rsp_size = MIN(resp->size - S_DS_GEN, + sizeof(pmpriv->assoc_rsp_buf)); + + memcpy(pmpriv->adapter, pmpriv->assoc_rsp_buf, &resp->params, + pmpriv->assoc_rsp_size); + + if (passoc_rsp->status_code) { + if (pmpriv->media_connected == MTRUE) { + if (pmpriv->port_ctrl_mode == MTRUE) + pmpriv->port_open = pmpriv->prior_port_status; + if (!memcmp + (pmpriv->adapter, cur_mac, + pmpriv->pattempted_bss_desc->mac_address, + MLAN_MAC_ADDR_LENGTH)) + wlan_reset_connect_state(pmpriv, MTRUE); + else + wlan_recv_event(pmpriv, + MLAN_EVENT_ID_DRV_ASSOC_FAILURE_REPORT, + MNULL); + } else + wlan_reset_connect_state(pmpriv, MTRUE); + pmpriv->adapter->dbg.num_cmd_assoc_failure++; + PRINTM(MERROR, "ASSOC_RESP: Association Failed, " + "status code = %d, error = 0x%x, a_id = 0x%x\n", + passoc_rsp->status_code, + wlan_le16_to_cpu(*(t_u16 *)&passoc_rsp->capability), + wlan_le16_to_cpu(passoc_rsp->a_id)); + + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* Send a Media Connected event, according to the Spec */ + pmpriv->media_connected = MTRUE; + + pmpriv->adapter->pps_uapsd_mode = MFALSE; + pmpriv->adapter->tx_lock_flag = MFALSE; + pmpriv->adapter->delay_null_pkt = MFALSE; + + /* Set the attempted BSSID Index to current */ + pbss_desc = pmpriv->pattempted_bss_desc; + + PRINTM(MCMND, "ASSOC_RESP: %-32s (a_id = 0x%x)\n", pbss_desc->ssid.ssid, + wlan_le16_to_cpu(passoc_rsp->a_id)); + /* Restore default extended capabilities */ + memcpy(pmpriv->adapter, &pmpriv->ext_cap, &pmpriv->def_ext_cap, + sizeof(pmpriv->ext_cap)); + /* Make a copy of current BSSID descriptor */ + memcpy(pmpriv->adapter, &pmpriv->curr_bss_params.bss_descriptor, + pbss_desc, sizeof(BSSDescriptor_t)); + + /* Update curr_bss_params */ + pmpriv->curr_bss_params.bss_descriptor.channel + = pbss_desc->phy_param_set.ds_param_set.current_chan; + + pmpriv->curr_bss_params.band = (t_u8)pbss_desc->bss_band; + + /* Store current channel for further reference. + * This would save one extra call to get current + * channel when disconnect/bw_ch event is raised. + */ + pmpriv->adapter->dfsr_channel = + pmpriv->curr_bss_params.bss_descriptor.channel; + + /* + * Adjust the timestamps in the scan table to be relative to the newly + * associated AP's TSF + */ + wlan_update_tsf_timestamps(pmpriv, pbss_desc); + + if (pbss_desc->wmm_ie.vend_hdr.element_id == WMM_IE) + pmpriv->curr_bss_params.wmm_enabled = MTRUE; + else + pmpriv->curr_bss_params.wmm_enabled = MFALSE; + + if ((pmpriv->wmm_required + || (pbss_desc->pht_cap && + (pbss_desc->pht_cap->ieee_hdr.element_id == HT_CAPABILITY)) + ) && pmpriv->curr_bss_params.wmm_enabled) + pmpriv->wmm_enabled = MTRUE; + else + pmpriv->wmm_enabled = MFALSE; + + pmpriv->curr_bss_params.wmm_uapsd_enabled = MFALSE; + + if (pmpriv->wmm_enabled == MTRUE) + pmpriv->curr_bss_params.wmm_uapsd_enabled + = pbss_desc->wmm_ie.qos_info.qos_uapsd; + + PRINTM(MINFO, "ASSOC_RESP: curr_pkt_filter is 0x%x\n", + pmpriv->curr_pkt_filter); + if (pmpriv->sec_info.wpa_enabled || pmpriv->sec_info.wpa2_enabled) + pmpriv->wpa_is_gtk_set = MFALSE; + + if (pmpriv->wmm_enabled) + /* Don't re-enable carrier until we get the WMM_GET_STATUS event */ + enable_data = MFALSE; + else + /* Since WMM is not enabled, setup the queues with the defaults */ + wlan_wmm_setup_queues(pmpriv); + + if (enable_data) + PRINTM(MINFO, "Post association, re-enabling data flow\n"); + + /* Reset SNR/NF/RSSI values */ + pmpriv->data_rssi_last = 0; + pmpriv->data_nf_last = 0; + pmpriv->data_rssi_avg = 0; + pmpriv->data_nf_avg = 0; + pmpriv->bcn_rssi_last = 0; + pmpriv->bcn_nf_last = 0; + pmpriv->bcn_rssi_avg = 0; + pmpriv->bcn_nf_avg = 0; + pmpriv->amsdu_rx_cnt = 0; + pmpriv->amsdu_tx_cnt = 0; + pmpriv->msdu_in_rx_amsdu_cnt = 0; + pmpriv->msdu_in_tx_amsdu_cnt = 0; + pmpriv->rxpd_rate = 0; + pmpriv->rxpd_rate_info = 0; + if (pbss_desc->pht_cap) { + if (GETHT_MAXAMSDU(pbss_desc->pht_cap->ht_cap.ht_cap_info)) + pmpriv->max_amsdu = MLAN_TX_DATA_BUF_SIZE_8K; + else + pmpriv->max_amsdu = MLAN_TX_DATA_BUF_SIZE_4K; + } + + wlan_save_curr_bcn(pmpriv); + + pmpriv->adapter->dbg.num_cmd_assoc_success++; + + PRINTM(MINFO, "ASSOC_RESP: Associated\n"); + pevent->bss_index = pmpriv->bss_index; + pevent->event_id = MLAN_EVENT_ID_DRV_CONNECTED; + pevent->event_len = MLAN_MAC_ADDR_LENGTH; + memcpy(pmpriv->adapter, (t_u8 *)pevent->event_buf, + (t_u8 *)pmpriv->curr_bss_params.bss_descriptor.mac_address, + MLAN_MAC_ADDR_LENGTH); + + /* Add the ra_list here for infra mode as there will be only 1 ra always */ + if (media_connected) { + /** replace ralist's mac address with new mac address */ + if (0 == + wlan_ralist_update(pmpriv, cur_mac, + pmpriv->curr_bss_params.bss_descriptor. + mac_address)) + wlan_ralist_add(pmpriv, + pmpriv->curr_bss_params.bss_descriptor. + mac_address); + wlan_11n_cleanup_reorder_tbl(pmpriv); + wlan_11n_deleteall_txbastream_tbl(pmpriv); + + } else + wlan_ralist_add(pmpriv, + pmpriv->curr_bss_params.bss_descriptor. + mac_address); + + wlan_recv_event(pmpriv, MLAN_EVENT_ID_DRV_CONNECTED, pevent); + + /* Send OBSS scan param to the application if available */ + wlan_2040_coex_event(pmpriv); + wlan_coex_ampdu_rxwinsize(pmpriv->adapter); + + if (!pmpriv->sec_info.wpa_enabled + && !pmpriv->sec_info.wpa2_enabled + && !pmpriv->sec_info.ewpa_enabled + && !pmpriv->sec_info.wapi_enabled && !pmpriv->wps.session_enable +#ifdef DRV_EMBEDDED_SUPPLICANT + && !supplicantIsEnabled(pmpriv->psapriv) +#endif + ) { + /* We are in Open/WEP mode, open port immediately */ + if (pmpriv->port_ctrl_mode == MTRUE) { + pmpriv->port_open = MTRUE; + PRINTM(MINFO, "ASSOC_RESP: port_status = OPEN\n"); + } + } + + if (pmpriv->sec_info.wpa_enabled + || pmpriv->sec_info.wpa2_enabled + || pmpriv->sec_info.ewpa_enabled + || pmpriv->sec_info.wapi_enabled || pmpriv->wps.session_enable +#ifdef DRV_EMBEDDED_SUPPLICANT + || (supplicantIsEnabled(pmpriv->psapriv)) +#endif + ) + pmpriv->adapter->scan_block = MTRUE; + +#ifdef DRV_EMBEDDED_SUPPLICANT + supplicantInitSession(pmpriv->psapriv, + (t_u8 *)&pmpriv->curr_bss_params.bss_descriptor. + ssid.ssid, + pmpriv->curr_bss_params.bss_descriptor.ssid. + ssid_len, + (t_u8 *)&pmpriv->curr_bss_params.bss_descriptor. + mac_address, (t_u8 *)&pmpriv->curr_addr); +#endif + +done: + /* Need to indicate IOCTL complete */ + if (pioctl_req != MNULL) { + if (ret != MLAN_STATUS_SUCCESS) { + if (passoc_rsp->status_code) + pioctl_req->status_code = + (wlan_le16_to_cpu + (*(t_u16 *)&passoc_rsp-> + capability) << 16) + + passoc_rsp->status_code; + else + pioctl_req->status_code = + MLAN_ERROR_CMD_ASSOC_FAIL; + } else { + pioctl_req->status_code = MLAN_ERROR_NO_ERROR; + } + } + + LEAVE(); + return ret; +} + +/** + * @brief This function prepares command of ad_hoc_start. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param pdata_buf A pointer cast of mlan_802_11_ssid structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_cmd_802_11_ad_hoc_start(IN mlan_private *pmpriv, + IN HostCmd_DS_COMMAND *cmd, IN t_void *pdata_buf) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_adapter *pmadapter = pmpriv->adapter; + HostCmd_DS_802_11_AD_HOC_START *padhoc_start = &cmd->params.adhoc_start; + BSSDescriptor_t *pbss_desc; + t_u32 cmd_append_size = 0; + t_u32 i; + t_u16 tmp_cap; + MrvlIEtypes_ChanListParamSet_t *pchan_tlv; + + MrvlIEtypes_RsnParamSet_t *prsn_ie_tlv; + MrvlIETypes_HTCap_t *pht_cap; + MrvlIETypes_HTInfo_t *pht_info; + t_u32 rx_mcs_supp = 0; + /* wpa ie for WPA_NONE AES */ + const t_u8 wpa_ie[24] = + { 0xdd, 0x16, 0x00, 0x50, 0xf2, 0x01, 0x01, 0x00, + 0x00, 0x50, 0xf2, 0x04, 0x01, 0x00, 0x00, 0x50, + 0xf2, 0x00, 0x01, 0x00, 0x00, 0x50, 0xf2, 0x00 + }; + t_s32 append_size_11h = 0; + t_u8 *pos = + (t_u8 *)padhoc_start + sizeof(HostCmd_DS_802_11_AD_HOC_START); + + ENTER(); + + if (!pmadapter) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_802_11_AD_HOC_START); + + pbss_desc = &pmpriv->curr_bss_params.bss_descriptor; + pmpriv->pattempted_bss_desc = pbss_desc; + + /* + * Fill in the parameters for 2 data structures: + * 1. HostCmd_DS_802_11_AD_HOC_START command + * 2. pbss_desc + * Driver will fill up SSID, bss_mode,IBSS param, Physical Param, + * probe delay, and Cap info. + * Firmware will fill up beacon period, Basic rates + * and operational rates. + */ + + memset(pmadapter, padhoc_start->ssid, 0, MLAN_MAX_SSID_LENGTH); + + memcpy(pmadapter, padhoc_start->ssid, + ((mlan_802_11_ssid *)pdata_buf)->ssid, + MIN(MLAN_MAX_SSID_LENGTH, + ((mlan_802_11_ssid *)pdata_buf)->ssid_len)); + + PRINTM(MINFO, "ADHOC_S_CMD: SSID = %s\n", padhoc_start->ssid); + + memset(pmadapter, pbss_desc->ssid.ssid, 0, MLAN_MAX_SSID_LENGTH); + memcpy(pmadapter, pbss_desc->ssid.ssid, + ((mlan_802_11_ssid *)pdata_buf)->ssid, + MIN(MLAN_MAX_SSID_LENGTH, + ((mlan_802_11_ssid *)pdata_buf)->ssid_len)); + + pbss_desc->ssid.ssid_len = + MIN(MLAN_MAX_SSID_LENGTH, + ((mlan_802_11_ssid *)pdata_buf)->ssid_len); + + /* Set the BSS mode */ + padhoc_start->bss_mode = HostCmd_BSS_MODE_IBSS; + pbss_desc->bss_mode = MLAN_BSS_MODE_IBSS; + padhoc_start->beacon_period = wlan_cpu_to_le16(pmpriv->beacon_period); + pbss_desc->beacon_period = pmpriv->beacon_period; + + /* Set Physical param set */ +/** Parameter IE Id */ +#define DS_PARA_IE_ID 3 +/** Parameter IE length */ +#define DS_PARA_IE_LEN 1 + + padhoc_start->phy_param_set.ds_param_set.element_id = DS_PARA_IE_ID; + padhoc_start->phy_param_set.ds_param_set.len = DS_PARA_IE_LEN; + + if (!wlan_get_cfp_by_band_and_channel + (pmadapter, pmadapter->adhoc_start_band, + (t_u16)pmpriv->adhoc_channel, pmadapter->region_channel)) { + + chan_freq_power_t *cfp; + cfp = wlan_get_cfp_by_band_and_channel(pmadapter, + pmadapter-> + adhoc_start_band, + FIRST_VALID_CHANNEL, + pmadapter-> + region_channel); + if (cfp) + pmpriv->adhoc_channel = (t_u8)cfp->channel; + } + + MASSERT(pmpriv->adhoc_channel); + + PRINTM(MINFO, "ADHOC_S_CMD: Creating ADHOC on Channel %d\n", + pmpriv->adhoc_channel); + + pmpriv->curr_bss_params.bss_descriptor.channel = pmpriv->adhoc_channel; + pmpriv->curr_bss_params.band = pmadapter->adhoc_start_band; + + pbss_desc->channel = pmpriv->adhoc_channel; + padhoc_start->phy_param_set.ds_param_set.current_chan = + pmpriv->adhoc_channel; + + memcpy(pmadapter, &pbss_desc->phy_param_set, + &padhoc_start->phy_param_set, sizeof(IEEEtypes_PhyParamSet_t)); + + pbss_desc->network_type_use = Wlan802_11DS; + + /* Set IBSS param set */ +/** IBSS parameter IE Id */ +#define IBSS_PARA_IE_ID 6 +/** IBSS parameter IE length */ +#define IBSS_PARA_IE_LEN 2 + + padhoc_start->ss_param_set.ibss_param_set.element_id = IBSS_PARA_IE_ID; + padhoc_start->ss_param_set.ibss_param_set.len = IBSS_PARA_IE_LEN; + padhoc_start->ss_param_set.ibss_param_set.atim_window + = wlan_cpu_to_le16(pmpriv->atim_window); + pbss_desc->atim_window = pmpriv->atim_window; + memcpy(pmadapter, &pbss_desc->ss_param_set, + &padhoc_start->ss_param_set, sizeof(IEEEtypes_SsParamSet_t)); + + /* Set Capability info */ + padhoc_start->cap.ess = 0; + padhoc_start->cap.ibss = 1; + pbss_desc->cap_info.ibss = 1; + + /* Set up privacy in pbss_desc */ + if (pmpriv->sec_info.wep_status == Wlan802_11WEPEnabled + || pmpriv->adhoc_aes_enabled + || pmpriv->sec_info.wpa_enabled || pmpriv->sec_info.ewpa_enabled) { +/** Ad-Hoc capability privacy on */ +#define AD_HOC_CAP_PRIVACY_ON 1 + PRINTM(MINFO, "ADHOC_S_CMD: wep_status set, Privacy to WEP\n"); + pbss_desc->privacy = Wlan802_11PrivFilter8021xWEP; + padhoc_start->cap.privacy = AD_HOC_CAP_PRIVACY_ON; + } else { + PRINTM(MWARN, "ADHOC_S_CMD: wep_status NOT set, Setting " + "Privacy to ACCEPT ALL\n"); + pbss_desc->privacy = Wlan802_11PrivFilterAcceptAll; + } + + memset(pmadapter, padhoc_start->DataRate, 0, + sizeof(padhoc_start->DataRate)); + + if ((pmpriv->adapter->region_code == COUNTRY_CODE_JP_40 || + pmpriv->adapter->region_code == COUNTRY_CODE_JP_FF) + && (pbss_desc->phy_param_set.ds_param_set.current_chan == 14)) { + wlan_get_active_data_rates(pmpriv, pmpriv->bss_mode, + BAND_B, padhoc_start->DataRate); + } else { + wlan_get_active_data_rates(pmpriv, pmpriv->bss_mode, + pmadapter->adhoc_start_band, + padhoc_start->DataRate); + } + + if ((pmadapter->adhoc_start_band & BAND_G) && + (pmpriv->curr_pkt_filter & HostCmd_ACT_MAC_ADHOC_G_PROTECTION_ON)) { + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_MAC_CONTROL, + HostCmd_ACT_GEN_SET, + 0, MNULL, &pmpriv->curr_pkt_filter); + + if (ret) { + PRINTM(MERROR, + "ADHOC_S_CMD: G Protection config failed\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + } + /* Find the last non zero */ + for (i = 0; i < sizeof(padhoc_start->DataRate) + && padhoc_start->DataRate[i]; i++) + /* XXX Do not delete no-operation line */ + ; + + pmpriv->curr_bss_params.num_of_rates = i; + + /* Copy the ad-hoc creating rates into Current BSS rate structure */ + memcpy(pmadapter, &pmpriv->curr_bss_params.data_rates, + &padhoc_start->DataRate, pmpriv->curr_bss_params.num_of_rates); + + PRINTM(MINFO, "ADHOC_S_CMD: Rates=%02x %02x %02x %02x\n", + padhoc_start->DataRate[0], padhoc_start->DataRate[1], + padhoc_start->DataRate[2], padhoc_start->DataRate[3]); + + PRINTM(MINFO, "ADHOC_S_CMD: AD HOC Start command is ready\n"); + + if (IS_SUPPORT_MULTI_BANDS(pmadapter)) { + /* Append a channel TLV */ + pchan_tlv = (MrvlIEtypes_ChanListParamSet_t *)pos; + pchan_tlv->header.type = wlan_cpu_to_le16(TLV_TYPE_CHANLIST); + pchan_tlv->header.len = + wlan_cpu_to_le16(sizeof(ChanScanParamSet_t)); + + memset(pmadapter, pchan_tlv->chan_scan_param, 0x00, + sizeof(ChanScanParamSet_t)); + pchan_tlv->chan_scan_param[0].chan_number = + (t_u8)pmpriv->curr_bss_params.bss_descriptor.channel; + + PRINTM(MINFO, "ADHOC_S_CMD: TLV Chan = %d\n", + pchan_tlv->chan_scan_param[0].chan_number); + + pchan_tlv->chan_scan_param[0].bandcfg.chanBand + = wlan_band_to_radio_type(pmpriv->curr_bss_params.band); + if (pmadapter->adhoc_start_band & BAND_GN + || pmadapter->adhoc_start_band & BAND_AN) { + if (pmadapter->chan_bandwidth == CHANNEL_BW_40MHZ_ABOVE) { + pchan_tlv->chan_scan_param[0].bandcfg. + chan2Offset = SEC_CHAN_ABOVE; + pchan_tlv->chan_scan_param[0].bandcfg. + chanWidth = CHAN_BW_40MHZ; + } else if (pmadapter->chan_bandwidth == + CHANNEL_BW_40MHZ_BELOW) { + pchan_tlv->chan_scan_param[0].bandcfg. + chan2Offset = SEC_CHAN_BELOW; + pchan_tlv->chan_scan_param[0].bandcfg. + chanWidth = CHAN_BW_40MHZ; + } + } + PRINTM(MINFO, "ADHOC_S_CMD: TLV Bandcfg = %x\n", + pchan_tlv->chan_scan_param[0].bandcfg); + pos += sizeof(pchan_tlv->header) + sizeof(ChanScanParamSet_t); + cmd_append_size += + sizeof(pchan_tlv->header) + sizeof(ChanScanParamSet_t); + } + + if (wlan_11d_create_dnld_countryinfo + (pmpriv, pmpriv->curr_bss_params.band)) { + PRINTM(MERROR, "ADHOC_S_CMD: dnld_countryinfo_11d failed\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* + * Call 11h start API to add any 11h flags/elements as TLV parameters + */ + append_size_11h = + wlan_11h_process_start(pmpriv, &pos, &padhoc_start->cap, + pmpriv->adhoc_channel, + &pbss_desc->wlan_11h_bss_info); + if (append_size_11h >= 0) + cmd_append_size += append_size_11h; + else { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + if (pmpriv->sec_info.ewpa_enabled) { + memcpy(pmadapter, pmpriv->wpa_ie, wpa_ie, sizeof(wpa_ie)); + pmpriv->wpa_ie_len = sizeof(wpa_ie); + } + + if (pmpriv->sec_info.wpa_enabled || pmpriv->sec_info.ewpa_enabled) { + prsn_ie_tlv = (MrvlIEtypes_RsnParamSet_t *)pos; + prsn_ie_tlv->header.type = (t_u16)pmpriv->wpa_ie[0]; + /* WPA_IE or RSN_IE */ + prsn_ie_tlv->header.type = prsn_ie_tlv->header.type & 0x00FF; + prsn_ie_tlv->header.type = + wlan_cpu_to_le16(prsn_ie_tlv->header.type); + prsn_ie_tlv->header.len = (t_u16)pmpriv->wpa_ie[1]; + prsn_ie_tlv->header.len = prsn_ie_tlv->header.len & 0x00FF; + if (prsn_ie_tlv->header.len <= (sizeof(pmpriv->wpa_ie) - 2)) + memcpy(pmadapter, prsn_ie_tlv->rsn_ie, + &pmpriv->wpa_ie[2], prsn_ie_tlv->header.len); + else { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + DBG_HEXDUMP(MCMD_D, "ADHOC_S_CMD: RSN IE", (t_u8 *)prsn_ie_tlv, + sizeof(prsn_ie_tlv->header) + + prsn_ie_tlv->header.len); + pos += sizeof(prsn_ie_tlv->header) + prsn_ie_tlv->header.len; + cmd_append_size += + sizeof(prsn_ie_tlv->header) + prsn_ie_tlv->header.len; + prsn_ie_tlv->header.len = + wlan_cpu_to_le16(prsn_ie_tlv->header.len); + } + + if (pmadapter->adhoc_11n_enabled == MTRUE) { + { + pht_cap = (MrvlIETypes_HTCap_t *)pos; + memset(pmadapter, pht_cap, 0, + sizeof(MrvlIETypes_HTCap_t)); + pht_cap->header.type = wlan_cpu_to_le16(HT_CAPABILITY); + pht_cap->header.len = sizeof(HTCap_t); + rx_mcs_supp = + GET_RXMCSSUPP(pmpriv->usr_dev_mcs_support); + /* Set MCS for 1x1/2x2 */ + memset(pmadapter, + (t_u8 *)pht_cap->ht_cap.supported_mcs_set, 0xff, + rx_mcs_supp); + wlan_fill_ht_cap_tlv(pmpriv, pht_cap, + pmpriv->curr_bss_params.band, + MTRUE); + HEXDUMP("ADHOC_START: HT_CAPABILITIES IE", + (t_u8 *)pht_cap, sizeof(MrvlIETypes_HTCap_t)); + pos += sizeof(MrvlIETypes_HTCap_t); + cmd_append_size += sizeof(MrvlIETypes_HTCap_t); + pht_cap->header.len = + wlan_cpu_to_le16(pht_cap->header.len); + } + { + pht_info = (MrvlIETypes_HTInfo_t *)pos; + memset(pmadapter, pht_info, 0, + sizeof(MrvlIETypes_HTInfo_t)); + pht_info->header.type = wlan_cpu_to_le16(HT_OPERATION); + pht_info->header.len = sizeof(HTInfo_t); + pht_info->ht_info.pri_chan = + (t_u8)pmpriv->curr_bss_params.bss_descriptor. + channel; + if ((pmadapter->chan_bandwidth == + CHANNEL_BW_40MHZ_ABOVE) || + (pmadapter->chan_bandwidth == + CHANNEL_BW_40MHZ_BELOW)) { + pht_info->ht_info.field2 = + pmadapter->chan_bandwidth; + SET_CHANWIDTH40(pht_info->ht_info.field2); + } + pht_info->ht_info.field3 = + wlan_cpu_to_le16(NON_GREENFIELD_STAS); + pht_info->ht_info.basic_mcs_set[0] = 0xff; + HEXDUMP("ADHOC_START: HT_INFORMATION IE", + (t_u8 *)pht_info, sizeof(MrvlIETypes_HTInfo_t)); + pos += sizeof(MrvlIETypes_HTInfo_t); + cmd_append_size += sizeof(MrvlIETypes_HTInfo_t); + pht_info->header.len = + wlan_cpu_to_le16(pht_info->header.len); + } + } + + cmd->size = + (t_u16) + wlan_cpu_to_le16((t_u16)(sizeof(HostCmd_DS_802_11_AD_HOC_START) + + S_DS_GEN + cmd_append_size)); + + memcpy(pmadapter, &tmp_cap, &padhoc_start->cap, sizeof(t_u16)); + + if (pmadapter->adhoc_start_band == BAND_B) + SHORT_SLOT_TIME_DISABLED(tmp_cap); + else + SHORT_SLOT_TIME_ENABLED(tmp_cap); + + tmp_cap = wlan_cpu_to_le16(tmp_cap); + memcpy(pmadapter, &padhoc_start->cap, &tmp_cap, sizeof(t_u16)); + + ret = MLAN_STATUS_SUCCESS; +done: + LEAVE(); + return ret; +} + +/** + * @brief This function prepares command of ad_hoc_join. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param pdata_buf Void cast of BSSDescriptor_t from the + * scan table to join + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_cmd_802_11_ad_hoc_join(IN mlan_private *pmpriv, + IN HostCmd_DS_COMMAND *cmd, IN t_void *pdata_buf) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_adapter *pmadapter = pmpriv->adapter; + HostCmd_DS_802_11_AD_HOC_JOIN *padhoc_join = &cmd->params.adhoc_join; + BSSDescriptor_t *pbss_desc = (BSSDescriptor_t *)pdata_buf; + MrvlIEtypes_ChanListParamSet_t *pchan_tlv; + MrvlIEtypes_RsnParamSet_t *prsn_ie_tlv; + t_u32 cmd_append_size = 0; + t_u16 tmp_cap; + t_u32 i, rates_size = 0; + t_u32 curr_pkt_filter; + t_u8 *pos = (t_u8 *)padhoc_join + sizeof(HostCmd_DS_802_11_AD_HOC_JOIN); + + ENTER(); + +/** Use G protection */ +#define USE_G_PROTECTION 0x02 + if (pbss_desc->erp_flags & USE_G_PROTECTION) { + curr_pkt_filter = + pmpriv-> + curr_pkt_filter | HostCmd_ACT_MAC_ADHOC_G_PROTECTION_ON; + + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_MAC_CONTROL, + HostCmd_ACT_GEN_SET, + 0, MNULL, &curr_pkt_filter); + if (ret) { + PRINTM(MERROR, + "ADHOC_J_CMD: G Protection config failed\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + } + + pmpriv->pattempted_bss_desc = pbss_desc; + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_802_11_AD_HOC_JOIN); + + padhoc_join->bss_descriptor.bss_mode = HostCmd_BSS_MODE_IBSS; + + padhoc_join->bss_descriptor.beacon_period + = wlan_cpu_to_le16(pbss_desc->beacon_period); + + memcpy(pmadapter, &padhoc_join->bss_descriptor.bssid, + &pbss_desc->mac_address, MLAN_MAC_ADDR_LENGTH); + + memcpy(pmadapter, &padhoc_join->bss_descriptor.ssid, + &pbss_desc->ssid.ssid, + MIN(MLAN_MAX_SSID_LENGTH, pbss_desc->ssid.ssid_len)); + + memcpy(pmadapter, &padhoc_join->bss_descriptor.phy_param_set, + &pbss_desc->phy_param_set, sizeof(IEEEtypes_PhyParamSet_t)); + + padhoc_join->bss_descriptor.phy_param_set.fh_param_set.dwell_time + = + wlan_cpu_to_le16(padhoc_join->bss_descriptor.phy_param_set. + fh_param_set.dwell_time); + + memcpy(pmadapter, &padhoc_join->bss_descriptor.ss_param_set, + &pbss_desc->ss_param_set, sizeof(IEEEtypes_SsParamSet_t)); + padhoc_join->bss_descriptor.ss_param_set.ibss_param_set.atim_window + = + wlan_cpu_to_le16(padhoc_join->bss_descriptor.ss_param_set. + ibss_param_set.atim_window); + + memcpy(pmadapter, &tmp_cap, &pbss_desc->cap_info, + sizeof(IEEEtypes_CapInfo_t)); + + tmp_cap &= CAPINFO_MASK; + + PRINTM(MINFO, "ADHOC_J_CMD: tmp_cap=%4X CAPINFO_MASK=%4lX\n", + tmp_cap, CAPINFO_MASK); + memcpy(pmadapter, &padhoc_join->bss_descriptor.cap, &tmp_cap, + sizeof(IEEEtypes_CapInfo_t)); + + /* Information on BSSID descriptor passed to FW */ + PRINTM(MINFO, + "ADHOC_J_CMD: BSSID = " MACSTR ", SSID = %s\n", + MAC2STR(padhoc_join->bss_descriptor.bssid), + padhoc_join->bss_descriptor.ssid); + + for (i = 0; i < WLAN_SUPPORTED_RATES && pbss_desc->supported_rates[i]; + i++) + /* XXX Do not delete no-operation line */ + ; + rates_size = i; + + /* Copy Data Rates from the Rates recorded in scan response */ + memset(pmadapter, padhoc_join->bss_descriptor.data_rates, 0, + sizeof(padhoc_join->bss_descriptor.data_rates)); + memcpy(pmadapter, padhoc_join->bss_descriptor.data_rates, + pbss_desc->supported_rates, rates_size); + + HEXDUMP("Adapted Rates:", padhoc_join->bss_descriptor.data_rates, + rates_size); + + /* Copy the adhoc join rates into Current BSS state structure */ + pmpriv->curr_bss_params.num_of_rates = rates_size; + memcpy(pmadapter, &pmpriv->curr_bss_params.data_rates, + pbss_desc->supported_rates, rates_size); + + /* Copy the channel information */ + pmpriv->curr_bss_params.bss_descriptor.channel = pbss_desc->channel; + pmpriv->curr_bss_params.band = (t_u8)pbss_desc->bss_band; + + if (pmpriv->sec_info.wep_status == Wlan802_11WEPEnabled + || pmpriv->adhoc_aes_enabled + || pmpriv->sec_info.wpa_enabled || pmpriv->sec_info.ewpa_enabled) + padhoc_join->bss_descriptor.cap.privacy = AD_HOC_CAP_PRIVACY_ON; + + if (IS_SUPPORT_MULTI_BANDS(pmadapter)) { + /* Append a channel TLV */ + pchan_tlv = (MrvlIEtypes_ChanListParamSet_t *)pos; + pchan_tlv->header.type = wlan_cpu_to_le16(TLV_TYPE_CHANLIST); + pchan_tlv->header.len = + wlan_cpu_to_le16(sizeof(ChanScanParamSet_t)); + + memset(pmadapter, pchan_tlv->chan_scan_param, 0x00, + sizeof(ChanScanParamSet_t)); + pchan_tlv->chan_scan_param[0].chan_number = + (pbss_desc->phy_param_set.ds_param_set.current_chan); + PRINTM(MINFO, "ADHOC_J_CMD: TLV Chan = %d\n", + pchan_tlv->chan_scan_param[0].chan_number); + + pchan_tlv->chan_scan_param[0].bandcfg.chanBand + = wlan_band_to_radio_type((t_u8)pbss_desc->bss_band); + + PRINTM(MINFO, "ADHOC_J_CMD: TLV Bandcfg = %x\n", + pchan_tlv->chan_scan_param[0].bandcfg); + pos += sizeof(pchan_tlv->header) + sizeof(ChanScanParamSet_t); + cmd_append_size += + sizeof(pchan_tlv->header) + sizeof(ChanScanParamSet_t); + } + + if (wlan_11d_create_dnld_countryinfo(pmpriv, (t_u8)pbss_desc->bss_band)) { + PRINTM(MERROR, "Dnld_countryinfo_11d failed\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + if (wlan_11d_parse_dnld_countryinfo + (pmpriv, pmpriv->pattempted_bss_desc)) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* + * Call 11h join API after capability bits are set so + * adhoc/infra 11h behavior can be properly triggered. + * pos modified if data is appended + */ + cmd_append_size += wlan_11h_process_join(pmpriv, &pos, + &padhoc_join->bss_descriptor. + cap, (t_u8)pbss_desc->bss_band, + pbss_desc->channel, + &pbss_desc->wlan_11h_bss_info); + + if (pmpriv->sec_info.wpa_enabled) { + prsn_ie_tlv = (MrvlIEtypes_RsnParamSet_t *)pos; + /* WPA_IE or RSN_IE */ + prsn_ie_tlv->header.type = (t_u16)pmpriv->wpa_ie[0]; + prsn_ie_tlv->header.type = prsn_ie_tlv->header.type & 0x00FF; + prsn_ie_tlv->header.type = + wlan_cpu_to_le16(prsn_ie_tlv->header.type); + prsn_ie_tlv->header.len = (t_u16)pmpriv->wpa_ie[1]; + prsn_ie_tlv->header.len = prsn_ie_tlv->header.len & 0x00FF; + if (prsn_ie_tlv->header.len <= (sizeof(pmpriv->wpa_ie) - 2)) + memcpy(pmadapter, prsn_ie_tlv->rsn_ie, + &pmpriv->wpa_ie[2], prsn_ie_tlv->header.len); + else { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + HEXDUMP("ADHOC_JOIN: RSN IE", (t_u8 *)prsn_ie_tlv, + sizeof(prsn_ie_tlv->header) + prsn_ie_tlv->header.len); + pos += sizeof(prsn_ie_tlv->header) + prsn_ie_tlv->header.len; + cmd_append_size += + sizeof(prsn_ie_tlv->header) + prsn_ie_tlv->header.len; + prsn_ie_tlv->header.len = + wlan_cpu_to_le16(prsn_ie_tlv->header.len); + } else if (pmpriv->sec_info.ewpa_enabled) { + prsn_ie_tlv = (MrvlIEtypes_RsnParamSet_t *)pos; + if (pbss_desc->pwpa_ie) { + prsn_ie_tlv->header.type = + (t_u16)(*(pbss_desc->pwpa_ie)).vend_hdr. + element_id; + prsn_ie_tlv->header.type = + prsn_ie_tlv->header.type & 0x00FF; + prsn_ie_tlv->header.type = + wlan_cpu_to_le16(prsn_ie_tlv->header.type); + prsn_ie_tlv->header.len = + (t_u16)(*(pbss_desc->pwpa_ie)).vend_hdr.len; + prsn_ie_tlv->header.len = + prsn_ie_tlv->header.len & 0x00FF; + if (prsn_ie_tlv->header.len <= (sizeof(pmpriv->wpa_ie))) { + memcpy(pmadapter, prsn_ie_tlv->rsn_ie, + &((*(pbss_desc->pwpa_ie)).vend_hdr. + oui[0]), prsn_ie_tlv->header.len); + } else { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + HEXDUMP("ADHOC_JOIN: RSN IE", (t_u8 *)prsn_ie_tlv, + sizeof(prsn_ie_tlv->header) + + prsn_ie_tlv->header.len); + pos += sizeof(prsn_ie_tlv->header) + + prsn_ie_tlv->header.len; + cmd_append_size += + sizeof(prsn_ie_tlv->header) + + prsn_ie_tlv->header.len; + prsn_ie_tlv->header.len = + wlan_cpu_to_le16(prsn_ie_tlv->header.len); + } + if (pbss_desc->prsn_ie) { + prsn_ie_tlv = (MrvlIEtypes_RsnParamSet_t *)pos; + prsn_ie_tlv->header.type = + (t_u16)(*(pbss_desc->prsn_ie)).ieee_hdr. + element_id; + prsn_ie_tlv->header.type = + prsn_ie_tlv->header.type & 0x00FF; + prsn_ie_tlv->header.type = + wlan_cpu_to_le16(prsn_ie_tlv->header.type); + prsn_ie_tlv->header.len = + (t_u16)(*(pbss_desc->prsn_ie)).ieee_hdr.len; + prsn_ie_tlv->header.len = + prsn_ie_tlv->header.len & 0x00FF; + if (prsn_ie_tlv->header.len <= (sizeof(pmpriv->wpa_ie))) { + memcpy(pmadapter, prsn_ie_tlv->rsn_ie, + &((*(pbss_desc->prsn_ie)).data[0]) + , prsn_ie_tlv->header.len); + } else { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + HEXDUMP("ADHOC_JOIN: RSN IE", (t_u8 *)prsn_ie_tlv, + sizeof(prsn_ie_tlv->header) + + prsn_ie_tlv->header.len); + pos += sizeof(prsn_ie_tlv->header) + + prsn_ie_tlv->header.len; + cmd_append_size += + sizeof(prsn_ie_tlv->header) + + prsn_ie_tlv->header.len; + prsn_ie_tlv->header.len = + wlan_cpu_to_le16(prsn_ie_tlv->header.len); + } + } + + if (ISSUPP_11NENABLED(pmadapter->fw_cap_info) + && wlan_11n_bandconfig_allowed(pmpriv, pbss_desc->bss_band) + ) + cmd_append_size += + wlan_cmd_append_11n_tlv(pmpriv, pbss_desc, &pos); + + cmd->size = + (t_u16) + wlan_cpu_to_le16((t_u16)(sizeof(HostCmd_DS_802_11_AD_HOC_JOIN) + + S_DS_GEN + cmd_append_size)); + + memcpy(pmadapter, &tmp_cap, &padhoc_join->bss_descriptor.cap, + sizeof(IEEEtypes_CapInfo_t)); + tmp_cap = wlan_cpu_to_le16(tmp_cap); + + memcpy(pmadapter, &padhoc_join->bss_descriptor.cap, + &tmp_cap, sizeof(IEEEtypes_CapInfo_t)); + +done: + LEAVE(); + return ret; +} + +/** + * @brief This function handles the command response of ad_hoc_start and + * ad_hoc_join + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_ret_802_11_ad_hoc(IN mlan_private *pmpriv, + IN HostCmd_DS_COMMAND *resp, IN t_void *pioctl_buf) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ioctl_req *pioctl_req = (mlan_ioctl_req *)pioctl_buf; + HostCmd_DS_802_11_AD_HOC_START_RESULT *padhoc_start_result = + &resp->params.adhoc_start_result; + HostCmd_DS_802_11_AD_HOC_JOIN_RESULT *padhoc_join_result = + &resp->params.adhoc_join_result; + BSSDescriptor_t *pbss_desc; + t_u16 command = resp->command; + t_u8 result = 0; + t_u8 event_buf[100]; + mlan_event *pevent = (mlan_event *)event_buf; + int ie_len = 0; + IEEEtypes_WmmParameter_t *pwmm_param_ie = MNULL; + mlan_adapter *pmadapter = pmpriv->adapter; + const t_u8 wmm_oui[4] = { 0x00, 0x50, 0xf2, 0x02 }; + + ENTER(); + + pmpriv->wmm_enabled = MFALSE; + if (command == HostCmd_CMD_802_11_AD_HOC_START) { + result = padhoc_start_result->result; + ie_len = resp->size - + (sizeof(HostCmd_DS_802_11_AD_HOC_START_RESULT) + + S_DS_GEN); + pwmm_param_ie = + (IEEEtypes_WmmParameter_t *)((t_u8 *)resp + + (sizeof + (HostCmd_DS_802_11_AD_HOC_START_RESULT) + + S_DS_GEN)); + } else { + result = padhoc_join_result->result; + ie_len = resp->size - + (sizeof(HostCmd_DS_802_11_AD_HOC_JOIN_RESULT) + + S_DS_GEN); + pwmm_param_ie = + (IEEEtypes_WmmParameter_t *)((t_u8 *)resp + + (sizeof + (HostCmd_DS_802_11_AD_HOC_JOIN_RESULT) + + S_DS_GEN)); + } + + pbss_desc = pmpriv->pattempted_bss_desc; + + /* + * Join result code 0 --> SUCCESS + */ + if (result) { + PRINTM(MERROR, "ADHOC_RESP Failed 0x%x\n", result); + if (pmpriv->media_connected == MTRUE) + wlan_reset_connect_state(pmpriv, MTRUE); + if (pmpriv->adhoc_state == ADHOC_STARTING) + pmpriv->adhoc_state = ADHOC_IDLE; + + memset(pmpriv->adapter, &pmpriv->curr_bss_params.bss_descriptor, + 0x00, sizeof(BSSDescriptor_t)); + + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* Send a Media Connected event, according to the Spec */ + pmpriv->media_connected = MTRUE; + + if (command == HostCmd_CMD_802_11_AD_HOC_START) { + PRINTM(MINFO, "ADHOC_S_RESP %s\n", pbss_desc->ssid.ssid); + + /* Update the created network descriptor with the new BSSID */ + memcpy(pmpriv->adapter, pbss_desc->mac_address, + padhoc_start_result->bssid, MLAN_MAC_ADDR_LENGTH); + + pmpriv->adhoc_state = ADHOC_STARTED; + if (pmpriv->adapter->state_rdh.stage == RDH_RESTART_INTFS) + wlan_11h_radar_detected_callback((t_void *)pmpriv); + } else { + /* + * Now the join cmd should be successful. + * If BSSID has changed use SSID to compare instead of BSSID + */ + PRINTM(MINFO, "ADHOC_J_RESP %s\n", pbss_desc->ssid.ssid); + + /* + * Make a copy of current BSSID descriptor, only needed + * for join since the current descriptor is already + * being used for adhoc start + */ + memcpy(pmpriv->adapter, &pmpriv->curr_bss_params.bss_descriptor, + pbss_desc, sizeof(BSSDescriptor_t)); + + pmpriv->adhoc_state = ADHOC_JOINED; + } + + /** process wmm ie */ + if (ie_len >= sizeof(IEEEtypes_VendorHeader_t)) { + if ((pwmm_param_ie->vend_hdr.element_id == VENDOR_SPECIFIC_221) + && !memcmp(pmadapter, pwmm_param_ie->vend_hdr.oui, wmm_oui, + sizeof(wmm_oui)) && + (pwmm_param_ie->vend_hdr.len + 2 == ie_len)) { + DBG_HEXDUMP(MCMD_D, "WMM Param", (t_u8 *)pwmm_param_ie, + ie_len); + memcpy(pmpriv->adapter, + (t_u8 *)&pmpriv->curr_bss_params.bss_descriptor. + wmm_ie, pwmm_param_ie, + MIN(sizeof(IEEEtypes_WmmParameter_t), + (pwmm_param_ie->vend_hdr.len + 2))); + pmpriv->wmm_enabled = MTRUE; + wlan_wmm_setup_queue_priorities(pmpriv, pwmm_param_ie); + wlan_wmm_setup_ac_downgrade(pmpriv); + } + } + /* Since WMM is not enabled, setup the queues with the defaults */ + if (!pmpriv->wmm_enabled) + wlan_wmm_setup_queues(pmpriv); + + PRINTM(MINFO, "ADHOC_RESP: Channel = %d\n", pmpriv->adhoc_channel); + PRINTM(MINFO, "ADHOC_RESP: BSSID = " MACSTR "\n", + MAC2STR(pmpriv->curr_bss_params.bss_descriptor.mac_address)); + + pevent->bss_index = pmpriv->bss_index; + pevent->event_id = MLAN_EVENT_ID_DRV_CONNECTED; + pevent->event_len = MLAN_MAC_ADDR_LENGTH; + memcpy(pmpriv->adapter, (t_u8 *)pevent->event_buf, + (t_u8 *)pmpriv->curr_bss_params.bss_descriptor.mac_address, + MLAN_MAC_ADDR_LENGTH); + wlan_recv_event(pmpriv, MLAN_EVENT_ID_DRV_CONNECTED, pevent); + wlan_save_curr_bcn(pmpriv); + +done: + /* Need to indicate IOCTL complete */ + if (pioctl_req != MNULL) { + if (ret != MLAN_STATUS_SUCCESS) + pioctl_req->status_code = MLAN_ERROR_CMD_ASSOC_FAIL; + else + pioctl_req->status_code = MLAN_ERROR_NO_ERROR; + } + + LEAVE(); + return ret; +} + +/** + * @brief Associated to a specific BSS discovered in a scan + * + * @param pmpriv A pointer to mlan_private structure + * @param pioctl_buf A pointer to MLAN IOCTL Request buffer + * @param pbss_desc A pointer to the BSS descriptor to associate with. + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_associate(IN mlan_private *pmpriv, + IN t_void *pioctl_buf, IN BSSDescriptor_t *pbss_desc) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u8 current_bssid[MLAN_MAC_ADDR_LENGTH]; + pmlan_ioctl_req pioctl_req = (mlan_ioctl_req *)pioctl_buf; + + ENTER(); + + /* Return error if the pmadapter or table entry + * is not marked as infra */ + if ((pmpriv->bss_mode != MLAN_BSS_MODE_INFRA) || + (pbss_desc->bss_mode != MLAN_BSS_MODE_INFRA)) { + if (pioctl_req) + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + memcpy(pmpriv->adapter, ¤t_bssid, + &pmpriv->curr_bss_params.bss_descriptor.mac_address, + sizeof(current_bssid)); + + /* Clear any past association response stored for application retrieval */ + pmpriv->assoc_rsp_size = 0; + + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_802_11_ASSOCIATE, + HostCmd_ACT_GEN_SET, 0, pioctl_buf, pbss_desc); + + LEAVE(); + return ret; +} + +/** + * @brief Start an Adhoc Network + * + * @param pmpriv A pointer to mlan_private structure + * @param pioctl_buf A pointer to MLAN IOCTL Request buffer + * @param padhoc_ssid The ssid of the Adhoc Network + * + * @return MLAN_STATUS_SUCCESS--success, MLAN_STATUS_FAILURE--fail + */ +mlan_status +wlan_adhoc_start(IN mlan_private *pmpriv, + IN t_void *pioctl_buf, IN mlan_802_11_ssid *padhoc_ssid) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + wlan_meas_state_t *pmeas_state = &pmpriv->adapter->state_meas; + t_u8 radar = MFALSE; + pmlan_ioctl_req pioctl_req = (mlan_ioctl_req *)pioctl_buf; + + ENTER(); + + /* + * If the report indicates no measurement was done, leave the default + * return value alone. + */ + if (!pmeas_state->meas_rpt_returned.rpt.basic.map.unmeasured) { + radar = pmeas_state->meas_rpt_returned.rpt.basic.map. + radar ? MTRUE : MFALSE; + } + + if (radar) { + if (pioctl_req) + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + ret = MLAN_STATUS_FAILURE; + LEAVE(); + return ret; + } + + PRINTM(MINFO, "Adhoc Channel = %d\n", pmpriv->adhoc_channel); + PRINTM(MINFO, "curr_bss_params.channel = %d\n", + pmpriv->curr_bss_params.bss_descriptor.channel); + PRINTM(MINFO, "curr_bss_params.band = %d\n", + pmpriv->curr_bss_params.band); + + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_802_11_AD_HOC_START, + HostCmd_ACT_GEN_SET, 0, pioctl_buf, padhoc_ssid); +#if defined(STA_SUPPORT) + if (ret == MLAN_STATUS_SUCCESS) + memcpy(pmpriv->adapter, &pmpriv->adhoc_last_start_ssid, + padhoc_ssid, sizeof(mlan_802_11_ssid)); +#endif + + LEAVE(); + return ret; +} + +/** + * @brief Join an adhoc network found in a previous scan + * + * @param pmpriv A pointer to mlan_private structure + * @param pioctl_buf A pointer to MLAN IOCTL Request buffer + * @param pbss_desc A pointer to the BSS descriptor found in a previous scan + * to attempt to join + * + * @return MLAN_STATUS_SUCCESS--success, MLAN_STATUS_FAILURE--fail + */ +mlan_status +wlan_adhoc_join(IN mlan_private *pmpriv, + IN t_void *pioctl_buf, IN BSSDescriptor_t *pbss_desc) +{ + pmlan_adapter pmadapter = pmpriv->adapter; + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_ioctl_req pioctl_req = (mlan_ioctl_req *)pioctl_buf; + + ENTER(); + + PRINTM(MINFO, "wlan_adhoc_join: CurBss.ssid =%s\n", + pmpriv->curr_bss_params.bss_descriptor.ssid.ssid); + PRINTM(MINFO, "wlan_adhoc_join: CurBss.ssid_len =%u\n", + pmpriv->curr_bss_params.bss_descriptor.ssid.ssid_len); + PRINTM(MINFO, "wlan_adhoc_join: ssid =%s\n", pbss_desc->ssid.ssid); + PRINTM(MINFO, "wlan_adhoc_join: ssid len =%u\n", + pbss_desc->ssid.ssid_len); + + /* Check if the requested SSID is already joined */ + if (pmpriv->curr_bss_params.bss_descriptor.ssid.ssid_len && + !wlan_ssid_cmp(pmadapter, &pbss_desc->ssid, + &pmpriv->curr_bss_params.bss_descriptor.ssid) && + (pmpriv->curr_bss_params.bss_descriptor.bss_mode == + MLAN_BSS_MODE_IBSS)) { + + PRINTM(MINFO, + "ADHOC_J_CMD: New ad-hoc SSID is the same as current, " + "not attempting to re-join\n"); + + if (pioctl_req) + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + PRINTM(MINFO, "curr_bss_params.channel = %d\n", + pmpriv->curr_bss_params.bss_descriptor.channel); + PRINTM(MINFO, "curr_bss_params.band = %d\n", + pmpriv->curr_bss_params.band); + + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_802_11_AD_HOC_JOIN, + HostCmd_ACT_GEN_SET, 0, pioctl_buf, pbss_desc); + + LEAVE(); + return ret; +} + +/** + * @brief Send Deauthentication Request or Stop the AdHoc network depending on mode + * + * @param pmpriv A pointer to mlan_private structure + * @param pioctl_req A pointer to mlan_ioctl_req structure + * @param deauth_param A pointer to mlan_deauth_param structure + * + * @return MLAN_STATUS_SUCCESS--success, MLAN_STATUS_FAILURE--fail, MLAN_STATUS_PENDING--pending + */ +mlan_status +wlan_disconnect(IN mlan_private *pmpriv, + IN mlan_ioctl_req *pioctl_req, + IN mlan_deauth_param *deauth_param) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_deauth_param local_param = { + .mac_addr = {0, 0, 0, 0, 0, 0}, + .reason_code = DEF_DEAUTH_REASON_CODE + }; + t_u8 zero_mac[] = { 0, 0, 0, 0, 0, 0 }; + + ENTER(); + + if (deauth_param) + memcpy(pmpriv->adapter, &local_param, deauth_param, + sizeof(*deauth_param)); + if (pmpriv->media_connected == MTRUE) { + if (pmpriv->bss_mode == MLAN_BSS_MODE_INFRA) { + if (!deauth_param || + !memcmp(pmpriv->adapter, deauth_param->mac_addr, + zero_mac, sizeof(zero_mac))) + memcpy(pmpriv->adapter, local_param.mac_addr, + (t_u8 *)&pmpriv->curr_bss_params. + bss_descriptor.mac_address, + MLAN_MAC_ADDR_LENGTH); +#ifdef WIFI_DIRECT_SUPPORT + if (pmpriv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT) + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_802_11_DISASSOCIATE, + HostCmd_ACT_GEN_SET, + 0, + (t_void *)pioctl_req, + &local_param); + else +#endif + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_802_11_DEAUTHENTICATE, + HostCmd_ACT_GEN_SET, + 0, + (t_void *)pioctl_req, + &local_param); + + if (ret == MLAN_STATUS_SUCCESS && pioctl_req) + ret = MLAN_STATUS_PENDING; + + } else if (pmpriv->bss_mode == MLAN_BSS_MODE_IBSS) { + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_802_11_AD_HOC_STOP, + HostCmd_ACT_GEN_SET, + 0, (t_void *)pioctl_req, MNULL); + + if (ret == MLAN_STATUS_SUCCESS && pioctl_req) + ret = MLAN_STATUS_PENDING; + } + } + + LEAVE(); + return ret; +} + +/** + * @brief Convert band to radio type used in channel TLV + * + * @param band Band enumeration to convert to a channel TLV radio type + * + * @return Radio type designator for use in a channel TLV + */ +t_u8 +wlan_band_to_radio_type(IN t_u8 band) +{ + t_u8 ret_radio_type; + + ENTER(); + + switch (band) { + case BAND_A: + case BAND_AN: + case BAND_A | BAND_AN: + ret_radio_type = BAND_5GHZ; + break; + case BAND_B: + case BAND_G: + case BAND_B | BAND_G: + default: + ret_radio_type = BAND_2GHZ; + break; + } + + LEAVE(); + return ret_radio_type; +} diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_join.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_join.h new file mode 100644 index 000000000000..f20f84cbf6f7 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_join.h @@ -0,0 +1,40 @@ +/** @file mlan_join.h + * + * @brief This file defines the interface for the WLAN infrastructure + * and adhoc join routines. + * + * Driver interface functions and type declarations for the join module + * implemented in mlan_join.c. Process all start/join requests for + * both adhoc and infrastructure networks + * + * Copyright (C) 2008-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 10/13/2008: initial version +******************************************************/ + +#ifndef _MLAN_JOIN_H_ +#define _MLAN_JOIN_H_ + +/** Size of buffer allocated to store the association response from firmware */ +#define MRVDRV_ASSOC_RSP_BUF_SIZE 500 + +/** Size of buffer allocated to store IEs passed to firmware in the assoc req */ +#define MRVDRV_GENIE_BUF_SIZE 256 + +#endif /* _MLAN_JOIN_H_ */ diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_main.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_main.h new file mode 100644 index 000000000000..176b73a02019 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_main.h @@ -0,0 +1,3613 @@ +/** @file mlan_main.h + * + * @brief This file defines the private and adapter data + * structures and declares global function prototypes used + * in MLAN module. + * + * Copyright (C) 2008-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 10/13/2008: initial version +******************************************************/ + +#ifndef _MLAN_MAIN_H_ +#define _MLAN_MAIN_H_ + +#ifdef DEBUG_LEVEL1 +extern t_void (*print_callback) (IN t_void *pmoal_handle, + IN t_u32 level, IN char *pformat, IN ... + ); + +extern mlan_status (*get_sys_time_callback) (IN t_void *pmoal_handle, + OUT t_u32 *psec, OUT t_u32 *pusec); + +extern t_u32 mlan_drvdbg; + +/* Reason Code 3: STA is leaving (or has left) IBSS or ESS */ +#define DEF_DEAUTH_REASON_CODE (0x3) + +#ifdef DEBUG_LEVEL2 +#define PRINTM_MINFO(msg...) do { \ + if ((mlan_drvdbg & MINFO) && (print_callback)) \ + print_callback(MNULL, MINFO, msg); \ + } while (0) +#define PRINTM_MWARN(msg...) do { \ + if ((mlan_drvdbg & MWARN) && (print_callback)) \ + print_callback(MNULL, MWARN, msg); \ + } while (0) +#define PRINTM_MENTRY(msg...) do { \ + if ((mlan_drvdbg & MENTRY) && (print_callback)) \ + print_callback(MNULL, MENTRY, msg); \ +} while (0) +#define PRINTM_GET_SYS_TIME(level, psec, pusec) \ +do { \ + if ((level & mlan_drvdbg) && (get_sys_time_callback))\ + get_sys_time_callback(MNULL, psec, pusec); \ +} while (0) + +/** Hexdump for level-2 debugging */ +#define HEXDUMP(x, y, z) \ +do { \ + if ((mlan_drvdbg & (MHEX_DUMP | MINFO)) && (print_callback)) \ + print_callback(MNULL, MHEX_DUMP | MINFO, x, y, z); \ +} while (0) + +#else + +#define PRINTM_MINFO(msg...) do {} while (0) +#define PRINTM_MWARN(msg...) do {} while (0) +#define PRINTM_MENTRY(msg...) do {} while (0) + +#define PRINTM_GET_SYS_TIME(level, psec, pusec) \ +do { \ + if ((level & mlan_drvdbg) && (get_sys_time_callback) \ + && (level != MINFO) && (level != MWARN)) \ + get_sys_time_callback(MNULL, psec, pusec); \ +} while (0) + +/** Hexdump for debugging */ +#define HEXDUMP(x, y, z) do {} while (0) + +#endif /* DEBUG_LEVEL2 */ + +#define PRINTM_MFW_D(msg...) do { \ + if ((mlan_drvdbg & MFW_D) && (print_callback)) \ + print_callback(MNULL, MFW_D, msg); \ + } while (0) +#define PRINTM_MCMD_D(msg...) do { \ + if ((mlan_drvdbg & MCMD_D) && (print_callback)) \ + print_callback(MNULL, MCMD_D, msg); \ + } while (0) +#define PRINTM_MDAT_D(msg...) do { \ + if ((mlan_drvdbg & MDAT_D) && (print_callback)) \ + print_callback(MNULL, MDAT_D, msg); \ + } while (0) +#define PRINTM_MIF_D(msg...) do { \ + if ((mlan_drvdbg & MIF_D) && (print_callback)) \ + print_callback(MNULL, MIF_D, msg); \ + } while (0) + +#define PRINTM_MIOCTL(msg...) do { \ + if ((mlan_drvdbg & MIOCTL) && (print_callback)) \ + print_callback(MNULL, MIOCTL, msg); \ + } while (0) +#define PRINTM_MINTR(msg...) do { \ + if ((mlan_drvdbg & MINTR) && (print_callback)) \ + print_callback(MNULL, MINTR, msg); \ + } while (0) +#define PRINTM_MEVENT(msg...) do { \ + if ((mlan_drvdbg & MEVENT) && (print_callback)) \ + print_callback(MNULL, MEVENT, msg); \ + } while (0) +#define PRINTM_MCMND(msg...) do { \ + if ((mlan_drvdbg & MCMND) && (print_callback)) \ + print_callback(MNULL, MCMND, msg); \ + } while (0) +#define PRINTM_MDATA(msg...) do { \ + if ((mlan_drvdbg & MDATA) && (print_callback)) \ + print_callback(MNULL, MDATA, msg); \ + } while (0) +#define PRINTM_MERROR(msg...) do { \ + if ((mlan_drvdbg & MERROR) && (print_callback)) \ + print_callback(MNULL, MERROR, msg); \ + } while (0) +#define PRINTM_MFATAL(msg...) do { \ + if ((mlan_drvdbg & MFATAL) && (print_callback)) \ + print_callback(MNULL, MFATAL, msg); \ + } while (0) +#define PRINTM_MMSG(msg...) do { \ + if ((mlan_drvdbg & MMSG) && (print_callback)) \ + print_callback(MNULL, MMSG, msg); \ + } while (0) + +#define PRINTM(level, msg...) PRINTM_##level((char *)msg) + +/** Log debug message */ +#ifdef __GNUC__ +#define PRINTM_NETINTF(level, pmpriv) \ +do { \ + if ((mlan_drvdbg & level) && pmpriv \ + && pmpriv->adapter->callbacks.moal_print_netintf) \ + pmpriv->adapter->callbacks.moal_print_netintf( \ + pmpriv->adapter->pmoal_handle, \ + pmpriv->bss_index, level); \ +} while (0) +#endif /* __GNUC__ */ + +/** Max hex dump data length */ +#define MAX_DATA_DUMP_LEN 64 + +/** Debug hexdump for level-1 debugging */ +#define DBG_HEXDUMP(level, x, y, z) \ +do { \ + if ((mlan_drvdbg & level) && print_callback) \ + print_callback(MNULL, MHEX_DUMP | level, x, y, z); \ +} while (0) + +#else /* DEBUG_LEVEL1 */ + +#define PRINTM(level, msg...) do {} while (0) + +#define PRINTM_NETINTF(level, pmpriv) do {} while (0) + +/** Debug hexdump for level-1 debugging */ +#define DBG_HEXDUMP(level, x, y, z) do {} while (0) + +/** Hexdump for debugging */ +#define HEXDUMP(x, y, z) do {} while (0) + +#define PRINTM_GET_SYS_TIME(level, psec, pusec) do { } while (0) + +#endif /* DEBUG_LEVEL1 */ + +/** Log entry point for debugging */ +#define ENTER() \ +do { \ + PRINTM(MENTRY, "Enter: %s\n", __func__); \ +} while (0) + +/** Log exit point for debugging */ +#define LEAVE() \ +do { \ + PRINTM(MENTRY, "Leave: %s\n", __func__); \ +} while (0) + +/** Find minimum */ +#ifndef MIN +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#endif + +/** Find maximum */ +#ifndef MAX +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#endif + +#ifdef memset +#undef memset +#endif +/** Memset routine */ +#define memset(adapter, s, c, len) \ + (adapter->callbacks.moal_memset(adapter->pmoal_handle, s, c, len)) + +#ifdef memmove +#undef memmove +#endif +/** Memmove routine */ +#define memmove(adapter, dest, src, len) \ + (adapter->callbacks.moal_memmove(adapter->pmoal_handle, dest, src, len)) + +#ifdef memcpy +#undef memcpy +#endif +/** Memcpy routine */ +#define memcpy(adapter, to, from, len) \ + (adapter->callbacks.moal_memcpy(adapter->pmoal_handle, to, from, len)) + +#ifdef memcmp +#undef memcmp +#endif +/** Memcmp routine */ +#define memcmp(adapter, s1, s2, len) \ + (adapter->callbacks.moal_memcmp(adapter->pmoal_handle, s1, s2, len)) + +/** Find number of elements */ +#ifndef NELEMENTS +#define NELEMENTS(x) (sizeof(x)/sizeof(x[0])) +#endif + +/** SWAP: swap t_u8 */ +#define SWAP_U8(a, b) {t_u8 t; t = a; a = b; b = t; } + +/** SWAP: swap t_u8 */ +#define SWAP_U16(a, b) {t_u16 t; t = a; a = b; b = t; } + +/** MLAN MNULL pointer */ +#define MNULL (0) + +/** 16 bits byte swap */ +#define swap_byte_16(x) \ +((t_u16)((((t_u16)(x) & 0x00ffU) << 8) | \ + (((t_u16)(x) & 0xff00U) >> 8))) + +/** 32 bits byte swap */ +#define swap_byte_32(x) \ +((t_u32)((((t_u32)(x) & 0x000000ffUL) << 24) | \ + (((t_u32)(x) & 0x0000ff00UL) << 8) | \ + (((t_u32)(x) & 0x00ff0000UL) >> 8) | \ + (((t_u32)(x) & 0xff000000UL) >> 24))) + +/** 64 bits byte swap */ +#define swap_byte_64(x) \ +((t_u64)((t_u64)(((t_u64)(x) & 0x00000000000000ffULL) << 56) | \ + (t_u64)(((t_u64)(x) & 0x000000000000ff00ULL) << 40) | \ + (t_u64)(((t_u64)(x) & 0x0000000000ff0000ULL) << 24) | \ + (t_u64)(((t_u64)(x) & 0x00000000ff000000ULL) << 8) | \ + (t_u64)(((t_u64)(x) & 0x000000ff00000000ULL) >> 8) | \ + (t_u64)(((t_u64)(x) & 0x0000ff0000000000ULL) >> 24) | \ + (t_u64)(((t_u64)(x) & 0x00ff000000000000ULL) >> 40) | \ + (t_u64)(((t_u64)(x) & 0xff00000000000000ULL) >> 56))) + +#ifdef BIG_ENDIAN_SUPPORT +/** Convert ulong n/w to host */ +#define mlan_ntohl(x) x +/** Convert host ulong to n/w */ +#define mlan_htonl(x) x +/** Convert n/w to host */ +#define mlan_ntohs(x) x +/** Convert host to n/w */ +#define mlan_htons(x) x +/** Convert from 16 bit little endian format to CPU format */ +#define wlan_le16_to_cpu(x) swap_byte_16(x) +/** Convert from 32 bit little endian format to CPU format */ +#define wlan_le32_to_cpu(x) swap_byte_32(x) +/** Convert from 64 bit little endian format to CPU format */ +#define wlan_le64_to_cpu(x) swap_byte_64(x) +/** Convert to 16 bit little endian format from CPU format */ +#define wlan_cpu_to_le16(x) swap_byte_16(x) +/** Convert to 32 bit little endian format from CPU format */ +#define wlan_cpu_to_le32(x) swap_byte_32(x) +/** Convert to 64 bit little endian format from CPU format */ +#define wlan_cpu_to_le64(x) swap_byte_64(x) + +/** Convert TxPD to little endian format from CPU format */ +#define endian_convert_TxPD(x) \ + { \ + (x)->tx_pkt_length = wlan_cpu_to_le16((x)->tx_pkt_length); \ + (x)->tx_pkt_offset = wlan_cpu_to_le16((x)->tx_pkt_offset); \ + (x)->tx_pkt_type = wlan_cpu_to_le16((x)->tx_pkt_type); \ + (x)->tx_control = wlan_cpu_to_le32((x)->tx_control); \ + (x)->tx_control_1 = wlan_cpu_to_le32((x)->tx_control_1); \ + } +/** Convert RxPD from little endian format to CPU format */ +#define endian_convert_RxPD(x) \ + { \ + (x)->rx_pkt_length = wlan_le16_to_cpu((x)->rx_pkt_length); \ + (x)->rx_pkt_offset = wlan_le16_to_cpu((x)->rx_pkt_offset); \ + (x)->rx_pkt_type = wlan_le16_to_cpu((x)->rx_pkt_type); \ + (x)->seq_num = wlan_le16_to_cpu((x)->seq_num); \ + (x)->rx_info = wlan_le32_to_cpu((x)->rx_info); \ + } +/** Convert RxPD extra header from little endian format to CPU format */ +#define endian_convert_RxPD_extra_header(x) \ + { \ + (x)->channel_flags = wlan_le16_to_cpu((x)->channel_flags); \ + } +#else +/** Convert ulong n/w to host */ +#define mlan_ntohl(x) swap_byte_32(x) +/** Convert host ulong to n/w */ +#define mlan_htonl(x) swap_byte_32(x) +/** Convert n/w to host */ +#define mlan_ntohs(x) swap_byte_16(x) +/** Convert host to n/w */ +#define mlan_htons(x) swap_byte_16(x) +/** Do nothing */ +#define wlan_le16_to_cpu(x) x +/** Do nothing */ +#define wlan_le32_to_cpu(x) x +/** Do nothing */ +#define wlan_le64_to_cpu(x) x +/** Do nothing */ +#define wlan_cpu_to_le16(x) x +/** Do nothing */ +#define wlan_cpu_to_le32(x) x +/** Do nothing */ +#define wlan_cpu_to_le64(x) x + +/** Convert TxPD to little endian format from CPU format */ +#define endian_convert_TxPD(x) do {} while (0) +/** Convert RxPD from little endian format to CPU format */ +#define endian_convert_RxPD(x) do {} while (0) +/** Convert RxPD extra header from little endian format to CPU format */ +#define endian_convert_RxPD_extra_header(x) do {} while (0) +#endif /* BIG_ENDIAN_SUPPORT */ + +/** Global moal_assert_callback */ +extern t_void (*assert_callback) (IN t_void *pmoal_handle, IN t_u32 cond); + +/** Assertion */ +#define MASSERT(cond) \ +do { \ + if (!(cond)) { \ + PRINTM(MFATAL, "ASSERT: %s: %i\n", __func__, __LINE__); \ + if (assert_callback) { \ + assert_callback(MNULL, (t_ptr)(cond)); \ + } else { \ + do {} while (1); \ + } \ + } \ +} while (0) + +/** Upload size */ +#define WLAN_UPLD_SIZE (2312) + +/** Maximum event buffer size */ +#define MAX_EVENT_SIZE 2048 + +#ifdef STA_SUPPORT +/** Maximum buffer size for ARP filter */ +#define ARP_FILTER_MAX_BUF_SIZE 68 +#endif /* STA_SUPPORT */ + +/** 60 seconds */ +#define MRVDRV_TIMER_60S 60000 +/** 10 seconds */ +#define MRVDRV_TIMER_10S 10000 +/** 5 seconds */ +#define MRVDRV_TIMER_5S 5000 +/** 1 second */ +#define MRVDRV_TIMER_1S 1000 + +/** Maximum size of multicast list */ +#define MRVDRV_MAX_MULTICAST_LIST_SIZE 32 +/** Maximum size of channel */ +#define MRVDRV_MAX_CHANNEL_SIZE 14 +/** Maximum length of SSID */ +#define MRVDRV_MAX_SSID_LENGTH 32 +/** WEP list macros & data structures */ +/** Size of key buffer in bytes */ +#define MRVL_KEY_BUFFER_SIZE_IN_BYTE 16 +/** Maximum length of WPA key */ +#define MRVL_MAX_KEY_WPA_KEY_LENGTH 32 + +/** Default listen interval */ +#define MLAN_DEFAULT_LISTEN_INTERVAL 20 + +/** Maximum number of region codes */ +#define MRVDRV_MAX_REGION_CODE 8 + +/** Maximum number of CFP codes for BG */ +#define MRVDRV_MAX_CFP_CODE_BG 0 +/** Maximum number of CFP codes for A */ +#define MRVDRV_MAX_CFP_CODE_A 5 + +/** high rx pending packets */ +#define HIGH_RX_PENDING 100 +/** low rx pending packets */ +#define LOW_RX_PENDING 80 + +/** Default region code */ +#define MRVDRV_DEFAULT_REGION_CODE 0x10 + +/** Default country code */ +#define MRVDRV_DEFAULT_COUNTRY_CODE "US" + +/** Japan country code */ +#define COUNTRY_CODE_JP_40 0x40 +/** Japan special country code */ +#define COUNTRY_CODE_JP_FF 0xFF + +/** Default factor for calculating beacon average */ +#define DEFAULT_BCN_AVG_FACTOR 8 +/** Default factor for calculating data average */ +#define DEFAULT_DATA_AVG_FACTOR 8 + +/** The first valid channel for use */ +#define FIRST_VALID_CHANNEL 0xff +/** Default Ad-Hoc channel */ +#define DEFAULT_AD_HOC_CHANNEL 6 +/** Default Ad-Hoc channel A */ +#define DEFAULT_AD_HOC_CHANNEL_A 36 + +/** Number of WEP keys */ +#define MRVL_NUM_WEP_KEY (4) + +/** Default multiple DTIM */ +#define MRVDRV_DEFAULT_MULTIPLE_DTIM 1 + +/** Default beacon missing timeout */ +#define DEFAULT_BCN_MISS_TIMEOUT 10 + +/** Maximum buffer space for beacons retrieved from scan responses */ +#define MAX_SCAN_BEACON_BUFFER 49152 +/** Default buffer space for beacons retrieved from scan responses */ +#define DEFAULT_SCAN_BEACON_BUFFER 4096 + +/** + * @brief Buffer pad space for newly allocated beacons/probe responses + * + * Beacons are typically 6 bytes longer than an equivalent probe response. + * For each scan response stored, allocate an extra byte pad at the end to + * allow easy expansion to store a beacon in the same memory a probe response + * previously contained + */ +#define SCAN_BEACON_ENTRY_PAD 6 + +/** Scan time specified in the channel TLV + * for each channel for passive scans + */ +#define MRVDRV_PASSIVE_SCAN_CHAN_TIME 200 + +/** Scan time specified in the channel TLV + * for each channel for active scans + */ +#define MRVDRV_ACTIVE_SCAN_CHAN_TIME 200 + +/** Scan time specified in the channel TLV + * for each channel for specific scans + */ +#define MRVDRV_SPECIFIC_SCAN_CHAN_TIME 110 + +/** + * Max total scan time in milliseconds + * The total scan time should be less than scan command timeout value (20s) + */ +#define MRVDRV_MAX_TOTAL_SCAN_TIME (MRVDRV_TIMER_10S * 2 - MRVDRV_TIMER_1S) + +/** Offset for GTK as it has version to skip past for GTK */ +#define RSN_GTK_OUI_OFFSET 2 + +/** If OUI is not found */ +#define MLAN_OUI_NOT_PRESENT 0 +/** If OUI is found */ +#define MLAN_OUI_PRESENT 1 + +/** Is cmd_resp, event or data packet received? */ +#define IS_CARD_RX_RCVD(adapter) (adapter->cmd_resp_received || \ + adapter->event_received || \ + adapter->data_received) +/** Type command */ +#define MLAN_TYPE_CMD 1 +/** Type data */ +#define MLAN_TYPE_DATA 0 +/** Type event */ +#define MLAN_TYPE_EVENT 3 + +/** Type single port aggr data */ +#define MLAN_TYPE_SPA_DATA 10 +/** OFFSET of 512 block number */ +#define OFFSET_OF_BLOCK_NUMBER 15 +/** OFFSET of SDIO Header */ +#define OFFSET_OF_SDIO_HEADER 28 +/** sdio max rx size for cmd53, 255 * 256, reserve 1 block for DMA alignment */ +#define SDIO_CMD53_MAX_SIZE 65280 +#define MAX_SUPPORT_AMSDU_SIZE 4096 +/** Maximum numbfer of registers to read for multiple port */ +#define MAX_MP_REGS 196 +/** Maximum port */ +#define MAX_PORT 32 + +#ifdef SDIO_MULTI_PORT_TX_AGGR +/** Multi port TX aggregation buffer size */ +#define SDIO_MP_TX_AGGR_DEF_BUF_SIZE (65280) /* 64K - 256 */ +#endif /* SDIO_MULTI_PORT_TX_AGGR */ + +#ifdef SDIO_MULTI_PORT_RX_AGGR +/** Multi port RX aggregation buffer size */ +#define SDIO_MP_RX_AGGR_DEF_BUF_SIZE (65280) /* 64K - 256 */ +#endif /* SDIO_MULTI_PORT_RX_AGGR */ + +/** High threshold at which to start drop packets */ +#define RX_HIGH_THRESHOLD 1024 +/** Low threshold to allow Rx BA */ +#define RX_LOW_THRESHOLD 128 + +/** Debug command number */ +#define DBG_CMD_NUM 10 + +/** Info for debug purpose */ +typedef struct _wlan_dbg { + /** Number of host to card command failures */ + t_u32 num_cmd_host_to_card_failure; + /** Number of host to card sleep confirm failures */ + t_u32 num_cmd_sleep_cfm_host_to_card_failure; + /** Number of host to card Tx failures */ + t_u32 num_tx_host_to_card_failure; + /** Number of card to host command/event failures */ + t_u32 num_cmdevt_card_to_host_failure; + /** Number of card to host Rx failures */ + t_u32 num_rx_card_to_host_failure; + /** Number of interrupt read failures */ + t_u32 num_int_read_failure; + /** Last interrupt status */ + t_u32 last_int_status; + /** Number of allocate buffer failure */ + t_u32 num_alloc_buffer_failure; + /** Number of pkt dropped */ + t_u32 num_pkt_dropped; + /** Number of deauthentication events */ + t_u32 num_event_deauth; + /** Number of disassosiation events */ + t_u32 num_event_disassoc; + /** Number of link lost events */ + t_u32 num_event_link_lost; + /** Number of deauthentication commands */ + t_u32 num_cmd_deauth; + /** Number of association comamnd successes */ + t_u32 num_cmd_assoc_success; + /** Number of association command failures */ + t_u32 num_cmd_assoc_failure; + /** Number of Tx timeouts */ + t_u32 num_tx_timeout; + /** Number of command timeouts */ + t_u32 num_cmd_timeout; + /** Timeout command ID */ + t_u16 timeout_cmd_id; + /** Timeout command action */ + t_u16 timeout_cmd_act; + /** List of last command IDs */ + t_u16 last_cmd_id[DBG_CMD_NUM]; + /** List of last command actions */ + t_u16 last_cmd_act[DBG_CMD_NUM]; + /** Last command index */ + t_u16 last_cmd_index; + /** List of last command response IDs */ + t_u16 last_cmd_resp_id[DBG_CMD_NUM]; + /** Last command response index */ + t_u16 last_cmd_resp_index; + /** List of last events */ + t_u16 last_event[DBG_CMD_NUM]; + /** Last event index */ + t_u16 last_event_index; + /** Number of no free command node */ + t_u16 num_no_cmd_node; +} wlan_dbg; + +/** Hardware status codes */ +typedef enum _WLAN_HARDWARE_STATUS { + WlanHardwareStatusReady, + WlanHardwareStatusGetHwSpec, + WlanHardwareStatusGetHwSpecdone, + WlanHardwareStatusInitializing, + WlanHardwareStatusInitdone, + WlanHardwareStatusReset, + WlanHardwareStatusClosing, + WlanHardwareStatusNotReady +} WLAN_HARDWARE_STATUS; + +/** WLAN_802_11_POWER_MODE */ +typedef enum _WLAN_802_11_POWER_MODE { + Wlan802_11PowerModeCAM, + Wlan802_11PowerModePSP +} WLAN_802_11_POWER_MODE; + +/** tx param */ +typedef struct _mlan_tx_param { + /** next packet length */ + t_u32 next_pkt_len; +} mlan_tx_param; + +/** PS_STATE */ +typedef enum _PS_STATE { + PS_STATE_AWAKE, + PS_STATE_PRE_SLEEP, + PS_STATE_SLEEP_CFM, + PS_STATE_SLEEP +} PS_STATE; + +/** Minimum flush timer for win size of 1 is 50 ms */ +#define MIN_FLUSH_TIMER_MS 50 +/** Minimum flush timer for win size of 1 is 15 ms */ +#define MIN_FLUSH_TIMER_15_MS 15 + +/** Tx BA stream table */ +typedef struct _TxBAStreamTbl TxBAStreamTbl; + +/** Add BA parameter data structure */ +typedef struct { + /** Window size for initiator */ + t_u32 tx_win_size; + /** Window size for receiver */ + t_u32 rx_win_size; + /** Block ack timeout */ + t_u32 timeout; + /** amsdu support for ADDBA request */ + t_u8 tx_amsdu; + /** amsdu support for ADDBA response */ + t_u8 rx_amsdu; +} add_ba_param_t; + +/** Tx aggregation data structure */ +typedef struct _txAggr_t { + /** AMPDU user */ + t_u8 ampdu_user; + /** AMPDU AP */ + t_u8 ampdu_ap; + /** AMSDU */ + t_u8 amsdu; +} tx_aggr_t; + +/** del ba threshold */ +#define DEL_BA_THRESHOLD 10 +/** BA stream status */ +typedef enum _baStatus_e { + BA_STREAM_NOT_SETUP = 0, + BA_STREAM_SETUP_INPROGRESS, + BA_STREAM_SETUP_COMPLETE +} baStatus_e; + +/** RA list table */ +typedef struct _raListTbl raListTbl; + +/** RA list table */ +struct _raListTbl { + /** Pointer to previous node */ + raListTbl *pprev; + /** Pointer to next node */ + raListTbl *pnext; + /** Buffer list head */ + mlan_list_head buf_head; + /** RA list buffer */ + t_u8 ra[MLAN_MAC_ADDR_LENGTH]; + /** total packets in RA list */ + t_u16 total_pkts; + /** packets received */ + t_u16 packet_count; + /** packet count threshold to setup BA */ + t_u8 ba_packet_threshold; + /** is 11n enabled */ + t_u8 is_11n_enabled; + /** max amsdu size */ + t_u16 max_amsdu; + /** BA stream status */ + baStatus_e ba_status; + /** del ba count */ + t_u8 del_ba_count; + /** amsdu in ampdu flag */ + t_u8 amsdu_in_ampdu; + /** tdls flag */ + t_u8 is_tdls_link; + /** tx_pause flag */ + t_u8 tx_pause; +}; + +/** TID table */ +typedef struct _tidTbl { + /** RA list head */ + mlan_list_head ra_list; + /** Current RA list */ + raListTbl *ra_list_curr; +} tid_tbl_t; + +/** Highest priority setting for a packet (uses voice AC) */ +#define WMM_HIGHEST_PRIORITY 7 +/** Highest priority TID */ +#define HIGH_PRIO_TID 7 +/** Lowest priority TID */ +#define LOW_PRIO_TID 0 +/** No packet priority (< lowest) */ +#define NO_PKT_PRIO_TID -1 + +/** Max driver packet delay in msec */ +#define WMM_DRV_DELAY_MAX 510 + +/** Struct of WMM DESC */ +typedef struct _wmm_desc { + /** TID table */ + tid_tbl_t tid_tbl_ptr[MAX_NUM_TID]; + /** Packets out */ + t_u32 packets_out[MAX_NUM_TID]; + /** Packets queued */ + t_u32 pkts_queued[MAX_NUM_TID]; + /** Packets paused */ + t_u32 pkts_paused[MAX_NUM_TID]; + /** Spin lock to protect ra_list */ + t_void *ra_list_spinlock; + + /** AC status */ + WmmAcStatus_t ac_status[MAX_AC_QUEUES]; + /** AC downgraded values */ + mlan_wmm_ac_e ac_down_graded_vals[MAX_AC_QUEUES]; + + /** Max driver packet delay sent to the firmware for expiry eval */ + t_u32 drv_pkt_delay_max; + + /** WMM queue priority table */ + t_u8 queue_priority[MAX_AC_QUEUES]; + /** User priority packet transmission control */ + t_u32 user_pri_pkt_tx_ctrl[WMM_HIGHEST_PRIORITY + 1]; /* UP: 0 to 7 */ + + /** Number of transmit packets queued */ + mlan_scalar tx_pkts_queued; + /** Tracks highest priority with a packet queued */ + mlan_scalar highest_queued_prio; +} wmm_desc_t; + +/** Security structure */ +typedef struct _wlan_802_11_security_t { + /** WPA enabled flag */ + t_u8 wpa_enabled; + /** E-Supplicant enabled flag */ + t_u8 ewpa_enabled; + /** WPA2 enabled flag */ + t_u8 wpa2_enabled; + /** WAPI enabled flag */ + t_u8 wapi_enabled; + /** WAPI key on flag */ + t_u8 wapi_key_on; + /** WEP status */ + WLAN_802_11_WEP_STATUS wep_status; + /** Authentication mode */ + t_u32 authentication_mode; + /** Encryption mode */ + t_u32 encryption_mode; +} wlan_802_11_security_t; + +/** Current Basic Service Set State Structure */ +typedef struct { + /** BSS descriptor */ + BSSDescriptor_t bss_descriptor; + /** WMM enable? */ + t_u8 wmm_enabled; + /** Uapsd enable?*/ + t_u8 wmm_uapsd_enabled; + /** Band */ + t_u8 band; + /** Number of rates supported */ + t_u32 num_of_rates; + /** Supported rates*/ + t_u8 data_rates[WLAN_SUPPORTED_RATES]; +} current_bss_params_t; + +/** Sleep_params */ +typedef struct _sleep_params_t { + /** Sleep parameter error */ + t_u16 sp_error; + /** Sleep parameter offset */ + t_u16 sp_offset; + /** Sleep parameter stable time */ + t_u16 sp_stable_time; + /** Sleep parameter calibration control */ + t_u8 sp_cal_control; + /** Sleep parameter external sleep clock */ + t_u8 sp_ext_sleep_clk; + /** Sleep parameter reserved */ + t_u16 sp_reserved; +} sleep_params_t; + +/** Sleep_period */ +typedef struct sleep_period_t { + /** Sleep period */ + t_u16 period; + /** Reserved */ + t_u16 reserved; +} sleep_period_t; + +/** mrvl_wep_key_t */ +typedef struct _mrvl_wep_key_t { + /** Length */ + t_u32 length; + /** WEP key index */ + t_u32 key_index; + /** WEP key length */ + t_u32 key_length; + /** WEP keys */ + t_u8 key_material[MRVL_KEY_BUFFER_SIZE_IN_BYTE]; +} mrvl_wep_key_t; + +/** Maximum number of region channel */ +#define MAX_REGION_CHANNEL_NUM 2 + +/** CFP dynamic (non-const) elements */ +typedef struct _cfp_dyn_t { + /** extra flags to specify channel usability + * bit 7 : if set, channel is disabled + * bit 4 : if set, 160MHz on channel is disabled + * bit 3 : if set, 80MHz on channel is disabled + * bit 2 : if set, 40MHz on channel is disabled + * bit 1 : if set, channel is DFS channel + * bit 0 : if set, channel is passive + */ + t_u8 flags; + /** TRUE: Channel is blacklisted (do not use) */ + t_bool blacklist; +} cfp_dyn_t; + +/** Chan-Freq-TxPower mapping table*/ +typedef struct _chan_freq_power_t { + /** Channel Number */ + t_u16 channel; + /** Frequency of this Channel */ + t_u32 freq; + /** Max allowed Tx power level */ + t_u16 max_tx_power; + /** TRUE:radar detect required for BAND A or passive scan for BAND B/G; + * FALSE:radar detect not required for BAND A or active scan for BAND B/G*/ + t_bool passive_scan_or_radar_detect; + /** Elements associated to cfp that change at run-time */ + cfp_dyn_t dynamic; +} chan_freq_power_t; + +/** Region-band mapping table */ +typedef struct _region_chan_t { + /** TRUE if this entry is valid */ + t_u8 valid; + /** Region code for US, Japan ... */ + t_u8 region; + /** Band B/G/A, used for BAND_CONFIG cmd */ + t_u8 band; + /** Actual No. of elements in the array below */ + t_u8 num_cfp; + /** chan-freq-txpower mapping table */ + chan_freq_power_t *pcfp; +} region_chan_t; + +/** State of 11d */ +typedef enum _state_11d_t { + DISABLE_11D = 0, + ENABLE_11D = 1, +} state_11d_t; + +#define DEFAULT_11D_STATE DISABLE_11D + +/** Domain regulatory information */ +typedef struct _wlan_802_11d_domain_reg { + /** Country Code */ + t_u8 country_code[COUNTRY_CODE_LEN]; + /** band that channels in sub_band belong to */ + t_u8 band; + /** No. of subband in below */ + t_u8 no_of_sub_band; + /** Subband data to send/last sent */ + IEEEtypes_SubbandSet_t sub_band[MRVDRV_MAX_SUBBAND_802_11D]; +} wlan_802_11d_domain_reg_t; + +/** Data for state machine */ +typedef struct _wlan_802_11d_state { + /** True for enabling 11D */ + state_11d_t enable_11d; + /** True for user enabling 11D */ + state_11d_t user_enable_11d; +} wlan_802_11d_state_t; + +/** 802.11h State information kept in the 'mlan_private' driver structure */ +typedef struct { + /** Indicate 11h is enabled from host */ + t_bool is_11h_host; + /** Indicates whether 11h is enabled in the driver */ + t_bool is_11h_enabled; + /** Indicates whether 11h is active in the firmware */ + t_bool is_11h_active; + /** Master device using automatic channel select */ + t_bool adhoc_auto_sel_chan; + /** Set when driver receives a STOP TX event from fw */ + t_bool tx_disabled; + /** Channel that ChanSwAnn was received for, non-zero = active */ + t_u8 dfs_slave_csa_chan; + /** Expiry for above variable, seconds in system time */ + t_u32 dfs_slave_csa_expire_at_sec; +} wlan_11h_interface_state_t; + +#if defined(UAP_SUPPORT) +/** UAP get info callback state kept in the 'mlan_private' driver structure */ +typedef struct { + /** UAP internal callback after wlan_uap_get_channel */ + /** (parameter is really pointer to mlan_private) */ + mlan_status (*get_chan_callback) (t_void *); + /** current ioctl_req (to be completed in callback) */ + pmlan_ioctl_req pioctl_req_curr; + /** band config from MrvlIEtypes_channel_band_t */ + Band_Config_t bandcfg; + /** channel from MrvlIEtypes_channel_band_t */ + t_u8 channel; + /** beacon period (in msec) from MrvlIEtypes_beacon_period_t */ + t_u16 beacon_period; + /** dtim period (no unit) from MrvlIEtypes_dtim_period_t */ + t_u8 dtim_period; +} wlan_uap_get_info_cb_t; +#endif + +/** Data structure for WPS information */ +typedef struct { + /** WPS IE */ + IEEEtypes_VendorSpecific_t wps_ie; + /** Session enable flag */ + t_u8 session_enable; +} wps_t; + +/** mlan_operations data structure */ +typedef struct _mlan_operations { + /** cmd init handler */ + mlan_status (*init_cmd) (IN t_void *priv, IN t_u8 first_bss); + /** ioctl handler */ + mlan_status (*ioctl) (t_void *adapter, pmlan_ioctl_req pioctl_req); + /** cmd handler */ + mlan_status (*prepare_cmd) (IN t_void *priv, + IN t_u16 cmd_no, + IN t_u16 cmd_action, + IN t_u32 cmd_oid, + IN t_void *pioctl_buf, + IN t_void *pdata_buf, IN t_void *pcmd_buf); + /** cmdresp handler */ + mlan_status (*process_cmdresp) (IN t_void *priv, + IN t_u16 cmdresp_no, + IN t_void *pcmd_buf, IN t_void *pioctl); + /** rx handler */ + mlan_status (*process_rx_packet) (IN t_void *adapter, + IN pmlan_buffer pmbuf); + /** event handler */ + mlan_status (*process_event) (IN t_void *priv); + /** txpd handler */ + t_void *(*process_txpd) (IN t_void *priv, IN pmlan_buffer pmbuf); + /** BSS role */ + mlan_bss_role bss_role; +} mlan_operations; + +/** Private structure for MLAN */ +typedef struct _mlan_private { + /** Pointer to mlan_adapter */ + struct _mlan_adapter *adapter; + /** BSS index */ + t_u8 bss_index; + /** BSS type */ + t_u8 bss_type; + /** BSS role */ + t_u8 bss_role; + /** BSS virtual flag */ + t_u8 bss_virtual; + /** BSS Priority */ + t_u8 bss_priority; + /** BSS number */ + t_u8 bss_num; + /** Frame type */ + t_u8 frame_type; + /** MAC address information */ + t_u8 curr_addr[MLAN_MAC_ADDR_LENGTH]; + /** Media connection status */ + t_bool media_connected; + + /** Current packet filter */ + t_u32 curr_pkt_filter; + /** Infrastructure mode */ + t_u32 bss_mode; + + /** Tx packet control */ + t_u32 pkt_tx_ctrl; + + /** Tx power level */ + t_s16 tx_power_level; + /** Maximum Tx power level */ + t_s8 max_tx_power_level; + /** Minimum Tx power level */ + t_s8 min_tx_power_level; + /** Tx rate */ + t_u8 tx_rate; + t_u8 tx_rate_info; + /** rxpd_htinfo */ + t_u8 rxpd_rate_info; + /** max amsdu size */ + t_u16 max_amsdu; + /** 802.11n Device Capabilities for 2.4GHz */ + t_u32 usr_dot_11n_dev_cap_bg; + /** 802.11n Device Capabilities for 5GHz */ + t_u32 usr_dot_11n_dev_cap_a; + /** MIMO abstraction of MCSs supported by device */ + t_u8 usr_dev_mcs_support; +#ifdef UAP_SUPPORT + /** UAP 11n flag */ + t_u8 is_11n_enabled; +#endif /* UAP_SUPPORT */ +#ifdef UAP_SUPPORT +#endif /* UAP_SUPPORT */ + /** dropped pkts */ + t_u32 num_drop_pkts; +#ifdef UAP_SUPPORT + /** packet forward control */ + t_u8 pkt_fwd; +#endif + /** TX beamforming capability */ + t_u32 tx_bf_cap; + /** Rx PD rate */ + t_u8 rxpd_rate; + /** Rate bitmap */ + t_u16 rate_bitmap; + /** Bitmap rates */ + t_u16 bitmap_rates[MAX_BITMAP_RATES_SIZE]; + /** Data rate */ + t_u32 data_rate; + /** Automatic data rate flag */ + t_u8 is_data_rate_auto; + /** Factor for calculating beacon average */ + t_u16 bcn_avg_factor; + /** Factor for calculating data average */ + t_u16 data_avg_factor; + /** Last data RSSI */ + t_s16 data_rssi_last; + /** Last data Noise Floor */ + t_s16 data_nf_last; + /** Average data RSSI */ + t_s16 data_rssi_avg; + /** Averag data Noise Floor */ + t_s16 data_nf_avg; + /** Last beacon RSSI */ + t_s16 bcn_rssi_last; + /** Last beacon Noise Floor */ + t_s16 bcn_nf_last; + /** Average beacon RSSI */ + t_s16 bcn_rssi_avg; + /** Average beacon Noise Floor */ + t_s16 bcn_nf_avg; + + /** Attempted BSS descriptor */ + BSSDescriptor_t *pattempted_bss_desc; + + /** GTK rekey data*/ + mlan_ds_misc_gtk_rekey_data gtk_rekey; + + /** Current SSID/BSSID related parameters*/ + current_bss_params_t curr_bss_params; + /** current channel flags */ + t_u32 curr_chan_flags; + /** User selected bands */ + t_u8 config_bands; + + /** Beacon period */ + t_u16 beacon_period; + /** Listen interval */ + t_u16 listen_interval; + /** ATIM window */ + t_u16 atim_window; + + /** AdHoc channel */ + t_u8 adhoc_channel; + /** AdHoc link sensed flag */ + t_u8 adhoc_is_link_sensed; + /** AdHoc operating state */ + t_u8 adhoc_state; +#if defined(STA_SUPPORT) + /** AdHoc operating state backup */ + t_u8 adhoc_state_prev; + /** AdHoc previous ssid used for Start */ + mlan_802_11_ssid adhoc_last_start_ssid; +#endif + /** FSM variable for 11d support */ + wlan_802_11d_state_t state_11d; + /** FSM variable for 11h support */ + wlan_11h_interface_state_t intf_state_11h; + /** 11k flag */ + t_u8 enable_11k; +#ifdef UAP_SUPPORT + /** Whether UAP interface has started */ + t_bool uap_bss_started; + /** Whether UAP interface start from hostapd */ + t_bool uap_host_based; + /**UAP operating channel*/ + t_u8 uap_channel; + /** state variable for UAP Get Info callback */ + wlan_uap_get_info_cb_t uap_state_chan_cb; +#endif /* UAP_SUPPORT */ + + /** Security related */ + /** Encryption parameter */ + wlan_802_11_security_t sec_info; + /** WEP keys */ + mrvl_wep_key_t wep_key[MRVL_NUM_WEP_KEY]; + /** Current WEP key index */ + t_u16 wep_key_curr_index; + /** EWPA query 0: disable, 1: enable */ + t_u8 ewpa_query; + /** Encryption Key*/ + t_u8 wpa_ie[256]; + /** WPA IE length */ + t_u8 wpa_ie_len; + /** GTK set flag */ + t_u8 wpa_is_gtk_set; + mlan_ds_encrypt_key aes_key; +#if defined(STA_SUPPORT) + /* Mgmt Frame Protection config */ + mlan_ds_misc_pmfcfg pmfcfg; +#endif + /** WAPI IE */ + t_u8 wapi_ie[256]; + /** WAPI IE length */ + t_u8 wapi_ie_len; + /** Pointer to the station table */ + mlan_list_head sta_list; + /** tdls pending queue */ + mlan_list_head tdls_pending_txq; + t_u16 tdls_idle_time; + + /** MGMT IE */ + custom_ie mgmt_ie[MAX_MGMT_IE_INDEX]; + /** mgmt frame passthru mask */ + t_u32 mgmt_frame_passthru_mask; + /** Advanced Encryption Standard */ + t_u8 adhoc_aes_enabled; + /** WMM required */ + t_u8 wmm_required; + /** WMM enabled */ + t_u8 wmm_enabled; + /** WMM qos info */ + t_u8 wmm_qosinfo; + /** saved WMM qos info */ + t_u8 saved_wmm_qosinfo; + /**host tdls uapsd support*/ + t_u8 host_tdls_uapsd_support; + /**host tdls channel switch support*/ + t_u8 host_tdls_cs_support; + /**supported channel IE len*/ + t_u8 chan_supp_len; + /**save channel support IE*/ + t_u8 chan_supp[MAX_IE_SIZE]; + /**supported regulatory classl IE len*/ + t_u8 supp_regulatory_class_len; + /**save support channel regulatory class IE*/ + t_u8 supp_regulatory_class[MAX_IE_SIZE]; + /**tdls cs off channel*/ + t_u8 tdls_cs_channel; + /** WMM related variable*/ + wmm_desc_t wmm; + + /** Pointer to the Transmit BA stream table*/ + mlan_list_head tx_ba_stream_tbl_ptr; + /** Pointer to the priorities for AMSDU/AMPDU table*/ + tx_aggr_t aggr_prio_tbl[MAX_NUM_TID]; + /** Pointer to the priorities for AMSDU/AMPDU table*/ + t_u8 addba_reject[MAX_NUM_TID]; + /** Pointer to the priorities for AMSDU/AMPDU table*/ + t_u8 ibss_ampdu[MAX_NUM_TID]; + /** Pointer to the priorities for AMSDU/AMPDU table*/ + t_u8 ibss_addba_reject[MAX_NUM_TID]; + /** Struct to store ADDBA parameters */ + add_ba_param_t add_ba_param; + /** user rx_win_size */ + t_u32 user_rxwinsize; + /** last rx_seq */ + t_u16 rx_seq[MAX_NUM_TID]; + /** Pointer to the Receive Reordering table*/ + mlan_list_head rx_reorder_tbl_ptr; + /** Lock for Rx packets */ + t_void *rx_pkt_lock; + +#ifdef STA_SUPPORT + /** Buffer to store the association response for application retrieval */ + t_u8 assoc_rsp_buf[MRVDRV_ASSOC_RSP_BUF_SIZE]; + /** Length of the data stored in assoc_rsp_buf */ + t_u32 assoc_rsp_size; + + /** Generic IEEE IEs passed from the application to be inserted into the + * association request to firmware + */ + t_u8 gen_ie_buf[MRVDRV_GENIE_BUF_SIZE]; + /** Length of the data stored in gen_ie_buf */ + t_u8 gen_ie_buf_len; + + t_u8 *pcurr_bcn_buf; + t_u32 curr_bcn_size; + t_void *curr_bcn_buf_lock; + + /** WPS */ + wps_t wps; +#endif /* STA_SUPPORT */ + + /** function table */ + mlan_operations ops; + /** tx pause flag */ + t_u8 tx_pause; + /** Port Control mode */ + t_u8 port_ctrl_mode; + + /** Port open flag */ + t_u8 port_open; + + /** Port open flag state at time of association attempt */ + t_u8 prior_port_status; + /** Bypass TX queue */ + mlan_list_head bypass_txq; + /** IP address operation */ + t_u32 op_code; + /** IP address */ + t_u8 ip_addr[IPADDR_LEN]; +#ifdef STA_SUPPORT + ExtCap_t ext_cap; + ExtCap_t def_ext_cap; +#endif + /** interface header len */ + t_u8 intf_hr_len; + /** Control TX AMPDU on infra link */ + t_u8 txaggrctrl; +#if defined(DRV_EMBEDDED_AUTHENTICATOR) || defined(DRV_EMBEDDED_SUPPLICANT) + t_void *psapriv; +#endif + /** rx per packet info */ + t_u8 rx_pkt_info; + /** received amsdu count*/ + t_u32 amsdu_rx_cnt; + /** received msdu count in amsdu*/ + t_u32 msdu_in_rx_amsdu_cnt; + /** tx amsdu count*/ + t_u32 amsdu_tx_cnt; + /** tx msdu count in amsdu*/ + t_u32 msdu_in_tx_amsdu_cnt; +} mlan_private, *pmlan_private; + +/** Tx BA stream table */ +struct _TxBAStreamTbl { + /** TxBAStreamTbl previous node */ + TxBAStreamTbl *pprev; + /** TxBAStreamTbl next node */ + TxBAStreamTbl *pnext; + /** TID */ + int tid; + /** RA */ + t_u8 ra[MLAN_MAC_ADDR_LENGTH]; + /** BA stream status */ + baStatus_e ba_status; + t_u8 amsdu; +}; + +/** RX reorder table */ +typedef struct _RxReorderTbl RxReorderTbl; + +typedef struct { + /** Timer for flushing */ + t_void *timer; + /** Timer set flag */ + t_u8 timer_is_set; + /** RxReorderTbl ptr */ + RxReorderTbl *ptr; + /** Priv pointer */ + mlan_private *priv; +} reorder_tmr_cnxt_t; + +/** RX reorder table */ +struct _RxReorderTbl { + /** RxReorderTbl previous node */ + RxReorderTbl *pprev; + /** RxReorderTbl next node */ + RxReorderTbl *pnext; + /** TID */ + int tid; + /** TA */ + t_u8 ta[MLAN_MAC_ADDR_LENGTH]; + /** Start window */ + int start_win; + /** last_seq */ + int last_seq; + /** Window size */ + int win_size; + /** Pointer to pointer to RxReorderTbl */ + t_void **rx_reorder_ptr; + /** Timer context */ + reorder_tmr_cnxt_t timer_context; + /** BA stream status */ + baStatus_e ba_status; + t_u8 amsdu; + /** no packet drop flag for rx_reorder_tbl */ + t_u8 force_no_drop; + /** flag for check start win */ + t_u8 check_start_win; + /** pkt receive after BA setup */ + t_u8 pkt_count; + /** flush data flag */ + t_u8 flush_data; +}; + +/** BSS priority node */ +typedef struct _mlan_bssprio_node mlan_bssprio_node; + +/** BSS priority node */ +struct _mlan_bssprio_node { + /** Pointer to previous node */ + mlan_bssprio_node *pprev; + /** Pointer to next node */ + mlan_bssprio_node *pnext; + /** Pointer to priv */ + pmlan_private priv; +}; + +/** BSS priority table */ +typedef struct _mlan_bssprio_tbl mlan_bssprio_tbl; + +/** BSS priority table */ +struct _mlan_bssprio_tbl { + /** BSS priority list head */ + mlan_list_head bssprio_head; + /** Current priority node */ + mlan_bssprio_node *bssprio_cur; +}; + +/** cmd_ctrl_node */ +typedef struct _cmd_ctrl_node cmd_ctrl_node; + +/** _cmd_ctrl_node */ +struct _cmd_ctrl_node { + /** Pointer to previous node */ + cmd_ctrl_node *pprev; + /** Pointer to next node */ + cmd_ctrl_node *pnext; + /** Pointer to priv */ + pmlan_private priv; + /** Command OID for sub-command use */ + t_u32 cmd_oid; + /** Command flag */ + t_u32 cmd_flag; + /** Pointer to mlan_buffer */ + mlan_buffer *cmdbuf; + /** Pointer to mlan_buffer */ + mlan_buffer *respbuf; + /** Command parameter */ + t_void *pdata_buf; + /** Pointer to mlan_ioctl_req if command is from IOCTL */ + t_void *pioctl_buf; + /** pre_allocated mlan_buffer for cmd */ + mlan_buffer *pmbuf; +}; + +/** default tdls wmm qosinfo */ +#define DEFAULT_TDLS_WMM_QOS_INFO 15 +/** default tdls sleep period */ +#define DEFAULT_TDLS_SLEEP_PERIOD 30 + +/** TDLS status */ +typedef enum _tdlsStatus_e { + TDLS_NOT_SETUP = 0, + TDLS_SETUP_INPROGRESS, + TDLS_SETUP_COMPLETE, + TDLS_SETUP_FAILURE, + TDLS_TEAR_DOWN, + TDLS_SWITCHING_CHANNEL, + TDLS_IN_BASE_CHANNEL, + TDLS_IN_OFF_CHANNEL, +} tdlsStatus_e; +/** station node */ +typedef struct _sta_node sta_node; + +/** station node*/ +struct _sta_node { + /** previous node */ + sta_node *pprev; + /** next node */ + sta_node *pnext; + /** station mac address */ + t_u8 mac_addr[MLAN_MAC_ADDR_LENGTH]; + /** wmm flag */ + t_u8 is_wmm_enabled; + /** 11n flag */ + t_u8 is_11n_enabled; + /** AMPDU STA */ + t_u8 ampdu_sta[MAX_NUM_TID]; + /** last rx_seq */ + t_u16 rx_seq[MAX_NUM_TID]; + /** max amsdu size */ + t_u16 max_amsdu; + /** HT cap */ + IEEEtypes_HTCap_t HTcap; + /** tdls status */ + tdlsStatus_e status; + /** SNR */ + t_s8 snr; + /** Noise Floor */ + t_s8 nf; + /** flag for host based tdls */ + t_u8 external_tdls; + /** peer capability */ + t_u16 capability; + /** peer support rates */ + t_u8 support_rate[32]; + /** rate size */ + t_u8 rate_len; + /*Qos capability info */ + t_u8 qos_info; + /** HT info in TDLS setup confirm*/ + IEEEtypes_HTInfo_t HTInfo; + /** peer BSSCO_20_40*/ + IEEEtypes_2040BSSCo_t BSSCO_20_40; + /*Extended capability */ + IEEEtypes_ExtCap_t ExtCap; + /*RSN IE */ + IEEEtypes_Generic_t rsn_ie; + /**Link ID*/ + IEEEtypes_LinkIDElement_t link_ie; + /** wapi key on off flag */ + t_u8 wapi_key_on; + /** tx pause status */ + t_u8 tx_pause; + /** station band mode */ + t_u8 bandmode; +#if defined(DRV_EMBEDDED_AUTHENTICATOR) || defined(DRV_EMBEDDED_SUPPLICANT) + t_void *cm_connectioninfo; +#endif +}; + +/** 802.11h State information kept in the 'mlan_adapter' driver structure */ +typedef struct { + /** Min TX Power capability sent to FW for 11h use and fw power control */ + t_s8 min_tx_power_capability; + /** Max TX Power capability sent to FW for 11h use and fw power control */ + t_s8 max_tx_power_capability; + /** User provisioned local power constraint sent in association requests */ + t_s8 usr_def_power_constraint; + /** Received CHANNEL_SWITCH_ANN event */ + t_bool recvd_chanswann_event; + /** Indicates an interface wants to enable master radar detection */ + t_bool master_radar_det_enable_pending; + /** Indicates an interface wants to enable slave radar detection */ + t_bool slave_radar_det_enable_pending; + /** Indicates whether master radar detection active in the firmware */ + t_bool is_master_radar_det_active; + /** Indicates whether slave radar detection active in the firmware */ + t_bool is_slave_radar_det_active; + /** Quiet IE */ + IEEEtypes_Quiet_t quiet_ie; +} wlan_11h_device_state_t; + +/** Enumeration for DFS Timestamp represents field */ +enum _dfs_timestamp_repr_e { + /** Ignore entry */ + DFS_TS_REPR_NOT_IN_USE = 0, + /** NOP (Non-Occupancy Period) start time */ + DFS_TS_REPR_NOP_START = 1, + /** CAC (Channel Availability Check) completion time */ + DFS_TS_REPR_CAC_COMPLETION +}; + +/** DFS Timestamp type used for marking NOP/CAC events */ +typedef struct _wlan_dfs_timestamp_t wlan_dfs_timestamp_t; + +/** DFS Timestamp type used for marking NOP/CAC events */ +struct _wlan_dfs_timestamp_t { + /** Pointer to previous node */ + wlan_dfs_timestamp_t *pprev; + /** Pointer to next node */ + wlan_dfs_timestamp_t *pnext; + /** WLAN Channel number */ + t_u8 channel; + /** What this timestamp represents */ + t_u8 represents; + /** reserved field */ + t_u16 reserved; + /** timestamp - seconds */ + t_u32 ts_sec; + /** timestamp - microseconds */ + t_u32 ts_usec; +}; + +/** DFS State information kept in the 'mlan_adapter' driver structure */ +typedef struct { + /** Indicates whether DFS channel check is occurring in firmware */ + t_bool dfs_check_pending; + /** Indicates whether DFS channel check found radar */ + t_bool dfs_radar_found; + /** Channel radar is being checked on. BAND_A is assumed. */ + t_u8 dfs_check_channel; + /** point to the priv which start the DFS check */ + t_void *dfs_check_priv; + /** Timestamp when we got last report, + * to determine if data is old or not. + */ + t_u32 dfs_report_time_sec; + /** List for holding dfs_timestamps for NOP/CAC events */ + mlan_list_head dfs_ts_head; +} wlan_dfs_device_state_t; + +/** Enumeration for mlan_ds_11h_radar_det_hndlg stages */ +enum _mlan_ds_11h_rdh_stages { + RDH_OFF = 0, + RDH_CHK_INTFS = 1, + RDH_STOP_TRAFFIC, + RDH_GET_INFO_CHANNEL, + RDH_GET_INFO_BEACON_DTIM, + RDH_SET_CUSTOM_IE, + RDH_REM_CUSTOM_IE, + RDH_STOP_INTFS, + RDH_SET_NEW_CHANNEL, + RDH_RESTART_INTFS, + RDH_RESTART_TRAFFIC +}; + +/** State info for Radar Detected Handling kept in 'mlan_adapter' */ +typedef struct { + /** Stage (of Operation) */ + t_u8 stage; + /** Number of interfaces to handle */ + t_u8 priv_list_count; + /** Index of interface in process (used by some stages) */ + t_u8 priv_curr_idx; + /** Current Channel (to leave) */ + t_u8 curr_channel; + /** New Channel (to switch to) */ + t_u8 new_channel; + /** UAP band_config */ + Band_Config_t uap_band_cfg; + /** BEACON*DTIM period (in msec; max of STA/UAP) */ + t_u16 max_bcn_dtim_ms; + /** tx block flag */ + t_u8 tx_block; + /** List of interfaces to handle */ + mlan_private *priv_list[MLAN_MAX_BSS_NUM]; +} wlan_radar_det_hndlg_state_t; + +#ifdef DFS_TESTING_SUPPORT +/** DFS/RDH testing exception settings kept in 'mlan_adapter' */ +typedef struct { + /** user-configured CAC period (in msec) */ + t_u32 user_cac_period_msec; + /** user-configured NOP period (in sec) */ + t_u16 user_nop_period_sec; + /** user-configured skip channel change on radar */ + t_bool no_channel_change_on_radar; + /** user-configured new channel to change to on radar */ + t_u8 fixed_new_channel_on_radar; +} wlan_dfs_testing_settings_t; +#endif /* DFS_SUPPORT_TESTING */ + +/** + * @brief Driver measurement state held in 'mlan_adapter' structure + * + * Used to record a measurement request that the driver is pending on + * the result (received measurement report). + */ +typedef struct { + /** + * Dialog token of a pending measurement request/report. Used to + * block execution while waiting for the specific dialog token + */ + t_u8 meas_rpt_pend_on; + + /** + * Measurement report received from the firmware that we were pending on + */ + HostCmd_DS_MEASUREMENT_REPORT meas_rpt_returned; + +} wlan_meas_state_t; + +#if defined(SDIO_MULTI_PORT_TX_AGGR) || defined(SDIO_MULTI_PORT_RX_AGGR) +/** + * @brief Link buffer into aggregate head buffer + * + * @param pmbuf_aggr Pointer to aggregation buffer + * @param pmbuf Pointer to buffer to copy + */ +static inline t_void +wlan_link_buf_to_aggr(pmlan_buffer pmbuf_aggr, pmlan_buffer pmbuf) +{ + /* link new buf at end of list */ + pmbuf->pnext = pmbuf_aggr; + pmbuf->pprev = pmbuf_aggr->pprev; + pmbuf->pparent = pmbuf_aggr; + pmbuf_aggr->pprev->pnext = pmbuf; + pmbuf_aggr->pprev = pmbuf; + pmbuf_aggr->use_count++; +} +#endif + +#ifdef SDIO_MULTI_PORT_TX_AGGR +/** data structure for SDIO MPA TX */ +typedef struct _sdio_mpa_tx { + /** allocated buf for tx aggreation */ + t_u8 *head_ptr; + /** multiport tx aggregation buffer pointer */ + t_u8 *buf; + /** multiport tx aggregation buffer length */ + t_u32 buf_len; + /** multiport tx aggregation packet count */ + t_u32 pkt_cnt; + /** multiport tx aggregation ports */ + t_u32 ports; + /** multiport tx aggregation starting port */ + t_u16 start_port; + /** multiport tx aggregation enable/disable flag */ + t_u8 enabled; + /** multiport tx aggregation buffer size */ + t_u32 buf_size; + /** multiport tx aggregation pkt aggr limit */ + t_u32 pkt_aggr_limit; + /** multiport write info */ + t_u16 mp_wr_info[SDIO_MP_AGGR_DEF_PKT_LIMIT]; + /** multiport rx aggregation mbuf array */ + pmlan_buffer mbuf_arr[SDIO_MP_AGGR_DEF_PKT_LIMIT]; +} sdio_mpa_tx; +#endif + +#ifdef SDIO_MULTI_PORT_RX_AGGR +/** data structure for SDIO MPA RX */ +typedef struct _sdio_mpa_rx { + /** allocated buf for rx aggreation */ + t_u8 *head_ptr; + /** multiport rx aggregation buffer pointer */ + t_u8 *buf; + /** multiport rx aggregation buffer length */ + t_u32 buf_len; + /** multiport rx aggregation packet count */ + t_u32 pkt_cnt; + /** multiport rx aggregation ports */ + t_u32 ports; + /** multiport rx aggregation starting port */ + t_u16 start_port; + + /** multiport rx aggregation mbuf array */ + pmlan_buffer mbuf_arr[SDIO_MP_AGGR_DEF_PKT_LIMIT]; + /** multiport rx aggregation pkt len array */ + t_u32 len_arr[SDIO_MP_AGGR_DEF_PKT_LIMIT]; + + /** multiport rx aggregation enable/disable flag */ + t_u8 enabled; + /** multiport rx aggregation buffer size */ + t_u32 buf_size; + /** multiport rx aggregation pkt aggr limit */ + t_u32 pkt_aggr_limit; +} sdio_mpa_rx; +#endif /* SDIO_MULTI_PORT_RX_AGGR */ + +/** mlan_init_para structure */ +typedef struct _mlan_init_para { +#ifdef MFG_CMD_SUPPORT + /** MFG mode */ + t_u32 mfg_mode; +#endif + /** SDIO interrupt mode (0: INT_MODE_SDIO, 1: INT_MODE_GPIO) */ + t_u32 int_mode; + /** GPIO interrupt pin number */ + t_u32 gpio_pin; +#ifdef SDIO_MULTI_PORT_TX_AGGR + /** SDIO MPA Tx */ + t_u32 mpa_tx_cfg; +#endif +#ifdef SDIO_MULTI_PORT_RX_AGGR + /** SDIO MPA Rx */ + t_u32 mpa_rx_cfg; +#endif + /** Auto deep sleep */ + t_u32 auto_ds; + /** IEEE PS mode */ + t_u32 ps_mode; + /** Max Tx buffer size */ + t_u32 max_tx_buf; + /** 802.11d configuration */ + t_u32 cfg_11d; + /** 802.11H DFS Master Radar Detect */ + t_u32 dfs_master_radar_det_en; + /** 802.11H DFS Slave Radar Detect */ + t_u32 dfs_slave_radar_det_en; + /** dev cap mask */ + t_u32 dev_cap_mask; + /** oob independent reset mode */ + t_u32 indrstcfg; + t_u32 drcs_chantime_mode; + t_bool fw_region; +} mlan_init_para, *pmlan_init_para; + +/** Adapter data structure for MLAN */ +typedef struct _mlan_adapter { + /** MOAL handle structure */ + t_void *pmoal_handle; + /** BSS Attributes */ + mlan_bss_attr bss_attr[MLAN_MAX_BSS_NUM]; + /** Private pointer */ + pmlan_private priv[MLAN_MAX_BSS_NUM]; + /** Total number of Priv number */ + t_u8 priv_num; + /** Priority table for bss */ + mlan_bssprio_tbl bssprio_tbl[MLAN_MAX_BSS_NUM]; + /** Callback table */ + mlan_callbacks callbacks; + /** Init parameters */ + mlan_init_para init_para; + /** mlan_lock for init/shutdown */ + t_void *pmlan_lock; + /** main_proc_lock for main_process */ + t_void *pmain_proc_lock; + /** mlan_processing */ + t_u32 mlan_processing; + /** main_process_cnt */ + t_u32 main_process_cnt; + /** mlan_rx_processing */ + t_u32 mlan_rx_processing; + /** rx_proc_lock for main_rx_process */ + t_void *prx_proc_lock; + /** rx work enable flag */ + t_u8 rx_work_flag; + /* number of rx pkts queued */ + t_u16 rx_pkts_queued; + /** more task flag */ + t_u32 more_task_flag; + /** delay task flag */ + t_u32 delay_task_flag; + /** Max tx buf size */ + t_u16 max_tx_buf_size; + /** Tx buf size */ + t_u16 tx_buf_size; + /** current tx buf size in fw */ + t_u16 curr_tx_buf_size; + /** flush data */ + t_u8 flush_data; + /** IO port */ + t_u32 ioport; + + /** STATUS variables */ + WLAN_HARDWARE_STATUS hw_status; + /** PnP SUPPORT */ + t_u8 surprise_removed; + + /** ECSA support */ + t_u8 ecsa_enable; + + /** Get log support */ + t_u8 getlog_enable; + + /** Radio on flag */ + t_u16 radio_on; + + /** Firmware release number */ + t_u32 fw_release_number; + /** firmware version */ + t_u8 fw_ver; + /** firmware minor version */ + t_u8 fw_min_ver; + /** uap firmware version */ + t_u8 uap_fw_ver; + /** mac address retrun from get_hw_spec */ + t_u8 permanent_addr[MLAN_MAC_ADDR_LENGTH]; + /** Number of antenna used */ + t_u16 number_of_antenna; + /** antenna info */ + t_u8 antinfo; + /** Firmware capability information */ + t_u32 fw_cap_info; + /** pint_lock for interrupt handling */ + t_void *pint_lock; + /** Interrupt status */ + t_u8 sdio_ireg; + /** number of interrupt receive */ + t_u32 num_of_irq; + /** max SDIO single port tx size */ + t_u16 max_sp_tx_size; + /** max SDIO single port rx size */ + t_u16 max_sp_rx_size; + /** SDIO multiple port read bitmap */ + t_u32 mp_rd_bitmap; + /** SDIO multiple port write bitmap */ + t_u32 mp_wr_bitmap; + /** SDIO end port from txbufcfg */ + t_u16 mp_end_port; + /** SDIO port mask calculated based on txbufcfg end port */ + t_u32 mp_data_port_mask; + /** Current available port for read */ + t_u8 curr_rd_port; + /** Current available port for write */ + t_u8 curr_wr_port; + /** FW update port number */ + t_u32 mp_update[SDIO_MP_AGGR_DEF_PKT_LIMIT * 2]; + /** Invalid port update count */ + t_u32 mp_invalid_update; + /** Array to store values of SDIO multiple port group registers */ + t_u8 *mp_regs; + /** allocated buf to read SDIO multiple port group registers */ + t_u8 *mp_regs_buf; + /** buffer to handle receive packet */ + t_u8 *rx_buf; + /** allocated buf for receive */ + t_u8 *rx_buffer; +#if defined(SDIO_MULTI_PORT_TX_AGGR) || defined(SDIO_MULTI_PORT_RX_AGGR) + /* see blk_queue_max_segment_size */ + t_u32 max_seg_size; + /* see blk_queue_max_segments */ + t_u16 max_segs; +#endif + +#ifdef SDIO_MULTI_PORT_TX_AGGR + /** data structure for SDIO MPA TX */ + sdio_mpa_tx mpa_tx; + /** packet number for tx aggr */ + t_u32 mpa_tx_count[SDIO_MP_AGGR_DEF_PKT_LIMIT]; + /** no more packets count*/ + t_u32 mpa_sent_last_pkt; + /** no write_ports count */ + t_u32 mpa_sent_no_ports; + /** last wr_bitmap from FW */ + t_u32 last_recv_wr_bitmap; + /** last mp_wr_bitmap */ + t_u32 last_mp_wr_bitmap[SDIO_MP_DBG_NUM]; + /** last ports for cmd53 write data */ + t_u32 last_mp_wr_ports[SDIO_MP_DBG_NUM]; + /** last length for cmd53 write data */ + t_u32 last_mp_wr_len[SDIO_MP_DBG_NUM]; + /** length info for cmd53 write data */ + t_u16 last_mp_wr_info[SDIO_MP_DBG_NUM * SDIO_MP_AGGR_DEF_PKT_LIMIT]; + /** last curr_wr_port */ + t_u8 last_curr_wr_port[SDIO_MP_DBG_NUM]; + + /** buffer for mp debug */ + t_u8 *mpa_buf; + /** length info for mp buf size */ + t_u32 mpa_buf_size; + + /** last mp_index */ + t_u8 last_mp_index; +#endif /* SDIO_MULTI_PORT_TX_AGGR */ + +#ifdef SDIO_MULTI_PORT_RX_AGGR + /** data structure for SDIO MPA RX */ + sdio_mpa_rx mpa_rx; + /** packet number for tx aggr */ + t_u32 mpa_rx_count[SDIO_MP_AGGR_DEF_PKT_LIMIT]; +#endif /* SDIO_MULTI_PORT_RX_AGGR */ + + /** SDIO interrupt mode (0: INT_MODE_SDIO, 1: INT_MODE_GPIO) */ + t_u32 int_mode; + /** GPIO interrupt pin number */ + t_u32 gpio_pin; + + /** Event cause */ + t_u32 event_cause; + /** Event buffer */ + pmlan_buffer pmlan_buffer_event; + /** Upload length */ + t_u32 upld_len; + /** Upload buffer*/ + t_u8 upld_buf[WLAN_UPLD_SIZE]; + /** Data sent: + * TRUE - Data is sent to fw, no Tx Done received + * FALSE - Tx done received for previous Tx + */ + t_u8 data_sent; + /** CMD sent: + * TRUE - CMD is sent to fw, no CMD Done received + * FALSE - CMD done received for previous CMD + */ + t_u8 cmd_sent; + /** CMD Response received: + * TRUE - CMD is response is received from fw, and yet to process + * FALSE - No cmd response to process + */ + t_u8 cmd_resp_received; + /** Event received: + * TRUE - Event received from fw, and yet to process + * FALSE - No events to process + */ + t_u8 event_received; + + /** Data received: + * TRUE - Data received from fw + * FALSE - No Data received + */ + t_u8 data_received; + + /** Command-related variables */ + /** Command sequence number */ + t_u16 seq_num; + /** Command controller nodes */ + cmd_ctrl_node *cmd_pool; + /** Current Command */ + cmd_ctrl_node *curr_cmd; + /** mlan_lock for command */ + t_void *pmlan_cmd_lock; + /** Number of command timeouts */ + t_u32 num_cmd_timeout; + /** Last init fw command id */ + t_u16 last_init_cmd; + /** Command timer */ + t_void *pmlan_cmd_timer; + /** Command timer set flag */ + t_u8 cmd_timer_is_set; + /** time stamp for command dnld */ + t_u32 dnld_cmd_in_secs; + + /** Command Queues */ + /** Free command buffers */ + mlan_list_head cmd_free_q; + /** Pending command buffers */ + mlan_list_head cmd_pending_q; + /** Command queue for scanning */ + mlan_list_head scan_pending_q; + /** ioctl pending queue */ + mlan_list_head ioctl_pending_q; + /** pending_ioctl flag */ + t_u8 pending_ioctl; + /** mlan_processing */ + t_u32 scan_processing; + /** firmware support for roaming*/ + t_u8 fw_roaming; + /** User set passphrase*/ + t_u8 userset_passphrase; + /** ext_scan enh support flag */ + t_u8 ext_scan_enh; + /** scan type: 0 legacy, 1: enhance scan*/ + t_u8 ext_scan_type; + /** ext scan timeout */ + t_u8 ext_scan_timeout; + /** coex scan flag */ + t_u8 coex_scan; + /** coex min scan time */ + t_u8 coex_min_scan_time; + /** coex max scan time */ + t_u8 coex_max_scan_time; + /** coex win size flag */ + t_u8 coex_win_size; + /** coex amdpdu tx win size */ + t_u8 coex_tx_win_size; + /** coex ampdu rx win size */ + t_u8 coex_rx_win_size; + /** Region code */ + t_u16 region_code; + /** Region Channel data */ + region_chan_t region_channel[MAX_REGION_CHANNEL_NUM]; + /** CFP table code for 2.4GHz */ + t_u8 cfp_code_bg; + /** CFP table code for 5GHz */ + t_u8 cfp_code_a; + wmm_ac_parameters_t ac_params[MAX_AC_QUEUES]; +#ifdef STA_SUPPORT + /** Universal Channel data */ + region_chan_t universal_channel[MAX_REGION_CHANNEL_NUM]; + /** Parsed region channel */ + parsed_region_chan_11d_t parsed_region_chan; +#endif /* STA_SUPPORT */ + /** 11D and Domain Regulatory Data */ + wlan_802_11d_domain_reg_t domain_reg; + /** Country Code */ + t_u8 country_code[COUNTRY_CODE_LEN]; + /** FSM variable for 11h support */ + wlan_11h_device_state_t state_11h; + /** FSM variable for DFS support */ + wlan_dfs_device_state_t state_dfs; + /** FSM variable for RDH support */ + wlan_radar_det_hndlg_state_t state_rdh; + /** variable to configure dfs channel switch count */ + t_s8 dfs_cs_count; +#ifdef DFS_TESTING_SUPPORT + /** User configured settings for DFS testing */ + wlan_dfs_testing_settings_t dfs_test_params; +#endif + /** FSM variable for MEAS support */ + wlan_meas_state_t state_meas; + /** Scan table */ + BSSDescriptor_t *pscan_table; + /** scan age in secs */ + t_u32 age_in_secs; + /** Active scan for hidden ssid triggered */ + t_u8 active_scan_triggered; + /** channel statstics */ + ChanStatistics_t *pchan_stats; + /** Number of records in the chan_stats */ + t_u32 num_in_chan_stats; + /** index of chan stats */ + t_u32 idx_chan_stats; + t_u8 bgscan_reported; + + /** Number of records in the scan table */ + t_u32 num_in_scan_table; + /** Scan probes */ + t_u16 scan_probes; + + /** Scan type */ + t_u8 scan_type; + /** Scan mode */ + t_u32 scan_mode; + /** Specific scan time */ + t_u16 specific_scan_time; + /** Active scan time */ + t_u16 active_scan_time; + /** Passive scan time */ + t_u16 passive_scan_time; + /** Scan block flag */ + t_u8 scan_block; + /** Extended scan or legacy scan */ + t_u8 ext_scan; + t_u16 bcn_buf_size; + /** Beacon buffer */ + t_u8 *bcn_buf; + /** Pointer to valid beacon buffer end */ + t_u8 *pbcn_buf_end; + /** allocate fixed scan beacon buffer size*/ + t_u32 fixed_beacon_buffer; + + /** F/W supported bands */ + t_u8 fw_bands; + /** User selected band to start adhoc network */ + t_u8 adhoc_start_band; + /** User selected bands */ + t_u8 config_bands; + /** Pointer to channel list last sent to the firmware for scanning */ + ChanScanParamSet_t *pscan_channels; + + /** Tx lock flag */ + t_u8 tx_lock_flag; + /** Rx lock flag */ + t_u8 rx_lock_flag; + /** main lock flag */ + t_u8 main_lock_flag; + /** Multi channel status */ + t_u8 mc_status; + + /** sleep_params_t */ + sleep_params_t sleep_params; + /** sleep_period_t (Enhanced Power Save) */ + sleep_period_t sleep_period; + /** saved sleep_period_t (Enhanced Power Save) */ + sleep_period_t saved_sleep_period; + + /** Power Save mode */ + /** + * Wlan802_11PowerModeCAM = disable + * Wlan802_11PowerModePSP = enable + */ + t_u16 ps_mode; + /** Power Save state */ + t_u32 ps_state; + /** Need to wakeup flag */ + t_u8 need_to_wakeup; + + /** Multiple DTIM */ + t_u16 multiple_dtim; + /** Local listen interval */ + t_u16 local_listen_interval; + /** Null packet interval */ + t_u16 null_pkt_interval; + + /** IEEE ps inactivity timout value */ + t_u16 inact_tmo; + /** Power save confirm sleep command buffer */ + pmlan_buffer psleep_cfm; + /** Beacon miss timeout */ + t_u16 bcn_miss_time_out; + + /** AdHoc awake period */ + t_u16 adhoc_awake_period; + + /** Firmware wakeup method */ + t_u16 fw_wakeup_method; + /** Firmware wakeup GPIO pin */ + t_u8 fw_wakeup_gpio_pin; + /** Deep Sleep flag */ + t_u8 is_deep_sleep; + /** Idle time */ + t_u16 idle_time; + /** Auto Deep Sleep enabled at init time */ + t_u8 init_auto_ds; + + /** delay null pkt flag */ + t_u8 delay_null_pkt; + /** Delay to PS in milliseconds */ + t_u16 delay_to_ps; + /** Enhanced PS mode */ + t_u16 enhanced_ps_mode; + /** Device wakeup required flag */ + t_u8 pm_wakeup_card_req; + + /** Gen NULL pkg */ + t_u16 gen_null_pkt; + + /** PPS/UAPSD mode flag */ + t_u16 pps_uapsd_mode; + /** Number of wakeup tries */ + t_u32 pm_wakeup_fw_try; + /** time stamp when host try to wake up firmware */ + t_u32 pm_wakeup_in_secs; + + /** Host Sleep configured flag */ + t_u8 is_hs_configured; + /** Host Sleep configuration */ + hs_config_param hs_cfg; + /** Host Sleep activated flag */ + t_u8 hs_activated; + /** Event body */ + t_u8 event_body[MAX_EVENT_SIZE]; + /** 802.11n device capabilities */ + t_u32 hw_dot_11n_dev_cap; + /** Device support for MIMO abstraction of MCSs */ + t_u8 hw_dev_mcs_support; +#ifdef STA_SUPPORT + /** Enable 11n support for adhoc start */ + t_u8 adhoc_11n_enabled; + /** Adhoc Secondary Channel Bandwidth */ + t_u8 chan_bandwidth; +#endif /* STA_SUPPORT */ + + /** max mgmt IE index in device */ + t_u16 max_mgmt_ie_index; + /** Head of Rx data queue */ + mlan_list_head rx_data_queue; +#ifdef MFG_CMD_SUPPORT + t_u32 mfg_mode; +#endif + /** Debug */ + wlan_dbg dbg; + + /** RX pending for forwarding packets */ + mlan_scalar pending_bridge_pkts; + +#ifdef STA_SUPPORT + /** ARP filter buffer */ + t_u8 arp_filter[ARP_FILTER_MAX_BUF_SIZE]; + /** ARP filter buffer size */ + t_u32 arp_filter_size; +#endif /* STA_SUPPORT */ + /** Minimum delay between HsActive and HostWake (in msec) */ + t_u16 min_wake_holdoff; + /** Host sleep wake interval(in msec) */ + t_u32 hs_wake_interval; + /** Host sleep inactivity timeout (in msec) */ + t_u32 hs_inactivity_timeout; + /** Parameter type for indication gpio*/ + t_u8 param_type_ind; + /** GPIO pin for indication wakeup source */ + t_u32 ind_gpio; + /** Level on ind_gpio pin for indication normal wakeup source */ + t_u32 level; + /** Parameter type for extend hscfg*/ + t_u8 param_type_ext; + /** Events that will be forced ignore */ + t_u32 event_force_ignore; + /** Events that will use extend gap to inform host*/ + t_u32 event_use_ext_gap; + /** Extend gap*/ + t_u8 ext_gap; + /** GPIO wave level for extend hscfg */ + t_u8 gpio_wave; + /** management frame wakeup filter config */ + mlan_mgmt_frame_wakeup mgmt_filter[MAX_MGMT_FRAME_FILTER]; + + /** Bypass TX queue pkt count */ + t_u16 bypass_pkt_count; +#if defined(STA_SUPPORT) + /** warm-reset IOCTL request buffer pointer */ + pmlan_ioctl_req pwarm_reset_ioctl_req; +#endif + /** SCAN IOCTL request buffer pointer */ + pmlan_ioctl_req pscan_ioctl_req; + /** DPD data pointer */ + t_u8 *pdpd_data; + /** DPD data length */ + t_u32 dpd_data_len; + /** region txpowerlimit cfg data buf pointer */ + t_u8 *ptxpwr_data; + /** region txpowerlimit cfg data len */ + t_u32 txpwr_data_len; + /** Cal data pointer */ + t_u8 *pcal_data; + /** Cal data length */ + t_u32 cal_data_len; + /** tdls status */ + /* TDLS_NOT_SETUP|TDLS_SWITCHING_CHANNEL|TDLS_IN_BASE_CHANNEL|TDLS_IN_SWITCH_CHANNEL */ + tdlsStatus_e tdls_status; + /** NetMon enabled */ + t_u32 enable_net_mon; + + /** Control coex RX window size configuration */ + t_u8 coex_rx_winsize; + /** DFS repeater */ + t_bool dfs_repeater; + /** DFSr channel */ + t_u32 dfsr_channel; + /** multi channel policy */ + t_bool mc_policy; + /** flag for sdio rx aggr */ + t_bool sdio_rx_aggr_enable; + /** fw rx block size */ + t_u16 sdio_rx_block_size; + /**channel param band config */ + t_u8 chanrpt_param_bandcfg; + /** d1 time */ + t_u64 d1; + /** d2 time */ + t_u64 d2; + /** host bbu clock delta */ + t_u64 host_bbu_clk_delta; + /** maximum station connection */ + t_u8 max_sta_conn; + otp_region_info_t *otp_region; + chan_freq_power_t *cfp_otp_bg; + t_u8 *tx_power_table_bg; + t_u32 tx_power_table_bg_size; + chan_freq_power_t *cfp_otp_a; + t_u8 *tx_power_table_a; + t_u32 tx_power_table_a_size; +} mlan_adapter, *pmlan_adapter; + +/** Ethernet packet type for EAPOL */ +#define MLAN_ETHER_PKT_TYPE_EAPOL (0x888E) +/** Ethernet packet type for WAPI */ +#define MLAN_ETHER_PKT_TYPE_WAPI (0x88B4) +/** Ethernet packet type offset */ +#define MLAN_ETHER_PKT_TYPE_OFFSET (12) + +mlan_status wlan_cmd_net_monitor(IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, IN t_void *pdata_buf); + +mlan_status wlan_ret_net_monitor(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf); + +mlan_status wlan_misc_ioctl_net_monitor(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req); + +void wlan_rxpdinfo_to_radiotapinfo(pmlan_private priv, + RxPD *prx_pd, radiotap_info * prt_info); + +mlan_status wlan_init_lock_list(IN pmlan_adapter pmadapter); +mlan_status wlan_init_priv_lock_list(IN pmlan_adapter pmadapter, + t_u8 start_index); +t_void wlan_free_lock_list(IN pmlan_adapter pmadapter); +mlan_status wlan_init_timer(IN pmlan_adapter pmadapter); +t_void wlan_free_timer(IN pmlan_adapter pmadapter); + +/* Function prototype */ +/** Download firmware */ +mlan_status wlan_dnld_fw(IN pmlan_adapter pmadapter, IN pmlan_fw_image pmfw); + +/** Initialize firmware */ +mlan_status wlan_init_fw(IN pmlan_adapter pmadapter); + +/** get hw spec complete */ +mlan_status wlan_get_hw_spec_complete(IN pmlan_adapter pmadapter); + +/** Initialize firmware complete */ +mlan_status wlan_init_fw_complete(IN pmlan_adapter pmadapter); + +/** Shutdown firmware complete */ +mlan_status wlan_shutdown_fw_complete(IN pmlan_adapter pmadapter); + +/** Receive event */ +mlan_status wlan_recv_event(pmlan_private priv, + mlan_event_id event_id, t_void *pmevent); + +/** Initialize mlan_adapter structure */ +t_void wlan_init_adapter(IN pmlan_adapter pmadapter); + +/** Initialize mlan_private structure */ +mlan_status wlan_init_priv(IN pmlan_private priv); + +/** Process event */ +mlan_status wlan_process_event(pmlan_adapter pmadapter); + +/** Prepare command */ +mlan_status wlan_prepare_cmd(IN pmlan_private priv, + IN t_u16 cmd_no, + IN t_u16 cmd_action, + IN t_u32 cmd_oid, + IN t_void *pioctl_buf, IN t_void *pdata_buf); + +/** cmd timeout handler */ +t_void wlan_cmd_timeout_func(t_void *function_context); +/** process host cmd */ +mlan_status wlan_misc_ioctl_host_cmd(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req); +/** process init/shutdown cmd*/ +mlan_status wlan_misc_ioctl_init_shutdown(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req); +/** process debug info */ +mlan_status wlan_get_info_debug_info(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req); + +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) +/** Set/Get BSS role */ +mlan_status wlan_bss_ioctl_bss_role(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req); +#endif + +mlan_status wlan_misc_get_correlated_time(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req); +mlan_status wlan_set_ewpa_mode(mlan_private *priv, mlan_ds_passphrase *psec_pp); +mlan_status wlan_find_bss(mlan_private *pmpriv, pmlan_ioctl_req pioctl_req); + +/* block main process */ +void mlan_block_main_process(mlan_adapter *pmadapter, t_u8 block); +/* block rx process */ +void mlan_block_rx_process(mlan_adapter *pmadapter, t_u8 block); +/** check pending command */ +int wlan_check_pending_cmd(mlan_adapter *pmadapter); +/** Allocate memory for adapter structure members */ +mlan_status wlan_allocate_adapter(pmlan_adapter pmadapter); +/** Free adapter */ +t_void wlan_free_adapter(pmlan_adapter pmadapter); +/** Free priv */ +t_void wlan_free_priv(mlan_private *pmpriv); +/** Allocate command buffer */ +mlan_status wlan_alloc_cmd_buffer(IN mlan_adapter *pmadapter); +/** Free command buffer */ +mlan_status wlan_free_cmd_buffer(IN mlan_adapter *pmadapter); +/** Request command lock */ +t_void wlan_request_cmd_lock(mlan_adapter *pmadapter); +/** Release command lock */ +t_void wlan_release_cmd_lock(mlan_adapter *pmadapter); +#ifdef STA_SUPPORT +/** Flush the scan pending queue */ +t_void wlan_flush_scan_queue(pmlan_adapter pmadapter); +mlan_status wlan_cancel_pending_scan_cmd(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); +#endif +/**Cancel pending command */ +t_void wlan_cancel_all_pending_cmd(pmlan_adapter pmadapter); +/**Cancel pending ioctl */ +t_void wlan_cancel_pending_ioctl(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); +/**Cancel bss pending ioctl */ +t_void wlan_cancel_bss_pending_cmd(pmlan_adapter pmadapter, t_u32 bss_index); + +/** Insert command to free queue */ +t_void wlan_insert_cmd_to_free_q(IN mlan_adapter *pmadapter, + IN cmd_ctrl_node *pcmd_node); + +/** Insert command to pending queue */ +t_void wlan_insert_cmd_to_pending_q(IN mlan_adapter *pmadapter, + IN cmd_ctrl_node *pcmd_node, + IN t_u32 addtail); + +/** Execute next command */ +mlan_status wlan_exec_next_cmd(mlan_adapter *pmadapter); +/** Proecess command response */ +mlan_status wlan_process_cmdresp(mlan_adapter *pmadapter); +/** Handle received packet, has extra handling for aggregate packets */ +mlan_status wlan_handle_rx_packet(pmlan_adapter pmadapter, pmlan_buffer pmbuf); +/** Process transmission */ +mlan_status wlan_process_tx(pmlan_private priv, pmlan_buffer pmbuf, + mlan_tx_param *tx_param); +/** Transmit a null data packet */ +mlan_status wlan_send_null_packet(pmlan_private priv, t_u8 flags); + +#if defined(SDIO_MULTI_PORT_TX_AGGR) || defined(SDIO_MULTI_PORT_RX_AGGR) +mlan_status wlan_alloc_sdio_mpa_buffers(IN mlan_adapter *pmadapter, + t_u32 mpa_tx_buf_size, + t_u32 mpa_rx_buf_size); + +mlan_status wlan_free_sdio_mpa_buffers(IN mlan_adapter *pmadapter); +#endif + +/** Process write data complete */ +mlan_status wlan_write_data_complete(pmlan_adapter pmlan_adapter, + pmlan_buffer pmbuf, mlan_status status); +/** Process receive packet complete */ +mlan_status wlan_recv_packet_complete(pmlan_adapter pmadapter, + pmlan_buffer pmbuf, mlan_status status); +/** Clean Tx Rx queues */ +t_void wlan_clean_txrx(pmlan_private priv); + +t_void wlan_add_buf_bypass_txqueue(mlan_adapter *pmadapter, pmlan_buffer pmbuf); +t_void wlan_process_bypass_tx(mlan_adapter *pmadapter); +t_void wlan_cleanup_bypass_txq(pmlan_private priv); +t_u8 wlan_bypass_tx_list_empty(mlan_adapter *pmadapter); + +/** Check if this is the last packet */ +t_u8 wlan_check_last_packet_indication(pmlan_private priv); + +#define MOAL_ALLOC_MLAN_BUFFER (0) +#define MOAL_MALLOC_BUFFER (1) + +/** function to allocate a mlan_buffer */ +pmlan_buffer wlan_alloc_mlan_buffer(mlan_adapter *pmadapter, t_u32 data_len, + t_u32 head_room, t_u32 malloc_flag); +/** function to free a mlan_buffer */ +t_void wlan_free_mlan_buffer(mlan_adapter *pmadapter, pmlan_buffer pmbuf); + +/** command resp handler for version ext */ +mlan_status wlan_ret_ver_ext(pmlan_private pmpriv, HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf); + +/** command resp handler for rx mgmt forward registration */ +mlan_status wlan_ret_rx_mgmt_ind(pmlan_private pmpriv, HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf); + +/** Check Power Save condition */ +t_void wlan_check_ps_cond(mlan_adapter *pmadapter); + +/** handle command for enhanced power save mode */ +mlan_status wlan_cmd_enh_power_mode(pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, + IN t_u16 ps_bitmap, IN t_void *pdata_buf); +/** handle command resp for enhanced power save mode */ +mlan_status wlan_ret_enh_power_mode(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf); + +/** handle commnand for cfg data */ +mlan_status wlan_cmd_cfg_data(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *pcmd, + IN t_u16 cmd_action, + IN t_u32 cmd_oid, IN t_void *pdata_buf); +/** handle command resp for cfg data */ +mlan_status wlan_ret_cfg_data(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN t_void *pioctl_buf); + +/** Process sleep confirm command response */ +void wlan_process_sleep_confirm_resp(pmlan_adapter pmadapter, t_u8 *pbuf, + t_u32 len); + +/** Perform hs related activities on receving the power up interrupt */ +void wlan_process_hs_config(pmlan_adapter pmadapter); + +mlan_status wlan_pm_reset_card(pmlan_adapter adapter); +mlan_status wlan_pm_wakeup_card(pmlan_adapter pmadapter); + +mlan_status wlan_process_802dot11_mgmt_pkt(mlan_private *priv, t_u8 *payload, + t_u32 payload_len, RxPD *prx_pd); + +mlan_status wlan_pm_ioctl_hscfg(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); + +#ifdef WIFI_DIRECT_SUPPORT +mlan_status wlan_bss_ioctl_wifi_direct_mode(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req); + +mlan_status wlan_cmd_wifi_direct_mode(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, + IN t_void *pdata_buf); +mlan_status wlan_ret_wifi_direct_mode(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf); + +mlan_status wlan_cmd_p2p_params_config(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, + IN t_void *pdata_buf); +mlan_status wlan_ret_p2p_params_config(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf); +mlan_status wlan_misc_p2p_config(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req); +#endif +/** get ralist info */ +int wlan_get_ralist_info(mlan_private *priv, ralist_info *buf); +/** dump ralist */ +void wlan_dump_ralist(mlan_private *priv); + +/** get pm info */ +mlan_status wlan_get_pm_info(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req); + +mlan_status wlan_bss_ioctl_bss_remove(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req); + +mlan_status wlan_misc_per_pkt_cfg(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req); + +mlan_status wlan_config_mgmt_filter(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req); +mlan_status wlan_get_hs_wakeup_reason(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req); + +mlan_status wlan_cmd_hs_wakeup_reason(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_void *pdata_buf); + +mlan_status wlan_ret_hs_wakeup_reason(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf); + +mlan_status wlan_radio_ioctl_radio_ctl(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req); +mlan_status wlan_radio_ioctl_remain_chan_cfg(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req); +mlan_status wlan_cmd_remain_on_channel(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, + IN t_void *pdata_buf); +mlan_status wlan_ret_remain_on_channel(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf); + +mlan_status wlan_radio_ioctl_ant_cfg(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req); + +mlan_status wlan_cmd_tx_rate_cfg(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, IN t_void *pdata_buf); +mlan_status wlan_ret_tx_rate_cfg(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf); + +mlan_status wlan_rate_ioctl_cfg(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req); +mlan_status wlan_ret_802_11_tx_rate_query(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf); + +mlan_status wlan_rate_ioctl_get_data_rate(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req); + +t_void wlan_host_sleep_activated_event(pmlan_private priv, t_u8 activated); +/** Handles the command response of hs_cfg */ +mlan_status wlan_ret_802_11_hs_cfg(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf); +/** Sends HS_WAKEUP event to applications */ +t_void wlan_host_sleep_wakeup_event(pmlan_private priv); + +mlan_status wlan_cmd_802_11_fw_wakeup_method(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, + IN t_u16 *pdata_buf); +mlan_status wlan_ret_fw_wakeup_method(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf); +mlan_status wlan_fw_wakeup_method(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req); + +/** Prepares command of robustcoex */ +mlan_status wlan_cmd_robustcoex(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, IN t_u16 *pdata_buf); +/** Set Robustcoex gpiocfg */ +mlan_status wlan_misc_robustcoex(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req); + +/** send get hw spec command to firmware */ +mlan_status wlan_adapter_get_hw_spec(IN pmlan_adapter pmadapter); +/** send adapter specific init cmd to firmware */ +mlan_status wlan_adapter_init_cmd(IN pmlan_adapter pmadapter); + +#ifdef RX_PACKET_COALESCE +mlan_status wlan_cmd_rx_pkt_coalesce_cfg(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, + IN t_void *pdata_buf); +mlan_status wlan_ret_rx_pkt_coalesce_cfg(IN pmlan_private pmpriv, + const IN HostCmd_DS_COMMAND *resp, + OUT mlan_ioctl_req *pioctl_buf); +#endif +mlan_status wlan_handle_event_multi_chan_info(IN pmlan_private pmpriv, + pmlan_buffer pevent); + +#ifdef STA_SUPPORT +/** warm reset */ +mlan_status wlan_misc_ioctl_warm_reset(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req); +/** Process received packet */ +mlan_status wlan_process_rx_packet(pmlan_adapter pmadapter, pmlan_buffer pmbuf); +/** ioctl handler for station mode */ +mlan_status wlan_ops_sta_ioctl(t_void *adapter, pmlan_ioctl_req pioctl_req); + +/** cmd handler for station mode */ +mlan_status wlan_ops_sta_prepare_cmd(IN t_void *priv, + IN t_u16 cmd_no, + IN t_u16 cmd_action, + IN t_u32 cmd_oid, + IN t_void *pioctl_buf, + IN t_void *pdata_buf, IN t_void *pcmd_buf); + +/** cmdresp handler for station mode */ +mlan_status wlan_ops_sta_process_cmdresp(IN t_void *priv, + IN t_u16 cmdresp_no, + IN t_void *pcmd_buf, + IN t_void *pioctl); + +/** rx handler for station mode */ +mlan_status wlan_ops_sta_process_rx_packet(IN t_void *adapter, + IN pmlan_buffer pmbuf); + +/** event handler for station mode */ +mlan_status wlan_ops_sta_process_event(IN t_void *priv); + +/** fill txpd for station mode */ +t_void *wlan_ops_sta_process_txpd(IN t_void *priv, IN pmlan_buffer pmbuf); + +/** send init cmd to firmware for station mode */ +mlan_status wlan_ops_sta_init_cmd(IN t_void *priv, IN t_u8 first_bss); + +/** Flush the scan table */ +mlan_status wlan_flush_scan_table(IN pmlan_adapter pmadapter); + +/** Scan for networks */ +mlan_status wlan_scan_networks(IN mlan_private *pmpriv, + IN t_void *pioctl_buf, + IN wlan_user_scan_cfg *puser_scan_in); + +/** Scan for specific SSID */ +mlan_status wlan_scan_specific_ssid(IN mlan_private *pmpriv, + IN t_void *pioctl_buf, + IN mlan_802_11_ssid *preq_ssid); + +/** Scan command handler */ +mlan_status wlan_cmd_802_11_scan(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *pcmd, + IN t_void *pdata_buf); + +/** Handler for scan command response */ +mlan_status wlan_ret_802_11_scan(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN t_void *pioctl_buf); + +/** Extended scan command handler */ +mlan_status wlan_cmd_802_11_scan_ext(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *pcmd, + IN t_void *pdata_buf); +/** Handler for extended scan command response */ +mlan_status wlan_ret_802_11_scan_ext(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN t_void *pioctl_buf); +/** Handler event for extended scan report */ +mlan_status wlan_handle_event_ext_scan_report(IN mlan_private *pmpriv, + IN mlan_buffer *pmbuf); +mlan_status wlan_handle_event_ext_scan_status(IN mlan_private *pmpriv, + IN mlan_buffer *pmbuf); + +/** check network compatibility */ +t_s32 wlan_is_network_compatible(IN mlan_private *pmpriv, + IN t_u32 index, IN t_u32 mode); + +/** Find an SSID in a list */ +t_s32 wlan_find_ssid_in_list(IN pmlan_private pmpriv, + IN mlan_802_11_ssid *ssid, + IN t_u8 *bssid, IN t_u32 mode); + +/** Find a BSSID in a list */ +t_s32 wlan_find_bssid_in_list(IN mlan_private *pmpriv, + IN t_u8 *bssid, IN t_u32 mode); + +/** Find best network */ +mlan_status wlan_find_best_network(IN mlan_private *pmpriv, + OUT mlan_ssid_bssid *preq_ssid_bssid); + +/** Compare two SSIDs */ +t_s32 wlan_ssid_cmp(IN pmlan_adapter pmadapter, + IN mlan_802_11_ssid *ssid1, IN mlan_802_11_ssid *ssid2); + +/** Associate */ +mlan_status wlan_associate(IN mlan_private *pmpriv, + IN t_void *pioctl_buf, IN BSSDescriptor_t *pBSSDesc); + +/** Associate command handler */ +mlan_status wlan_cmd_802_11_associate(IN mlan_private *pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_void *pdata_buf); + +/** Handler for association command response */ +mlan_status wlan_ret_802_11_associate(IN mlan_private *pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN t_void *pioctl_buf); + +/** Reset connected state */ +t_void wlan_reset_connect_state(IN pmlan_private priv, IN t_u8 drv_disconnect); + +t_void wlan_2040_coex_event(pmlan_private pmpriv); + +/** convert band to radio type */ +t_u8 wlan_band_to_radio_type(IN t_u8 band); +/** convert radio_type to band */ +t_u8 radio_type_to_band(t_u8 chanBand); + +/** Disconnect */ +mlan_status wlan_disconnect(IN mlan_private *pmpriv, + IN mlan_ioctl_req *pioctl_req, + IN mlan_deauth_param *deauth_param); + +/** Ad-Hoc start */ +mlan_status wlan_adhoc_start(IN mlan_private *pmpriv, + IN t_void *pioctl_buf, + IN mlan_802_11_ssid *padhoc_ssid); + +/** Ad-Hoc join */ +mlan_status wlan_adhoc_join(IN mlan_private *pmpriv, + IN t_void *pioctl_buf, + IN BSSDescriptor_t *pBSSDesc); + +/** Ad-Hoc start command handler */ +mlan_status wlan_cmd_802_11_ad_hoc_start(IN mlan_private *pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_void *pdata_buf); + +/** Ad-Hoc command handler */ +mlan_status wlan_cmd_802_11_ad_hoc_join(IN mlan_private *pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_void *pdata_buf); + +/** Handler for Ad-Hoc commands */ +mlan_status wlan_ret_802_11_ad_hoc(IN mlan_private *pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN t_void *pioctl_buf); + +/** Handler for bgscan query commands */ +mlan_status wlan_cmd_802_11_bg_scan_query(IN mlan_private *pmpriv, + IN HostCmd_DS_COMMAND *pcmd, + IN t_void *pdata_buf); +/** Handler for bgscan config command */ +mlan_status wlan_cmd_bgscan_config(IN mlan_private *pmpriv, + IN HostCmd_DS_COMMAND *pcmd, + IN t_void *pdata_buf); +/** Hander for bgscan config command response */ +mlan_status wlan_ret_bgscan_config(IN mlan_private *pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf); +mlan_status wlan_ret_802_11_bgscan_query(IN mlan_private *pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf); + +/** Get Channel-Frequency-Power by band and channel */ +chan_freq_power_t *wlan_get_cfp_by_band_and_channel(pmlan_adapter pmadapter, + t_u8 band, t_u16 channel, + region_chan_t + *region_channel); +/** Find Channel-Frequency-Power by band and channel */ +chan_freq_power_t *wlan_find_cfp_by_band_and_channel(mlan_adapter *pmadapter, + t_u8 band, t_u16 channel); +/** Find Channel-Frequency-Power by band and frequency */ +chan_freq_power_t *wlan_find_cfp_by_band_and_freq(mlan_adapter *pmadapter, + t_u8 band, t_u32 freq); +/** Get Tx power of channel from Channel-Frequency-Power */ +t_u8 wlan_get_txpwr_of_chan_from_cfp(mlan_private *pmpriv, t_u8 channel); +/** find frequency from band and channel */ +t_u32 wlan_find_freq_from_band_chan(t_u8, t_u8); + +/* Save a beacon buffer of the current bss descriptor */ +t_void wlan_save_curr_bcn(IN mlan_private *pmpriv); +/* Free a beacon buffer of the current bss descriptor */ +t_void wlan_free_curr_bcn(IN mlan_private *pmpriv); + +#endif /* STA_SUPPORT */ + +/* Rate related functions */ +t_u8 wlan_convert_v14_rate_ht_info(t_u8 ht_info); +/** Convert index into data rate */ +t_u32 wlan_index_to_data_rate(pmlan_adapter pmadapter, t_u8 index, + t_u8 rate_info); +/** Get active data rates */ +t_u32 wlan_get_active_data_rates(mlan_private *pmpriv, t_u32 bss_mode, + t_u8 config_bands, WLAN_802_11_RATES rates); +/** Get supported data rates */ +t_u32 wlan_get_supported_rates(mlan_private *pmpriv, t_u32 bss_mode, + t_u8 config_bands, WLAN_802_11_RATES rates); +/** Convert data rate to index */ +t_u8 wlan_data_rate_to_index(pmlan_adapter pmadapter, t_u32 rate); +/** Check if rate is auto */ +t_u8 wlan_is_rate_auto(mlan_private *pmpriv); +/** Get rate index */ +int wlan_get_rate_index(pmlan_adapter pmadapter, t_u16 *rateBitmap, int size); + +/* CFP related functions */ +/** Region code index table */ +extern t_u16 region_code_index[MRVDRV_MAX_REGION_CODE]; +/** The table to keep CFP code for BG */ +extern t_u16 cfp_code_index_bg[MRVDRV_MAX_CFP_CODE_BG]; +/** The table to keep CFP code for A */ +extern t_u16 cfp_code_index_a[MRVDRV_MAX_CFP_CODE_A]; + +/** Set region table */ +mlan_status wlan_set_regiontable(mlan_private *pmpriv, t_u8 region, t_u8 band); +/** Get radar detection requirements*/ +t_bool wlan_get_cfp_radar_detect(mlan_private *priv, t_u8 chnl); +/** check if scan type is passive for b/g band*/ +t_bool wlan_bg_scan_type_is_passive(mlan_private *priv, t_u8 chnl); +/** check if channel is disabled */ +t_bool wlan_is_chan_disabled(mlan_private *priv, t_u8 band, t_u8 chan); +/** check if channel is blacklisted */ +t_bool wlan_is_chan_blacklisted(mlan_private *priv, t_u8 band, t_u8 chan); +/** set blacklist setting for a channel */ +t_bool wlan_set_chan_blacklist(mlan_private *priv, t_u8 band, t_u8 chan, + t_bool bl); + +/* 802.11D related functions */ +/** Initialize 11D */ +t_void wlan_11d_priv_init(mlan_private *pmpriv); +/** Initialize 11D */ +t_void wlan_11d_init(mlan_adapter *pmadapter); +/** Enable 11D */ +mlan_status wlan_11d_enable(mlan_private *pmpriv, t_void *pioctl_buf, + state_11d_t flag); +/** Get if 11D is enabled */ +t_bool wlan_11d_is_enabled(mlan_private *pmpriv); +/** Get if priv is station */ +t_bool wlan_is_station(mlan_private *pmpriv); +/** Command handler for 11D country info */ +mlan_status wlan_cmd_802_11d_domain_info(mlan_private *pmpriv, + HostCmd_DS_COMMAND *pcmd, + t_u16 cmd_action); +/** Handler for 11D country info command response */ +mlan_status wlan_ret_802_11d_domain_info(mlan_private *pmpriv, + HostCmd_DS_COMMAND *resp); +#ifdef STA_SUPPORT +/** Convert channel to frequency */ +t_u32 wlan_11d_chan_2_freq(pmlan_adapter pmadapter, t_u8 chan, t_u8 band); +/** Set 11D universal table */ +mlan_status wlan_11d_set_universaltable(mlan_private *pmpriv, t_u8 band); +/** Clear 11D region table */ +mlan_status wlan_11d_clear_parsedtable(mlan_private *pmpriv); +/** Create 11D country information for downloading */ +mlan_status wlan_11d_create_dnld_countryinfo(mlan_private *pmpriv, t_u8 band); +/** Get scan type from 11D info */ +t_u8 wlan_11d_get_scan_type(pmlan_adapter pmadapter, t_u8 band, t_u8 chan, + parsed_region_chan_11d_t *parsed_region_chan); +/** Parse 11D country info */ +mlan_status wlan_11d_parse_dnld_countryinfo(mlan_private *pmpriv, + BSSDescriptor_t *pBSSDesc); +/** Prepare 11D domain information for download */ +mlan_status wlan_11d_prepare_dnld_domain_info_cmd(mlan_private *pmpriv); +/** Parse 11D country information into domain info */ +mlan_status wlan_11d_parse_domain_info(pmlan_adapter pmadapter, + IEEEtypes_CountryInfoFullSet_t + *country_info, t_u8 band, + parsed_region_chan_11d_t + *parsed_region_chan); +/** Configure 11D domain info command */ +mlan_status wlan_11d_cfg_domain_info(IN pmlan_adapter pmadapter, + IN mlan_ioctl_req *pioctl_req); +#endif /* STA_SUPPORT */ +#ifdef UAP_SUPPORT +/** Handle 11D domain information from UAP */ +mlan_status wlan_11d_handle_uap_domain_info(mlan_private *pmpriv, + t_u8 band, + t_u8 *domain_tlv, + t_void *pioctl_buf); +#endif + +/** This function converts region string to CFP table code */ +mlan_status wlan_misc_country_2_cfp_table_code(IN pmlan_adapter pmadapter, + IN t_u8 *country_code, + OUT t_u8 *cfp_bg, + OUT t_u8 *cfp_a); + +/** This function finds if given country code is in EU table */ +t_bool wlan_is_etsi_country(pmlan_adapter pmadapter, t_u8 *country_code); + +/** check if station list is empty */ +t_u8 wlan_is_station_list_empty(mlan_private *priv); +/** get station node */ +sta_node *wlan_get_station_entry(mlan_private *priv, t_u8 *mac); +/** delete station list */ +t_void wlan_delete_station_list(pmlan_private priv); +/** delete station entry */ +t_void wlan_delete_station_entry(mlan_private *priv, t_u8 *mac); +/** add station entry */ +sta_node *wlan_add_station_entry(mlan_private *priv, t_u8 *mac); +/** process uap rx packet */ + +void wlan_check_sta_capability(pmlan_private priv, pmlan_buffer pevent, + sta_node *sta_ptr); +/** find specific ie */ +t_u8 *wlan_get_specific_ie(pmlan_private priv, t_u8 *ie_buf, t_u8 ie_len, + IEEEtypes_ElementId_e id); +t_u8 wlan_is_wmm_ie_present(pmlan_adapter pmadapter, t_u8 *pbuf, t_u16 buf_len); + +/** + * @brief This function checks whether a station TDLS link is enabled or not + * + * @param priv A pointer to mlan_private + * @param mac station mac address + * @return TDLS_NOT_SETUP/TDLS_SETUP_INPROGRESS/TDLS_SETUP_COMPLETE/TDLS_SETUP_FAILURE/TDLS_TEAR_DOWN + */ +static INLINE tdlsStatus_e +wlan_get_tdls_link_status(mlan_private *priv, t_u8 *mac) +{ + sta_node *sta_ptr = MNULL; + sta_ptr = wlan_get_station_entry(priv, mac); + if (sta_ptr) + return sta_ptr->status; + return TDLS_NOT_SETUP; +} + +/** + * @brief This function checks if TDLS link is in channel switching + * + * @param status tdls link status + * @return MTRUE/MFALSE + */ +static INLINE int +wlan_is_tdls_link_chan_switching(tdlsStatus_e status) +{ + return (status == TDLS_SWITCHING_CHANNEL) ? MTRUE : MFALSE; +} + +/** + * @brief This function checks if send command to firmware is allowed + * + * @param status tdls link status + * @return MTRUE/MFALSE + */ +static INLINE int +wlan_is_send_cmd_allowed(tdlsStatus_e status) +{ + int ret = MTRUE; + switch (status) { + case TDLS_SWITCHING_CHANNEL: + case TDLS_IN_OFF_CHANNEL: + ret = MFALSE; + break; + default: + break; + } + return ret; +} + +/** + * @brief This function checks if TDLS link is setup + * + * @param status tdls link status + * @return MTRUE/MFALSE + */ +static INLINE int +wlan_is_tdls_link_setup(tdlsStatus_e status) +{ + int ret = MFALSE; + switch (status) { + case TDLS_SWITCHING_CHANNEL: + case TDLS_IN_OFF_CHANNEL: + case TDLS_IN_BASE_CHANNEL: + case TDLS_SETUP_COMPLETE: + ret = MTRUE; + break; + default: + break; + } + return ret; +} + +/** + * @brief This function checks tx_pause flag for peer + * + * @param priv A pointer to mlan_private + * @param ra Address of the receiver STA + * + * @return MTRUE or MFALSE + */ +static int INLINE +wlan_is_tx_pause(mlan_private *priv, t_u8 *ra) +{ + sta_node *sta_ptr = MNULL; + sta_ptr = wlan_get_station_entry(priv, ra); + if (sta_ptr) + return sta_ptr->tx_pause; + return MFALSE; +} + +t_void wlan_update_ralist_tx_pause(pmlan_private priv, t_u8 *mac, + t_u8 tx_pause); + +#ifdef UAP_SUPPORT +mlan_status wlan_process_uap_rx_packet(IN mlan_private *priv, + IN pmlan_buffer pmbuf); +t_void wlan_drop_tx_pkts(pmlan_private priv); +#endif /* UAP_SUPPORT */ + +#ifdef UAP_SUPPORT +/* process the recevied packet and bridge the packet */ +mlan_status wlan_uap_recv_packet(IN mlan_private *priv, IN pmlan_buffer pmbuf); +#endif /* UAP_SUPPORT */ + +mlan_status wlan_misc_ioctl_coalescing_status(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req); + +mlan_status wlan_misc_ioctl_custom_ie_list(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req, + IN t_bool send_ioctl); + +mlan_status wlan_cmd_get_hw_spec(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *pcmd); +mlan_status wlan_ret_get_hw_spec(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN t_void *pioctl_buf); +mlan_status wlan_cmd_sdio_rx_aggr_cfg(IN HostCmd_DS_COMMAND *pcmd, + IN t_u16 cmd_action, + IN t_void *pdata_buf); +mlan_status wlan_ret_sdio_rx_aggr_cfg(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp); + +mlan_status wlan_misc_ioctl_mac_control(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req); +mlan_status wlan_cmd_mac_control(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *pcmd, + IN t_u16 cmd_action, IN t_void *pdata_buf); +mlan_status wlan_ret_mac_control(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf); + +mlan_status wlan_cmd_cw_mode_ctrl(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, IN t_void *pdata_buf); +mlan_status wlan_ret_cw_mode_ctrl(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf); + +mlan_status wlan_cmd_802_11_radio_control(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, + IN t_void *pdata_buf); +mlan_status wlan_ret_802_11_radio_control(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf); + +mlan_status wlan_cmd_802_11_rf_antenna(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, + IN t_void *pdata_buf); +mlan_status wlan_ret_802_11_rf_antenna(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf); + +mlan_status wlan_ret_reg_access(mlan_adapter *pmadapter, + t_u16 type, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf); +mlan_status wlan_ret_mem_access(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf); + +mlan_status wlan_reg_mem_ioctl_reg_rw(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req); +mlan_status wlan_reg_mem_ioctl_read_eeprom(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req); +mlan_status wlan_reg_mem_ioctl_mem_rw(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req); +mlan_status wlan_cmd_reg_access(IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, IN t_void *pdata_buf); +mlan_status wlan_cmd_mem_access(IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, IN t_void *pdata_buf); + +int wlan_get_tdls_list(mlan_private *priv, tdls_peer_info *buf); +t_void wlan_hold_tdls_packets(pmlan_private priv, t_u8 *mac); +t_void wlan_restore_tdls_packets(pmlan_private priv, t_u8 *mac, + tdlsStatus_e status); +t_void wlan_update_non_tdls_ralist(mlan_private *priv, t_u8 *mac, + t_u8 tx_pause); +mlan_status wlan_misc_ioctl_tdls_config(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req); +void wlan_11n_send_delba_to_peer(mlan_private *priv, t_u8 *ra); +void wlan_process_tdls_action_frame(pmlan_private priv, t_u8 *pbuf, t_u32 len); +mlan_status wlan_misc_ioctl_tdls_oper(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req); + +mlan_status + +wlan_misc_ioctl_tdls_get_ies(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req); +mlan_status wlan_misc_ioctl_tdls_idle_time(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req); + +t_void wlan_tdls_config(IN pmlan_private pmpriv, IN t_u8 enable); +mlan_status wlan_misc_ioctl_tdls_cs_channel(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req); +mlan_status wlan_get_info_ver_ext(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req); + +mlan_status wlan_reg_rx_mgmt_ind(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req); + +#ifdef DEBUG_LEVEL1 +mlan_status wlan_set_drvdbg(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req); +#endif + +#ifdef STA_SUPPORT +mlan_status wlan_misc_ext_capa_cfg(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req); + +t_u32 wlan_is_ext_capa_support(IN mlan_private *pmpriv); +#endif + +#ifdef STA_SUPPORT +void wlan_add_ext_capa_info_ie(IN mlan_private *pmpriv, OUT t_u8 **pptlv_out); +#endif + +mlan_status wlan_cmd_boot_sleep(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, IN t_void *pdata_buf); + +mlan_status wlan_ret_boot_sleep(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf); + +#define BW_20MHZ 0 +#define BW_40MHZ 1 +#define BW_80MHZ 2 +#define BW_160MHZ 3 +int wlan_add_supported_oper_class_ie(IN mlan_private *pmpriv, + OUT t_u8 **pptlv_out, + t_u8 curr_oper_class); +mlan_status wlan_get_curr_oper_class(mlan_private *pmpriv, t_u8 channel, + t_u8 bw, t_u8 *oper_class); +mlan_status wlan_check_operclass_validation(mlan_private *pmpriv, t_u8 channel, + t_u8 oper_class); +mlan_status wlan_misc_ioctl_operclass_validation(IN pmlan_adapter pmadapter, + IN mlan_ioctl_req *pioctl_req); +mlan_status wlan_misc_ioctl_oper_class(IN pmlan_adapter pmadapter, + IN mlan_ioctl_req *pioctl_req); + +t_u8 wlan_adjust_data_rate(mlan_private *priv, t_u8 rx_rate, t_u8 rate_info); +t_u8 wlan_adjust_antenna(pmlan_private priv, RxPD *prx_pd); + +mlan_status wlan_misc_otp_user_data(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req); + +mlan_status wlan_misc_ioctl_txcontrol(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req); + +mlan_status wlan_misc_ioctl_region(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req); + +#ifdef RX_PACKET_COALESCE +mlan_status + +wlan_misc_ioctl_rx_pkt_coalesce_config(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req); +#endif + +mlan_status wlan_misc_ioctl_multi_chan_config(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req); + +mlan_status wlan_cmd_multi_chan_cfg(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, IN t_void *pdata_buf); + +mlan_status wlan_ret_multi_chan_cfg(IN pmlan_private pmpriv, + const IN HostCmd_DS_COMMAND *resp, + OUT mlan_ioctl_req *pioctl_buf); + +mlan_status wlan_misc_ioctl_multi_chan_policy(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req); + +mlan_status wlan_cmd_multi_chan_policy(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, + IN t_void *pdata_buf); + +mlan_status wlan_ret_multi_chan_policy(IN pmlan_private pmpriv, + const IN HostCmd_DS_COMMAND *resp, + OUT mlan_ioctl_req *pioctl_buf); + +mlan_status wlan_misc_ioctl_drcs_config(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req); + +mlan_status wlan_cmd_drcs_cfg(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, IN t_void *pdata_buf); + +mlan_status wlan_ret_drcs_cfg(IN pmlan_private pmpriv, + const IN HostCmd_DS_COMMAND *resp, + OUT mlan_ioctl_req *pioctl_buf); + +void wlan_bt_coex_wlan_param_update_event(pmlan_private priv, + pmlan_buffer pevent); + +mlan_status wlan_misc_ioctl_dfs_repeater_cfg(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req); + +t_bool wlan_check_interface_active(mlan_adapter *pmadapter); + +mlan_status wlan_misc_ioctl_coalesce_cfg(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req); + +mlan_status wlan_misc_ioctl_pmic_configure(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req); + +mlan_status wlan_misc_ioctl_cwmode_ctrl(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req); + +mlan_status wlan_misc_ioctl_ind_rst_cfg(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req); +mlan_status wlan_cmd_ind_rst_cfg(IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, IN t_void *pdata_buf); +mlan_status wlan_ret_ind_rst_cfg(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf); + +mlan_status wlan_cmd_802_11_supplicant_pmk(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, + IN t_void *pdata_buf); + +mlan_status wlan_ret_802_11_supplicant_pmk(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf); +mlan_status wlan_sec_ioctl_passphrase(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req); + +mlan_status wlan_misc_ioctl_get_tsf(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req); +void wlan_add_fw_cfp_tables(pmlan_private pmpriv, t_u8 *buf, t_u16 buf_left); + +void wlan_free_fw_cfp_tables(mlan_adapter *pmadapter); + +mlan_status wlan_misc_chan_reg_cfg(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req); + +mlan_status wlan_get_cfpinfo(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req); + +mlan_status wlan_cmd_get_tsf(pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, IN t_u16 cmd_action); +mlan_status wlan_ret_get_tsf(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf); + +t_u8 wlan_ft_akm_is_used(mlan_private *pmpriv, t_u8 *rsn_ie); + +mlan_status wlan_clear_fw_roaming_pmk(IN pmlan_private pmpriv); + +mlan_status wlan_cmd_ps_inactivity_timeout(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, + IN t_void *pdata_buf); + +mlan_status wlan_cmd_host_clock_cfg(IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, IN t_void *pdata_buf); +mlan_status wlan_ret_host_clock_cfg(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf); + +t_u8 wlan_ieee_rateid_to_mrvl_rateid(IN mlan_private *priv, + IN t_u16 IeeeMacRate, IN t_u8 *dst_mac); +t_u8 wlan_mrvl_rateid_to_ieee_rateid(IN t_u8 rate); + +mlan_status wlan_ret_chan_region_cfg(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf); +#if defined(SYSKT_MULTI) && defined(OOB_WAKEUP) || defined(SUSPEND_SDIO_PULL_DOWN) +mlan_status wlan_cmd_sdio_pull_ctl(pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action); +#endif + +mlan_status wlan_misc_ioctl_fw_dump_event(IN pmlan_adapter pmadapter, + IN mlan_ioctl_req *pioctl_req); +mlan_status wlan_cmd_fw_dump_event(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, IN t_void *pdata_buf); + +mlan_status wlan_misc_bootsleep(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req); + +/** + * @brief RA based queueing + * + * @param priv A pointer to mlan_private structure + * + * @return MTRUE or MFALSE + */ +static INLINE t_u8 +queuing_ra_based(pmlan_private priv) +{ + /* + * Currently we assume if we are in Infra, then DA=RA. This might not be + * true in the future + */ + if ((priv->bss_mode == MLAN_BSS_MODE_INFRA) && + (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA)) + return MFALSE; + + return MTRUE; +} + +/** + * @brief Copy Rates + * + * @param dest A pointer to Dest Buf + * @param pos The position for copy + * @param src A pointer to Src Buf + * @param len The len of Src Buf + * + * @return Number of Rates copied + */ +static INLINE t_u32 +wlan_copy_rates(t_u8 *dest, t_u32 pos, t_u8 *src, int len) +{ + int i; + + for (i = 0; i < len && src[i]; i++, pos++) { + if (pos >= sizeof(WLAN_802_11_RATES)) + break; + dest[pos] = src[i]; + } + + return pos; +} + +/** + * @brief strlen + * + * @param str A pointer to string + * + * @return Length of string + */ +static INLINE t_u32 +wlan_strlen(const char *str) +{ + t_u32 i; + + for (i = 0; str[i] != 0; i++) ; + + return i; +} + +/** + * @brief iscdigit + * + * @param chr A char + * + * @return Non zero if chr is a hex, else 0 + */ +static INLINE t_u32 +wlan_isxdigit(t_u8 chr) +{ + return (chr <= 'f' && chr >= 'a') || (chr <= 'F' && chr >= 'A') || + (chr <= '9' && chr >= '0'); +} + +/** + * @brief isspace + * + * @param A chr + * + * @return Non zero if chr is space etc, else 0 + */ +static INLINE t_u32 +wlan_isspace(t_u8 chr) +{ + return chr <= ' ' && (chr == ' ' || (chr <= 13 && chr >= 9)); +} + +/** delay unit */ +typedef enum _delay_unit { + USEC, + MSEC, + SEC, +} t_delay_unit; + +/** delay function */ +t_void wlan_delay_func(mlan_adapter *pmadapter, t_u32 delay, t_delay_unit u); + +/** delay function wrapper */ +#define wlan_delay(p, n) wlan_delay_func(p, n, SEC) +/** delay function wrapper */ +#define wlan_mdelay(p, n) wlan_delay_func(p, n, MSEC) +/** delay function wrapper */ +#define wlan_udelay(p, n) wlan_delay_func(p, n, USEC) + +/** + * @brief This function check if there are pending cmd + * in cmd pending Q + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return MTRUE -- cmd pending + * MFALSE -- no pending cmd + */ +static INLINE int +wlan_is_cmd_pending(mlan_adapter *pmadapter) +{ + int ret; + cmd_ctrl_node *pcmd_node = MNULL; + wlan_request_cmd_lock(pmadapter); + pcmd_node = + (cmd_ctrl_node *)util_peek_list(pmadapter->pmoal_handle, + &pmadapter->cmd_pending_q, + MNULL, MNULL); + if (pcmd_node) + ret = MTRUE; + else + ret = MFALSE; + wlan_release_cmd_lock(pmadapter); + return ret; +} + +/** Get BSS number from priv */ +#define GET_BSS_NUM(priv) ((priv)->bss_num) + +/** + * @brief This function returns priv based on the BSS num and BSS type + * + * @param pmadapter A pointer to mlan_adapter + * @param bss_num BSS number + * @param bss_type BSS type + * + * @return Pointer to mlan_private + */ +static INLINE mlan_private * +wlan_get_priv_by_id(mlan_adapter *pmadapter, t_u32 bss_num, t_u32 bss_type) +{ + int i; + + for (i = 0; i < MIN(pmadapter->priv_num, MLAN_MAX_BSS_NUM); i++) { + if (pmadapter->priv[i]) { + if ((pmadapter->priv[i]->bss_num == bss_num) && + (pmadapter->priv[i]->bss_type == bss_type)) + return pmadapter->priv[i]; + } + } + return MNULL; +} + +/** + * @brief This function returns first available priv + * based on the BSS role + * + * @param pmadapter A pointer to mlan_adapter + * @param bss_role BSS role or MLAN_BSS_ROLE_ANY + * + * @return Pointer to mlan_private + */ +static INLINE mlan_private * +wlan_get_priv(mlan_adapter *pmadapter, mlan_bss_role bss_role) +{ + int i; + + for (i = 0; i < MIN(pmadapter->priv_num, MLAN_MAX_BSS_NUM); i++) { + if (pmadapter->priv[i]) { + if (bss_role == MLAN_BSS_ROLE_ANY || + GET_BSS_ROLE(pmadapter->priv[i]) == bss_role) + return pmadapter->priv[i]; + } + } + return MNULL; +} + +/** + * @brief This function counts the number of occurences for a certain + * condition among privs. Which privs are checked can be configured + * via a second condition. + * + * @param pmadapter A pointer to mlan_adapter + * @param count_cond Function pointer to condition to count on privs + * @param check_cond Function pointer to condition to decide whether priv + * should be counted or not. Use MNULL to check all privs. + * + * @return Count of privs where count_cond returned MTRUE. + */ +static int INLINE +wlan_count_priv_cond(mlan_adapter *pmadapter, + t_bool (*count_cond) (IN pmlan_private pmpriv), + t_bool (*check_cond) (IN pmlan_private pmpriv)) +{ + pmlan_private pmpriv; + int count = 0; + int i; + + if (pmadapter == MNULL || count_cond == MNULL) + return 0; + + for (i = 0; i < pmadapter->priv_num; i++) { + pmpriv = pmadapter->priv[i]; + if (pmpriv) { + if ((check_cond == MNULL) || + (check_cond && check_cond(pmpriv))) { + if (count_cond(pmpriv)) + count++; + } + } + } + + return count; +} + +/** + * @brief This function runs a procedure on each priv. + * Which privs it is run on can be configured via a condition. + * + * @param pmadapter A pointer to mlan_adapter + * @param operation Function pointer to produedure to operate on priv + * @param check_cond Function pointer to condition to decide whether priv + * operated on or not. Use MNULL to run on all privs. + * + * @return Number of privs that operation was run on. + */ +static int INLINE +wlan_do_task_on_privs(mlan_adapter *pmadapter, + t_void (*operation) (IN pmlan_private pmpriv), + t_bool (*check_cond) (IN pmlan_private pmpriv)) +{ + pmlan_private pmpriv; + int count = 0; + int i; + + if (pmadapter == MNULL || operation == MNULL) + return 0; + + for (i = 0; i < pmadapter->priv_num; i++) { + pmpriv = pmadapter->priv[i]; + if (pmpriv) { + if ((check_cond == MNULL) || + (check_cond && check_cond(pmpriv))) { + operation(pmpriv); + count++; + } + } + } + + return count; +} + +/** + * @brief This function builds a list of privs that test for a condition + * This is useful if you need to do a number of operations on the same set + * of privs. For one-off tasks, the above two functions might be better. + * + * @param pmadapter A pointer to mlan_adapter + * @param check_cond Function pointer to condition to decide whether priv + * should be placed in the list. + * @param ppriv_list Output param. Externally supplied array of mlan_private* + * to hold priv's that test positive with check_cond. + * Array size should be at least pmadapter->priv_num. + * + * @return Number of privs in ppriv_list + * + * @sa wlan_count_priv_cond + */ +static int INLINE +wlan_get_privs_by_cond(mlan_adapter *pmadapter, + t_bool (*check_cond) (IN pmlan_private pmpriv), + mlan_private **ppriv_list) +{ + pmlan_private pmpriv; + int count = 0; + int i; + + if (pmadapter == MNULL || check_cond == MNULL || ppriv_list == MNULL) + return 0; + + for (i = 0; i < pmadapter->priv_num; i++) { + pmpriv = pmadapter->priv[i]; + if (pmpriv) { + if (check_cond(pmpriv)) + ppriv_list[count++] = pmpriv; + } + } + + return count; +} + +/** + * @brief This function builds a list of privs that test against two conditions + * This is useful if you need to do a number of operations on the same set + * of privs. Can choose whether both conditions (AND) or either condition (OR) + * is required. + * + * @param pmadapter A pointer to mlan_adapter + * @param check_cond Function pointer to condition to decide whether priv + * should be placed in the list. + * @param check_cond_2 Function pointer to second condition to check. + * @param and_conditions If MTRUE, both conditions must be met (AND), + * else either condition can be met (OR). + * @param ppriv_list Output param. Externally supplied array of mlan_private* + * to hold priv's that test positive with check_cond. + * Array size should be at least pmadapter->priv_num. + * + * @return Number of privs in ppriv_list + * + * @sa wlan_count_priv_cond, wlan_get_privs_by_cond + */ +static int INLINE +wlan_get_privs_by_two_cond(mlan_adapter *pmadapter, + t_bool (*check_cond) (IN pmlan_private pmpriv), + t_bool (*check_cond_2) (IN pmlan_private pmpriv), + t_bool and_conditions, mlan_private **ppriv_list) +{ + pmlan_private pmpriv; + int count = 0; + int i; + + if (pmadapter == MNULL || check_cond == MNULL || + check_cond_2 == MNULL || ppriv_list == MNULL) + return 0; + + for (i = 0; i < pmadapter->priv_num; i++) { + pmpriv = pmadapter->priv[i]; + if (pmpriv) { + if (and_conditions) { + if (check_cond(pmpriv) && check_cond_2(pmpriv)) + ppriv_list[count++] = pmpriv; + } else { + if (check_cond(pmpriv) || check_cond_2(pmpriv)) + ppriv_list[count++] = pmpriv; + } + } + } + + return count; +} +#endif /* !_MLAN_MAIN_H_ */ diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_meas.c b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_meas.c new file mode 100644 index 000000000000..f118f3145f43 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_meas.c @@ -0,0 +1,463 @@ +/** + * @file mlan_meas.c + * + * @brief Implementation of measurement interface code with the app/firmware + * + * Driver implementation for sending and retrieving measurement requests + * and responses. + * + * Current use is limited to 802.11h. + * + * Requires use of the following preprocessor define: + * - ENABLE_MEAS + * + * Copyright (C) 2008-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/************************************************************* +Change Log: + 03/24/2009: initial version +************************************************************/ + +#include "mlan.h" +#include "mlan_join.h" +#include "mlan_util.h" +#include "mlan_fw.h" +#include "mlan_main.h" +#include "mlan_ioctl.h" +#include "mlan_meas.h" + +/** Default measurement duration when not provided by the application */ +#define WLAN_MEAS_DEFAULT_MEAS_DURATION 1000U /* TUs */ + +#ifdef DEBUG_LEVEL2 +/** String descriptions of the different measurement enums. Debug display */ +static const char *meas_type_str[WLAN_MEAS_NUM_TYPES] = { + "basic", +}; + +/******************************************************** + Local Functions +********************************************************/ + +/** + * @brief Retrieve the measurement string representation of a meas_type enum + * Used for debug display only + * + * @param meas_type Measurement type enumeration input for string lookup + * + * @return Constant string representing measurement type + */ +static const char * +wlan_meas_get_meas_type_str(MeasType_t meas_type) +{ + if (meas_type <= WLAN_MEAS_11H_MAX_TYPE) + return meas_type_str[meas_type]; + + return "Invld"; +} +#endif + +/** + * @brief Debug print display of the input measurement request + * + * @param pmeas_req Pointer to the measurement request to display + * + * @return N/A + */ +static + void +wlan_meas_dump_meas_req(const HostCmd_DS_MEASUREMENT_REQUEST *pmeas_req) +{ + ENTER(); + + PRINTM(MINFO, "Meas: Req: ------------------------------\n"); + + PRINTM(MINFO, "Meas: Req: mac_addr: " MACSTR "\n", + MAC2STR(pmeas_req->mac_addr)); + + PRINTM(MINFO, "Meas: Req: dlgTkn: %d\n", pmeas_req->dialog_token); + PRINTM(MINFO, "Meas: Req: mode: dm[%c] rpt[%c] req[%c]\n", + pmeas_req->req_mode.duration_mandatory ? 'X' : ' ', + pmeas_req->req_mode.report ? 'X' : ' ', + pmeas_req->req_mode.request ? 'X' : ' '); + PRINTM(MINFO, "Meas: Req: : en[%c] par[%c]\n", + pmeas_req->req_mode.enable ? 'X' : ' ', + pmeas_req->req_mode.parallel ? 'X' : ' '); +#ifdef DEBUG_LEVEL2 + PRINTM(MINFO, "Meas: Req: measTyp: %s\n", + wlan_meas_get_meas_type_str(pmeas_req->meas_type)); +#endif + + switch (pmeas_req->meas_type) { + case WLAN_MEAS_BASIC: + /* Lazy cheat, fields of bas, cca, rpi union match on the request */ + PRINTM(MINFO, "Meas: Req: chan: %u\n", + pmeas_req->req.basic.channel); + PRINTM(MINFO, "Meas: Req: strt: %llu\n", + wlan_le64_to_cpu(pmeas_req->req.basic.start_time)); + PRINTM(MINFO, "Meas: Req: dur: %u\n", + wlan_le16_to_cpu(pmeas_req->req.basic.duration)); + break; + default: + PRINTM(MINFO, "Meas: Req: \n"); + break; + } + + PRINTM(MINFO, "Meas: Req: ------------------------------\n"); + LEAVE(); +} + +/** + * @brief Debug print display of the input measurement report + * + * @param pmeas_rpt Pointer to measurement report to display + * + * @return N/A + */ +static + void +wlan_meas_dump_meas_rpt(const HostCmd_DS_MEASUREMENT_REPORT *pmeas_rpt) +{ + MeasType_t type; + ENTER(); + + PRINTM(MINFO, "Meas: Rpt: ------------------------------\n"); + PRINTM(MINFO, "Meas: Rpt: mac_addr: " MACSTR "\n", + MAC2STR(pmeas_rpt->mac_addr)); + + PRINTM(MINFO, "Meas: Rpt: dlgTkn: %d\n", pmeas_rpt->dialog_token); + + PRINTM(MINFO, "Meas: Rpt: rptMode: (%x): Rfs[%c] ICp[%c] Lt[%c]\n", + *(t_u8 *)&pmeas_rpt->rpt_mode, + pmeas_rpt->rpt_mode.refused ? 'X' : ' ', + pmeas_rpt->rpt_mode.incapable ? 'X' : ' ', + pmeas_rpt->rpt_mode.late ? 'X' : ' '); +#ifdef DEBUG_LEVEL2 + PRINTM(MINFO, "Meas: Rpt: measTyp: %s\n", + wlan_meas_get_meas_type_str(pmeas_rpt->meas_type)); +#endif + + type = wlan_le32_to_cpu(pmeas_rpt->meas_type); + switch (type) { + case WLAN_MEAS_BASIC: + PRINTM(MINFO, "Meas: Rpt: chan: %u\n", + pmeas_rpt->rpt.basic.channel); + PRINTM(MINFO, "Meas: Rpt: strt: %llu\n", + wlan_le64_to_cpu(pmeas_rpt->rpt.basic.start_time)); + PRINTM(MINFO, "Meas: Rpt: dur: %u\n", + wlan_le16_to_cpu(pmeas_rpt->rpt.basic.duration)); + PRINTM(MINFO, "Meas: Rpt: bas: (%x): unmsd[%c], radar[%c]\n", + *(t_u8 *)&(pmeas_rpt->rpt.basic.map), + pmeas_rpt->rpt.basic.map.unmeasured ? 'X' : ' ', + pmeas_rpt->rpt.basic.map.radar ? 'X' : ' '); + PRINTM(MINFO, "Meas: Rpt: bas: unidSig[%c] ofdm[%c] bss[%c]\n", + pmeas_rpt->rpt.basic.map.unidentified_sig ? 'X' : ' ', + pmeas_rpt->rpt.basic.map.ofdm_preamble ? 'X' : ' ', + pmeas_rpt->rpt.basic.map.bss ? 'X' : ' '); + break; + default: + PRINTM(MINFO, "Meas: Rpt: \n"); + break; + } + + PRINTM(MINFO, "Meas: Rpt: ------------------------------\n"); + LEAVE(); +} + +/** + * @brief Retrieve a measurement report from the firmware + * + * Callback from command processing when a measurement report is received + * from the firmware. Perform the following when a report is received: + * + * -# Debug displays the report if compiled with the appropriate flags + * -# If we are pending on a specific measurement report token, and it + * matches the received report's token, store the report and wake up + * any pending threads + * + * @param pmpriv Private driver information structure + * @param resp HostCmd_DS_COMMAND struct returned from the firmware command + * passing a HostCmd_DS_MEASUREMENT_REPORT structure. + * + * @return MLAN_STATUS_SUCCESS + */ +static int +wlan_meas_cmdresp_get_report(mlan_private *pmpriv, + const HostCmd_DS_COMMAND *resp) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + const HostCmd_DS_MEASUREMENT_REPORT *pmeas_rpt = &resp->params.meas_rpt; + + ENTER(); + + PRINTM(MINFO, "Meas: Rpt: %#x-%u, Seq=%u, Ret=%u\n", + resp->command, resp->size, resp->seq_num, resp->result); + + /* Debug displays the measurement report */ + wlan_meas_dump_meas_rpt(pmeas_rpt); + + /* + * Check if we are pending on a measurement report and it matches + * the dialog token of the received report: + */ + if (pmadapter->state_meas.meas_rpt_pend_on + && pmadapter->state_meas.meas_rpt_pend_on == + pmeas_rpt->dialog_token) { + PRINTM(MINFO, "Meas: Rpt: RCV'd Pend on meas #%d\n", + pmadapter->state_meas.meas_rpt_pend_on); + + /* Clear the pending report indicator */ + pmadapter->state_meas.meas_rpt_pend_on = 0; + + /* Copy the received report into the measurement state for retrieval */ + memcpy(pmadapter, &pmadapter->state_meas.meas_rpt_returned, + pmeas_rpt, + sizeof(pmadapter->state_meas.meas_rpt_returned)); + + /* + * Wake up any threads pending on the wait queue + */ + wlan_recv_event(pmpriv, MLAN_EVENT_ID_DRV_MEAS_REPORT, MNULL); + } + + LEAVE(); + + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Prepare CMD_MEASURMENT_REPORT firmware command + * + * @param pmpriv Private driver information structure + * @param pcmd_ptr Output parameter: Pointer to the command being prepared + * for the firmware + * @param pinfo_buf HostCmd_DS_MEASUREMENT_REQUEST passed as void data block + * + * @return MLAN_STATUS_SUCCESS + */ +static int +wlan_meas_cmd_request(mlan_private *pmpriv, + HostCmd_DS_COMMAND *pcmd_ptr, const void *pinfo_buf) +{ + const HostCmd_DS_MEASUREMENT_REQUEST *pmeas_req = + (HostCmd_DS_MEASUREMENT_REQUEST *)pinfo_buf; + + ENTER(); + + pcmd_ptr->command = HostCmd_CMD_MEASUREMENT_REQUEST; + pcmd_ptr->size = sizeof(HostCmd_DS_MEASUREMENT_REQUEST) + S_DS_GEN; + + memcpy(pmpriv->adapter, &pcmd_ptr->params.meas_req, pmeas_req, + sizeof(pcmd_ptr->params.meas_req)); + + PRINTM(MINFO, "Meas: Req: %#x-%u, Seq=%u, Ret=%u\n", + pcmd_ptr->command, pcmd_ptr->size, pcmd_ptr->seq_num, + pcmd_ptr->result); + + wlan_meas_dump_meas_req(pmeas_req); + + LEAVE(); + + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Retrieve a measurement report from the firmware + * + * The firmware will send a EVENT_MEAS_REPORT_RDY event when it + * completes or receives a measurement report. The event response + * handler will then start a HostCmd_CMD_MEASUREMENT_REPORT firmware command + * which gets completed for transmission to the firmware in this routine. + * + * @param pmpriv Private driver information structure + * @param pcmd_ptr Output parameter: Pointer to the command being prepared + * for the firmware + * + * @return MLAN_STATUS_SUCCESS + */ +static int +wlan_meas_cmd_get_report(mlan_private *pmpriv, HostCmd_DS_COMMAND *pcmd_ptr) +{ + ENTER(); + + pcmd_ptr->command = HostCmd_CMD_MEASUREMENT_REPORT; + pcmd_ptr->size = sizeof(HostCmd_DS_MEASUREMENT_REPORT) + S_DS_GEN; + + memset(pmpriv->adapter, &pcmd_ptr->params.meas_rpt, 0x00, + sizeof(pcmd_ptr->params.meas_rpt)); + + /* + * Set the meas_rpt.mac_addr to our mac address to get a meas report, + * setting the mac to another STA address instructs the firmware + * to transmit this measurement report frame instead + */ + memcpy(pmpriv->adapter, pcmd_ptr->params.meas_rpt.mac_addr, + pmpriv->curr_addr, sizeof(pcmd_ptr->params.meas_rpt.mac_addr)); + + LEAVE(); + + return MLAN_STATUS_SUCCESS; +} + +/******************************************************** + Global functions +********************************************************/ + +/** + * @brief Send the input measurement request to the firmware. + * + * If the dialog token in the measurement request is set to 0, the function + * will use an local static auto-incremented token in the measurement + * request. This ensures the dialog token is always set. + * + * If wait_for_resp_timeout is set, the function will block its return on + * a timeout or returned measurement report that matches the requests + * dialog token. + * + * @param pmpriv Private driver information structure + * @param pmeas_req Pointer to the measurement request to send + * @param wait_for_resp_timeout Timeout value of the measurement request + * in ms. + * @param pioctl_req Pointer to IOCTL request buffer + * @param pmeas_rpt Output parameter: Pointer for the resulting + * measurement report + * + * @return + * - 0 for success + * - -ETIMEDOUT if the measurement report does not return before + * the timeout expires + * - Error return from wlan_prepare_cmd routine otherwise + */ +int +wlan_meas_util_send_req(mlan_private *pmpriv, + HostCmd_DS_MEASUREMENT_REQUEST *pmeas_req, + t_u32 wait_for_resp_timeout, pmlan_ioctl_req pioctl_req, + HostCmd_DS_MEASUREMENT_REPORT *pmeas_rpt) +{ + static t_u8 auto_dialog_tok; + wlan_meas_state_t *pmeas_state = &pmpriv->adapter->state_meas; + int ret; + + ENTER(); + + /* If dialogTok was set to 0 or not provided, autoset */ + pmeas_req->dialog_token = (pmeas_req->dialog_token ? + pmeas_req->dialog_token : ++auto_dialog_tok); + + /* Check for rollover of the dialog token. Avoid using 0 as a token */ + pmeas_req->dialog_token = (pmeas_req->dialog_token ? + pmeas_req->dialog_token : 1); + + /* + * If the request is to pend waiting for the result, set the dialog token + * of this measurement request in the state structure. The measurement + * report handling routines can then check the incoming measurement + * reports for a match with this dialog token. + */ + if (wait_for_resp_timeout) { + pmeas_state->meas_rpt_pend_on = pmeas_req->dialog_token; + PRINTM(MINFO, "Meas: Req: START Pend on meas #%d\n", + pmeas_req->dialog_token); + } + + /* Send the measurement request to the firmware */ + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_MEASUREMENT_REQUEST, + HostCmd_ACT_GEN_SET, + 0, (t_void *)pioctl_req, (void *)pmeas_req); + + LEAVE(); + return ret; +} + +/** + * @brief Prepare the HostCmd_DS_Command structure for a measurement command. + * + * Use the Command field to determine if the command being set up is for + * 11h and call one of the local command handlers accordingly for: + * + * - HostCmd_CMD_MEASUREMENT_REQUEST + * - HostCmd_CMD_MEASUREMENT_REPORT + * + * @param pmpriv Private driver information structure + * @param pcmd_ptr Output parameter: Pointer to the command being prepared + * for the firmware + * @param pinfo_buf Void buffer passthrough with data necessary for a + * specific command type + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + * + */ +int +wlan_meas_cmd_process(mlan_private *pmpriv, + HostCmd_DS_COMMAND *pcmd_ptr, const void *pinfo_buf) +{ + int ret = MLAN_STATUS_SUCCESS; + + ENTER(); + switch (pcmd_ptr->command) { + case HostCmd_CMD_MEASUREMENT_REQUEST: + ret = wlan_meas_cmd_request(pmpriv, pcmd_ptr, pinfo_buf); + break; + case HostCmd_CMD_MEASUREMENT_REPORT: + ret = wlan_meas_cmd_get_report(pmpriv, pcmd_ptr); + break; + default: + ret = MLAN_STATUS_FAILURE; + } + + pcmd_ptr->command = wlan_cpu_to_le16(pcmd_ptr->command); + pcmd_ptr->size = wlan_cpu_to_le16(pcmd_ptr->size); + LEAVE(); + return ret; +} + +/** + * @brief Handle the command response from the firmware for a measurement + * command + * + * Use the Command field to determine if the command response being + * is for meas. Call the local command response handler accordingly for: + * + * - HostCmd_CMD_802_MEASUREMENT_REQUEST + * - HostCmd_CMD_802_MEASUREMENT_REPORT + * + * @param pmpriv Private driver information structure + * @param resp HostCmd_DS_COMMAND struct returned from the firmware command + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +int +wlan_meas_cmdresp_process(mlan_private *pmpriv, const HostCmd_DS_COMMAND *resp) +{ + int ret = MLAN_STATUS_SUCCESS; + + ENTER(); + switch (resp->command) { + case HostCmd_CMD_MEASUREMENT_REQUEST: + PRINTM(MINFO, "Meas: Req Resp: Sz=%u, Seq=%u, Ret=%u\n", + resp->size, resp->seq_num, resp->result); + break; + case HostCmd_CMD_MEASUREMENT_REPORT: + ret = wlan_meas_cmdresp_get_report(pmpriv, resp); + break; + default: + ret = MLAN_STATUS_FAILURE; + } + + LEAVE(); + return ret; +} diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_meas.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_meas.h new file mode 100644 index 000000000000..b25f91560471 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_meas.h @@ -0,0 +1,55 @@ +/** + * @file mlan_meas.h + * + * @brief Interface for the measurement module implemented in mlan_meas.c + * + * Driver interface functions and type declarations for the measurement module + * implemented in mlan_meas.c + * + * @sa mlan_meas.c + * + * Copyright (C) 2008-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +/************************************************************* +Change Log: + 03/25/2009: initial version +************************************************************/ + +#ifndef _MLAN_MEAS_H_ +#define _MLAN_MEAS_H_ + +#include "mlan_fw.h" + +/* Send a given measurement request to the firmware, report back the result */ +extern int + +wlan_meas_util_send_req(mlan_private *pmpriv, + HostCmd_DS_MEASUREMENT_REQUEST *pmeas_req, + t_u32 wait_for_resp_timeout, pmlan_ioctl_req pioctl_req, + HostCmd_DS_MEASUREMENT_REPORT *pmeas_rpt); + +/* Setup a measurement command before it is sent to the firmware */ +extern int wlan_meas_cmd_process(mlan_private *pmpriv, + HostCmd_DS_COMMAND *pcmd_ptr, + const t_void *pinfo_buf); + +/* Handle a given measurement command response from the firmware */ +extern int wlan_meas_cmdresp_process(mlan_private *pmpriv, + const HostCmd_DS_COMMAND *resp); + +#endif /* _MLAN_MEAS_H_ */ diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_misc.c b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_misc.c new file mode 100644 index 000000000000..51d37c654141 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_misc.c @@ -0,0 +1,4962 @@ +/** + * @file mlan_misc.c + * + * @brief This file include miscellaneous functions for MLAN module + * + * Copyright (C) 2009-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/************************************************************* +Change Log: + 05/11/2009: initial version +************************************************************/ +#include "mlan.h" +#ifdef STA_SUPPORT +#include "mlan_join.h" +#endif /* STA_SUPPORT */ +#include "mlan_util.h" +#include "mlan_fw.h" +#include "mlan_main.h" +#include "mlan_wmm.h" +#include "mlan_11n.h" +#include "mlan_sdio.h" +#ifdef UAP_SUPPORT +#include "mlan_uap.h" +#endif +#ifdef DRV_EMBEDDED_AUTHENTICATOR +#include "authenticator_api.h" +#endif +/******************************************************** + Local Variables +********************************************************/ + +/******************************************************** + Global Variables +********************************************************/ +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) +extern mlan_operations *mlan_ops[]; +#endif +extern t_u8 ac_to_tid[4][2]; + +/******************************************************** + Local Functions +********************************************************/ + +/** Custom IE auto index and mask */ +#define MLAN_CUSTOM_IE_AUTO_IDX_MASK 0xffff +/** Custom IE mask for delete operation */ +#define MLAN_CUSTOM_IE_DELETE_MASK 0 +/** Custom IE mask for create new index */ +#define MLAN_CUSTOM_IE_NEW_MASK 0x8000 +/** Custom IE header size */ +#define MLAN_CUSTOM_IE_HDR_SIZE (sizeof(custom_ie)-MAX_IE_SIZE) + +/** + * @brief Check if current custom IE index is used on other interfaces. + * + * @param pmpriv A pointer to mlan_private structure + * @param idx index to check for in use + * + * @return MLAN_STATUS_SUCCESS --unused, otherwise used. + */ +static mlan_status +wlan_is_custom_ie_index_unused(IN pmlan_private pmpriv, IN t_u16 idx) +{ + t_u8 i = 0; + pmlan_adapter pmadapter = pmpriv->adapter; + pmlan_private priv; + ENTER(); + + for (i = 0; i < pmadapter->priv_num; i++) { + priv = pmadapter->priv[i]; + /* Check for other interfaces only */ + if (priv && priv->bss_index != pmpriv->bss_index) { + + if (priv->mgmt_ie[idx].mgmt_subtype_mask && + priv->mgmt_ie[idx].ie_length) { + /* used entry found */ + LEAVE(); + return MLAN_STATUS_FAILURE; + } + } + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Get the custom IE index + * + * @param pmpriv A pointer to mlan_private structure + * @param pioctl_req A pointer to ioctl request buffer + * @param mask mask value for which the index to be returned + * @param ie_data a pointer to custom_ie structure + * @param idx will hold the computed index + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +static mlan_status +wlan_custom_ioctl_get_autoidx(IN pmlan_private pmpriv, + IN pmlan_ioctl_req pioctl_req, + IN t_u16 mask, + IN custom_ie *ie_data, OUT t_u16 *idx) +{ + t_u16 index = 0, insert = MFALSE; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + /* Determine the index where the IE needs to be inserted */ + while (!insert) { + while (index < + MIN(pmpriv->adapter->max_mgmt_ie_index, + MAX_MGMT_IE_INDEX)) { + if (pmpriv->mgmt_ie[index].mgmt_subtype_mask == + MLAN_CUSTOM_IE_AUTO_IDX_MASK) { + index++; + continue; + } + if (pmpriv->mgmt_ie[index].mgmt_subtype_mask == mask) { + /* Duplicate IE should be avoided */ + if (pmpriv->mgmt_ie[index].ie_length) { + if (!memcmp + (pmpriv->adapter, + pmpriv->mgmt_ie[index].ie_buffer, + ie_data->ie_buffer, + pmpriv->mgmt_ie[index]. + ie_length)) { + PRINTM(MINFO, + "IE with the same mask exists at index %d mask=0x%x\n", + index, mask); + *idx = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + goto done; + } + } + /* Check if enough space is available */ + if (pmpriv->mgmt_ie[index].ie_length + + ie_data->ie_length > MAX_IE_SIZE) { + index++; + continue; + } + insert = MTRUE; + break; + } + index++; + } + if (!insert) { + for (index = 0; + index < MIN(pmpriv->adapter->max_mgmt_ie_index, + MAX_MGMT_IE_INDEX); index++) { + if (pmpriv->mgmt_ie[index].ie_length == 0) { + /* + * Check if this index is in use + * by other interface If yes, + * move ahead to next index + */ + if (MLAN_STATUS_SUCCESS == + wlan_is_custom_ie_index_unused + (pmpriv, index)) { + insert = MTRUE; + break; + } else { + PRINTM(MINFO, + "Skipping IE index %d in use.\n", + index); + } + } + } + } + if (index == pmpriv->adapter->max_mgmt_ie_index && !insert) { + PRINTM(MERROR, "Failed to Set the IE buffer\n"); + if (pioctl_req) + pioctl_req->status_code = MLAN_ERROR_IOCTL_FAIL; + ret = MLAN_STATUS_FAILURE; + goto done; + } + } + + *idx = index; +done: + LEAVE(); + return ret; +} + +/** + * @brief Delete custom IE + * + * @param pmpriv A pointer to mlan_private structure + * @param pioctl_req A pointer to ioctl request buffer + * @param ie_data a pointer to custom_ie structure + * @param idx index supplied + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ + +static mlan_status +wlan_custom_ioctl_auto_delete(IN pmlan_private pmpriv, + IN pmlan_ioctl_req pioctl_req, + IN custom_ie *ie_data, IN t_u16 idx) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_adapter pmadapter = pmpriv->adapter; + t_u16 index = 0, insert = MFALSE, del_len; + t_u8 del_ie[MAX_IE_SIZE], ie[MAX_IE_SIZE]; + t_s32 cnt, tmp_len = 0; + t_u8 *tmp_ie; + + ENTER(); + memset(pmpriv->adapter, del_ie, 0, MAX_IE_SIZE); + memcpy(pmpriv->adapter, del_ie, ie_data->ie_buffer, + MIN(MAX_IE_SIZE, ie_data->ie_length)); + del_len = MIN(MAX_IE_SIZE - 1, ie_data->ie_length); + + if (MLAN_CUSTOM_IE_AUTO_IDX_MASK == idx) + ie_data->ie_index = 0; + + for (index = 0; + index < MIN(pmadapter->max_mgmt_ie_index, MAX_MGMT_IE_INDEX); + index++) { + if (MLAN_CUSTOM_IE_AUTO_IDX_MASK != idx) + index = idx; + tmp_ie = pmpriv->mgmt_ie[index].ie_buffer; + tmp_len = pmpriv->mgmt_ie[index].ie_length; + cnt = 0; + while (tmp_len) { + if (!memcmp(pmpriv->adapter, tmp_ie, del_ie, del_len)) { + memcpy(pmpriv->adapter, ie, + pmpriv->mgmt_ie[index].ie_buffer, cnt); + if (pmpriv->mgmt_ie[index].ie_length > + (cnt + del_len)) + memcpy(pmpriv->adapter, &ie[cnt], + &pmpriv->mgmt_ie[index]. + ie_buffer[MIN + ((MAX_IE_SIZE - 1), + (cnt + del_len))], + (pmpriv->mgmt_ie[index]. + ie_length - (cnt + del_len))); + memset(pmpriv->adapter, + &pmpriv->mgmt_ie[index].ie_buffer, 0, + sizeof(pmpriv->mgmt_ie[index]. + ie_buffer)); + memcpy(pmpriv->adapter, + &pmpriv->mgmt_ie[index].ie_buffer, ie, + pmpriv->mgmt_ie[index].ie_length - + del_len); + pmpriv->mgmt_ie[index].ie_length -= del_len; + if (MLAN_CUSTOM_IE_AUTO_IDX_MASK == idx) + /* set a bit to indicate caller about update */ + ie_data->ie_index |= + (((t_u16)1) << index); + insert = MTRUE; + tmp_ie = pmpriv->mgmt_ie[index].ie_buffer; + tmp_len = pmpriv->mgmt_ie[index].ie_length; + cnt = 0; + continue; + } + tmp_ie++; + tmp_len--; + cnt++; + } + if (MLAN_CUSTOM_IE_AUTO_IDX_MASK != idx) + break; + } + if (index == pmadapter->max_mgmt_ie_index && !insert) { + PRINTM(MERROR, "Failed to Clear IE buffer\n"); + if (pioctl_req) + pioctl_req->status_code = MLAN_ERROR_IOCTL_FAIL; + ret = MLAN_STATUS_FAILURE; + } + LEAVE(); + return ret; +} + +/******************************************************** + Global Functions +********************************************************/ + +/** + * @brief Get correlated time + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +mlan_status +wlan_misc_get_correlated_time(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + pmlan_private pmpriv = MNULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_misc_cfg *misc = MNULL; + mlan_ds_host_clock *host_clock = MNULL; + + ENTER(); + misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + host_clock = &misc->param.host_clock; + + if (pioctl_req != MNULL) { + pmpriv = pmadapter->priv[pioctl_req->bss_index]; + } else { + PRINTM(MERROR, "MLAN IOCTL information is not present\n"); + ret = MLAN_STATUS_FAILURE; + goto exit; + } + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_HOST_CLOCK_CFG, + HostCmd_ACT_GEN_GET, + 0, (t_void *)pioctl_req, (t_void *)host_clock); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + +exit: + LEAVE(); + return ret; +} + +/** + * @brief send host cmd + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +mlan_status +wlan_misc_ioctl_host_cmd(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_misc_cfg *misc = MNULL; + + ENTER(); + + misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, + 0, + 0, + 0, + (t_void *)pioctl_req, + (t_void *)&misc->param.hostcmd); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Send function init/shutdown command to firmware + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +mlan_status +wlan_misc_ioctl_init_shutdown(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_misc_cfg *misc_cfg = MNULL; + t_u16 cmd; + + ENTER(); + + misc_cfg = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + if (misc_cfg->param.func_init_shutdown == MLAN_FUNC_INIT) + cmd = HostCmd_CMD_FUNC_INIT; + else if (misc_cfg->param.func_init_shutdown == MLAN_FUNC_SHUTDOWN) + cmd = HostCmd_CMD_FUNC_SHUTDOWN; + else { + PRINTM(MERROR, "Unsupported parameter\n"); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_FAILURE; + goto exit; + } + + /* Send command to firmware */ + ret = wlan_prepare_cmd(pmpriv, + cmd, + HostCmd_ACT_GEN_SET, + 0, (t_void *)pioctl_req, MNULL); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + +exit: + LEAVE(); + return ret; +} + +/** + * @brief Get debug information + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success + */ +mlan_status +wlan_get_info_debug_info(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + pmlan_private pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_get_info *info; + mlan_debug_info *debug_info = MNULL; + t_u32 i; + t_u8 *ptid; + + ENTER(); + + info = (mlan_ds_get_info *)pioctl_req->pbuf; + debug_info = (mlan_debug_info *)info->param.debug_info; + + if (pioctl_req->action == MLAN_ACT_GET) { + ptid = ac_to_tid[WMM_AC_BK]; + debug_info->wmm_ac_bk = + pmpriv->wmm.packets_out[ptid[0]] + + pmpriv->wmm.packets_out[ptid[1]]; + ptid = ac_to_tid[WMM_AC_BE]; + debug_info->wmm_ac_be = + pmpriv->wmm.packets_out[ptid[0]] + + pmpriv->wmm.packets_out[ptid[1]]; + ptid = ac_to_tid[WMM_AC_VI]; + debug_info->wmm_ac_vi = + pmpriv->wmm.packets_out[ptid[0]] + + pmpriv->wmm.packets_out[ptid[1]]; + ptid = ac_to_tid[WMM_AC_VO]; + debug_info->wmm_ac_vo = + pmpriv->wmm.packets_out[ptid[0]] + + pmpriv->wmm.packets_out[ptid[1]]; + debug_info->max_tx_buf_size = (t_u32)pmadapter->max_tx_buf_size; + debug_info->tx_buf_size = (t_u32)pmadapter->tx_buf_size; + debug_info->curr_tx_buf_size = + (t_u32)pmadapter->curr_tx_buf_size; + debug_info->rx_tbl_num = + wlan_get_rxreorder_tbl(pmpriv, debug_info->rx_tbl); + debug_info->tx_tbl_num = + wlan_get_txbastream_tbl(pmpriv, debug_info->tx_tbl); + debug_info->ralist_num = + wlan_get_ralist_info(pmpriv, debug_info->ralist); + debug_info->tdls_peer_num = + wlan_get_tdls_list(pmpriv, debug_info->tdls_peer_list); + debug_info->ps_mode = pmadapter->ps_mode; + debug_info->ps_state = pmadapter->ps_state; +#ifdef STA_SUPPORT + debug_info->is_deep_sleep = pmadapter->is_deep_sleep; +#endif /* STA_SUPPORT */ + debug_info->pm_wakeup_card_req = pmadapter->pm_wakeup_card_req; + debug_info->pm_wakeup_fw_try = pmadapter->pm_wakeup_fw_try; + debug_info->pm_wakeup_in_secs = pmadapter->pm_wakeup_in_secs; + debug_info->is_hs_configured = pmadapter->is_hs_configured; + debug_info->hs_activated = pmadapter->hs_activated; + debug_info->pps_uapsd_mode = pmadapter->pps_uapsd_mode; + debug_info->sleep_pd = pmadapter->sleep_period.period; + debug_info->qos_cfg = pmpriv->wmm_qosinfo; + debug_info->tx_lock_flag = pmadapter->tx_lock_flag; + debug_info->port_open = pmpriv->port_open; + debug_info->bypass_pkt_count = pmadapter->bypass_pkt_count; + debug_info->scan_processing = pmadapter->scan_processing; + debug_info->mlan_processing = pmadapter->mlan_processing; + debug_info->main_lock_flag = pmadapter->main_lock_flag; + debug_info->main_process_cnt = pmadapter->main_process_cnt; + debug_info->delay_task_flag = pmadapter->delay_task_flag; + debug_info->num_cmd_host_to_card_failure + = pmadapter->dbg.num_cmd_host_to_card_failure; + debug_info->num_cmd_sleep_cfm_host_to_card_failure + = pmadapter->dbg.num_cmd_sleep_cfm_host_to_card_failure; + debug_info->num_tx_host_to_card_failure + = pmadapter->dbg.num_tx_host_to_card_failure; + debug_info->num_alloc_buffer_failure = + pmadapter->dbg.num_alloc_buffer_failure; + debug_info->num_pkt_dropped = pmadapter->dbg.num_pkt_dropped; + + debug_info->num_cmdevt_card_to_host_failure + = pmadapter->dbg.num_cmdevt_card_to_host_failure; + debug_info->num_rx_card_to_host_failure + = pmadapter->dbg.num_rx_card_to_host_failure; + debug_info->num_int_read_failure = + pmadapter->dbg.num_int_read_failure; + debug_info->last_int_status = pmadapter->dbg.last_int_status; + debug_info->num_event_deauth = pmadapter->dbg.num_event_deauth; + debug_info->num_event_disassoc = + pmadapter->dbg.num_event_disassoc; + debug_info->num_event_link_lost = + pmadapter->dbg.num_event_link_lost; + debug_info->num_cmd_deauth = pmadapter->dbg.num_cmd_deauth; + debug_info->num_cmd_assoc_success = + pmadapter->dbg.num_cmd_assoc_success; + debug_info->num_cmd_assoc_failure = + pmadapter->dbg.num_cmd_assoc_failure; + debug_info->num_tx_timeout = pmadapter->dbg.num_tx_timeout; + debug_info->num_cmd_timeout = pmadapter->num_cmd_timeout; + debug_info->dbg_num_cmd_timeout = + pmadapter->dbg.num_cmd_timeout; + debug_info->timeout_cmd_id = pmadapter->dbg.timeout_cmd_id; + debug_info->timeout_cmd_act = pmadapter->dbg.timeout_cmd_act; + memcpy(pmadapter, debug_info->last_cmd_id, + pmadapter->dbg.last_cmd_id, + sizeof(pmadapter->dbg.last_cmd_id)); + memcpy(pmadapter, debug_info->last_cmd_act, + pmadapter->dbg.last_cmd_act, + sizeof(pmadapter->dbg.last_cmd_act)); + debug_info->last_cmd_index = pmadapter->dbg.last_cmd_index; + memcpy(pmadapter, debug_info->last_cmd_resp_id, + pmadapter->dbg.last_cmd_resp_id, + sizeof(pmadapter->dbg.last_cmd_resp_id)); + debug_info->last_cmd_resp_index = + pmadapter->dbg.last_cmd_resp_index; + memcpy(pmadapter, debug_info->last_event, + pmadapter->dbg.last_event, + sizeof(pmadapter->dbg.last_event)); + debug_info->last_event_index = pmadapter->dbg.last_event_index; + debug_info->num_no_cmd_node = pmadapter->dbg.num_no_cmd_node; + debug_info->pending_cmd = + (pmadapter->curr_cmd) ? pmadapter->dbg. + last_cmd_id[pmadapter->dbg.last_cmd_index] : 0; + debug_info->dnld_cmd_in_secs = pmadapter->dnld_cmd_in_secs; + debug_info->mp_rd_bitmap = pmadapter->mp_rd_bitmap; + debug_info->mp_wr_bitmap = pmadapter->mp_wr_bitmap; + debug_info->curr_rd_port = pmadapter->curr_rd_port; + debug_info->curr_wr_port = pmadapter->curr_wr_port; + debug_info->mp_invalid_update = pmadapter->mp_invalid_update; + debug_info->num_of_irq = pmadapter->num_of_irq; + memcpy(pmadapter, debug_info->mp_update, pmadapter->mp_update, + sizeof(pmadapter->mp_update)); +#ifdef SDIO_MULTI_PORT_TX_AGGR + memcpy(pmadapter, debug_info->mpa_tx_count, + pmadapter->mpa_tx_count, + sizeof(pmadapter->mpa_tx_count)); + debug_info->mpa_sent_last_pkt = pmadapter->mpa_sent_last_pkt; + debug_info->mpa_sent_no_ports = pmadapter->mpa_sent_no_ports; + debug_info->last_recv_wr_bitmap = + pmadapter->last_recv_wr_bitmap; + debug_info->last_mp_index = pmadapter->last_mp_index; + memcpy(pmadapter, debug_info->last_mp_wr_bitmap, + pmadapter->last_mp_wr_bitmap, + sizeof(pmadapter->last_mp_wr_bitmap)); + memcpy(pmadapter, debug_info->last_mp_wr_ports, + pmadapter->last_mp_wr_ports, + sizeof(pmadapter->last_mp_wr_ports)); + memcpy(pmadapter, debug_info->last_mp_wr_len, + pmadapter->last_mp_wr_len, + sizeof(pmadapter->last_mp_wr_len)); + memcpy(pmadapter, debug_info->last_mp_wr_info, + pmadapter->last_mp_wr_info, + sizeof(pmadapter->last_mp_wr_info)); + memcpy(pmadapter, debug_info->last_curr_wr_port, + pmadapter->last_curr_wr_port, + sizeof(pmadapter->last_curr_wr_port)); + debug_info->mpa_buf = pmadapter->mpa_buf; + debug_info->mpa_buf_size = pmadapter->mpa_buf_size; +#endif /* SDIO_MULTI_PORT_TX_AGGR */ +#ifdef SDIO_MULTI_PORT_RX_AGGR + debug_info->sdio_rx_aggr = pmadapter->sdio_rx_aggr_enable; + memcpy(pmadapter, debug_info->mpa_rx_count, + pmadapter->mpa_rx_count, + sizeof(pmadapter->mpa_rx_count)); +#endif + debug_info->data_sent = pmadapter->data_sent; + debug_info->cmd_sent = pmadapter->cmd_sent; + debug_info->cmd_resp_received = pmadapter->cmd_resp_received; + debug_info->tx_pkts_queued = + util_scalar_read(pmadapter->pmoal_handle, + &pmpriv->wmm.tx_pkts_queued, MNULL, + MNULL); +#ifdef UAP_SUPPORT + debug_info->num_bridge_pkts = + util_scalar_read(pmadapter->pmoal_handle, + &pmadapter->pending_bridge_pkts, + pmadapter->callbacks.moal_spin_lock, + pmadapter->callbacks.moal_spin_unlock); + debug_info->num_drop_pkts = pmpriv->num_drop_pkts; +#endif + debug_info->mlan_processing = pmadapter->mlan_processing; + debug_info->mlan_rx_processing = pmadapter->mlan_rx_processing; + debug_info->rx_pkts_queued = pmadapter->rx_pkts_queued; + debug_info->mlan_adapter = pmadapter; + debug_info->mlan_adapter_size = sizeof(mlan_adapter); + debug_info->mlan_priv_num = pmadapter->priv_num; + for (i = 0; i < pmadapter->priv_num; i++) { + debug_info->mlan_priv[i] = pmadapter->priv[i]; + debug_info->mlan_priv_size[i] = sizeof(mlan_private); + } + } + + pioctl_req->data_read_written = + sizeof(mlan_debug_info) + MLAN_SUB_COMMAND_SIZE; + + LEAVE(); + return ret; +} + +/** + * @brief Set/Get the MAC control configuration. + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING -- success, otherwise fail + */ +mlan_status +wlan_misc_ioctl_mac_control(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_misc_cfg *misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u16 cmd_action = 0; + + ENTER(); + + misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + + if (pioctl_req->action == MLAN_ACT_GET) { + misc->param.mac_ctrl = pmpriv->curr_pkt_filter; + } else { + pmpriv->curr_pkt_filter = misc->param.mac_ctrl; + cmd_action = HostCmd_ACT_GEN_SET; + + /* Send command to firmware */ + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_MAC_CONTROL, + cmd_action, 0, + (t_void *)pioctl_req, + &misc->param.mac_ctrl); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + } + + LEAVE(); + return ret; +} + +/** + * @brief This function wakes up the card. + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_pm_wakeup_card(IN pmlan_adapter pmadapter) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u32 age_ts_usec; + pmlan_callbacks pcb = &pmadapter->callbacks; + + ENTER(); + PRINTM(MEVENT, "Wakeup device...\n"); + pmadapter->callbacks.moal_get_system_time(pmadapter->pmoal_handle, + &pmadapter->pm_wakeup_in_secs, + &age_ts_usec); + + if (pmadapter->fw_wakeup_method == WAKEUP_FW_THRU_GPIO) { + /* GPIO_PORT_TO_LOW(); */ + } else + ret = pcb->moal_write_reg(pmadapter->pmoal_handle, + HOST_TO_CARD_EVENT_REG, + HOST_POWER_UP); + LEAVE(); + return ret; +} + +/** + * @brief This function resets the PM setting of the card. + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_pm_reset_card(IN pmlan_adapter pmadapter) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_callbacks pcb = &pmadapter->callbacks; + + ENTER(); + + if (pmadapter->fw_wakeup_method == WAKEUP_FW_THRU_GPIO) { + /* GPIO_PORT_TO_HIGH(); */ + } else + ret = pcb->moal_write_reg(pmadapter->pmoal_handle, + HOST_TO_CARD_EVENT_REG, 0); + + LEAVE(); + return ret; +} + +/** + * @brief Set/Get HS configuration + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING --success, otherwise fail + */ +mlan_status +wlan_pm_ioctl_hscfg(IN pmlan_adapter pmadapter, IN pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_pm_cfg *pm = MNULL; + mlan_status status = MLAN_STATUS_SUCCESS; + t_u32 prev_cond = 0; + + ENTER(); + + pm = (mlan_ds_pm_cfg *)pioctl_req->pbuf; + + switch (pioctl_req->action) { + case MLAN_ACT_SET: +#ifdef STA_SUPPORT + if (pmadapter->pps_uapsd_mode) { + PRINTM(MINFO, + "Host Sleep IOCTL is blocked in UAPSD/PPS mode\n"); + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + status = MLAN_STATUS_FAILURE; + break; + } +#endif /* STA_SUPPORT */ + if (pm->param.hs_cfg.is_invoke_hostcmd == MTRUE) { + if (pm->param.hs_cfg.conditions == + HOST_SLEEP_CFG_CANCEL) { + if (pmadapter->is_hs_configured == MFALSE) { + /* Already cancelled */ + break; + } + /* Save previous condition */ + prev_cond = pmadapter->hs_cfg.conditions; + pmadapter->hs_cfg.conditions = + pm->param.hs_cfg.conditions; + } else if (pmadapter->hs_cfg.conditions == + HOST_SLEEP_CFG_CANCEL) { + /* Return failure if no parameters for HS enable */ + pioctl_req->status_code = + MLAN_ERROR_INVALID_PARAMETER; + status = MLAN_STATUS_FAILURE; + break; + } + status = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_802_11_HS_CFG_ENH, + HostCmd_ACT_GEN_SET, + 0, (t_void *)pioctl_req, + (t_void *)(&pmadapter-> + hs_cfg)); + if (status == MLAN_STATUS_SUCCESS) + status = MLAN_STATUS_PENDING; + if (pm->param.hs_cfg.conditions == + HOST_SLEEP_CFG_CANCEL) { + /* Restore previous condition */ + pmadapter->hs_cfg.conditions = prev_cond; + } + } else { + pmadapter->hs_cfg.conditions = + pm->param.hs_cfg.conditions; + pmadapter->hs_cfg.gpio = (t_u8)pm->param.hs_cfg.gpio; + pmadapter->hs_cfg.gap = (t_u8)pm->param.hs_cfg.gap; + pmadapter->param_type_ind = + (t_u8)pm->param.hs_cfg.param_type_ind; + pmadapter->ind_gpio = (t_u8)pm->param.hs_cfg.ind_gpio; + pmadapter->level = (t_u8)pm->param.hs_cfg.level; + pmadapter->param_type_ext = + (t_u8)pm->param.hs_cfg.param_type_ext; + pmadapter->event_force_ignore = + pm->param.hs_cfg.event_force_ignore; + pmadapter->event_use_ext_gap = + pm->param.hs_cfg.event_use_ext_gap; + pmadapter->ext_gap = pm->param.hs_cfg.ext_gap; + pmadapter->gpio_wave = pm->param.hs_cfg.gpio_wave; + pmadapter->hs_wake_interval = + pm->param.hs_cfg.hs_wake_interval; + } + break; + case MLAN_ACT_GET: + pm->param.hs_cfg.conditions = pmadapter->hs_cfg.conditions; + pm->param.hs_cfg.gpio = pmadapter->hs_cfg.gpio; + pm->param.hs_cfg.gap = pmadapter->hs_cfg.gap; + pm->param.hs_cfg.param_type_ind = pmadapter->param_type_ind; + pm->param.hs_cfg.ind_gpio = pmadapter->ind_gpio; + pm->param.hs_cfg.level = pmadapter->level; + pm->param.hs_cfg.param_type_ext = pmadapter->param_type_ext; + pm->param.hs_cfg.event_force_ignore = + pmadapter->event_force_ignore; + pm->param.hs_cfg.event_use_ext_gap = + pmadapter->event_use_ext_gap; + pm->param.hs_cfg.ext_gap = pmadapter->ext_gap; + pm->param.hs_cfg.gpio_wave = pmadapter->gpio_wave; + pm->param.hs_cfg.hs_wake_interval = pmadapter->hs_wake_interval; + break; + default: + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + status = MLAN_STATUS_FAILURE; + break; + } + + LEAVE(); + return status; +} + +/** + * @brief Get/Set the firmware wakeup method + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING --success, otherwise fail + */ +mlan_status +wlan_fw_wakeup_method(IN pmlan_adapter pmadapter, IN pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u16 cmd_action; + mlan_ds_pm_cfg *pmcfg = (mlan_ds_pm_cfg *)pioctl_req->pbuf; + + ENTER(); + + if (pioctl_req->action == MLAN_ACT_SET) + cmd_action = HostCmd_ACT_GEN_SET; + else + cmd_action = HostCmd_ACT_GEN_GET; + + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_802_11_FW_WAKE_METHOD, + cmd_action, + 0, + (t_void *)pioctl_req, + &pmcfg->param.fw_wakeup_params); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + LEAVE(); + return ret; +} + +/** + * @brief Set Robustcoex gpiocfg + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING --success, otherwise fail + */ +mlan_status +wlan_misc_robustcoex(IN pmlan_adapter pmadapter, IN pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u16 cmd_action; + mlan_ds_misc_cfg *robust_coex_cfg = + (mlan_ds_misc_cfg *)pioctl_req->pbuf; + + ENTER(); + + if (pioctl_req->action == MLAN_ACT_SET) + cmd_action = HostCmd_ACT_GEN_SET; + else + cmd_action = HostCmd_ACT_GEN_GET; + + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_802_11_ROBUSTCOEX, + cmd_action, + 0, + (t_void *)pioctl_req, + &robust_coex_cfg->param.robustcoexparams); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + LEAVE(); + return ret; +} + +/** + * @brief This function allocates a mlan_buffer. + * + * @param pmadapter Pointer to mlan_adapter + * @param data_len Data length + * @param head_room head_room reserved in mlan_buffer + * @param malloc_flag flag to user moal_malloc + * @return mlan_buffer pointer or MNULL + */ +pmlan_buffer +wlan_alloc_mlan_buffer(mlan_adapter *pmadapter, t_u32 data_len, t_u32 head_room, + t_u32 malloc_flag) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_buffer pmbuf = MNULL; + t_u32 buf_size = 0; + t_u8 *tmp_buf = MNULL; + pmlan_callbacks pcb = &pmadapter->callbacks; + + ENTER(); + + /* make sure that the data length is at least SDIO block size */ + data_len = + (data_len + MLAN_SDIO_BLOCK_SIZE - + 1) / MLAN_SDIO_BLOCK_SIZE * MLAN_SDIO_BLOCK_SIZE; + + /* head_room is not implemented for malloc mlan buffer */ + + switch (malloc_flag) { + case MOAL_MALLOC_BUFFER: + buf_size = sizeof(mlan_buffer) + data_len + DMA_ALIGNMENT; + ret = pcb->moal_malloc(pmadapter->pmoal_handle, buf_size, + MLAN_MEM_DEF | MLAN_MEM_DMA, + (t_u8 **)&pmbuf); + if ((ret != MLAN_STATUS_SUCCESS) || !pmbuf) { + pmbuf = MNULL; + goto exit; + } + memset(pmadapter, pmbuf, 0, sizeof(mlan_buffer)); + + pmbuf->pdesc = MNULL; + /* Align address */ + pmbuf->pbuf = + (t_u8 *)ALIGN_ADDR((t_u8 *)pmbuf + sizeof(mlan_buffer), + DMA_ALIGNMENT); + pmbuf->data_offset = 0; + pmbuf->data_len = data_len; + pmbuf->flags |= MLAN_BUF_FLAG_MALLOC_BUF; + break; + + case MOAL_ALLOC_MLAN_BUFFER: + /* use moal_alloc_mlan_buffer, head_room supported */ + ret = pcb->moal_alloc_mlan_buffer(pmadapter->pmoal_handle, + data_len + DMA_ALIGNMENT + + head_room, &pmbuf); + if ((ret != MLAN_STATUS_SUCCESS) || !pmbuf) { + PRINTM(MERROR, "Failed to allocate 'mlan_buffer'\n"); + goto exit; + } + pmbuf->data_offset = head_room; + tmp_buf = + (t_u8 *)ALIGN_ADDR(pmbuf->pbuf + pmbuf->data_offset, + DMA_ALIGNMENT); + pmbuf->data_offset += + (t_u32)(tmp_buf - (pmbuf->pbuf + pmbuf->data_offset)); + pmbuf->data_len = data_len; + pmbuf->flags = 0; + break; + } + +exit: + LEAVE(); + return pmbuf; +} + +/** + * @brief This function frees a mlan_buffer. + * + * @param pmadapter Pointer to mlan_adapter + * @param pmbuf Pointer to mlan_buffer + * + * @return N/A + */ +t_void +wlan_free_mlan_buffer(mlan_adapter *pmadapter, pmlan_buffer pmbuf) +{ + pmlan_callbacks pcb = &pmadapter->callbacks; + ENTER(); + + if (pcb && pmbuf) { + if (pmbuf->flags & MLAN_BUF_FLAG_BRIDGE_BUF) + util_scalar_decrement(pmadapter->pmoal_handle, + &pmadapter->pending_bridge_pkts, + pmadapter->callbacks. + moal_spin_lock, + pmadapter->callbacks. + moal_spin_unlock); + if (pmbuf->flags & MLAN_BUF_FLAG_MALLOC_BUF) + pcb->moal_mfree(pmadapter->pmoal_handle, (t_u8 *)pmbuf); + else + pcb->moal_free_mlan_buffer(pmadapter->pmoal_handle, + pmbuf); + } + + LEAVE(); + return; +} + +/** + * @brief Delay function implementation + * + * @param pmadapter A pointer to mlan_adapter structure + * @param delay Delay value + * @param u Units of delay (sec, msec or usec) + * + * @return N/A + */ +t_void +wlan_delay_func(mlan_adapter *pmadapter, t_u32 delay, t_delay_unit u) +{ + t_u32 now_tv_sec, now_tv_usec; + t_u32 upto_tv_sec, upto_tv_usec; + pmlan_callbacks pcb = &pmadapter->callbacks; + + ENTER(); + + if (pcb->moal_udelay) { + if (u == SEC) + delay *= 1000000; + else if (u == MSEC) + delay *= 1000; + pcb->moal_udelay(pmadapter->pmoal_handle, delay); + } else { + + pcb->moal_get_system_time(pmadapter->pmoal_handle, &upto_tv_sec, + &upto_tv_usec); + + switch (u) { + case SEC: + upto_tv_sec += delay; + break; + case MSEC: + delay *= 1000; + case USEC: + upto_tv_sec += (delay / 1000000); + upto_tv_usec += (delay % 1000000); + break; + } + + do { + pcb->moal_get_system_time(pmadapter->pmoal_handle, + &now_tv_sec, &now_tv_usec); + if (now_tv_sec > upto_tv_sec) { + LEAVE(); + return; + } + + if ((now_tv_sec == upto_tv_sec) && + (now_tv_usec >= upto_tv_usec)) { + LEAVE(); + return; + } + } while (MTRUE); + } + + LEAVE(); + return; +} + +/** + * @brief Send coalescing status command to firmware + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +mlan_status +wlan_misc_ioctl_coalescing_status(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_misc_cfg *misc = MNULL; + t_u16 cmd_action = 0; + + ENTER(); + + misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + + if (pioctl_req->action == MLAN_ACT_SET) + cmd_action = HostCmd_ACT_GEN_SET; + else + cmd_action = HostCmd_ACT_GEN_GET; + + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_802_11_IBSS_COALESCING_STATUS, + cmd_action, + 0, + (t_void *)pioctl_req, + &misc->param.coalescing_status); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief BSS remove + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, MLAN_STATUS_FAILURE + */ +mlan_status +wlan_bss_ioctl_bss_remove(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + ENTER(); + wlan_cancel_bss_pending_cmd(pmadapter, pioctl_req->bss_index); + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) +/** + * @brief Set/Get BSS role + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, MLAN_STATUS_FAILURE + */ +mlan_status +wlan_bss_ioctl_bss_role(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_bss *bss = MNULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + HostCmd_DS_VERSION_EXT dummy; +#if defined(WIFI_DIRECT_SUPPORT) + t_u8 bss_mode; +#endif + t_u8 i, global_band = 0; + int j; + + ENTER(); + + bss = (mlan_ds_bss *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_GET) { + bss->param.bss_role = GET_BSS_ROLE(pmpriv); + } else { + if (GET_BSS_ROLE(pmpriv) == bss->param.bss_role) { + PRINTM(MIOCTL, "BSS ie already in the desired role!\n"); + goto done; + } + mlan_block_rx_process(pmadapter, MTRUE); + /** Switch BSS role */ + wlan_free_priv(pmpriv); + + pmpriv->bss_role = bss->param.bss_role; + if (pmpriv->bss_type == MLAN_BSS_TYPE_UAP) + pmpriv->bss_type = MLAN_BSS_TYPE_STA; + else if (pmpriv->bss_type == MLAN_BSS_TYPE_STA) + pmpriv->bss_type = MLAN_BSS_TYPE_UAP; + /* Initialize private structures */ + wlan_init_priv(pmpriv); + mlan_block_rx_process(pmadapter, MFALSE); + /* Initialize function table */ + for (j = 0; mlan_ops[j]; j++) { + if (mlan_ops[j]->bss_role == GET_BSS_ROLE(pmpriv)) { + memcpy(pmadapter, &pmpriv->ops, mlan_ops[j], + sizeof(mlan_operations)); + } + } + + for (i = 0; i < pmadapter->priv_num; i++) { + if (pmadapter->priv[i] && + GET_BSS_ROLE(pmadapter->priv[i]) == + MLAN_BSS_ROLE_STA) + global_band |= pmadapter->priv[i]->config_bands; + } + + if (global_band != pmadapter->config_bands) { + if (wlan_set_regiontable + (pmpriv, (t_u8)pmadapter->region_code, + global_band | pmadapter->adhoc_start_band)) { + pioctl_req->status_code = MLAN_ERROR_IOCTL_FAIL; + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + if (wlan_11d_set_universaltable + (pmpriv, + global_band | pmadapter->adhoc_start_band)) { + pioctl_req->status_code = MLAN_ERROR_IOCTL_FAIL; + LEAVE(); + return MLAN_STATUS_FAILURE; + } + pmadapter->config_bands = global_band; + } + + /* Issue commands to initialize firmware */ +#if defined(WIFI_DIRECT_SUPPORT) + if (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_STA) + bss_mode = BSS_MODE_WIFIDIRECT_CLIENT; + else + bss_mode = BSS_MODE_WIFIDIRECT_GO; + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_SET_BSS_MODE, + HostCmd_ACT_GEN_SET, 0, MNULL, + &bss_mode); + if (ret) + goto done; +#endif + ret = pmpriv->ops.init_cmd(pmpriv, MFALSE); + if (ret == MLAN_STATUS_FAILURE) + goto done; + + /* Issue dummy Get command to complete the ioctl */ + memset(pmadapter, &dummy, 0, sizeof(HostCmd_DS_VERSION_EXT)); + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_VERSION_EXT, + HostCmd_ACT_GEN_GET, 0, + (t_void *)pioctl_req, (t_void *)&dummy); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + } + +done: + LEAVE(); + return ret; +} +#endif + +/** + * @brief Set the custom IE + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * @param send_ioctl Flag to indicate if ioctl should be sent with cmd + * (MTRUE if from moal/user, MFALSE if internal) + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +mlan_status +wlan_misc_ioctl_custom_ie_list(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req, + IN t_bool send_ioctl) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_misc_cfg *misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + custom_ie *ie_data = MNULL; + t_u16 cmd_action = 0, index, mask, i, len, app_data_len; + t_s32 ioctl_len; + t_u8 *tmp_ie; + + ENTER(); + + if ((misc->param.cust_ie.len == 0) || + (misc->param.cust_ie.len == sizeof(t_u16))) { + pioctl_req->action = MLAN_ACT_GET; + /* Get the IE */ + cmd_action = HostCmd_ACT_GEN_GET; + } else { + /* ioctl_len : ioctl length from application, start with + * misc->param.cust_ie.len and reach upto 0 */ + ioctl_len = misc->param.cust_ie.len; + + /* app_data_len : length from application, start with 0 + * and reach upto ioctl_len */ + app_data_len = sizeof(MrvlIEtypesHeader_t); + misc->param.cust_ie.len = 0; + + while (ioctl_len > 0) { + ie_data = (custom_ie *)(((t_u8 *)&misc->param.cust_ie) + + app_data_len); + ioctl_len -= + (ie_data->ie_length + MLAN_CUSTOM_IE_HDR_SIZE); + app_data_len += + (ie_data->ie_length + MLAN_CUSTOM_IE_HDR_SIZE); + + index = ie_data->ie_index; + mask = ie_data->mgmt_subtype_mask; + + /* Need to be Autohandled */ + if (MLAN_CUSTOM_IE_AUTO_IDX_MASK == index) { + /* Automatic Deletion */ + if (mask == MLAN_CUSTOM_IE_DELETE_MASK) { + ret = wlan_custom_ioctl_auto_delete + (pmpriv, pioctl_req, ie_data, + index); + /* if IE to delete is not found, return error */ + if (ret == MLAN_STATUS_FAILURE) + goto done; + index = ie_data->ie_index; + memset(pmadapter, ie_data, 0, + sizeof(custom_ie) * + MAX_MGMT_IE_INDEX_TO_FW); + len = 0; + for (i = 0; + i < pmadapter->max_mgmt_ie_index; + i++) { + /* Check if index is updated before sending to FW */ + if (index & ((t_u16)1) << i) { + memcpy(pmadapter, + (t_u8 *)ie_data + + len, &i, + sizeof(ie_data-> + ie_index)); + len += sizeof(ie_data-> + ie_index); + memcpy(pmadapter, + (t_u8 *)ie_data + + len, + &pmpriv-> + mgmt_ie[i]. + mgmt_subtype_mask, + sizeof(ie_data-> + mgmt_subtype_mask)); + len += sizeof(ie_data-> + mgmt_subtype_mask); + memcpy(pmadapter, + (t_u8 *)ie_data + + len, + &pmpriv-> + mgmt_ie[i]. + ie_length, + sizeof(ie_data-> + ie_length)); + len += sizeof(ie_data-> + ie_length); + if (pmpriv->mgmt_ie[i]. + ie_length) { + memcpy(pmadapter, (t_u8 *)ie_data + len, &pmpriv->mgmt_ie[i].ie_buffer, pmpriv->mgmt_ie[i].ie_length); + len += pmpriv-> + mgmt_ie + [i]. + ie_length; + } + } + } + misc->param.cust_ie.len += len; + pioctl_req->action = MLAN_ACT_SET; + cmd_action = HostCmd_ACT_GEN_SET; + } else { /* Automatic Addition */ + if (MLAN_STATUS_FAILURE == + wlan_custom_ioctl_get_autoidx + (pmpriv, pioctl_req, mask, ie_data, + &index)) { + PRINTM(MERROR, + "Failed to Set the IE buffer\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + mask &= ~MLAN_CUSTOM_IE_NEW_MASK; + if (MLAN_CUSTOM_IE_AUTO_IDX_MASK == + index) { + ret = MLAN_STATUS_SUCCESS; + goto done; + } + tmp_ie = (t_u8 *)&pmpriv-> + mgmt_ie[index].ie_buffer; + memcpy(pmadapter, + tmp_ie + + pmpriv->mgmt_ie[index].ie_length, + &ie_data->ie_buffer, + ie_data->ie_length); + pmpriv->mgmt_ie[index].ie_length += + ie_data->ie_length; + pmpriv->mgmt_ie[index].ie_index = index; + pmpriv->mgmt_ie[index]. + mgmt_subtype_mask = mask; + + pioctl_req->action = MLAN_ACT_SET; + cmd_action = HostCmd_ACT_GEN_SET; + ie_data->ie_index = index; + ie_data->ie_length = + pmpriv->mgmt_ie[index]. + ie_length; + memcpy(pmadapter, &ie_data->ie_buffer, + &pmpriv->mgmt_ie[index]. + ie_buffer, + pmpriv->mgmt_ie[index]. + ie_length); + misc->param.cust_ie.len += + pmpriv->mgmt_ie[index]. + ie_length + + MLAN_CUSTOM_IE_HDR_SIZE; + } + } else { + if (index >= pmadapter->max_mgmt_ie_index) { + PRINTM(MERROR, + "Invalid custom IE index %d\n", + index); + ret = MLAN_STATUS_FAILURE; + goto done; + } + /* Set/Clear the IE and save it */ + if (ie_data->mgmt_subtype_mask == + MLAN_CUSTOM_IE_DELETE_MASK && + ie_data->ie_length) { + PRINTM(MINFO, "Clear the IE buffer\n"); + ret = wlan_custom_ioctl_auto_delete + (pmpriv, pioctl_req, ie_data, + index); + /* if IE to delete is not found, return error */ + if (ret == MLAN_STATUS_FAILURE) + goto done; + memset(pmadapter, ie_data, 0, + sizeof(custom_ie) * + MAX_MGMT_IE_INDEX_TO_FW); + memcpy(pmadapter, (t_u8 *)ie_data, + &pmpriv->mgmt_ie[index], + pmpriv->mgmt_ie[index]. + ie_length + + MLAN_CUSTOM_IE_HDR_SIZE); + } else { + /* + * Check if this index is being used on + * any other interfaces. If yes, then + * the request needs to be rejected. + */ + ret = wlan_is_custom_ie_index_unused + (pmpriv, index); + if (ret == MLAN_STATUS_FAILURE) { + PRINTM(MERROR, + "IE index is used by other interface.\n"); + PRINTM(MERROR, + "Set or delete on index %d is not allowed.\n", + index); + pioctl_req->status_code = + MLAN_ERROR_IOCTL_FAIL; + goto done; + } + PRINTM(MINFO, "Set the IE buffer\n"); + if (ie_data->mgmt_subtype_mask == + MLAN_CUSTOM_IE_DELETE_MASK) + ie_data->ie_length = 0; + else { + if ((pmpriv->mgmt_ie[index]. + mgmt_subtype_mask == + ie_data->mgmt_subtype_mask) + && (pmpriv->mgmt_ie[index]. + ie_length == + ie_data->ie_length) && + !memcmp(pmpriv->adapter, + pmpriv-> + mgmt_ie[index]. + ie_buffer, + ie_data->ie_buffer, + ie_data-> + ie_length)) { + PRINTM(MIOCTL, + "same custom ie already configured!\n"); + if (ioctl_len <= 0 && + misc->param.cust_ie. + len == 0) { + goto done; + } else { + /* remove matching IE from app buffer */ + app_data_len -= + ie_data-> + ie_length + + + MLAN_CUSTOM_IE_HDR_SIZE; + memmove(pmadapter, (t_u8 *)ie_data, ie_data->ie_buffer + ie_data->ie_length, ioctl_len); + continue; + } + } + } + memset(pmadapter, + &pmpriv->mgmt_ie[index], 0, + sizeof(custom_ie)); + memcpy(pmadapter, + &pmpriv->mgmt_ie[index], ie_data, + sizeof(custom_ie)); + } + + misc->param.cust_ie.len += + pmpriv->mgmt_ie[index].ie_length + + MLAN_CUSTOM_IE_HDR_SIZE; + pioctl_req->action = MLAN_ACT_SET; + cmd_action = HostCmd_ACT_GEN_SET; + } + } + } + + /* Send command to firmware */ + if (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_STA) { + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_MGMT_IE_LIST, + cmd_action, + 0, + (send_ioctl) ? (t_void *)pioctl_req : + MNULL, &misc->param.cust_ie); + } +#ifdef UAP_SUPPORT + else if (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_UAP) { + ret = wlan_prepare_cmd(pmpriv, + HOST_CMD_APCMD_SYS_CONFIGURE, + cmd_action, + 0, + (send_ioctl) ? (t_void *)pioctl_req : + MNULL, + (send_ioctl) ? MNULL : &misc->param. + cust_ie); + } +#endif + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; +done: + LEAVE(); + return ret; +} + +/** + * @brief Read/write adapter register + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +mlan_status +wlan_reg_mem_ioctl_reg_rw(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_reg_mem *reg_mem = MNULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u16 cmd_action = 0, cmd_no; + + ENTER(); + + reg_mem = (mlan_ds_reg_mem *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_GET) + cmd_action = HostCmd_ACT_GEN_GET; + else + cmd_action = HostCmd_ACT_GEN_SET; + + switch (reg_mem->param.reg_rw.type) { + case MLAN_REG_MAC: + cmd_no = HostCmd_CMD_MAC_REG_ACCESS; + break; + case MLAN_REG_BBP: + cmd_no = HostCmd_CMD_BBP_REG_ACCESS; + break; + case MLAN_REG_RF: + cmd_no = HostCmd_CMD_RF_REG_ACCESS; + break; + case MLAN_REG_CAU: + cmd_no = HostCmd_CMD_CAU_REG_ACCESS; + break; + case MLAN_REG_PSU: + cmd_no = HostCmd_CMD_TARGET_ACCESS; + break; + default: + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + ret = MLAN_STATUS_FAILURE; + goto exit; + } + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, cmd_no, cmd_action, + 0, (t_void *)pioctl_req, + (t_void *)®_mem->param.reg_rw); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + +exit: + LEAVE(); + return ret; +} + +/** + * @brief Read the EEPROM contents of the card + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +mlan_status +wlan_reg_mem_ioctl_read_eeprom(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_reg_mem *reg_mem = MNULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u16 cmd_action = 0; + + ENTER(); + + reg_mem = (mlan_ds_reg_mem *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_GET) + cmd_action = HostCmd_ACT_GEN_GET; + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_802_11_EEPROM_ACCESS, + cmd_action, 0, (t_void *)pioctl_req, + (t_void *)®_mem->param.rd_eeprom); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Read/write memory of device + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +mlan_status +wlan_reg_mem_ioctl_mem_rw(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_reg_mem *reg_mem = MNULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u16 cmd_action = 0; + + ENTER(); + + reg_mem = (mlan_ds_reg_mem *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_GET) + cmd_action = HostCmd_ACT_GEN_GET; + else + cmd_action = HostCmd_ACT_GEN_SET; + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_MEM_ACCESS, + cmd_action, 0, + (t_void *)pioctl_req, + (t_void *)®_mem->param.mem_rw); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief This function will check if station list is empty + * + * @param priv A pointer to mlan_private + * + * @return MFALSE/MTRUE + */ +t_u8 +wlan_is_station_list_empty(mlan_private *priv) +{ + ENTER(); + if (!(util_peek_list(priv->adapter->pmoal_handle, + &priv->sta_list, + priv->adapter->callbacks.moal_spin_lock, + priv->adapter->callbacks.moal_spin_unlock))) { + LEAVE(); + return MTRUE; + } + LEAVE(); + return MFALSE; +} + +/** + * @brief This function will return the pointer to station entry in station list + * table which matches the give mac address + * + * @param priv A pointer to mlan_private + * @param mac mac address to find in station list table + * + * @return A pointer to structure sta_node + */ +sta_node * +wlan_get_station_entry(mlan_private *priv, t_u8 *mac) +{ + sta_node *sta_ptr; + + ENTER(); + + if (!mac) { + LEAVE(); + return MNULL; + } + sta_ptr = (sta_node *)util_peek_list(priv->adapter->pmoal_handle, + &priv->sta_list, + priv->adapter->callbacks. + moal_spin_lock, + priv->adapter->callbacks. + moal_spin_unlock); + + while (sta_ptr && (sta_ptr != (sta_node *)&priv->sta_list)) { + if (!memcmp + (priv->adapter, sta_ptr->mac_addr, mac, + MLAN_MAC_ADDR_LENGTH)) { + LEAVE(); + return sta_ptr; + } + sta_ptr = sta_ptr->pnext; + } + LEAVE(); + return MNULL; +} + +/** + * @brief This function will add a pointer to station entry in station list + * table with the give mac address, if it does not exist already + * + * @param priv A pointer to mlan_private + * @param mac mac address to find in station list table + * + * @return A pointer to structure sta_node + */ +sta_node * +wlan_add_station_entry(mlan_private *priv, t_u8 *mac) +{ + sta_node *sta_ptr = MNULL; + mlan_adapter *pmadapter = priv->adapter; + + ENTER(); + pmadapter->callbacks.moal_spin_lock(pmadapter->pmoal_handle, + priv->wmm.ra_list_spinlock); + + sta_ptr = wlan_get_station_entry(priv, mac); + if (sta_ptr) + goto done; + if (priv->adapter->callbacks. + moal_malloc(priv->adapter->pmoal_handle, sizeof(sta_node), + MLAN_MEM_DEF, (t_u8 **)&sta_ptr)) { + PRINTM(MERROR, "Failed to allocate memory for station node\n"); + pmadapter->callbacks.moal_spin_unlock(pmadapter->pmoal_handle, + priv->wmm. + ra_list_spinlock); + LEAVE(); + return MNULL; + } + memset(priv->adapter, sta_ptr, 0, sizeof(sta_node)); + memcpy(priv->adapter, sta_ptr->mac_addr, mac, MLAN_MAC_ADDR_LENGTH); + util_enqueue_list_tail(priv->adapter->pmoal_handle, &priv->sta_list, + (pmlan_linked_list)sta_ptr, + priv->adapter->callbacks.moal_spin_lock, + priv->adapter->callbacks.moal_spin_unlock); +#ifdef DRV_EMBEDDED_AUTHENTICATOR + if ((GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) && + IsAuthenticatorEnabled(priv->psapriv)) + authenticator_init_client(priv->psapriv, + &sta_ptr->cm_connectioninfo, mac); +#endif +done: + pmadapter->callbacks.moal_spin_unlock(pmadapter->pmoal_handle, + priv->wmm.ra_list_spinlock); + LEAVE(); + return sta_ptr; +} + +/** + * @brief This function will delete a station entry from station list + * + * + * @param priv A pointer to mlan_private + * @param mac station's mac address + * + * @return N/A + */ +t_void +wlan_delete_station_entry(mlan_private *priv, t_u8 *mac) +{ + sta_node *sta_ptr = MNULL; + mlan_adapter *pmadapter = priv->adapter; + ENTER(); + pmadapter->callbacks.moal_spin_lock(pmadapter->pmoal_handle, + priv->wmm.ra_list_spinlock); + sta_ptr = wlan_get_station_entry(priv, mac); + if (sta_ptr) { +#ifdef DRV_EMBEDDED_AUTHENTICATOR + if ((GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) && + IsAuthenticatorEnabled(priv->psapriv)) + authenticator_free_client(priv->psapriv, + sta_ptr->cm_connectioninfo); +#endif + util_unlink_list(priv->adapter->pmoal_handle, &priv->sta_list, + (pmlan_linked_list)sta_ptr, + priv->adapter->callbacks.moal_spin_lock, + priv->adapter->callbacks.moal_spin_unlock); + priv->adapter->callbacks.moal_mfree(priv->adapter->pmoal_handle, + (t_u8 *)sta_ptr); + } + pmadapter->callbacks.moal_spin_unlock(pmadapter->pmoal_handle, + priv->wmm.ra_list_spinlock); + LEAVE(); + return; +} + +/** + * @brief Clean up wapi station list + * + * @param priv Pointer to the mlan_private driver data struct + * + * @return N/A + */ +t_void +wlan_delete_station_list(pmlan_private priv) +{ + sta_node *sta_ptr; + + ENTER(); + while ((sta_ptr = + (sta_node *)util_dequeue_list(priv->adapter->pmoal_handle, + &priv->sta_list, + priv->adapter->callbacks. + moal_spin_lock, + priv->adapter->callbacks. + moal_spin_unlock))) { +#ifdef DRV_EMBEDDED_AUTHENTICATOR + if ((GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) && + IsAuthenticatorEnabled(priv->psapriv)) + authenticator_free_client(priv->psapriv, + sta_ptr->cm_connectioninfo); +#endif + priv->adapter->callbacks.moal_mfree(priv->adapter->pmoal_handle, + (t_u8 *)sta_ptr); + } + LEAVE(); + return; +} + +/** + * @brief Get tdls peer list + * + * @param priv A pointer to mlan_private structure + * @param buf A pointer to tdls_peer_info buf + * @return number of tdls peer + */ +int +wlan_get_tdls_list(mlan_private *priv, tdls_peer_info *buf) +{ + tdls_peer_info *peer_info = buf; + sta_node *sta_ptr = MNULL; + int count = 0; + ENTER(); + if (priv->bss_type != MLAN_BSS_TYPE_STA) { + LEAVE(); + return count; + } + sta_ptr = (sta_node *)util_peek_list(priv->adapter->pmoal_handle, + &priv->sta_list, + priv->adapter->callbacks. + moal_spin_lock, + priv->adapter->callbacks. + moal_spin_unlock); + if (!sta_ptr) { + LEAVE(); + return count; + } + while (sta_ptr != (sta_node *)&priv->sta_list) { + if (sta_ptr->status == TDLS_SETUP_COMPLETE) { + peer_info->snr = sta_ptr->snr; + peer_info->nf = sta_ptr->nf; + memcpy(priv->adapter, peer_info->mac_addr, + sta_ptr->mac_addr, MLAN_MAC_ADDR_LENGTH); + memcpy(priv->adapter, peer_info->ht_cap, + &sta_ptr->HTcap, sizeof(IEEEtypes_HTCap_t)); + memcpy(priv->adapter, peer_info->ext_cap, + &sta_ptr->ExtCap, sizeof(IEEEtypes_ExtCap_t)); + peer_info++; + count++; + } + sta_ptr = sta_ptr->pnext; + if (count >= MLAN_MAX_TDLS_PEER_SUPPORTED) + break; + } + LEAVE(); + return count; +} + +/** + * @brief Set the TDLS configuration to FW. + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +mlan_status +wlan_misc_ioctl_tdls_config(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_misc_cfg *misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + tdls_all_config *tdls_all_cfg = + (tdls_all_config *)misc->param.tdls_config.tdls_data; + t_u8 event_buf[100]; + mlan_event *pevent = (mlan_event *)event_buf; + tdls_tear_down_event *tdls_evt = + (tdls_tear_down_event *)pevent->event_buf; + sta_node *sta_ptr = MNULL; + MrvlIEtypes_Data_t *pMrvlTlv = MNULL; + t_u8 *pos = MNULL; + t_u16 remain_len = 0; + + ENTER(); + + if (misc->param.tdls_config.tdls_action == WLAN_TDLS_TEAR_DOWN_REQ) { + sta_ptr = + wlan_get_station_entry(pmpriv, + tdls_all_cfg->u.tdls_tear_down. + peer_mac_addr); + if (sta_ptr && sta_ptr->external_tdls) { + pevent->bss_index = pmpriv->bss_index; + pevent->event_id = MLAN_EVENT_ID_DRV_TDLS_TEARDOWN_REQ; + pevent->event_len = sizeof(tdls_tear_down_event); + memcpy(pmpriv->adapter, (t_u8 *)tdls_evt->peer_mac_addr, + tdls_all_cfg->u.tdls_tear_down.peer_mac_addr, + MLAN_MAC_ADDR_LENGTH); + tdls_evt->reason_code = + tdls_all_cfg->u.tdls_tear_down.reason_code; + wlan_recv_event(pmpriv, + MLAN_EVENT_ID_DRV_TDLS_TEARDOWN_REQ, + pevent); + LEAVE(); + return ret; + } + } + if (misc->param.tdls_config.tdls_action == WLAN_HOST_TDLS_CONFIG) { + pmpriv->host_tdls_uapsd_support = + tdls_all_cfg->u.host_tdls_cfg.uapsd_support; + pmpriv->host_tdls_cs_support = + tdls_all_cfg->u.host_tdls_cfg.cs_support; + pos = tdls_all_cfg->u.host_tdls_cfg.tlv_buffer; + remain_len = tdls_all_cfg->u.host_tdls_cfg.tlv_len; + while (remain_len > sizeof(MrvlIEtypesHeader_t)) { + remain_len -= sizeof(MrvlIEtypesHeader_t); + pMrvlTlv = (MrvlIEtypes_Data_t *)pos; + switch (pMrvlTlv->header.type) { + case SUPPORTED_CHANNELS: + pmpriv->chan_supp_len = + (t_u8)pMrvlTlv->header.len; + memset(pmadapter, pmpriv->chan_supp, 0, + sizeof(pmpriv->chan_supp)); + memcpy(pmadapter, pmpriv->chan_supp, + pMrvlTlv->data, MIN(pMrvlTlv->header.len, + MAX_IE_SIZE)); + DBG_HEXDUMP(MCMD_D, "TDLS supported channel", + pmpriv->chan_supp, + pmpriv->chan_supp_len); + break; + case REGULATORY_CLASS: + pmpriv->supp_regulatory_class_len = + (t_u8)pMrvlTlv->header.len; + memset(pmadapter, pmpriv->supp_regulatory_class, + 0, + sizeof(pmpriv->supp_regulatory_class)); + memcpy(pmadapter, pmpriv->supp_regulatory_class, + pMrvlTlv->data, MIN(pMrvlTlv->header.len, + MAX_IE_SIZE)); + DBG_HEXDUMP(MCMD_D, + "TDLS supported regulatory class", + pmpriv->supp_regulatory_class, + pmpriv->supp_regulatory_class_len); + break; + default: + break; + } + remain_len -= pMrvlTlv->header.len; + pos += sizeof(MrvlIEtypesHeader_t) + + pMrvlTlv->header.len; + } + LEAVE(); + return ret; + } + pioctl_req->action = MLAN_ACT_SET; + + /* Send command to firmware */ + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_TDLS_CONFIG, + HostCmd_ACT_GEN_SET, + 0, + (t_void *)pioctl_req, &misc->param.tdls_config); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + LEAVE(); + return ret; +} + +/** + * @brief enable tdls config for cs and uapsd. + * + * @param pmpriv A pointer to mlan_private structure + * @param enable MTRUE/MFALSE + * + * @return N/A + */ +t_void +wlan_tdls_config(IN pmlan_private pmpriv, IN t_u8 enable) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + mlan_callbacks *pcb = (mlan_callbacks *)&pmadapter->callbacks; + mlan_ds_misc_tdls_config *tdls_config = MNULL; + tdls_all_config *tdls_all_cfg = MNULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + ret = pcb->moal_malloc(pmadapter->pmoal_handle, + sizeof(mlan_ds_misc_tdls_config), MLAN_MEM_DEF, + (t_u8 **)&tdls_config); + if (ret != MLAN_STATUS_SUCCESS || !tdls_config) { + PRINTM(MERROR, "Memory allocation for tdls_config failed!\n"); + LEAVE(); + return; + } + memset(pmadapter, (t_u8 *)tdls_config, 0, + sizeof(mlan_ds_misc_tdls_config)); + tdls_all_cfg = (tdls_all_config *)tdls_config->tdls_data; + tdls_all_cfg->u.tdls_config.enable = enable; + tdls_config->tdls_action = WLAN_TDLS_CONFIG; + /* Send command to firmware */ + wlan_prepare_cmd(pmpriv, + HostCmd_CMD_TDLS_CONFIG, + HostCmd_ACT_GEN_SET, 0, MNULL, tdls_config); + PRINTM(MCMND, "tdls_config: enable=%d\n", enable); + + if (tdls_config) + pcb->moal_mfree(pmadapter->pmoal_handle, (t_u8 *)tdls_config); + + LEAVE(); +} + +/** + * @brief set tdls channel switch parameters. + * + * @param pmpriv A pointer to mlan_private structure + * + * @return N/A + */ +t_void +wlan_tdls_cs_param_config(IN pmlan_private pmpriv) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + mlan_callbacks *pcb = (mlan_callbacks *)&pmadapter->callbacks; + mlan_ds_misc_tdls_config *tdls_config = MNULL; + tdls_all_config *tdls_all_cfg = MNULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + ret = pcb->moal_malloc(pmadapter->pmoal_handle, + sizeof(mlan_ds_misc_tdls_config), MLAN_MEM_DEF, + (t_u8 **)&tdls_config); + if (ret != MLAN_STATUS_SUCCESS || !tdls_config) { + PRINTM(MERROR, "Memory allocation for tdls_config failed!\n"); + LEAVE(); + return; + } + memset(pmadapter, (t_u8 *)tdls_config, 0, + sizeof(mlan_ds_misc_tdls_config)); + + tdls_all_cfg = (tdls_all_config *)tdls_config->tdls_data; + tdls_config->tdls_action = WLAN_TDLS_CS_PARAMS; + tdls_all_cfg->u.tdls_cs_params.unit_time = 2; + tdls_all_cfg->u.tdls_cs_params.threshold_otherlink = 10; + tdls_all_cfg->u.tdls_cs_params.threshold_directlink = 0; + + /* Send command to firmware */ + wlan_prepare_cmd(pmpriv, + HostCmd_CMD_TDLS_CONFIG, + HostCmd_ACT_GEN_SET, 0, MNULL, tdls_config); + + if (tdls_config) + pcb->moal_mfree(pmadapter->pmoal_handle, (t_u8 *)tdls_config); + + LEAVE(); +} + +/** + * @brief start tdls channel switch + * + * @param pmpriv A pointer to mlan_private structure + * @param peer_mac_addr A pointer to peer mac address + * @param pioctl_buf A pointer to ioctl request buffer + * + * @return N/A + */ +t_void +wlan_tdls_cs_start(IN pmlan_private pmpriv, + IN t_u8 *peer_mac_addr, IN pmlan_ioctl_req pioctl_buf) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + mlan_callbacks *pcb = (mlan_callbacks *)&pmadapter->callbacks; + mlan_ds_misc_tdls_config *tdls_config = MNULL; + tdls_all_config *tdls_all_cfg = MNULL; + mlan_ds_misc_cfg *misc = MNULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + ret = pcb->moal_malloc(pmadapter->pmoal_handle, + sizeof(mlan_ds_misc_tdls_config), MLAN_MEM_DEF, + (t_u8 **)&tdls_config); + if (ret != MLAN_STATUS_SUCCESS || !tdls_config) { + PRINTM(MERROR, "Memory allocation for tdls_config failed!\n"); + LEAVE(); + return; + } + memset(pmadapter, (t_u8 *)tdls_config, 0, + sizeof(mlan_ds_misc_tdls_config)); + + if (pioctl_buf) { + misc = (mlan_ds_misc_cfg *)pioctl_buf->pbuf; + memcpy(pmpriv->adapter, tdls_config, &misc->param.tdls_config, + sizeof(mlan_ds_misc_tdls_config)); + tdls_all_cfg = (tdls_all_config *)tdls_config->tdls_data; + if (tdls_all_cfg->u.tdls_chan_switch.primary_channel > 14) { + tdls_all_cfg->u.tdls_chan_switch. + secondary_channel_offset = + wlan_get_second_channel_offset(tdls_all_cfg->u. + tdls_chan_switch. + primary_channel); + } + PRINTM(MCMND, "Start TDLS CS: channel=%d\n", + tdls_all_cfg->u.tdls_chan_switch.primary_channel); + } else { + tdls_all_cfg = (tdls_all_config *)tdls_config->tdls_data; + tdls_config->tdls_action = WLAN_TDLS_INIT_CHAN_SWITCH; + memcpy(pmpriv->adapter, + tdls_all_cfg->u.tdls_chan_switch.peer_mac_addr, + peer_mac_addr, MLAN_MAC_ADDR_LENGTH); + tdls_all_cfg->u.tdls_chan_switch.primary_channel = + pmpriv->tdls_cs_channel; + if (pmpriv->tdls_cs_channel > 14) { + tdls_all_cfg->u.tdls_chan_switch.band = BAND_5GHZ; + tdls_all_cfg->u.tdls_chan_switch. + secondary_channel_offset = + wlan_get_second_channel_offset(pmpriv-> + tdls_cs_channel); + } else { + tdls_all_cfg->u.tdls_chan_switch.band = BAND_2GHZ; + } + PRINTM(MCMND, "Start TDLS CS: channel=%d\n", + pmpriv->tdls_cs_channel); + } + tdls_all_cfg->u.tdls_chan_switch.switch_time = 10; + tdls_all_cfg->u.tdls_chan_switch.switch_timeout = 16; + tdls_all_cfg->u.tdls_chan_switch.regulatory_class = 12; + tdls_all_cfg->u.tdls_chan_switch.periodicity = 1; + + /* Send command to firmware */ + wlan_prepare_cmd(pmpriv, + HostCmd_CMD_TDLS_CONFIG, + HostCmd_ACT_GEN_SET, 0, MNULL, tdls_config); + + if (tdls_config) + pcb->moal_mfree(pmadapter->pmoal_handle, (t_u8 *)tdls_config); + + LEAVE(); +} + +/** + * @brief stop tdls channel switch + * + * @param pmpriv A pointer to mlan_private structure + * @param peer_mac_addr A pointer to peer mac address + * @return N/A + */ +t_void +wlan_tdls_cs_stop(IN pmlan_private pmpriv, IN t_u8 *peer_mac_addr) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + mlan_callbacks *pcb = (mlan_callbacks *)&pmadapter->callbacks; + mlan_ds_misc_tdls_config *tdls_config = MNULL; + tdls_all_config *tdls_all_cfg = MNULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + ret = pcb->moal_malloc(pmadapter->pmoal_handle, + sizeof(mlan_ds_misc_tdls_config), MLAN_MEM_DEF, + (t_u8 **)&tdls_config); + if (ret != MLAN_STATUS_SUCCESS || !tdls_config) { + PRINTM(MERROR, "Memory allocation for tdls_config failed!\n"); + LEAVE(); + return; + } + memset(pmadapter, (t_u8 *)tdls_config, 0, + sizeof(mlan_ds_misc_tdls_config)); + + tdls_all_cfg = (tdls_all_config *)tdls_config->tdls_data; + tdls_config->tdls_action = WLAN_TDLS_STOP_CHAN_SWITCH; + + memcpy(pmpriv->adapter, + tdls_all_cfg->u.tdls_stop_chan_switch.peer_mac_addr, + peer_mac_addr, MLAN_MAC_ADDR_LENGTH); + PRINTM(MCMND, "Stop TDLS CS\n"); + /* Send command to firmware */ + wlan_prepare_cmd(pmpriv, + HostCmd_CMD_TDLS_CONFIG, + HostCmd_ACT_GEN_SET, 0, MNULL, tdls_config); + + if (tdls_config) + pcb->moal_mfree(pmadapter->pmoal_handle, (t_u8 *)tdls_config); + + LEAVE(); +} + +/** + * @brief Set/Get the TDLS off channel. + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +mlan_status +wlan_misc_ioctl_tdls_cs_channel(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_misc_cfg *misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + + ENTER(); + + if (MLAN_ACT_GET == pioctl_req->action) + misc->param.tdls_cs_channel = pmpriv->tdls_cs_channel; + else if (MLAN_ACT_SET == pioctl_req->action) { + pmpriv->tdls_cs_channel = misc->param.tdls_cs_channel; + + } + LEAVE(); + return ret; +} + +/** + * @brief Set/Get the TDLS idle time. + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +mlan_status +wlan_misc_ioctl_tdls_idle_time(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_misc_cfg *misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + + ENTER(); + + if (MLAN_ACT_GET == pioctl_req->action) { + misc->param.tdls_idle_time = pmpriv->tdls_idle_time; + } else if (MLAN_ACT_SET == pioctl_req->action) { + pmpriv->tdls_idle_time = misc->param.tdls_idle_time; + } + LEAVE(); + return ret; +} + +/** + * @brief Set the TDLS operation to FW. + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +mlan_status +wlan_misc_ioctl_tdls_oper(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_misc_cfg *misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + mlan_ds_misc_tdls_oper *ptdls_oper = &misc->param.tdls_oper; + t_u8 event_buf[100]; + mlan_event *ptdls_event = (mlan_event *)event_buf; + tdls_tear_down_event *tdls_evt = + (tdls_tear_down_event *)ptdls_event->event_buf; + sta_node *sta_ptr = MNULL; + t_u8 i = 0; + + ENTER(); + sta_ptr = wlan_get_station_entry(pmpriv, ptdls_oper->peer_mac); + switch (ptdls_oper->tdls_action) { + case WLAN_TDLS_ENABLE_LINK: + if (sta_ptr && (sta_ptr->status != TDLS_SETUP_FAILURE)) { + PRINTM(MMSG, "TDLS: Enable link " MACSTR " success\n", + MAC2STR(ptdls_oper->peer_mac)); + sta_ptr->status = TDLS_SETUP_COMPLETE; + pmadapter->tdls_status = TDLS_IN_BASE_CHANNEL; + if (!pmpriv->txaggrctrl) + wlan_11n_send_delba_to_peer(pmpriv, + pmpriv-> + curr_bss_params. + bss_descriptor. + mac_address); + if (sta_ptr->HTcap.ieee_hdr.element_id == HT_CAPABILITY) { + sta_ptr->is_11n_enabled = MTRUE; + if (GETHT_MAXAMSDU + (sta_ptr->HTcap.ht_cap.ht_cap_info)) + sta_ptr->max_amsdu = + MLAN_TX_DATA_BUF_SIZE_8K; + else + sta_ptr->max_amsdu = + MLAN_TX_DATA_BUF_SIZE_4K; + for (i = 0; i < MAX_NUM_TID; i++) { + if (sta_ptr->is_11n_enabled) + sta_ptr->ampdu_sta[i] = + pmpriv-> + aggr_prio_tbl[i]. + ampdu_user; + else + sta_ptr->ampdu_sta[i] = + BA_STREAM_NOT_ALLOWED; + } + memset(pmpriv->adapter, sta_ptr->rx_seq, 0xff, + sizeof(sta_ptr->rx_seq)); + } + wlan_restore_tdls_packets(pmpriv, ptdls_oper->peer_mac, + TDLS_SETUP_COMPLETE); + if (ISSUPP_EXTCAP_TDLS_CHAN_SWITCH + (sta_ptr->ExtCap.ext_cap)) { + wlan_tdls_config(pmpriv, MTRUE); + wlan_tdls_cs_param_config(pmpriv); + /**tdls cs start*/ + if (pmpriv->tdls_cs_channel && + pmpriv->tdls_cs_channel != + pmpriv->curr_bss_params.bss_descriptor. + channel) + wlan_tdls_cs_start(pmpriv, + ptdls_oper->peer_mac, + MNULL); + } + } else { + PRINTM(MMSG, "TDLS: Enable link " MACSTR " fail\n", + MAC2STR(ptdls_oper->peer_mac)); + /*for supplicant 2.0, we need send event to request teardown, + **for latest supplicant, we only need return fail, and supplicant will send teardown packet and disable tdls link*/ + if (sta_ptr) { + ptdls_event->bss_index = pmpriv->bss_index; + ptdls_event->event_id = + MLAN_EVENT_ID_DRV_TDLS_TEARDOWN_REQ; + ptdls_event->event_len = + sizeof(tdls_tear_down_event); + memcpy(pmpriv->adapter, + (t_u8 *)tdls_evt->peer_mac_addr, + ptdls_oper->peer_mac, + MLAN_MAC_ADDR_LENGTH); + tdls_evt->reason_code = + MLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED; + wlan_recv_event(pmpriv, + MLAN_EVENT_ID_DRV_TDLS_TEARDOWN_REQ, + ptdls_event); + wlan_restore_tdls_packets(pmpriv, + ptdls_oper->peer_mac, + TDLS_TEAR_DOWN); + if (sta_ptr->is_11n_enabled) { + wlan_cleanup_reorder_tbl(pmpriv, + ptdls_oper-> + peer_mac); + wlan_11n_cleanup_txbastream_tbl(pmpriv, + ptdls_oper-> + peer_mac); + } + wlan_delete_station_entry(pmpriv, + ptdls_oper->peer_mac); + if (MTRUE == wlan_is_station_list_empty(pmpriv)) + pmadapter->tdls_status = TDLS_NOT_SETUP; + else + pmadapter->tdls_status = + TDLS_IN_BASE_CHANNEL; + } + ret = MLAN_STATUS_FAILURE; + } + wlan_recv_event(pmpriv, MLAN_EVENT_ID_DRV_DEFER_HANDLING, + MNULL); + break; + case WLAN_TDLS_DISABLE_LINK: + /* Send command to firmware to delete tdls link */ + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_TDLS_OPERATION, + HostCmd_ACT_GEN_SET, + 0, (t_void *)pioctl_req, ptdls_oper); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + break; + case WLAN_TDLS_CREATE_LINK: + PRINTM(MIOCTL, "CREATE TDLS LINK\n"); + if (sta_ptr && sta_ptr->status == TDLS_SETUP_INPROGRESS) { + PRINTM(MIOCTL, "We already create the link\n"); + break; + } + if (!sta_ptr) + sta_ptr = + wlan_add_station_entry(pmpriv, + misc->param.tdls_oper. + peer_mac); + if (sta_ptr) { + sta_ptr->status = TDLS_SETUP_INPROGRESS; + sta_ptr->external_tdls = MTRUE; + wlan_hold_tdls_packets(pmpriv, + misc->param.tdls_oper.peer_mac); + } + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_TDLS_OPERATION, + HostCmd_ACT_GEN_SET, + 0, (t_void *)pioctl_req, ptdls_oper); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + break; + case WLAN_TDLS_CONFIG_LINK: + if (!sta_ptr || sta_ptr->status == TDLS_SETUP_FAILURE) { + PRINTM(MERROR, "Can not CONFIG TDLS Link\n"); + ret = MLAN_STATUS_FAILURE; + break; + } + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_TDLS_OPERATION, + HostCmd_ACT_GEN_SET, + 0, (t_void *)pioctl_req, ptdls_oper); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + break; + case WLAN_TDLS_INIT_CHAN_SWITCH: + if (sta_ptr && + ISSUPP_EXTCAP_TDLS_CHAN_SWITCH(sta_ptr->ExtCap.ext_cap)) { + wlan_tdls_config(pmpriv, MTRUE); + wlan_tdls_cs_param_config(pmpriv); + /**tdls cs start*/ + wlan_tdls_cs_start(pmpriv, ptdls_oper->peer_mac, + pioctl_req); + } + wlan_recv_event(pmpriv, MLAN_EVENT_ID_DRV_DEFER_HANDLING, + MNULL); + break; + default: + break; + } + LEAVE(); + return ret; +} + +/** + * @brief Get AP's ext capability + * + * @param pmpriv A pointer to mlan_adapter structure + * @param ext_cap A pointer to ExtCap_t structure + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +void +wlan_get_ap_ext_cap(mlan_private *pmpriv, ExtCap_t *ext_cap) +{ + pmlan_adapter pmadapter = pmpriv->adapter; + BSSDescriptor_t *pbss_desc; + pbss_desc = &pmpriv->curr_bss_params.bss_descriptor; + memset(pmadapter, ext_cap, 0, sizeof(ExtCap_t)); + if (pbss_desc->pext_cap) { + memcpy(pmadapter, (t_u8 *)ext_cap, + (t_u8 *)pbss_desc->pext_cap + sizeof(IEEEtypes_Header_t), + pbss_desc->pext_cap->ieee_hdr.len); + } + return; +} + +/** + * @brief Set the TDLS operation to FW. + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +mlan_status +wlan_misc_ioctl_tdls_get_ies(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_misc_cfg *misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + mlan_ds_misc_tdls_ies *tdls_ies = &misc->param.tdls_ies; + mlan_status ret = MLAN_STATUS_SUCCESS; + BSSDescriptor_t *pbss_desc; + t_u32 usr_dot_11n_dev_cap; + IEEEtypes_ExtCap_t *ext_cap = MNULL; + ExtCap_t ap_ext_cap; + IEEEtypes_HTCap_t *ht_cap = MNULL; + IEEEtypes_HTInfo_t *ht_info = MNULL; + t_u8 supp_chan[] = { 1, 11 }; + t_u8 regulatory_class[] = { 1, + /**current class*/ + 1, 2, 3, 4, 12, 22, 23, 24, 25, 27, 28, 29, 30, 32, 33 + }; /**list regulatory class*/ + IEEEtypes_Generic_t *pSupp_chan = MNULL, *pRegulatory_class = MNULL; + sta_node *sta_ptr = MNULL; + ENTER(); + + /* We don't need peer information for TDLS setup */ + if (!(tdls_ies->flags & TDLS_IE_FLAGS_SETUP)) + sta_ptr = wlan_get_station_entry(pmpriv, tdls_ies->peer_mac); + pbss_desc = &pmpriv->curr_bss_params.bss_descriptor; + wlan_get_ap_ext_cap(pmpriv, &ap_ext_cap); + if (pbss_desc->bss_band & BAND_A) + usr_dot_11n_dev_cap = pmpriv->usr_dot_11n_dev_cap_a; + else + usr_dot_11n_dev_cap = pmpriv->usr_dot_11n_dev_cap_bg; + + /** fill the extcap */ + if (tdls_ies->flags & TDLS_IE_FLAGS_EXTCAP) { + ext_cap = (IEEEtypes_ExtCap_t *)tdls_ies->ext_cap; + ext_cap->ieee_hdr.element_id = EXT_CAPABILITY; + ext_cap->ieee_hdr.len = sizeof(ExtCap_t); + SET_EXTCAP_TDLS(ext_cap->ext_cap); + RESET_EXTCAP_TDLS_UAPSD(ext_cap->ext_cap); + RESET_EXTCAP_TDLS_CHAN_SWITCH(ext_cap->ext_cap); + + if (pmpriv->host_tdls_uapsd_support) { + /* uapsd in tdls confirm frame */ + if (tdls_ies->flags & TDLS_IE_FLAGS_HTINFO) { + if (sta_ptr && + ISSUPP_EXTCAP_TDLS_UAPSD(sta_ptr->ExtCap. + ext_cap)) + SET_EXTCAP_TDLS_UAPSD(ext_cap->ext_cap); + } else { + SET_EXTCAP_TDLS_UAPSD(ext_cap->ext_cap); + } + } + /* channel switch support */ + if (pmpriv->host_tdls_cs_support && + !IS_EXTCAP_TDLS_CHLSWITCHPROHIB(ap_ext_cap)) { + /* channel switch in tdls confirm frame */ + if (tdls_ies->flags & TDLS_IE_FLAGS_HTINFO) { + if (sta_ptr && + ISSUPP_EXTCAP_TDLS_CHAN_SWITCH(sta_ptr-> + ExtCap. + ext_cap)) + SET_EXTCAP_TDLS_CHAN_SWITCH(ext_cap-> + ext_cap); + } else { + SET_EXTCAP_TDLS_CHAN_SWITCH(ext_cap->ext_cap); + } + } + + DBG_HEXDUMP(MCMD_D, "TDLS extcap", tdls_ies->ext_cap, + sizeof(IEEEtypes_ExtCap_t)); + } + + /** default qos info is 0xf, compare with peer device qos info for tdls confirm */ + if (tdls_ies->flags & TDLS_IE_FLAGS_QOS_INFO) { + if (sta_ptr && sta_ptr->rate_len) + tdls_ies->QosInfo = sta_ptr->qos_info & 0xf; + PRINTM(MCMND, "TDLS Qos info=0x%x\n", tdls_ies->QosInfo); + } + + /** fill the htcap based on hwspec */ + if (tdls_ies->flags & TDLS_IE_FLAGS_HTCAP) { + ht_cap = (IEEEtypes_HTCap_t *)tdls_ies->ht_cap; + memset(pmadapter, ht_cap, 0, sizeof(IEEEtypes_HTCap_t)); + if ((sta_ptr && + !ISSUPP_EXTCAP_TDLS_CHAN_SWITCH(sta_ptr->ExtCap.ext_cap)) + || IS_EXTCAP_TDLS_CHLSWITCHPROHIB(ap_ext_cap)) + wlan_fill_ht_cap_ie(pmpriv, ht_cap, + pbss_desc->bss_band); + else if (pmpriv->host_tdls_cs_support && + (pmadapter->fw_bands & BAND_A)) + wlan_fill_ht_cap_ie(pmpriv, ht_cap, BAND_A); + else + wlan_fill_ht_cap_ie(pmpriv, ht_cap, + pbss_desc->bss_band); + DBG_HEXDUMP(MCMD_D, "TDLS htcap", tdls_ies->ht_cap, + sizeof(IEEEtypes_HTCap_t)); + } + /** fill the htinfo */ + if (tdls_ies->flags & TDLS_IE_FLAGS_HTINFO) { + ht_info = (IEEEtypes_HTInfo_t *)tdls_ies->ht_info; + pbss_desc = &pmpriv->curr_bss_params.bss_descriptor; + ht_info->ieee_hdr.element_id = HT_OPERATION; + ht_info->ieee_hdr.len = sizeof(HTInfo_t); + ht_info->ht_info.pri_chan = pbss_desc->channel; + /* follow AP's channel bandwidth */ + if (ISSUPP_CHANWIDTH40(usr_dot_11n_dev_cap) && + pbss_desc->pht_info && + ISALLOWED_CHANWIDTH40(pbss_desc->pht_info->ht_info. + field2)) { + ht_info->ht_info.field2 = + pbss_desc->pht_info->ht_info.field2; + } else { + ht_info->ht_info.field2 = + wlan_get_second_channel_offset(pbss_desc-> + channel); + } + if (sta_ptr) + memcpy(pmadapter, &sta_ptr->HTInfo, tdls_ies->ht_info, + sizeof(IEEEtypes_HTInfo_t)); + DBG_HEXDUMP(MCMD_D, "TDLS htinfo", tdls_ies->ht_info, + sizeof(IEEEtypes_HTInfo_t)); + } + + /** supported channels andl regulatory IE*/ + if (pmpriv->host_tdls_cs_support && + (tdls_ies->flags & TDLS_IE_FLAGS_SUPP_CS_IE) + && !IS_EXTCAP_TDLS_CHLSWITCHPROHIB(ap_ext_cap)) { + /** supported channels IE*/ + pSupp_chan = (IEEEtypes_Generic_t *)tdls_ies->supp_chan; + pSupp_chan->ieee_hdr.element_id = SUPPORTED_CHANNELS; + if (pmpriv->chan_supp_len) { + pSupp_chan->ieee_hdr.len = pmpriv->chan_supp_len; + memcpy(pmadapter, pSupp_chan->data, pmpriv->chan_supp, + pmpriv->chan_supp_len); + } else { + pSupp_chan->ieee_hdr.len = sizeof(supp_chan); + memcpy(pmadapter, pSupp_chan->data, supp_chan, + sizeof(supp_chan)); + } + DBG_HEXDUMP(MCMD_D, "TDLS supported channel", + tdls_ies->supp_chan, + pSupp_chan->ieee_hdr.len + + sizeof(IEEEtypes_Header_t)); + + /**fill supported Regulatory Class IE*/ + pRegulatory_class = + (IEEEtypes_Generic_t *)tdls_ies->regulatory_class; + pRegulatory_class->ieee_hdr.element_id = REGULATORY_CLASS; + if (pmpriv->supp_regulatory_class_len) { + pRegulatory_class->ieee_hdr.len = + pmpriv->supp_regulatory_class_len; + memcpy(pmadapter, pRegulatory_class->data, + pmpriv->supp_regulatory_class, + pmpriv->supp_regulatory_class_len); + } else { + pRegulatory_class->ieee_hdr.len = + sizeof(regulatory_class); + memcpy(pmadapter, pRegulatory_class->data, + regulatory_class, sizeof(regulatory_class)); + } + DBG_HEXDUMP(MCMD_D, "TDLS supported regulatory class", + tdls_ies->regulatory_class, + pRegulatory_class->ieee_hdr.len + + sizeof(IEEEtypes_Header_t)); + } + LEAVE(); + return ret; +} + +/** + * @brief Get extended version information + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +mlan_status +wlan_get_info_ver_ext(IN pmlan_adapter pmadapter, IN pmlan_ioctl_req pioctl_req) +{ + pmlan_private pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_get_info *pinfo = (mlan_ds_get_info *)pioctl_req->pbuf; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_VERSION_EXT, + HostCmd_ACT_GEN_GET, + 0, + (t_void *)pioctl_req, + &pinfo->param.ver_ext.version_str_sel); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +#ifdef DEBUG_LEVEL1 +/** + * @brief Set driver debug bit masks in order to enhance performance + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +mlan_status +wlan_set_drvdbg(IN pmlan_adapter pmadapter, IN pmlan_ioctl_req pioctl_req) +{ + mlan_ds_misc_cfg *misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + /* Set driver debug bit masks */ + mlan_drvdbg = misc->param.drvdbg; + + LEAVE(); + return ret; +} +#endif + +/** + * @brief Rx mgmt frame forward register + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +mlan_status +wlan_reg_rx_mgmt_ind(IN pmlan_adapter pmadapter, IN pmlan_ioctl_req pioctl_req) +{ + pmlan_private pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_misc_cfg *misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + /* Set passthru mask for mgmt frame */ + pmpriv->mgmt_frame_passthru_mask = misc->param.mgmt_subtype_mask; + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_RX_MGMT_IND, + pioctl_req->action, + 0, + (t_void *)pioctl_req, + &misc->param.mgmt_subtype_mask); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief This function processes the 802.11 mgmt Frame + * + * @param priv A pointer to mlan_private + * + * @param payload A pointer to the received buffer + * @param payload_len Length of the received buffer + * @param prx_pd A pointer to RxPD + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_process_802dot11_mgmt_pkt(IN mlan_private *priv, + IN t_u8 *payload, + IN t_u32 payload_len, IN RxPD *prx_pd) +{ + pmlan_adapter pmadapter = priv->adapter; + pmlan_callbacks pcb = &pmadapter->callbacks; + mlan_status ret = MLAN_STATUS_SUCCESS; + wlan_802_11_header *pieee_pkt_hdr = MNULL; + t_u16 sub_type = 0; + t_u8 *event_buf = MNULL; + mlan_event *pevent = MNULL; + t_u8 unicast = 0; + IEEE80211_MGMT *mgmt = MNULL; + t_u8 category = 0; + t_u8 action_code = 0; + struct timestamps tstamps; + ENTER(); + if (payload_len > (MAX_EVENT_SIZE - sizeof(mlan_event))) { + PRINTM(MERROR, "Dropping large mgmt frame,len =%d\n", + payload_len); + LEAVE(); + return ret; + } + /* Check packet type-subtype and compare with mgmt_passthru_mask + * If event is needed to host, just eventify it */ + pieee_pkt_hdr = (wlan_802_11_header *)payload; + sub_type = IEEE80211_GET_FC_MGMT_FRAME_SUBTYPE(pieee_pkt_hdr->frm_ctl); + if (((1 << sub_type) & priv->mgmt_frame_passthru_mask) == 0) { + PRINTM(MINFO, "Dropping mgmt frame for subtype %d snr=%d.\n", + sub_type, prx_pd->snr); + LEAVE(); + return ret; + } + switch (sub_type) { + case SUBTYPE_ASSOC_REQUEST: + case SUBTYPE_REASSOC_REQUEST: + case SUBTYPE_DISASSOC: + case SUBTYPE_DEAUTH: + case SUBTYPE_AUTH: + case SUBTYPE_PROBE_RESP: + unicast = MTRUE; + break; + case SUBTYPE_ACTION: + category = *(payload + sizeof(wlan_802_11_header)); + action_code = *(payload + sizeof(wlan_802_11_header) + 1); + if (category == IEEE_MGMT_ACTION_CATEGORY_BLOCK_ACK) { + PRINTM(MINFO, + "Drop BLOCK ACK action frame: action_code=%d\n", + action_code); + LEAVE(); + return ret; + } + if ((category == IEEE_MGMT_ACTION_CATEGORY_PUBLIC) && + (action_code == BSS_20_40_COEX)) { + PRINTM(MINFO, + "Drop 20/40 BSS Coexistence Management frame\n"); + LEAVE(); + return ret; + } + if ((category == CATEGORY_PUBLIC) && + (action_code == TDLS_DISCOVERY_RESPONSE)) { + pcb->moal_updata_peer_signal(pmadapter->pmoal_handle, + priv->bss_index, + pieee_pkt_hdr->addr2, + prx_pd->snr, prx_pd->nf); + PRINTM(MINFO, + "Rx: TDLS discovery response, nf=%d, snr=%d\n", + prx_pd->nf, prx_pd->snr); + } + if ((category == IEEE_MGMT_ACTION_CATEGORY_UNPROTECT_WNM) && + (action_code == 0x1)) { + prx_pd->toa_tod_tstamps = + wlan_le64_to_cpu(prx_pd->toa_tod_tstamps); + tstamps.t3 = prx_pd->toa_tod_tstamps >> 32; + tstamps.t2 = (t_u32)prx_pd->toa_tod_tstamps; + tstamps.t2_err = 0; + tstamps.t3_err = 0; + tstamps.ingress_time = pmadapter->host_bbu_clk_delta; + pcb->moal_do_div(tstamps.ingress_time, 10); + tstamps.ingress_time += tstamps.t2; //t2, t3 is 10ns and delta is in 1 ns unit; + PRINTM(MINFO, "T2: %d, T3: %d, ingress: %lu\n", + tstamps.t2, tstamps.t3, tstamps.ingress_time); + } + unicast = MTRUE; + break; + default: + break; + } + if (unicast == MTRUE) { + if (memcmp + (pmadapter, pieee_pkt_hdr->addr1, priv->curr_addr, + MLAN_MAC_ADDR_LENGTH)) { + PRINTM(MINFO, + "Dropping mgmt frame for others: type=%d " MACSTR + "\n", sub_type, MAC2STR(pieee_pkt_hdr->addr1)); + LEAVE(); + return ret; + } + } + /* Allocate memory for event buffer */ + ret = pcb->moal_malloc(pmadapter->pmoal_handle, MAX_EVENT_SIZE, + MLAN_MEM_DEF, &event_buf); + if ((ret != MLAN_STATUS_SUCCESS) || !event_buf) { + PRINTM(MERROR, "Could not allocate buffer for event buf\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + pevent = (pmlan_event)event_buf; + pevent->bss_index = priv->bss_index; + mgmt = (IEEE80211_MGMT *)payload; + if (sub_type == SUBTYPE_ACTION && + mgmt->u.ft_resp.category == FT_CATEGORY && + mgmt->u.ft_resp.action == FT_ACTION_RESPONSE && + mgmt->u.ft_resp.status_code == 0) { + PRINTM(MCMND, "FT Action response received\n"); +#define FT_ACTION_HEAD_LEN (24 + 6 +16) + pevent->event_id = MLAN_EVENT_ID_DRV_FT_RESPONSE; + pevent->event_len = + payload_len + MLAN_MAC_ADDR_LENGTH - FT_ACTION_HEAD_LEN; + memcpy(pmadapter, (t_u8 *)pevent->event_buf, + &mgmt->u.ft_resp.target_ap_addr, MLAN_MAC_ADDR_LENGTH); + memcpy(pmadapter, + (t_u8 *)(pevent->event_buf + MLAN_MAC_ADDR_LENGTH), + payload + FT_ACTION_HEAD_LEN, + payload_len - FT_ACTION_HEAD_LEN); + } else if (sub_type == SUBTYPE_AUTH && + mgmt->u.auth.auth_alg == MLAN_AUTH_MODE_FT && + mgmt->u.auth.auth_transaction == 2 && + mgmt->u.auth.status_code == 0) { + PRINTM(MCMND, "FT auth response received \n"); +#define AUTH_PACKET_LEN (24 + 6 +6) + pevent->event_id = MLAN_EVENT_ID_DRV_FT_RESPONSE; + pevent->event_len = + payload_len + MLAN_MAC_ADDR_LENGTH - AUTH_PACKET_LEN; + memcpy(pmadapter, (t_u8 *)pevent->event_buf, mgmt->sa, + MLAN_MAC_ADDR_LENGTH); + memcpy(pmadapter, + (t_u8 *)(pevent->event_buf + MLAN_MAC_ADDR_LENGTH), + payload + AUTH_PACKET_LEN, + payload_len - AUTH_PACKET_LEN); + } else { + pevent->event_id = MLAN_EVENT_ID_DRV_MGMT_FRAME; + pevent->event_len = payload_len + sizeof(pevent->event_id); + memcpy(pmadapter, (t_u8 *)pevent->event_buf, + (t_u8 *)&pevent->event_id, sizeof(pevent->event_id)); + memcpy(pmadapter, + (t_u8 *)(pevent->event_buf + sizeof(pevent->event_id)), + payload, payload_len); + //Append timestamp info at the end of event + if ((category == IEEE_MGMT_ACTION_CATEGORY_UNPROTECT_WNM) && + (action_code == 0x1)) { + memcpy(pmadapter, + (t_u8 *)(pevent->event_buf + + sizeof(pevent->event_id) + payload_len), + &tstamps, sizeof(struct timestamps)); + pevent->event_len = + payload_len + sizeof(pevent->event_id) + + sizeof(struct timestamps); + } + + } + wlan_recv_event(priv, pevent->event_id, pevent); + if (event_buf) + pcb->moal_mfree(pmadapter->pmoal_handle, event_buf); + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +#ifdef STA_SUPPORT +/** + * @brief Extended capabilities configuration + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +mlan_status +wlan_misc_ext_capa_cfg(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + pmlan_private pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_misc_cfg *misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (MLAN_ACT_GET == pioctl_req->action) + memcpy(pmpriv->adapter, &misc->param.ext_cap, &pmpriv->ext_cap, + sizeof(misc->param.ext_cap)); + else if (MLAN_ACT_SET == pioctl_req->action) { + memcpy(pmpriv->adapter, &pmpriv->ext_cap, &misc->param.ext_cap, + sizeof(misc->param.ext_cap)); + /* Save default Extended Capability */ + memcpy(pmpriv->adapter, &pmpriv->def_ext_cap, &pmpriv->ext_cap, + sizeof(pmpriv->ext_cap)); + } + + LEAVE(); + return ret; +} + +/** + * @brief Check whether Extended Capabilities IE support + * + * @param pmpriv A pointer to mlan_private structure + * + * @return MTRUE or MFALSE; + */ +t_u32 +wlan_is_ext_capa_support(mlan_private *pmpriv) +{ + ENTER(); + + if (ISSUPP_EXTCAP_TDLS(pmpriv->ext_cap) + || ISSUPP_EXTCAP_INTERWORKING(pmpriv->ext_cap) + || ISSUPP_EXTCAP_BSS_TRANSITION(pmpriv->ext_cap) + || ISSUPP_EXTCAP_QOS_MAP(pmpriv->ext_cap) + ) { + LEAVE(); + return MTRUE; + } else { + LEAVE(); + return MFALSE; + } +} +#endif + +#ifdef STA_SUPPORT +/** + * @brief Add Extended Capabilities IE + * + * @param pmpriv A pointer to mlan_private structure + * @param pptlv_out A pointer to TLV to fill in + * + * @return N/A + */ +void +wlan_add_ext_capa_info_ie(IN mlan_private *pmpriv, OUT t_u8 **pptlv_out) +{ + MrvlIETypes_ExtCap_t *pext_cap = MNULL; + + ENTER(); + + pext_cap = (MrvlIETypes_ExtCap_t *)*pptlv_out; + memset(pmpriv->adapter, pext_cap, 0, sizeof(MrvlIETypes_ExtCap_t)); + pext_cap->header.type = wlan_cpu_to_le16(EXT_CAPABILITY); + pext_cap->header.len = wlan_cpu_to_le16(sizeof(ExtCap_t)); + if (pmpriv->adapter->ecsa_enable) + SET_EXTCAP_EXT_CHANNEL_SWITCH(pmpriv->ext_cap); + else + RESET_EXTCAP_EXT_CHANNEL_SWITCH(pmpriv->ext_cap); + SET_EXTCAP_FTMI(pmpriv->ext_cap); + SET_EXTCAP_INTERNETWORKING(pmpriv->ext_cap); + memcpy(pmpriv->adapter, &pext_cap->ext_cap, &pmpriv->ext_cap, + sizeof(pmpriv->ext_cap)); + *pptlv_out += sizeof(MrvlIETypes_ExtCap_t); + + LEAVE(); +} +#endif + +/** + * @brief Get OTP user data + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +mlan_status +wlan_misc_otp_user_data(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + pmlan_private pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_misc_cfg *misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + mlan_status ret = MLAN_STATUS_FAILURE; + + ENTER(); + + if (misc->param.otp_user_data.user_data_length > MAX_OTP_USER_DATA_LEN) { + PRINTM(MERROR, "Invalid OTP user data length\n"); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + LEAVE(); + return ret; + } + + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_OTP_READ_USER_DATA, + HostCmd_ACT_GEN_GET, + 0, + (t_void *)pioctl_req, + &misc->param.otp_user_data); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief This function will search for the specific ie + * + * @param priv A pointer to mlan_private + * @param pevent A pointer to event buf + * @param sta_ptr A pointer to sta_node + * + * @return N/A + */ +void +wlan_check_sta_capability(pmlan_private priv, pmlan_buffer pevent, + sta_node *sta_ptr) +{ + t_u16 tlv_type, tlv_len; + t_u16 frame_control, frame_sub_type = 0; + t_u8 *assoc_req_ie = MNULL; + t_u8 ie_len = 0, assoc_ie_len = 0; + IEEEtypes_HTCap_t *pht_cap = MNULL; +#ifdef UAP_SUPPORT + t_u8 *ext_rate = MNULL, *erp = MNULL; +#endif + + int tlv_buf_left = pevent->data_len - ASSOC_EVENT_FIX_SIZE; + MrvlIEtypesHeader_t *tlv = (MrvlIEtypesHeader_t *) + (pevent->pbuf + pevent->data_offset + ASSOC_EVENT_FIX_SIZE); + MrvlIETypes_MgmtFrameSet_t *mgmt_tlv = MNULL; + + ENTER(); + while (tlv_buf_left >= (int)sizeof(MrvlIEtypesHeader_t)) { + tlv_type = wlan_le16_to_cpu(tlv->type); + tlv_len = wlan_le16_to_cpu(tlv->len); + if ((sizeof(MrvlIEtypesHeader_t) + tlv_len) > + (unsigned int)tlv_buf_left) { + PRINTM(MERROR, "wrong tlv: tlvLen=%d, tlvBufLeft=%d\n", + tlv_len, tlv_buf_left); + break; + } + if (tlv_type == TLV_TYPE_MGMT_FRAME) { + mgmt_tlv = (MrvlIETypes_MgmtFrameSet_t *)tlv; + memcpy(priv->adapter, &frame_control, + (t_u8 *)&(mgmt_tlv->frame_control), + sizeof(frame_control)); + frame_sub_type = + IEEE80211_GET_FC_MGMT_FRAME_SUBTYPE + (frame_control); + if ((mgmt_tlv->frame_control.type == 0) && + ((frame_sub_type == SUBTYPE_BEACON) +#ifdef UAP_SUPPORT + || (frame_sub_type == SUBTYPE_ASSOC_REQUEST) + || (frame_sub_type == SUBTYPE_REASSOC_REQUEST) +#endif + )) { + if (frame_sub_type == SUBTYPE_BEACON) + assoc_ie_len = + sizeof(IEEEtypes_Beacon_t); +#ifdef UAP_SUPPORT + else if (frame_sub_type == + SUBTYPE_ASSOC_REQUEST) + assoc_ie_len = + sizeof(IEEEtypes_AssocRqst_t); + else if (frame_sub_type == + SUBTYPE_REASSOC_REQUEST) + assoc_ie_len = + sizeof(IEEEtypes_ReAssocRqst_t); +#endif + ie_len = tlv_len - + sizeof(IEEEtypes_FrameCtl_t) - + assoc_ie_len; + assoc_req_ie = + (t_u8 *)tlv + + sizeof(MrvlIETypes_MgmtFrameSet_t) + + assoc_ie_len; + sta_ptr->is_wmm_enabled = + wlan_is_wmm_ie_present(priv->adapter, + assoc_req_ie, + ie_len); + PRINTM(MCMND, "STA: is_wmm_enabled=%d\n", + sta_ptr->is_wmm_enabled); + pht_cap = + (IEEEtypes_HTCap_t *) + wlan_get_specific_ie(priv, assoc_req_ie, + ie_len, + HT_CAPABILITY); + if (pht_cap) { + PRINTM(MCMND, "STA supports 11n\n"); + sta_ptr->is_11n_enabled = MTRUE; + memcpy(priv->adapter, + (t_u8 *)&sta_ptr->HTcap, pht_cap, + sizeof(IEEEtypes_HTCap_t)); + if (GETHT_MAXAMSDU + (wlan_le16_to_cpu + (pht_cap->ht_cap.ht_cap_info))) + sta_ptr->max_amsdu = + MLAN_TX_DATA_BUF_SIZE_8K; + else + sta_ptr->max_amsdu = + MLAN_TX_DATA_BUF_SIZE_4K; + } else { + PRINTM(MCMND, + "STA doesn't support 11n\n"); + } +#ifdef UAP_SUPPORT + /* Note: iphone6 does not have ERP_INFO */ + ext_rate = + wlan_get_specific_ie(priv, assoc_req_ie, + ie_len, + EXTENDED_SUPPORTED_RATES); + erp = wlan_get_specific_ie(priv, assoc_req_ie, + ie_len, ERP_INFO); + if (!ext_rate) + PRINTM(MCMND, + "STA doesn't support EXTENDED_SUPPORTED_RATES\n"); + if (!erp) + PRINTM(MCMND, + "STA doesn't support ERP_INFO\n"); + if (sta_ptr->is_11n_enabled) { + if (priv->uap_channel <= 14) + sta_ptr->bandmode = BAND_GN; + else + sta_ptr->bandmode = BAND_AN; + } else if (ext_rate || erp) { + if (priv->uap_channel <= 14) + sta_ptr->bandmode = BAND_G; + else + sta_ptr->bandmode = BAND_A; + } else + sta_ptr->bandmode = BAND_B; +#endif +#ifdef DRV_EMBEDDED_AUTHENTICATOR + if (IsAuthenticatorEnabled(priv->psapriv)) + authenticator_get_sta_security_info + (priv->psapriv, + sta_ptr->cm_connectioninfo, + assoc_req_ie, ie_len); +#endif + break; + } + } + tlv_buf_left -= (sizeof(MrvlIEtypesHeader_t) + tlv_len); + tlv = (MrvlIEtypesHeader_t *)((t_u8 *)tlv + tlv_len + + sizeof(MrvlIEtypesHeader_t)); + } + LEAVE(); + + return; +} + +/** + * @brief check if WMM ie present. + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pbuf A pointer to IE buffer + * @param buf_len IE buffer len + * + * @return MTRUE/MFALSE + */ +t_u8 +wlan_is_wmm_ie_present(pmlan_adapter pmadapter, t_u8 *pbuf, t_u16 buf_len) +{ + t_u16 bytes_left = buf_len; + IEEEtypes_ElementId_e element_id; + t_u8 *pcurrent_ptr = pbuf; + t_u8 element_len; + t_u16 total_ie_len; + IEEEtypes_VendorSpecific_t *pvendor_ie; + const t_u8 wmm_oui[4] = { 0x00, 0x50, 0xf2, 0x02 }; + t_u8 find_wmm_ie = MFALSE; + + ENTER(); + + /* Process variable IE */ + while (bytes_left >= 2) { + element_id = (IEEEtypes_ElementId_e)(*((t_u8 *)pcurrent_ptr)); + element_len = *((t_u8 *)pcurrent_ptr + 1); + total_ie_len = element_len + sizeof(IEEEtypes_Header_t); + + if (bytes_left < total_ie_len) { + PRINTM(MERROR, "InterpretIE: Error in processing IE, " + "bytes left < IE length\n"); + bytes_left = 0; + continue; + } + switch (element_id) { + case VENDOR_SPECIFIC_221: + pvendor_ie = (IEEEtypes_VendorSpecific_t *)pcurrent_ptr; + if (!memcmp + (pmadapter, pvendor_ie->vend_hdr.oui, wmm_oui, + sizeof(wmm_oui))) { + find_wmm_ie = MTRUE; + PRINTM(MINFO, "find WMM IE\n"); + } + break; + default: + break; + } + pcurrent_ptr += element_len + 2; + /* Need to account for IE ID and IE Len */ + bytes_left -= (element_len + 2); + if (find_wmm_ie) + break; + } + + LEAVE(); + return find_wmm_ie; +} + +/** + * @brief This function will search for the specific ie + * + * + * @param priv A pointer to mlan_private + * @param ie_buf A pointer to ie_buf + * @param ie_len total ie length + * @param id ie's id + * + * @return ie's poiner or MNULL + */ +t_u8 * +wlan_get_specific_ie(pmlan_private priv, t_u8 *ie_buf, t_u8 ie_len, + IEEEtypes_ElementId_e id) +{ + t_u32 bytes_left = ie_len; + t_u8 *pcurrent_ptr = ie_buf; + t_u16 total_ie_len; + t_u8 *ie_ptr = MNULL; + IEEEtypes_ElementId_e element_id; + t_u8 element_len; + + ENTER(); + + DBG_HEXDUMP(MCMD_D, "ie", ie_buf, ie_len); + while (bytes_left >= 2) { + element_id = (IEEEtypes_ElementId_e)(*((t_u8 *)pcurrent_ptr)); + element_len = *((t_u8 *)pcurrent_ptr + 1); + total_ie_len = element_len + sizeof(IEEEtypes_Header_t); + if (bytes_left < total_ie_len) { + PRINTM(MERROR, "InterpretIE: Error in processing IE, " + "bytes left < IE length\n"); + break; + } + if (element_id == id) { + PRINTM(MCMND, "Find IE: id=%d\n", id); + DBG_HEXDUMP(MCMND, "IE", pcurrent_ptr, total_ie_len); + ie_ptr = pcurrent_ptr; + break; + } + pcurrent_ptr += element_len + 2; + /* Need to account for IE ID and IE Len */ + bytes_left -= (element_len + 2); + } + + LEAVE(); + + return ie_ptr; +} + +/** + * @brief Get pm info + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success + */ +mlan_status +wlan_get_pm_info(IN pmlan_adapter pmadapter, IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_pm_cfg *pm_cfg = MNULL; + + ENTER(); + + pm_cfg = (mlan_ds_pm_cfg *)pioctl_req->pbuf; + pm_cfg->param.ps_info.is_suspend_allowed = MTRUE; + wlan_request_cmd_lock(pmadapter); + if (util_peek_list(pmadapter->pmoal_handle, &pmadapter->cmd_pending_q, + MNULL, MNULL) || pmadapter->curr_cmd + || !wlan_bypass_tx_list_empty(pmadapter) + || !wlan_wmm_lists_empty(pmadapter) + || pmadapter->sdio_ireg) { + pm_cfg->param.ps_info.is_suspend_allowed = MFALSE; + PRINTM(MIOCTL, + "PM: cmd_pending_q=%p,curr_cmd=%p,wmm_list_empty=%d, by_pass=%d sdio_ireg=0x%x\n", + util_peek_list(pmadapter->pmoal_handle, + &pmadapter->cmd_pending_q, MNULL, MNULL), + pmadapter->curr_cmd, wlan_wmm_lists_empty(pmadapter), + wlan_bypass_tx_list_empty(pmadapter), + pmadapter->sdio_ireg); + } + wlan_release_cmd_lock(pmadapter); + LEAVE(); + return ret; +} + +/** + * @brief Get hs wakeup reason + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success + */ +mlan_status +wlan_get_hs_wakeup_reason(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + pmlan_private pmpriv = pmadapter->priv[pioctl_req->bss_index]; + pmlan_ds_pm_cfg pm_cfg = MNULL; + mlan_status ret = MLAN_STATUS_FAILURE; + + ENTER(); + + pm_cfg = (mlan_ds_pm_cfg *)pioctl_req->pbuf; + + /* Send command to firmware */ + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_HS_WAKEUP_REASON, + HostCmd_ACT_GEN_GET, + 0, + (t_void *)pioctl_req, + &pm_cfg->param.wakeup_reason); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; + +} + +/** + * @brief Set/Get radio status + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +mlan_status +wlan_radio_ioctl_radio_ctl(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_radio_cfg *radio_cfg = MNULL; + t_u16 cmd_action = 0; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + + ENTER(); + + radio_cfg = (mlan_ds_radio_cfg *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_SET) { + if (pmadapter->radio_on == radio_cfg->param.radio_on_off) { + ret = MLAN_STATUS_SUCCESS; + goto exit; + } else { + if (pmpriv->media_connected == MTRUE) { + ret = MLAN_STATUS_FAILURE; + goto exit; + } + cmd_action = HostCmd_ACT_GEN_SET; + } + } else + cmd_action = HostCmd_ACT_GEN_GET; + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_802_11_RADIO_CONTROL, + cmd_action, + 0, + (t_void *)pioctl_req, + &radio_cfg->param.radio_on_off); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + +exit: + LEAVE(); + return ret; +} + +/** + * @brief Set/Get antenna configuration + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +mlan_status +wlan_radio_ioctl_ant_cfg(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_radio_cfg *radio_cfg = MNULL; + t_u16 cmd_action = 0; + mlan_ds_ant_cfg_1x1 *ant_cfg; + + ENTER(); + + radio_cfg = (mlan_ds_radio_cfg *)pioctl_req->pbuf; + + if (pioctl_req->action == MLAN_ACT_SET) { + /* User input validation */ + if (!radio_cfg->param.ant_cfg_1x1.antenna || + ((radio_cfg->param.ant_cfg_1x1.antenna != RF_ANTENNA_AUTO) + && (radio_cfg->param.ant_cfg_1x1.antenna & 0xFFFC))) { + PRINTM(MERROR, "Invalid antenna setting\n"); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_FAILURE; + goto exit; + } + cmd_action = HostCmd_ACT_GEN_SET; + } else + cmd_action = HostCmd_ACT_GEN_GET; + + /* Cast it to t_u16, antenna mode for command HostCmd_CMD_802_11_RF_ANTENNA requires 2 bytes */ + ant_cfg = &radio_cfg->param.ant_cfg_1x1; + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_802_11_RF_ANTENNA, + cmd_action, + 0, (t_void *)pioctl_req, (t_void *)ant_cfg); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + +exit: + LEAVE(); + return ret; +} + +/** + * @brief Get rate bitmap + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status +wlan_rate_ioctl_get_rate_bitmap(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + + ENTER(); + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_TX_RATE_CFG, + HostCmd_ACT_GEN_GET, + 0, (t_void *)pioctl_req, MNULL); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Set rate bitmap + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status +wlan_rate_ioctl_set_rate_bitmap(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_ds_rate *ds_rate = MNULL; + mlan_status ret = MLAN_STATUS_FAILURE; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + t_u16 *bitmap_rates = MNULL; + + ENTER(); + + ds_rate = (mlan_ds_rate *)pioctl_req->pbuf; + bitmap_rates = ds_rate->param.rate_cfg.bitmap_rates; + + PRINTM(MINFO, "RateBitmap=%04x%04x%04x%04x%04x%04x%04x%04x" + "%04x%04x%04x%04x%04x%04x%04x%04x%04x%04x, " + "IsRateAuto=%d, DataRate=%d\n", + bitmap_rates[17], bitmap_rates[16], + bitmap_rates[15], bitmap_rates[14], + bitmap_rates[13], bitmap_rates[12], + bitmap_rates[11], bitmap_rates[10], + bitmap_rates[9], bitmap_rates[8], + bitmap_rates[7], bitmap_rates[6], + bitmap_rates[5], bitmap_rates[4], + bitmap_rates[3], bitmap_rates[2], + bitmap_rates[1], bitmap_rates[0], + pmpriv->is_data_rate_auto, pmpriv->data_rate); + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_TX_RATE_CFG, + HostCmd_ACT_GEN_SET, + 0, (t_void *)pioctl_req, (t_void *)bitmap_rates); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Get rate value + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status +wlan_rate_ioctl_get_rate_value(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_ds_rate *rate = MNULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + + ENTER(); + + rate = (mlan_ds_rate *)pioctl_req->pbuf; + rate->param.rate_cfg.is_rate_auto = pmpriv->is_data_rate_auto; + pioctl_req->data_read_written = + sizeof(mlan_rate_cfg_t) + MLAN_SUB_COMMAND_SIZE; + + /* If not connected, set rate to the lowest in each band */ + if (pmpriv->media_connected != MTRUE) { + if (pmpriv->config_bands & (BAND_B | BAND_G)) { + /* Return the lowest supported rate for BG band */ + rate->param.rate_cfg.rate = SupportedRates_BG[0] & 0x7f; + } else if (pmpriv->config_bands & (BAND_A | BAND_B)) { + /* Return the lowest supported rate for A band */ + rate->param.rate_cfg.rate = SupportedRates_BG[0] & 0x7f; + } else if (pmpriv->config_bands & BAND_A) { + /* Return the lowest supported rate for A band */ + rate->param.rate_cfg.rate = SupportedRates_A[0] & 0x7f; + } else if (pmpriv->config_bands & BAND_G) { + /* Return the lowest supported rate for G band */ + rate->param.rate_cfg.rate = SupportedRates_G[0] & 0x7f; + } else if (pmpriv->config_bands & BAND_B) { + /* Return the lowest supported rate for B band */ + rate->param.rate_cfg.rate = SupportedRates_B[0] & 0x7f; + } else if (pmpriv->config_bands & BAND_GN) { + /* Return the lowest supported rate for N band */ + rate->param.rate_cfg.rate = SupportedRates_N[0] & 0x7f; + } else { + PRINTM(MMSG, "Invalid Band 0x%x\n", + pmpriv->config_bands); + } + + } else { + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_802_11_TX_RATE_QUERY, + HostCmd_ACT_GEN_GET, + 0, (t_void *)pioctl_req, MNULL); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + } + + LEAVE(); + return ret; +} + +/** + * @brief Set rate value + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status +wlan_rate_ioctl_set_rate_value(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_ds_rate *ds_rate = MNULL; + WLAN_802_11_RATES rates; + t_u8 *rate = MNULL; + int rate_index = 0; + t_u16 bitmap_rates[MAX_BITMAP_RATES_SIZE]; + t_u32 i = 0; + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + + ENTER(); + + ds_rate = (mlan_ds_rate *)pioctl_req->pbuf; + + if (ds_rate->param.rate_cfg.is_rate_auto) { + memset(pmadapter, bitmap_rates, 0, sizeof(bitmap_rates)); + /* Support all HR/DSSS rates */ + bitmap_rates[0] = 0x000F; + /* Support all OFDM rates */ + bitmap_rates[1] = 0x00FF; + /* Rates talbe [0] HR/DSSS,[1] OFDM,[2..9] HT,[10..17] VHT */ + /* Support all HT-MCSs rate */ + for (i = 0; i < NELEMENTS(pmpriv->bitmap_rates) - 3 - 8; i++) + bitmap_rates[i + 2] = 0xFFFF; + bitmap_rates[9] = 0x3FFF; + /* Set to 0 as default value */ + for (i = 0; i < NELEMENTS(pmpriv->bitmap_rates) - 10; i++) + bitmap_rates[i + 10] = 0x0; + } else { + memset(pmadapter, rates, 0, sizeof(rates)); + wlan_get_active_data_rates(pmpriv, pmpriv->bss_mode, + (pmpriv->bss_mode == + MLAN_BSS_MODE_INFRA) ? pmpriv-> + config_bands : pmadapter-> + adhoc_start_band, rates); + rate = rates; + for (i = 0; (rate[i] && i < WLAN_SUPPORTED_RATES); i++) { + PRINTM(MINFO, "Rate=0x%X Wanted=0x%X\n", rate[i], + ds_rate->param.rate_cfg.rate); + if ((rate[i] & 0x7f) == + (ds_rate->param.rate_cfg.rate & 0x7f)) + break; + } + if (!rate[i] || (i == WLAN_SUPPORTED_RATES)) { + PRINTM(MERROR, "The fixed data rate 0x%X is out " + "of range\n", ds_rate->param.rate_cfg.rate); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_FAILURE; + goto exit; + } + memset(pmadapter, bitmap_rates, 0, sizeof(bitmap_rates)); + + rate_index = + wlan_data_rate_to_index(pmadapter, + ds_rate->param.rate_cfg.rate); + + /* Only allow b/g rates to be set */ + if (rate_index >= MLAN_RATE_INDEX_HRDSSS0 && + rate_index <= MLAN_RATE_INDEX_HRDSSS3) + bitmap_rates[0] = 1 << rate_index; + else { + rate_index -= 1; /* There is a 0x00 in the table */ + if (rate_index >= MLAN_RATE_INDEX_OFDM0 && + rate_index <= MLAN_RATE_INDEX_OFDM7) + bitmap_rates[1] = + 1 << (rate_index - + MLAN_RATE_INDEX_OFDM0); + } + } + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_TX_RATE_CFG, + HostCmd_ACT_GEN_SET, + 0, (t_void *)pioctl_req, bitmap_rates); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + +exit: + LEAVE(); + return ret; +} + +/** + * @brief Get rate index + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status +wlan_rate_ioctl_get_rate_index(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + + ENTER(); + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_TX_RATE_CFG, + HostCmd_ACT_GEN_GET, + 0, (t_void *)pioctl_req, MNULL); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Set rate index + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status +wlan_rate_ioctl_set_rate_index(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + t_u32 rate_index; + t_u32 rate_format; + t_u32 i; + mlan_ds_rate *ds_rate = MNULL; + mlan_status ret = MLAN_STATUS_FAILURE; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + t_u16 bitmap_rates[MAX_BITMAP_RATES_SIZE]; + + ENTER(); + + ds_rate = (mlan_ds_rate *)pioctl_req->pbuf; + rate_format = ds_rate->param.rate_cfg.rate_format; + rate_index = ds_rate->param.rate_cfg.rate; + + if (ds_rate->param.rate_cfg.is_rate_auto) { + memset(pmadapter, bitmap_rates, 0, sizeof(bitmap_rates)); + /* Rates talbe [0]: HR/DSSS;[1]: OFDM; [2..9] HT; */ + /* Support all HR/DSSS rates */ + bitmap_rates[0] = 0x000F; + /* Support all OFDM rates */ + bitmap_rates[1] = 0x00FF; + /* Support all HT-MCSs rate */ + for (i = 2; i < 9; i++) + bitmap_rates[i] = 0xFFFF; + bitmap_rates[9] = 0x3FFF; + } else { + PRINTM(MINFO, "Rate index is %d\n", rate_index); + memset(pmadapter, bitmap_rates, 0, sizeof(bitmap_rates)); + if (rate_format == MLAN_RATE_FORMAT_LG) { + /* Bitmap of HR/DSSS rates */ + if ((rate_index >= MLAN_RATE_INDEX_HRDSSS0) && + (rate_index <= MLAN_RATE_INDEX_HRDSSS3)) { + bitmap_rates[0] = 1 << rate_index; + ret = MLAN_STATUS_SUCCESS; + /* Bitmap of OFDM rates */ + } else if ((rate_index >= MLAN_RATE_INDEX_OFDM0) && + (rate_index <= MLAN_RATE_INDEX_OFDM7)) { + bitmap_rates[1] = + 1 << (rate_index - + MLAN_RATE_INDEX_OFDM0); + ret = MLAN_STATUS_SUCCESS; + } + } else if (rate_format == MLAN_RATE_FORMAT_HT) { + if ((rate_index >= MLAN_RATE_INDEX_MCS0) && + (rate_index <= MLAN_RATE_INDEX_MCS32)) { + bitmap_rates[2 + (rate_index / 16)] = + 1 << (rate_index % 16); + ret = MLAN_STATUS_SUCCESS; + } + } + + if (ret == MLAN_STATUS_FAILURE) { + PRINTM(MERROR, "Invalid MCS index=%d. \n", rate_index); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + LEAVE(); + return MLAN_STATUS_FAILURE; + } + } + + PRINTM(MINFO, "RateBitmap=%04x%04x%04x%04x%04x%04x%04x%04x" + "%04x%04x%04x%04x%04x%04x%04x%04x%04x%04x, " + "IsRateAuto=%d, DataRate=%d\n", + bitmap_rates[17], bitmap_rates[16], + bitmap_rates[15], bitmap_rates[14], + bitmap_rates[13], bitmap_rates[12], + bitmap_rates[11], bitmap_rates[10], + bitmap_rates[9], bitmap_rates[8], + bitmap_rates[7], bitmap_rates[6], + bitmap_rates[5], bitmap_rates[4], + bitmap_rates[3], bitmap_rates[2], + bitmap_rates[1], bitmap_rates[0], + pmpriv->is_data_rate_auto, pmpriv->data_rate); + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_TX_RATE_CFG, + HostCmd_ACT_GEN_SET, + 0, (t_void *)pioctl_req, (t_void *)bitmap_rates); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Rate configuration command handler + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING --success, otherwise fail + */ +mlan_status +wlan_rate_ioctl_cfg(IN pmlan_adapter pmadapter, IN pmlan_ioctl_req pioctl_req) +{ + mlan_ds_rate *rate = MNULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + rate = (mlan_ds_rate *)pioctl_req->pbuf; + if (rate->param.rate_cfg.rate_type == MLAN_RATE_BITMAP) { + if (pioctl_req->action == MLAN_ACT_GET) + status = wlan_rate_ioctl_get_rate_bitmap(pmadapter, + pioctl_req); + else + status = wlan_rate_ioctl_set_rate_bitmap(pmadapter, + pioctl_req); + } else if (rate->param.rate_cfg.rate_type == MLAN_RATE_VALUE) { + if (pioctl_req->action == MLAN_ACT_GET) + status = wlan_rate_ioctl_get_rate_value(pmadapter, + pioctl_req); + else + status = wlan_rate_ioctl_set_rate_value(pmadapter, + pioctl_req); + } else { + if (pioctl_req->action == MLAN_ACT_GET) + status = wlan_rate_ioctl_get_rate_index(pmadapter, + pioctl_req); + else + status = wlan_rate_ioctl_set_rate_index(pmadapter, + pioctl_req); + } + + LEAVE(); + return status; +} + +/** + * @brief Get data rates + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +mlan_status +wlan_rate_ioctl_get_data_rate(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (pioctl_req->action != MLAN_ACT_GET) { + ret = MLAN_STATUS_FAILURE; + goto exit; + } + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_802_11_TX_RATE_QUERY, + HostCmd_ACT_GEN_GET, + 0, (t_void *)pioctl_req, MNULL); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + +exit: + LEAVE(); + return ret; +} + +/** + * @brief Set/Get remain on channel setting + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +mlan_status +wlan_radio_ioctl_remain_chan_cfg(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_radio_cfg *radio_cfg = MNULL; + t_u16 cmd_action = 0; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + + ENTER(); + + radio_cfg = (mlan_ds_radio_cfg *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_SET) + cmd_action = HostCmd_ACT_GEN_SET; + else + cmd_action = HostCmd_ACT_GEN_GET; + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_802_11_REMAIN_ON_CHANNEL, + cmd_action, + 0, + (t_void *)pioctl_req, + &radio_cfg->param.remain_chan); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +#ifdef WIFI_DIRECT_SUPPORT +/** + * @brief Set/Get wifi_direct_mode + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +mlan_status +wlan_bss_ioctl_wifi_direct_mode(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_bss *bss = MNULL; + + t_u16 cmd_action = 0; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + + ENTER(); + + bss = (mlan_ds_bss *)pioctl_req->pbuf; + + if (pioctl_req->action == MLAN_ACT_SET) + cmd_action = HostCmd_ACT_GEN_SET; + else + cmd_action = HostCmd_ACT_GEN_GET; + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, + HOST_CMD_WIFI_DIRECT_MODE_CONFIG, + cmd_action, + 0, (t_void *)pioctl_req, &bss->param.wfd_mode); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Set/Get p2p config + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +mlan_status +wlan_misc_p2p_config(IN pmlan_adapter pmadapter, IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_misc_cfg *misc_cfg = MNULL; + t_u16 cmd_action = 0; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + + ENTER(); + + misc_cfg = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_SET) + cmd_action = HostCmd_ACT_GEN_SET; + else + cmd_action = HostCmd_ACT_GEN_GET; + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, + HOST_CMD_P2P_PARAMS_CONFIG, + cmd_action, + 0, + (t_void *)pioctl_req, + &misc_cfg->param.p2p_config); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} +#endif + +/** + * @brief Set coalesce config + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +mlan_status +wlan_misc_ioctl_coalesce_cfg(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_misc_cfg *misc_cfg = MNULL; + t_u16 cmd_action = 0; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + + ENTER(); + + misc_cfg = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_SET) + cmd_action = HostCmd_ACT_GEN_SET; + else + cmd_action = HostCmd_ACT_GEN_GET; + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_COALESCE_CFG, + cmd_action, + 0, + (t_void *)pioctl_req, + &misc_cfg->param.coalesce_cfg); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Get/Set Tx control configuration + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_misc_ioctl_txcontrol(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_misc_cfg *misc = MNULL; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + + ENTER(); + + misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_SET) + pmpriv->pkt_tx_ctrl = misc->param.tx_control; + else + misc->param.tx_control = pmpriv->pkt_tx_ctrl; + + LEAVE(); + return ret; +} + +#ifdef RX_PACKET_COALESCE +/** + * @brief Get/Set RX packet coalescing configuration + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_misc_ioctl_rx_pkt_coalesce_config(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_misc_cfg *misc = MNULL; + t_u16 cmd_action = 0; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + + ENTER(); + + misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + + if (pioctl_req->action == MLAN_ACT_SET) + cmd_action = HostCmd_ACT_GEN_SET; + else + cmd_action = HostCmd_ACT_GEN_GET; + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_RX_PKT_COALESCE_CFG, + cmd_action, + 0, + (t_void *)pioctl_req, &misc->param.rx_coalesce); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} +#endif +/** + * @brief Get/Set channel time and buffer weight configuration + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_misc_ioctl_multi_chan_config(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_misc_cfg *misc = MNULL; + t_u16 cmd_action = 0; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + + ENTER(); + + misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + + if (pioctl_req->action == MLAN_ACT_SET) + cmd_action = HostCmd_ACT_GEN_SET; + else + cmd_action = HostCmd_ACT_GEN_GET; + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_MULTI_CHAN_CONFIG, + cmd_action, + 0, + (t_void *)pioctl_req, + &misc->param.multi_chan_cfg); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Get/Set multi-channel policy setting + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_misc_ioctl_multi_chan_policy(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_misc_cfg *misc = MNULL; + t_u16 cmd_action = 0; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + + ENTER(); + + misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + + if (pioctl_req->action == MLAN_ACT_SET) { + if (pmadapter->dfs_repeater) { + PRINTM(MMSG, + "DFS-Repeater is on, can not enable DRCS\n"); + ret = MLAN_STATUS_FAILURE; + goto fail; + } + cmd_action = HostCmd_ACT_GEN_SET; + } else { + cmd_action = HostCmd_ACT_GEN_GET; + } + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_MULTI_CHAN_POLICY, + cmd_action, + 0, + (t_void *)pioctl_req, + &misc->param.multi_chan_policy); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; +fail: + LEAVE(); + return ret; +} + +/** + * @brief Get/Set DRCS configuration + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_misc_ioctl_drcs_config(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_misc_cfg *misc = MNULL; + t_u16 cmd_action = 0; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + + ENTER(); + + misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + + if (pioctl_req->action == MLAN_ACT_SET) + cmd_action = HostCmd_ACT_GEN_SET; + else + cmd_action = HostCmd_ACT_GEN_GET; + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_DRCS_CONFIG, + cmd_action, + 0, (t_void *)pioctl_req, &misc->param.drcs_cfg); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Is any uAP started or STA connected? + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return MTRUE/MFALSE + */ +t_bool +wlan_check_interface_active(mlan_adapter *pmadapter) +{ + t_bool ret = MFALSE; + pmlan_private pmpriv; + int i; + + if (pmadapter == MNULL) + return MFALSE; + + for (i = 0; i < pmadapter->priv_num; i++) { + pmpriv = pmadapter->priv[i]; + if (pmpriv) { +#ifdef UAP_SUPPORT + if (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_UAP) + ret = pmpriv->uap_bss_started; + else +#endif + if (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_STA) + ret = pmpriv->media_connected; + } + if (ret) + return MTRUE; + } + + return MFALSE; +} + +/** + * @brief Get/Set DFS REPEATER mode + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_misc_ioctl_dfs_repeater_cfg(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_misc_cfg *misc = MNULL; + t_u16 cmd_action = 0; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + + ENTER(); + + misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + + if (pioctl_req->action == MLAN_ACT_SET) { + /* Make sure no interface is active + * before setting the dfs repeater mode + */ + if (wlan_check_interface_active(pmadapter)) { + PRINTM(MMSG, "DFS-Repeater active priv found," + " skip enabling the mode.\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* If DRCS is on then we should not set + * DFS-repeater mode */ + if (pmadapter->mc_policy) { + PRINTM(MERROR, + "DFS-repeater cannot be started when DRCS is on\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + cmd_action = HostCmd_ACT_GEN_SET; + } else { + cmd_action = HostCmd_ACT_GEN_GET; + } + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, HostCmd_DFS_REPEATER_MODE, cmd_action, 0, + (t_void *)pioctl_req, &misc->param.dfs_repeater); + +done: + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Configure PMIC in Firmware + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_misc_ioctl_pmic_configure(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + + ENTER(); + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, + HOST_CMD_PMIC_CONFIGURE, + HostCmd_ACT_GEN_SET, + 0, (t_void *)pioctl_req, MNULL); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/* @brief Set/Get CW Mode Level control + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_misc_ioctl_cwmode_ctrl(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_misc_cfg *misc = MNULL; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + t_u16 cmd_action = 0; + + ENTER(); + + misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + + if (pioctl_req->action == MLAN_ACT_SET) + cmd_action = HostCmd_ACT_GEN_SET; + else + cmd_action = HostCmd_ACT_GEN_GET; + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_CW_MODE_CTRL, + cmd_action, + 0, (t_void *)pioctl_req, &misc->param.cwmode); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Set/Get WPA passphrase for esupplicant + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +mlan_status +wlan_sec_ioctl_passphrase(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_sec_cfg *sec = MNULL; + t_u16 cmd_action = 0; +#ifdef STA_SUPPORT + BSSDescriptor_t *pbss_desc; + int i = 0; +#endif + ENTER(); + + sec = (mlan_ds_sec_cfg *)pioctl_req->pbuf; +#ifdef DRV_EMBEDDED_SUPPLICANT + if (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_STA && + !IS_FW_SUPPORT_SUPPLICANT(pmpriv->adapter)) { + if (sec->param.passphrase.psk_type == MLAN_PSK_QUERY) + SupplicantQueryPassphrase(pmpriv->psapriv, + (void *)&sec->param. + passphrase); + else if (sec->param.passphrase.psk_type == MLAN_PSK_CLEAR) + SupplicantClearPMK(pmpriv->psapriv, + (void *)&sec->param.passphrase); + else + SupplicantSetPassphrase(pmpriv->psapriv, + (void *)&sec->param.passphrase); + + LEAVE(); + return ret; + } +#endif + + if (pioctl_req->action == MLAN_ACT_SET) { + if (sec->param.passphrase.psk_type == MLAN_PSK_CLEAR) + cmd_action = HostCmd_ACT_GEN_REMOVE; + else + cmd_action = HostCmd_ACT_GEN_SET; + } else if (pioctl_req->action == MLAN_ACT_CLEAR) { + cmd_action = HostCmd_ACT_GEN_REMOVE; + } else { + if (sec->param.passphrase.psk_type == MLAN_PSK_QUERY) { +#ifdef STA_SUPPORT + if (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_STA && + sec->param.passphrase.ssid.ssid_len == 0) { + i = wlan_find_bssid_in_list(pmpriv, + (t_u8 *)&sec->param. + passphrase.bssid, + MLAN_BSS_MODE_AUTO); + if (i >= 0) { + pbss_desc = &pmadapter->pscan_table[i]; + memcpy(pmadapter, + &sec->param.passphrase.ssid, + &pbss_desc->ssid, + sizeof(mlan_802_11_ssid)); + memset(pmadapter, + &sec->param.passphrase.bssid, 0, + MLAN_MAC_ADDR_LENGTH); + PRINTM(MINFO, + "PSK_QUERY: found ssid=%s\n", + sec->param.passphrase.ssid.ssid); + } + } else +#endif + memset(pmadapter, &sec->param.passphrase.bssid, + 0, MLAN_MAC_ADDR_LENGTH); + } + cmd_action = HostCmd_ACT_GEN_GET; + } + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_SUPPLICANT_PMK, + cmd_action, + 0, (t_void *)pioctl_req, (t_void *)sec); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Set per packet Txctl and Rxinfo configuration + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +mlan_status +wlan_misc_per_pkt_cfg(IN pmlan_adapter pmadapter, IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_misc_cfg *misc = MNULL; + + ENTER(); + + misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + pmpriv->rx_pkt_info = MFALSE; + if (misc->param.txrx_pkt_ctrl & RX_PKT_INFO) + pmpriv->rx_pkt_info = MTRUE; + + LEAVE(); + return ret; +} + +/** + * @brief Set/Get region code + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +mlan_status +wlan_misc_ioctl_region(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_misc_cfg *misc = MNULL; + int i; + + ENTER(); + + misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_GET) { + misc->param.region_code = pmadapter->region_code; + } else { + if (pmadapter->otp_region && pmadapter->otp_region->force_reg) { + PRINTM(MERROR, + "ForceRegionRule is set in the on-chip OTP" + " memory\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + for (i = 0; i < MRVDRV_MAX_REGION_CODE; i++) { + /* Use the region code to search for the index */ + if (misc->param.region_code == region_code_index[i]) { + pmadapter->region_code = + (t_u16)misc->param.region_code; + break; + } + } + /* It's unidentified region code */ + if (i >= MRVDRV_MAX_REGION_CODE) { + PRINTM(MERROR, "Region Code not identified\n"); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + LEAVE(); + return MLAN_STATUS_FAILURE; + } + pmadapter->cfp_code_bg = misc->param.region_code; + pmadapter->cfp_code_a = misc->param.region_code; + if (wlan_set_regiontable(pmpriv, (t_u8)pmadapter->region_code, + pmadapter->config_bands | pmadapter-> + adhoc_start_band)) { + pioctl_req->status_code = MLAN_ERROR_IOCTL_FAIL; + ret = MLAN_STATUS_FAILURE; + } + } + pioctl_req->data_read_written = sizeof(t_u32) + MLAN_SUB_COMMAND_SIZE; + + LEAVE(); + return ret; +} + +/** + * @brief Configure GPIO independent reset + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +mlan_status +wlan_misc_ioctl_ind_rst_cfg(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_misc_cfg *misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u16 cmd_action = 0; + + ENTER(); + + if (pioctl_req->action == MLAN_ACT_GET) + cmd_action = HostCmd_ACT_GEN_GET; + else + cmd_action = HostCmd_ACT_GEN_SET; + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_INDEPENDENT_RESET_CFG, + cmd_action, 0, + (t_void *)pioctl_req, + (t_void *)&misc->param.ind_rst_cfg); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Get timestamp from firmware + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +mlan_status +wlan_misc_ioctl_get_tsf(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u16 cmd_action = 0; + + ENTER(); + + if (pioctl_req->action == MLAN_ACT_GET) + cmd_action = HostCmd_ACT_GEN_GET; + else { + PRINTM(MERROR, "No support set tsf!"); + return MLAN_STATUS_FAILURE; + } + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_GET_TSF, + cmd_action, 0, (t_void *)pioctl_req, MNULL); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Create custom regulatory cfg + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +mlan_status +wlan_misc_chan_reg_cfg(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u16 cmd_action = 0; + + ENTER(); + + if (pioctl_req->action == MLAN_ACT_GET) + cmd_action = HostCmd_ACT_GEN_GET; + else { + PRINTM(MERROR, "No support set channel region cfg!"); + return MLAN_STATUS_FAILURE; + } + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_CHAN_REGION_CFG, + cmd_action, 0, (t_void *)pioctl_req, MNULL); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Check operating class validation + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req Pointer to the IOCTL request buffer + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_misc_ioctl_operclass_validation(IN pmlan_adapter pmadapter, + IN mlan_ioctl_req *pioctl_req) +{ + pmlan_private pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_misc_cfg *misc = MNULL; + t_u8 channel, oper_class; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + channel = misc->param.bw_chan_oper.channel; + oper_class = misc->param.bw_chan_oper.oper_class; + if (pioctl_req->action == MLAN_ACT_GET) { + ret = wlan_check_operclass_validation(pmpriv, channel, + oper_class); + } else { + PRINTM(MERROR, "Unsupported cmd_action\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + LEAVE(); + return ret; +} + +/** + * @brief Get non-global operating class + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req Pointer to the IOCTL request buffer + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_misc_ioctl_oper_class(IN pmlan_adapter pmadapter, + IN mlan_ioctl_req *pioctl_req) +{ + pmlan_private pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_misc_cfg *misc = MNULL; + t_u8 channel, bandwidth, oper_class; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + channel = misc->param.bw_chan_oper.channel; + switch (misc->param.bw_chan_oper.bandwidth) { + case 20: + bandwidth = BW_20MHZ; + break; + case 40: + bandwidth = BW_40MHZ; + break; + case 80: + bandwidth = BW_80MHZ; + break; + default: + bandwidth = BW_20MHZ; + break; + } + + if (pioctl_req->action == MLAN_ACT_GET) { + ret = wlan_get_curr_oper_class(pmpriv, channel, bandwidth, + &oper_class); + misc->param.bw_chan_oper.oper_class = oper_class; + } else { + PRINTM(MERROR, "Unsupported cmd_action\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + LEAVE(); + return ret; +} + +/** + * @brief config dynamic bandwidth + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req Pointer to the IOCTL request buffer + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_misc_ioctl_fw_dump_event(IN pmlan_adapter pmadapter, + IN mlan_ioctl_req *pioctl_req) +{ + pmlan_private pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_misc_cfg *misc = MNULL; + t_u16 cmd_action = 0; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + + if (pioctl_req->action == MLAN_ACT_SET) + cmd_action = HostCmd_ACT_GEN_SET; + else if (pioctl_req->action == MLAN_ACT_GET) + cmd_action = HostCmd_ACT_GEN_GET; + else { + PRINTM(MERROR, "Unsupported cmd_action\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_FW_DUMP_EVENT, + cmd_action, 0, (t_void *)pioctl_req, MNULL); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Set/Get the network monitor configuration. + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +mlan_status +wlan_misc_ioctl_net_monitor(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv; + mlan_ds_misc_cfg *misc; + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u16 cmd_action = 0; + + ENTER(); + + if (!pioctl_req) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + pmpriv = pmadapter->priv[pioctl_req->bss_index]; + + misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + + if (misc->param.net_mon.enable_net_mon == CHANNEL_SPEC_SNIFFER_MODE) { + /* Net monitor IOCTL not allowed in connected state */ + if (pmpriv->media_connected == MTRUE) { + PRINTM(MERROR, + "IOCTL not allowed in connected state\n"); + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + LEAVE(); + return MLAN_STATUS_FAILURE; + } + } + + if (pioctl_req->action == MLAN_ACT_SET) + cmd_action = HostCmd_ACT_GEN_SET; + else + cmd_action = HostCmd_ACT_GEN_GET; + + /* Send command to firmware */ + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_802_11_NET_MONITOR, + cmd_action, + 0, (t_void *)pioctl_req, &misc->param.net_mon); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief config boot sleep + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req Pointer to the IOCTL request buffer + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_misc_bootsleep(IN pmlan_adapter pmadapter, IN pmlan_ioctl_req pioctl_req) +{ + pmlan_private pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_misc_cfg *misc = MNULL; + t_u16 cmd_action = 0; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + + if (pioctl_req->action == MLAN_ACT_SET) + cmd_action = HostCmd_ACT_GEN_SET; + else if (pioctl_req->action == MLAN_ACT_GET) + cmd_action = HostCmd_ACT_GEN_GET; + else { + PRINTM(MERROR, "Unsupported cmd_action 0x%x\n", + pioctl_req->action); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_BOOT_SLEEP, + cmd_action, + 0, + (t_void *)pioctl_req, &misc->param.boot_sleep); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_module.c b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_module.c new file mode 100644 index 000000000000..9d707ea401b2 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_module.c @@ -0,0 +1,54 @@ +/** @file mlan_module.c + * + * @brief This file declares the exported symbols from MLAN. + * + * Copyright (C) 2008-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 12/08/2008: initial version +******************************************************/ + +#ifdef LINUX +#include +#include "mlan_decl.h" +#include "mlan_ioctl.h" + +EXPORT_SYMBOL(mlan_register); +EXPORT_SYMBOL(mlan_unregister); +EXPORT_SYMBOL(mlan_init_fw); +EXPORT_SYMBOL(mlan_set_init_param); +EXPORT_SYMBOL(mlan_dnld_fw); +EXPORT_SYMBOL(mlan_shutdown_fw); +EXPORT_SYMBOL(mlan_send_packet); +EXPORT_SYMBOL(mlan_ioctl); +EXPORT_SYMBOL(mlan_main_process); +EXPORT_SYMBOL(mlan_rx_process); +EXPORT_SYMBOL(mlan_select_wmm_queue); +EXPORT_SYMBOL(mlan_interrupt); +#if defined(SYSKT) +EXPORT_SYMBOL(mlan_hs_callback); +#endif /* SYSKT_MULTI || SYSKT */ + +EXPORT_SYMBOL(mlan_pm_wakeup_card); +EXPORT_SYMBOL(mlan_is_main_process_running); + +MODULE_DESCRIPTION("M-WLAN MLAN Driver"); +MODULE_AUTHOR("Marvell International Ltd."); +MODULE_VERSION(MLAN_RELEASE_VERSION); +MODULE_LICENSE("GPL"); +#endif /* LINUX */ diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_scan.c b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_scan.c new file mode 100644 index 000000000000..6c46e9cd9bde --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_scan.c @@ -0,0 +1,5531 @@ +/** @file mlan_scan.c + * + * @brief Functions implementing wlan scan IOCTL and firmware command APIs + * + * IOCTL handlers as well as command preparation and response routines + * for sending scan commands to the firmware. + * + * Copyright (C) 2008-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 10/28/2008: initial version +******************************************************/ + +#include "mlan.h" +#include "mlan_join.h" +#include "mlan_util.h" +#include "mlan_fw.h" +#include "mlan_main.h" +#include "mlan_11n.h" +#include "mlan_11h.h" +#ifdef DRV_EMBEDDED_SUPPLICANT +#include "authenticator_api.h" +#endif +/******************************************************** + Local Constants +********************************************************/ +#define MRVDRV_MAX_CHANNELS_PER_SCAN 40 +/** The maximum number of channels the firmware can scan per command */ +#define MRVDRV_MAX_CHANNELS_PER_SPECIFIC_SCAN 4 + +/** + * Number of channels to scan per firmware scan command issuance. + * + * Number restricted to prevent hitting the limit on the amount of scan data + * returned in a single firmware scan command. + */ +#define MRVDRV_CHANNELS_PER_SCAN_CMD 4 + +/** Memory needed to store a max sized Channel List TLV for a firmware scan */ +#define CHAN_TLV_MAX_SIZE (sizeof(MrvlIEtypesHeader_t) \ + + (MRVDRV_MAX_CHANNELS_PER_SPECIFIC_SCAN \ + * sizeof(ChanScanParamSet_t))) + +/** Memory needed to store supported rate */ +#define RATE_TLV_MAX_SIZE (sizeof(MrvlIEtypes_RatesParamSet_t) + HOSTCMD_SUPPORTED_RATES) + +/** Memory needed to store a max number/size WildCard + * SSID TLV for a firmware scan */ +#define WILDCARD_SSID_TLV_MAX_SIZE \ + (MRVDRV_MAX_SSID_LIST_LENGTH * \ + (sizeof(MrvlIEtypes_WildCardSsIdParamSet_t) + \ + MRVDRV_MAX_SSID_LENGTH)) + +/** WPS TLV MAX size is MAX IE size plus 2 bytes for + * t_u16 MRVL TLV extension */ +#define WPS_TLV_MAX_SIZE (sizeof(IEEEtypes_VendorSpecific_t) + 2) +/** Maximum memory needed for a wlan_scan_cmd_config + * with all TLVs at max */ +#define MAX_SCAN_CFG_ALLOC (sizeof(wlan_scan_cmd_config) \ + + sizeof(MrvlIEtypes_NumProbes_t) \ + + sizeof(MrvlIETypes_HTCap_t) \ + + CHAN_TLV_MAX_SIZE \ + + RATE_TLV_MAX_SIZE \ + + WILDCARD_SSID_TLV_MAX_SIZE \ + + WPS_TLV_MAX_SIZE) + +/******************************************************** + Local Variables +********************************************************/ + +/** + * Interally used to send a configured scan cmd between + * driver routines + */ +typedef union { + /** Scan configuration (variable length) */ + wlan_scan_cmd_config config; + /** Max allocated block */ + t_u8 config_alloc_buf[MAX_SCAN_CFG_ALLOC]; +} wlan_scan_cmd_config_tlv; + +/******************************************************** + Global Variables +********************************************************/ + +/******************************************************** + Local Functions +********************************************************/ +/** Cipher suite definition */ +enum cipher_suite { + CIPHER_SUITE_WEP40, + CIPHER_SUITE_TKIP, + CIPHER_SUITE_CCMP, + CIPHER_SUITE_WEP104, + CIPHER_SUITE_MAX +}; + +static t_u8 wpa_oui[CIPHER_SUITE_MAX][4] = { + {0x00, 0x50, 0xf2, 0x01}, /* WEP40 */ + {0x00, 0x50, 0xf2, 0x02}, /* TKIP */ + {0x00, 0x50, 0xf2, 0x04}, /* AES */ + {0x00, 0x50, 0xf2, 0x05}, /* WEP104 */ +}; + +static t_u8 rsn_oui[CIPHER_SUITE_MAX][4] = { + {0x00, 0x0f, 0xac, 0x01}, /* WEP40 */ + {0x00, 0x0f, 0xac, 0x02}, /* TKIP */ + {0x00, 0x0f, 0xac, 0x04}, /* AES */ + {0x00, 0x0f, 0xac, 0x05}, /* WEP104 */ +}; + +/** + * @brief Convert radio type scan parameter to a band config used in join cmd + * + * @param radio_type Scan parameter indicating the radio used for a channel + * in a scan command. + * + * @return Band type conversion of scanBand used in join/assoc cmds + * + */ +t_u8 +radio_type_to_band(t_u8 radio_type) +{ + t_u8 ret_band; + + switch (radio_type) { + case BAND_5GHZ: + ret_band = BAND_A; + break; + case BAND_2GHZ: + default: + ret_band = BAND_G; + break; + } + + return ret_band; +} + +/** + * @brief This function will update the channel statistics from scan result + * + * @param pmpriv A pointer to mlan_private structure + * @param pchanstats_tlv A pointer to MrvlIEtypes_ChannelStats_t tlv + * + * @return NA + */ +void +wlan_update_chan_statistics(mlan_private *pmpriv, + MrvlIEtypes_ChannelStats_t *pchanstats_tlv) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + t_u8 i; + ChanStatistics_t *pchan_stats = + (ChanStatistics_t *)((t_u8 *)pchanstats_tlv + + sizeof(MrvlIEtypesHeader_t)); + t_u8 num_chan = + wlan_le16_to_cpu(pchanstats_tlv->header.len) / + sizeof(ChanStatistics_t); + + ENTER(); + + for (i = 0; i < num_chan; i++) { + if (pmadapter->idx_chan_stats >= pmadapter->num_in_chan_stats) { + PRINTM(MERROR, + "Over flow: idx_chan_stats=%d, num_in_chan_stats=%d\n", + pmadapter->idx_chan_stats, + pmadapter->num_in_chan_stats); + break; + } + pchan_stats->total_networks = + wlan_le16_to_cpu(pchan_stats->total_networks); + pchan_stats->cca_scan_duration = + wlan_le16_to_cpu(pchan_stats->cca_scan_duration); + pchan_stats->cca_busy_duration = + wlan_le16_to_cpu(pchan_stats->cca_busy_duration); + PRINTM(MCMND, + "chan=%d, noise=%d, total_network=%d scan_duration=%d, busy_duration=%d\n", + pchan_stats->chan_num, pchan_stats->noise, + pchan_stats->total_networks, + pchan_stats->cca_scan_duration, + pchan_stats->cca_busy_duration); + memcpy(pmadapter, + &pmadapter->pchan_stats[pmadapter->idx_chan_stats], + pchan_stats, sizeof(ChanStatistics_t)); + pmadapter->idx_chan_stats++; + pchan_stats++; + } + LEAVE(); + return; +} + +/** + * @brief This function will parse a given IE for a given OUI + * + * Parse a given WPA/RSN IE to find if it has a given oui in PTK, + * if no OUI found for PTK it returns 0. + * + * @param pbss_desc A pointer to current BSS descriptor + * @return 0 on failure to find OUI, 1 on success. + */ +static t_u8 +search_oui_in_ie(mlan_adapter *pmadapter, IEBody *ie_body, t_u8 *oui) +{ + t_u8 count; + + count = ie_body->PtkCnt[0]; + + ENTER(); + /* There could be multiple OUIs for PTK hence + * 1) Take the length. + * 2) Check all the OUIs for AES. + * 3) If one of them is AES then pass success. + */ + while (count) { + if (!memcmp + (pmadapter, ie_body->PtkBody, oui, + sizeof(ie_body->PtkBody))) { + LEAVE(); + return MLAN_OUI_PRESENT; + } + + --count; + if (count) { + ie_body = (IEBody *)((t_u8 *)ie_body + + sizeof(ie_body->PtkBody)); + } + } + + PRINTM(MINFO, "The OUI %x:%x:%x:%x is not found in PTK\n", oui[0], + oui[1], oui[2], oui[3]); + LEAVE(); + return MLAN_OUI_NOT_PRESENT; +} + +/** + * @brief This function will pass the correct ie and oui to search_oui_in_ie + * + * Check the pbss_desc for appropriate IE and then check if RSN IE has AES + * OUI in it. If RSN IE does not have AES in PTK then return 0; + * + * @param pbss_desc A pointer to current BSS descriptor + * @return 0 on failure to find AES OUI, 1 on success. + */ +static t_u8 +is_rsn_oui_present(mlan_adapter *pmadapter, BSSDescriptor_t *pbss_desc, + t_u32 cipher_suite) +{ + t_u8 *oui = MNULL; + IEBody *ie_body = MNULL; + t_u8 ret = MLAN_OUI_NOT_PRESENT; + + ENTER(); + if (((pbss_desc->prsn_ie) && + ((*(pbss_desc->prsn_ie)).ieee_hdr.element_id == RSN_IE))) { + ie_body = + (IEBody *)(((t_u8 *)pbss_desc->prsn_ie->data) + + RSN_GTK_OUI_OFFSET); + oui = &rsn_oui[cipher_suite][0]; + ret = search_oui_in_ie(pmadapter, ie_body, oui); + if (ret) { + LEAVE(); + return ret; + } + } + LEAVE(); + return ret; +} + +/** + * @brief This function will pass the correct ie and oui to search_oui_in_ie + * + * Check the pbss_desc for appropriate IE and then check if WPA IE has AES + * OUI in it. If WPA IE does not have AES in PTK then return 0; + * + * @param pbss_desc A pointer to current BSS descriptor + * @return 0 on failure to find AES OUI, 1 on success. + */ +static t_u8 +is_wpa_oui_present(mlan_adapter *pmadapter, BSSDescriptor_t *pbss_desc, + t_u32 cipher_suite) +{ + t_u8 *oui = MNULL; + IEBody *ie_body = MNULL; + t_u8 ret = MLAN_OUI_NOT_PRESENT; + + ENTER(); + if (((pbss_desc->pwpa_ie) && + ((*(pbss_desc->pwpa_ie)).vend_hdr.element_id == WPA_IE))) { + ie_body = (IEBody *)pbss_desc->pwpa_ie->data; + oui = &wpa_oui[cipher_suite][0]; + ret = search_oui_in_ie(pmadapter, ie_body, oui); + if (ret) { + LEAVE(); + return ret; + } + } + LEAVE(); + return ret; +} + +/** + * @brief compare config band and a band from the scan result, + * which is defined by functiion radio_type_to_band(t_u8 radio_type) above + * + * @param cfg_band: band configured + * scan_band: band from scan result + * + * @return matched: non-zero. unmatched: 0 + * + */ +static t_u8 +wlan_is_band_compatible(t_u8 cfg_band, t_u8 scan_band) +{ + t_u8 band; + switch (scan_band) { + case BAND_A: + band = BAND_A | BAND_AN; + break; + case BAND_G: + default: + band = BAND_B | BAND_G | BAND_GN; + } + return cfg_band & band; +} + +/** + * @brief This function finds the best SSID in the Scan List + * + * Search the scan table for the best SSID that also matches the current + * adapter network preference (infrastructure or adhoc) + * + * @param pmpriv A pointer to mlan_private structure + * @return index in BSSID list + */ +static t_s32 +wlan_find_best_network_in_list(IN mlan_private *pmpriv) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + t_u32 mode = pmpriv->bss_mode; + t_s32 best_net = -1; + t_s32 best_rssi = 0; + t_u32 i; + + ENTER(); + + PRINTM(MINFO, "Num of BSSIDs = %d\n", pmadapter->num_in_scan_table); + + for (i = 0; i < pmadapter->num_in_scan_table; i++) { + switch (mode) { + case MLAN_BSS_MODE_INFRA: + case MLAN_BSS_MODE_IBSS: + if (wlan_is_network_compatible(pmpriv, i, mode) >= 0) { + if (SCAN_RSSI(pmadapter->pscan_table[i].rssi) > + best_rssi) { + best_rssi = + SCAN_RSSI(pmadapter-> + pscan_table[i].rssi); + best_net = i; + } + } + break; + case MLAN_BSS_MODE_AUTO: + default: + if (SCAN_RSSI(pmadapter->pscan_table[i].rssi) > + best_rssi) { + best_rssi = + SCAN_RSSI(pmadapter->pscan_table[i]. + rssi); + best_net = i; + } + break; + } + } + + LEAVE(); + return best_net; +} + +/** + * @brief Create a channel list for the driver to scan based on region info + * + * Use the driver region/band information to construct a comprehensive list + * of channels to scan. This routine is used for any scan that is not + * provided a specific channel list to scan. + * + * @param pmpriv A pointer to mlan_private structure + * @param puser_scan_in MNULL or pointer to scan configuration parameters + * @param pscan_chan_list Output parameter: Resulting channel list to scan + * @param filtered_scan Flag indicating whether or not a BSSID or SSID filter + * is being sent in the command to firmware. Used to + * increase the number of channels sent in a scan + * command and to disable the firmware channel scan + * filter. + * + * @return N/A + */ +static t_void +wlan_scan_create_channel_list(IN mlan_private *pmpriv, + IN const wlan_user_scan_cfg *puser_scan_in, + OUT ChanScanParamSet_t *pscan_chan_list, + IN t_u8 filtered_scan) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + region_chan_t *pscan_region; + chan_freq_power_t *cfp; + t_u32 region_idx; + t_u32 chan_idx = 0; + t_u32 next_chan; + t_u8 scan_type; + t_u8 radio_type; + + ENTER(); + + for (region_idx = 0; + region_idx < NELEMENTS(pmadapter->region_channel); region_idx++) { + + if (wlan_11d_is_enabled(pmpriv) && + pmpriv->media_connected != MTRUE) { + /* Scan all the supported chan for the first scan */ + if (!pmadapter->universal_channel[region_idx].valid) + continue; + pscan_region = + &pmadapter->universal_channel[region_idx]; + } else { + if (!pmadapter->region_channel[region_idx].valid) + continue; + pscan_region = &pmadapter->region_channel[region_idx]; + } + + if (puser_scan_in && !puser_scan_in->chan_list[0].chan_number && + puser_scan_in->chan_list[0].radio_type & BAND_SPECIFIED) { + radio_type = + puser_scan_in->chan_list[0]. + radio_type & ~BAND_SPECIFIED; + if (!radio_type && (pscan_region->band != BAND_B) && + (pscan_region->band != BAND_G)) + continue; + if (radio_type && (pscan_region->band != BAND_A)) + continue; + } + if (!wlan_is_band_compatible + (pmpriv->config_bands | pmadapter->adhoc_start_band, + pscan_region->band)) + continue; + for (next_chan = 0; + next_chan < pscan_region->num_cfp; + next_chan++, chan_idx++) { + /* Set the default scan type to the user specified type, will later + * be changed to passive on a per channel basis if restricted by + * regulatory requirements (11d or 11h) + */ + scan_type = pmadapter->scan_type; + cfp = pscan_region->pcfp + next_chan; + + switch (pscan_region->band) { + case BAND_A: + pscan_chan_list[chan_idx].bandcfg.chanBand = + BAND_5GHZ; + /* Passive scan on DFS channels */ + if (wlan_11h_radar_detect_required + (pmpriv, (t_u8)cfp->channel)) + scan_type = MLAN_SCAN_TYPE_PASSIVE; + break; + case BAND_B: + case BAND_G: + if (wlan_bg_scan_type_is_passive + (pmpriv, (t_u8)cfp->channel)) { + scan_type = MLAN_SCAN_TYPE_PASSIVE; + } + pscan_chan_list[chan_idx].bandcfg.chanBand = + BAND_2GHZ; + break; + default: + pscan_chan_list[chan_idx].bandcfg.chanBand = + BAND_2GHZ; + break; + } + + if (puser_scan_in && + puser_scan_in->chan_list[0].scan_time) { + pscan_chan_list[chan_idx].max_scan_time = + wlan_cpu_to_le16((t_u16)puser_scan_in-> + chan_list[0]. + scan_time); + } else if (scan_type == MLAN_SCAN_TYPE_PASSIVE) { + pscan_chan_list[chan_idx].max_scan_time = + wlan_cpu_to_le16(pmadapter-> + passive_scan_time); + } else if (filtered_scan) { + pscan_chan_list[chan_idx].max_scan_time = + wlan_cpu_to_le16(pmadapter-> + specific_scan_time); + } else { + pscan_chan_list[chan_idx].max_scan_time = + wlan_cpu_to_le16(pmadapter-> + active_scan_time); + } + + if (scan_type == MLAN_SCAN_TYPE_PASSIVE) { + pscan_chan_list[chan_idx].chan_scan_mode. + passive_scan = MTRUE; + pscan_chan_list[chan_idx].chan_scan_mode. + hidden_ssid_report = MTRUE; + } else { + pscan_chan_list[chan_idx].chan_scan_mode. + passive_scan = MFALSE; + } + + pscan_chan_list[chan_idx].chan_number = + (t_u8)cfp->channel; + + if (filtered_scan) { + pscan_chan_list[chan_idx].chan_scan_mode. + disable_chan_filt = MTRUE; + } + } + } + + LEAVE(); +} + +/** + * @brief Add WPS IE to probe request frame + * + * @param pmpriv A pointer to mlan_private structure + * @param pptlv_out A pointer to TLV to fill in + * + * @return N/A + */ +static void +wlan_add_wps_probe_request_ie(IN mlan_private *pmpriv, OUT t_u8 **pptlv_out) +{ + MrvlIEtypesHeader_t *tlv; + + ENTER(); + + if (pmpriv->wps.wps_ie.vend_hdr.len) { + tlv = (MrvlIEtypesHeader_t *)*pptlv_out; + tlv->type = wlan_cpu_to_le16(VENDOR_SPECIFIC_221); + tlv->len = wlan_cpu_to_le16(pmpriv->wps.wps_ie.vend_hdr.len); + *pptlv_out += sizeof(MrvlIEtypesHeader_t); + memcpy(pmpriv->adapter, *pptlv_out, + pmpriv->wps.wps_ie.vend_hdr.oui, + pmpriv->wps.wps_ie.vend_hdr.len); + *pptlv_out += (pmpriv->wps.wps_ie.vend_hdr.len + + sizeof(MrvlIEtypesHeader_t)); + } + LEAVE(); +} + +/** + * @brief Construct and send multiple scan config commands to the firmware + * + * Previous routines have created a wlan_scan_cmd_config with any requested + * TLVs. This function splits the channel TLV into max_chan_per_scan lists + * and sends the portion of the channel TLV along with the other TLVs + * to the wlan_cmd routines for execution in the firmware. + * + * @param pmpriv A pointer to mlan_private structure + * @param pioctl_buf A pointer to MLAN IOCTL Request buffer + * @param max_chan_per_scan Maximum number channels to be included in each + * scan command sent to firmware + * @param filtered_scan Flag indicating whether or not a BSSID or SSID + * filter is being used for the firmware command + * scan command sent to firmware + * @param pscan_cfg_out Scan configuration used for this scan. + * @param pchan_tlv_out Pointer in the pscan_cfg_out where the channel TLV + * should start. This is past any other TLVs that + * must be sent down in each firmware command. + * @param pscan_chan_list List of channels to scan in max_chan_per_scan segments + * + * @return MLAN_STATUS_SUCCESS or error return otherwise + */ +static mlan_status +wlan_scan_channel_list(IN mlan_private *pmpriv, + IN t_void *pioctl_buf, + IN t_u32 max_chan_per_scan, + IN t_u8 filtered_scan, + OUT wlan_scan_cmd_config *pscan_cfg_out, + OUT MrvlIEtypes_ChanListParamSet_t *pchan_tlv_out, + IN ChanScanParamSet_t *pscan_chan_list) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_adapter *pmadapter = pmpriv->adapter; + ChanScanParamSet_t *ptmp_chan_list; + ChanScanParamSet_t *pstart_chan; + pmlan_ioctl_req pioctl_req = (mlan_ioctl_req *)pioctl_buf; + t_u8 *pchan_tlv_out_temp = MNULL; + t_u8 *ptlv_temp = MNULL; + t_bool foundJPch14 = MFALSE; + t_u16 tlv_buf_len = 0; + t_u32 tlv_idx; + t_u32 total_scan_time; + t_u32 done_early; + t_u32 cmd_no; + t_u32 first_chan = 1; + mlan_callbacks *pcb = (mlan_callbacks *)&pmadapter->callbacks; + + ENTER(); + + if (!pscan_cfg_out || !pchan_tlv_out || !pscan_chan_list) { + PRINTM(MINFO, "Scan: Null detect: %p, %p, %p\n", + pscan_cfg_out, pchan_tlv_out, pscan_chan_list); + if (pioctl_req) + pioctl_req->status_code = MLAN_ERROR_CMD_SCAN_FAIL; + LEAVE(); + return MLAN_STATUS_FAILURE; + } + if (!pscan_chan_list->chan_number) { + PRINTM(MERROR, "Scan: No channel configured\n"); + if (pioctl_req) + pioctl_req->status_code = MLAN_ERROR_CMD_SCAN_FAIL; + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + /* check expiry before preparing scan list - may affect blacklist */ + wlan_11h_get_csa_closed_channel(pmpriv); + + pchan_tlv_out->header.type = wlan_cpu_to_le16(TLV_TYPE_CHANLIST); + + /* Set the temp channel struct pointer to the start of the desired list */ + ptmp_chan_list = pscan_chan_list; + + /* + * Loop through the desired channel list, sending a new firmware scan + * commands for each max_chan_per_scan channels (or for 1,6,11 + * individually if configured accordingly) + */ + while (ptmp_chan_list->chan_number) { + + tlv_idx = 0; + total_scan_time = 0; + pchan_tlv_out->header.len = 0; + pstart_chan = ptmp_chan_list; + done_early = MFALSE; + + /* + * Construct the Channel TLV for the scan command. Continue to + * insert channel TLVs until: + * - the tlv_idx hits the maximum configured per scan command + * - the next channel to insert is 0 (end of desired + * channel list) + * - done_early is set (controlling individual + * scanning of 1,6,11) + */ + while (tlv_idx < max_chan_per_scan && + ptmp_chan_list->chan_number && !done_early) { + if (wlan_is_chan_blacklisted + (pmpriv, + radio_type_to_band(ptmp_chan_list->bandcfg. + chanBand), + ptmp_chan_list->chan_number) || + wlan_is_chan_disabled(pmpriv, + radio_type_to_band + (ptmp_chan_list->bandcfg. + chanBand), + ptmp_chan_list-> + chan_number)) { + ptmp_chan_list++; + continue; + } + + if (first_chan) { + ptmp_chan_list->chan_scan_mode.first_chan = + MTRUE; + first_chan = 0; + } + + PRINTM(MINFO, + "Scan: Chan(%3d), bandcfg(%x), Mode(%d,%d), Dur(%d)\n", + ptmp_chan_list->chan_number, + ptmp_chan_list->bandcfg, + ptmp_chan_list->chan_scan_mode.passive_scan, + ptmp_chan_list->chan_scan_mode.disable_chan_filt, + wlan_le16_to_cpu(ptmp_chan_list->max_scan_time)); + + if (foundJPch14 == MTRUE) { + foundJPch14 = MFALSE; + /* Restore the TLV buffer */ + pchan_tlv_out = + (MrvlIEtypes_ChanListParamSet_t *) + pchan_tlv_out_temp; + pchan_tlv_out->header.type = + wlan_cpu_to_le16(TLV_TYPE_CHANLIST); + pchan_tlv_out->header.len = 0; + if (ptlv_temp) { + memcpy(pmadapter, + pscan_cfg_out->tlv_buf, + ptlv_temp, tlv_buf_len); + pcb->moal_mfree(pmadapter->pmoal_handle, + ptlv_temp); + ptlv_temp = MNULL; + } + } + + /* Special Case: For Japan, Scan on CH14 for 11G rates is not allowed + Hence Rates TLV needs to be updated to support only 11B rates */ + if ((pmadapter->region_code == COUNTRY_CODE_JP_40 || + pmadapter->region_code == COUNTRY_CODE_JP_FF) + && (ptmp_chan_list->chan_number == 14) + && (pmadapter->ext_scan_type != EXT_SCAN_ENHANCE) + ) { + + t_u8 *ptlv_pos = pscan_cfg_out->tlv_buf; + t_u16 old_ratetlv_len, new_ratetlv_len; + MrvlIEtypesHeader_t *header; + MrvlIEtypes_RatesParamSet_t *prates_tlv; + + /* Preserve the current TLV buffer */ + ret = pcb->moal_malloc(pmadapter->pmoal_handle, + MAX_SCAN_CFG_ALLOC - + CHAN_TLV_MAX_SIZE, + MLAN_MEM_DEF, + (t_u8 **)&ptlv_temp); + if (ret != MLAN_STATUS_SUCCESS || !ptlv_temp) { + PRINTM(MERROR, + "Memory allocation for pscan_cfg_out failed!\n"); + if (pioctl_req) + pioctl_req->status_code = + MLAN_ERROR_NO_MEM; + LEAVE(); + return MLAN_STATUS_FAILURE; + } + pchan_tlv_out_temp = (t_u8 *)pchan_tlv_out; + tlv_buf_len = + (t_u32)(pchan_tlv_out_temp - + pscan_cfg_out->tlv_buf); + memcpy(pmadapter, ptlv_temp, ptlv_pos, + tlv_buf_len); + + /* Search for Rates TLV */ + while ((!foundJPch14) && + (ptlv_pos < pchan_tlv_out_temp)) { + header = (MrvlIEtypesHeader_t *) + ptlv_pos; + if (header->type == + wlan_cpu_to_le16(TLV_TYPE_RATES)) + foundJPch14 = MTRUE; + else + ptlv_pos += + (sizeof + (MrvlIEtypesHeader_t) + + wlan_le16_to_cpu + (header->len)); + } + + if (foundJPch14) { + /* Update the TLV buffer with *new* Rates TLV and rearrange remaining TLV buffer */ + prates_tlv = + (MrvlIEtypes_RatesParamSet_t *) + ptlv_pos; + old_ratetlv_len = + sizeof(MrvlIEtypesHeader_t) + + wlan_le16_to_cpu(prates_tlv-> + header.len); + + prates_tlv->header.len = + wlan_copy_rates(prates_tlv-> + rates, 0, + SupportedRates_B, + sizeof + (SupportedRates_B)); + new_ratetlv_len = + sizeof(MrvlIEtypesHeader_t) + + prates_tlv->header.len; + prates_tlv->header.len = + wlan_cpu_to_le16(prates_tlv-> + header.len); + + memmove(pmadapter, + ptlv_pos + new_ratetlv_len, + ptlv_pos + old_ratetlv_len, + (t_u32)(pchan_tlv_out_temp - + (ptlv_pos + + old_ratetlv_len))); + pchan_tlv_out = + (MrvlIEtypes_ChanListParamSet_t + *) + (pchan_tlv_out_temp - + (old_ratetlv_len - + new_ratetlv_len)); + pchan_tlv_out->header.type = + wlan_cpu_to_le16 + (TLV_TYPE_CHANLIST); + pchan_tlv_out->header.len = 0; + } + } + + /* Copy the current channel TLV to the command being prepared */ + memcpy(pmadapter, + pchan_tlv_out->chan_scan_param + tlv_idx, + ptmp_chan_list, + sizeof(pchan_tlv_out->chan_scan_param)); + + /* Increment the TLV header length by the size appended */ + pchan_tlv_out->header.len += + sizeof(pchan_tlv_out->chan_scan_param); + + /* + * The tlv buffer length is set to the number of + * bytes of the between the channel tlv pointer + * and the start of the tlv buffer. This + * compensates for any TLVs that were appended + * before the channel list. + */ + pscan_cfg_out->tlv_buf_len = + (t_u32)((t_u8 *)pchan_tlv_out - + pscan_cfg_out->tlv_buf); + + /* Add the size of the channel tlv header and the data length */ + pscan_cfg_out->tlv_buf_len += + (sizeof(pchan_tlv_out->header) + + pchan_tlv_out->header.len); + + /* Increment the index to the channel tlv we are constructing */ + tlv_idx++; + + /* Count the total scan time per command */ + total_scan_time += + wlan_le16_to_cpu(ptmp_chan_list->max_scan_time); + + done_early = MFALSE; + + /* + * Stop the loop if the *current* channel is in the 1,6,11 set + * and we are not filtering on a BSSID or SSID. + */ + if (!filtered_scan && + (ptmp_chan_list->chan_number == 1 || + ptmp_chan_list->chan_number == 6 || + ptmp_chan_list->chan_number == 11)) { + done_early = MTRUE; + } + + /* + * Stop the loop if the *current* channel is 14 + * and region code is Japan (0x40 or 0xFF) + */ + if ((pmadapter->region_code == COUNTRY_CODE_JP_40 || + pmadapter->region_code == COUNTRY_CODE_JP_FF) + && (ptmp_chan_list->chan_number == 14)) { + done_early = MTRUE; + } + + /* Increment the tmp pointer to the next channel to be scanned */ + ptmp_chan_list++; + + /* + * Stop the loop if the *next* channel is in the 1,6,11 set. + * This will cause it to be the only channel scanned on the next + * interation + */ + if (!filtered_scan && + (ptmp_chan_list->chan_number == 1 || + ptmp_chan_list->chan_number == 6 || + ptmp_chan_list->chan_number == 11)) { + done_early = MTRUE; + } + + /* + * Stop the loop if the *next* channel is 14 + * and region code is Japan (0x40 or 0xFF) + */ + if ((pmadapter->region_code == COUNTRY_CODE_JP_40 || + pmadapter->region_code == COUNTRY_CODE_JP_FF) + && (ptmp_chan_list->chan_number == 14)) { + done_early = MTRUE; + } + if (pmadapter->ext_scan && pmadapter->ext_scan_enh && + pmadapter->ext_scan_type == EXT_SCAN_ENHANCE) + done_early = MFALSE; + } + + /* The total scan time should be less than scan command timeout value */ + if (total_scan_time > MRVDRV_MAX_TOTAL_SCAN_TIME) { + PRINTM(MMSG, + "Total scan time %d ms is over limit (%d ms), scan skipped\n", + total_scan_time, MRVDRV_MAX_TOTAL_SCAN_TIME); + if (pioctl_req) + pioctl_req->status_code = + MLAN_ERROR_CMD_SCAN_FAIL; + ret = MLAN_STATUS_FAILURE; + break; + } + + pchan_tlv_out->header.len = + wlan_cpu_to_le16(pchan_tlv_out->header.len); + + pmadapter->pscan_channels = pstart_chan; + + /* Send the scan command to the firmware with the specified cfg */ + if (pmadapter->ext_scan) + cmd_no = HostCmd_CMD_802_11_SCAN_EXT; + else + cmd_no = HostCmd_CMD_802_11_SCAN; + ret = wlan_prepare_cmd(pmpriv, + cmd_no, + HostCmd_ACT_GEN_SET, + 0, MNULL, pscan_cfg_out); + if (ret) + break; + } + + LEAVE(); + + if (ptlv_temp) + pcb->moal_mfree(pmadapter->pmoal_handle, ptlv_temp); + + if (ret) + return MLAN_STATUS_FAILURE; + + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Construct a wlan_scan_cmd_config structure to use in scan commands + * + * Application layer or other functions can invoke wlan_scan_networks + * with a scan configuration supplied in a wlan_ioctl_user_scan_cfg struct. + * This structure is used as the basis of one or many wlan_scan_cmd_config + * commands that are sent to the command processing module and sent to + * firmware. + * + * Create a wlan_scan_cmd_config based on the following user supplied + * parameters (if present): + * - SSID filter + * - BSSID filter + * - Number of Probes to be sent + * - Channel list + * + * If the SSID or BSSID filter is not present, disable/clear the filter. + * If the number of probes is not set, use the adapter default setting + * Qualify the channel + * + * @param pmpriv A pointer to mlan_private structure + * @param puser_scan_in MNULL or pointer to scan config parameters + * @param pscan_cfg_out Output parameter: Resulting scan configuration + * @param ppchan_list_out Output parameter: Pointer to the start of the + * channel TLV portion of the output scan config + * @param pscan_chan_list Output parameter: Pointer to the resulting + * channel list to scan + * @param pmax_chan_per_scan Output parameter: Number of channels to scan for + * each issuance of the firmware scan command + * @param pfiltered_scan Output parameter: Flag indicating whether or not + * a BSSID or SSID filter is being sent in the + * command to firmware. Used to increase the number + * of channels sent in a scan command and to + * disable the firmware channel scan filter. + * @param pscan_current_only Output parameter: Flag indicating whether or not + * we are only scanning our current active channel + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status +wlan_scan_setup_scan_config(IN mlan_private *pmpriv, + IN wlan_user_scan_cfg *puser_scan_in, + OUT wlan_scan_cmd_config *pscan_cfg_out, + OUT MrvlIEtypes_ChanListParamSet_t + **ppchan_list_out, + OUT ChanScanParamSet_t *pscan_chan_list, + OUT t_u8 *pmax_chan_per_scan, + OUT t_u8 *pfiltered_scan, + OUT t_u8 *pscan_current_only) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + mlan_status ret = MLAN_STATUS_SUCCESS; + MrvlIEtypes_NumProbes_t *pnum_probes_tlv; + MrvlIEtypes_WildCardSsIdParamSet_t *pwildcard_ssid_tlv; + MrvlIEtypes_RatesParamSet_t *prates_tlv; + MrvlIEtypes_Bssid_List_t *pbssid_tlv; + + const t_u8 zero_mac[MLAN_MAC_ADDR_LENGTH] = { 0, 0, 0, 0, 0, 0 }; + t_u8 *ptlv_pos; + t_u32 num_probes; + t_u32 ssid_len; + t_u32 chan_idx; + t_u32 scan_type; + t_u16 scan_dur; + t_u8 channel; + t_u8 radio_type; + t_u32 ssid_idx; + t_u8 ssid_filter; + WLAN_802_11_RATES rates; + t_u32 rates_size; + MrvlIETypes_HTCap_t *pht_cap; + + MrvlIEtypes_ScanChanGap_t *pscan_gap_tlv; + MrvlIEtypes_BssMode_t *pbss_mode; + ENTER(); + + /* The tlv_buf_len is calculated for each scan command. The TLVs added + * in this routine will be preserved since the routine that sends + * the command will append channelTLVs at *ppchan_list_out. The + * difference between the *ppchan_list_out and the tlv_buf start will be + * used to calculate the size of anything we add in this routine. + */ + pscan_cfg_out->tlv_buf_len = 0; + + /* Running tlv pointer. Assigned to ppchan_list_out at end of function + * so later routines know where channels can be added to the command buf + */ + ptlv_pos = pscan_cfg_out->tlv_buf; + + /* Initialize the scan as un-filtered; the flag is later set to + * TRUE below if a SSID or BSSID filter is sent in the command + */ + *pfiltered_scan = MFALSE; + + /* Initialize the scan as not being only on the current channel. If + * the channel list is customized, only contains one channel, and + * is the active channel, this is set true and data flow is not halted. + */ + *pscan_current_only = MFALSE; + + if (puser_scan_in) { + + ssid_filter = MFALSE; + + /* Set the bss type scan filter, use Adapter setting if unset */ + pscan_cfg_out->bss_mode = (puser_scan_in->bss_mode + ? (t_u8)puser_scan_in->bss_mode : + (t_u8)pmadapter->scan_mode); + + /* Set the number of probes to send, use Adapter setting if unset */ + num_probes = + (puser_scan_in->num_probes ? puser_scan_in-> + num_probes : pmadapter->scan_probes); + /* + * Set the BSSID filter to the incoming configuration, + * if non-zero. If not set, it will remain disabled + * (all zeros). + */ + memcpy(pmadapter, pscan_cfg_out->specific_bssid, + puser_scan_in->specific_bssid, + sizeof(pscan_cfg_out->specific_bssid)); + + if (pmadapter->ext_scan + && memcmp(pmadapter, pscan_cfg_out->specific_bssid, + &zero_mac, sizeof(zero_mac))) { + pbssid_tlv = (MrvlIEtypes_Bssid_List_t *)ptlv_pos; + pbssid_tlv->header.type = TLV_TYPE_BSSID; + pbssid_tlv->header.len = MLAN_MAC_ADDR_LENGTH; + memcpy(pmadapter, pbssid_tlv->bssid, + puser_scan_in->specific_bssid, + MLAN_MAC_ADDR_LENGTH); + ptlv_pos += sizeof(MrvlIEtypes_Bssid_List_t); + } + + for (ssid_idx = 0; + ((ssid_idx < NELEMENTS(puser_scan_in->ssid_list)) + && (*puser_scan_in->ssid_list[ssid_idx].ssid || + puser_scan_in->ssid_list[ssid_idx].max_len)); + ssid_idx++) { + + ssid_len = + wlan_strlen((char *)puser_scan_in-> + ssid_list[ssid_idx].ssid); + + pwildcard_ssid_tlv + = + (MrvlIEtypes_WildCardSsIdParamSet_t *)ptlv_pos; + pwildcard_ssid_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_WILDCARDSSID); + pwildcard_ssid_tlv->header.len = + (t_u16)(ssid_len + + sizeof(pwildcard_ssid_tlv-> + max_ssid_length)); + pwildcard_ssid_tlv->max_ssid_length = + puser_scan_in->ssid_list[ssid_idx].max_len; + + memcpy(pmadapter, pwildcard_ssid_tlv->ssid, + puser_scan_in->ssid_list[ssid_idx].ssid, + MIN(MLAN_MAX_SSID_LENGTH, ssid_len)); + + ptlv_pos += (sizeof(pwildcard_ssid_tlv->header) + + pwildcard_ssid_tlv->header.len); + + pwildcard_ssid_tlv->header.len + = + wlan_cpu_to_le16(pwildcard_ssid_tlv->header. + len); + + PRINTM(MINFO, "Scan: ssid_list[%d]: %s, %d\n", + ssid_idx, + pwildcard_ssid_tlv->ssid, + pwildcard_ssid_tlv->max_ssid_length); + + if (ssid_len) { + ssid_filter = MTRUE; + if (!puser_scan_in->ssid_list[ssid_idx].max_len) { + PRINTM(MCMND, "user scan: %s\n", + pwildcard_ssid_tlv->ssid); + puser_scan_in->ssid_filter = MTRUE; + } + } + } + + /* + * The default number of channels sent in the command is low to + * ensure the response buffer from the firmware does not + * truncate scan results. That is not an issue with an SSID or + * BSSID filter applied to the scan results in the firmware. + */ + if ((ssid_idx && ssid_filter) || + memcmp(pmadapter, pscan_cfg_out->specific_bssid, &zero_mac, + sizeof(zero_mac))) { + *pfiltered_scan = MTRUE; + } + + } else { + pscan_cfg_out->bss_mode = (t_u8)pmadapter->scan_mode; + num_probes = pmadapter->scan_probes; + } + + /* + * If a specific BSSID or SSID is used, the number of channels in + * the scan command will be increased to the absolute maximum. + */ + if (*pfiltered_scan) + *pmax_chan_per_scan = MRVDRV_MAX_CHANNELS_PER_SPECIFIC_SCAN; + else + *pmax_chan_per_scan = MRVDRV_CHANNELS_PER_SCAN_CMD; + + if (puser_scan_in && puser_scan_in->scan_chan_gap) { + *pmax_chan_per_scan = MRVDRV_MAX_CHANNELS_PER_SPECIFIC_SCAN; + PRINTM(MINFO, "Scan: channel gap = %d\n", + puser_scan_in->scan_chan_gap); + pscan_gap_tlv = (MrvlIEtypes_ScanChanGap_t *)ptlv_pos; + pscan_gap_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_SCAN_CHANNEL_GAP); + pscan_gap_tlv->header.len = sizeof(pscan_gap_tlv->gap); + pscan_gap_tlv->gap = + wlan_cpu_to_le16((t_u16)puser_scan_in->scan_chan_gap); + ptlv_pos += + sizeof(pscan_gap_tlv->header) + + pscan_gap_tlv->header.len; + pscan_gap_tlv->header.len = + wlan_cpu_to_le16(pscan_gap_tlv->header.len); + } + if (pmadapter->ext_scan) { + pbss_mode = (MrvlIEtypes_BssMode_t *) ptlv_pos; + pbss_mode->header.type = wlan_cpu_to_le16(TLV_TYPE_BSS_MODE); + pbss_mode->header.len = sizeof(pbss_mode->bss_mode); + pbss_mode->bss_mode = pscan_cfg_out->bss_mode; + ptlv_pos += sizeof(pbss_mode->header) + pbss_mode->header.len; + pbss_mode->header.len = wlan_cpu_to_le16(pbss_mode->header.len); + if (pmadapter->ext_scan_enh) { + if (puser_scan_in) { + if (puser_scan_in->ext_scan_type == + EXT_SCAN_ENHANCE) + pmadapter->ext_scan_type = + EXT_SCAN_ENHANCE; + else + pmadapter->ext_scan_type = + EXT_SCAN_DEFAULT; + } else if (pmadapter->ext_scan == EXT_SCAN_TYPE_ENH) + pmadapter->ext_scan_type = EXT_SCAN_ENHANCE; + else + pmadapter->ext_scan_type = EXT_SCAN_DEFAULT; + if (pmadapter->ext_scan_type == EXT_SCAN_ENHANCE) + *pmax_chan_per_scan = + MRVDRV_MAX_CHANNELS_PER_SCAN; + } + } + /* If the input config or adapter has the number of Probes set, add tlv */ + if (num_probes) { + + PRINTM(MINFO, "Scan: num_probes = %d\n", num_probes); + + pnum_probes_tlv = (MrvlIEtypes_NumProbes_t *)ptlv_pos; + pnum_probes_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_NUMPROBES); + pnum_probes_tlv->header.len = + sizeof(pnum_probes_tlv->num_probes); + pnum_probes_tlv->num_probes = + wlan_cpu_to_le16((t_u16)num_probes); + + ptlv_pos += + sizeof(pnum_probes_tlv->header) + + pnum_probes_tlv->header.len; + + pnum_probes_tlv->header.len = + wlan_cpu_to_le16(pnum_probes_tlv->header.len); + } + + /* Append rates tlv */ + memset(pmadapter, rates, 0, sizeof(rates)); + + rates_size = wlan_get_supported_rates(pmpriv, pmpriv->bss_mode, + (pmpriv->bss_mode == + MLAN_BSS_MODE_INFRA) ? pmpriv-> + config_bands : pmadapter-> + adhoc_start_band, rates); + + prates_tlv = (MrvlIEtypes_RatesParamSet_t *)ptlv_pos; + prates_tlv->header.type = wlan_cpu_to_le16(TLV_TYPE_RATES); + prates_tlv->header.len = wlan_cpu_to_le16((t_u16)rates_size); + memcpy(pmadapter, prates_tlv->rates, rates, rates_size); + ptlv_pos += sizeof(prates_tlv->header) + rates_size; + + PRINTM(MINFO, "SCAN_CMD: Rates size = %d\n", rates_size); + + if (ISSUPP_11NENABLED(pmpriv->adapter->fw_cap_info) + && (pmpriv->config_bands & BAND_GN + || pmpriv->config_bands & BAND_AN)) { + pht_cap = (MrvlIETypes_HTCap_t *)ptlv_pos; + memset(pmadapter, pht_cap, 0, sizeof(MrvlIETypes_HTCap_t)); + pht_cap->header.type = wlan_cpu_to_le16(HT_CAPABILITY); + pht_cap->header.len = sizeof(HTCap_t); + wlan_fill_ht_cap_tlv(pmpriv, pht_cap, pmpriv->config_bands, + MTRUE); + HEXDUMP("SCAN: HT_CAPABILITIES IE", (t_u8 *)pht_cap, + sizeof(MrvlIETypes_HTCap_t)); + ptlv_pos += sizeof(MrvlIETypes_HTCap_t); + pht_cap->header.len = wlan_cpu_to_le16(pht_cap->header.len); + } + + if (wlan_is_ext_capa_support(pmpriv)) + wlan_add_ext_capa_info_ie(pmpriv, &ptlv_pos); + if (pmpriv->adapter->ecsa_enable) { + t_u8 bandwidth = BW_20MHZ; + t_u8 oper_class = 1; + t_u32 usr_dot_11n_dev_cap; + if (pmpriv->media_connected) { + if (pmpriv->config_bands & BAND_A) + usr_dot_11n_dev_cap = + pmpriv->usr_dot_11n_dev_cap_a; + else + usr_dot_11n_dev_cap = + pmpriv->usr_dot_11n_dev_cap_bg; + if (usr_dot_11n_dev_cap & MBIT(17)) { + bandwidth = BW_40MHZ; + } + wlan_get_curr_oper_class(pmpriv, + pmpriv->curr_bss_params. + bss_descriptor.channel, + bandwidth, &oper_class); + } + wlan_add_supported_oper_class_ie(pmpriv, &ptlv_pos, oper_class); + } + wlan_add_wps_probe_request_ie(pmpriv, &ptlv_pos); + + if (puser_scan_in && puser_scan_in->proberesp_only) { + MrvlIEtypes_OnlyProberesp_t *proberesp_only = + (MrvlIEtypes_OnlyProberesp_t *) ptlv_pos; + memset(pmadapter, proberesp_only, 0, + sizeof(MrvlIEtypes_OnlyProberesp_t)); + proberesp_only->header.type = + wlan_cpu_to_le16(TLV_TYPE_ONLYPROBERESP); + proberesp_only->header.len = wlan_cpu_to_le16(sizeof(t_u8)); + proberesp_only->proberesp_only = puser_scan_in->proberesp_only; + ptlv_pos += sizeof(MrvlIEtypes_OnlyProberesp_t); + } + + /* + * Set the output for the channel TLV to the address in the tlv buffer + * past any TLVs that were added in this function (SSID, num_probes). + * Channel TLVs will be added past this for each scan command, + * preserving the TLVs that were previously added. + */ + *ppchan_list_out = (MrvlIEtypes_ChanListParamSet_t *)ptlv_pos; + + if (puser_scan_in && puser_scan_in->chan_list[0].chan_number) { + + PRINTM(MINFO, "Scan: Using supplied channel list\n"); + + for (chan_idx = 0; + chan_idx < WLAN_USER_SCAN_CHAN_MAX + && puser_scan_in->chan_list[chan_idx].chan_number; + chan_idx++) { + + channel = + puser_scan_in->chan_list[chan_idx].chan_number; + (pscan_chan_list + chan_idx)->chan_number = channel; + + radio_type = + puser_scan_in->chan_list[chan_idx].radio_type; + (pscan_chan_list + chan_idx)->bandcfg.chanBand = + radio_type; + + scan_type = + puser_scan_in->chan_list[chan_idx].scan_type; + if (scan_type == MLAN_SCAN_TYPE_UNCHANGED) + scan_type = pmadapter->scan_type; + + if (radio_type == BAND_5GHZ) { + if (pmadapter->fw_bands & BAND_A) + PRINTM(MINFO, + "UserScan request for A Band channel %d!!\n", + channel); + else { + PRINTM(MERROR, + "Scan in A band is not allowed!!\n"); + ret = MLAN_STATUS_FAILURE; + LEAVE(); + return ret; + + } + } + + /* Prevent active scanning on a radar controlled channel */ + if (radio_type == BAND_5GHZ) { + if (pmadapter->active_scan_triggered == MFALSE) + if (wlan_11h_radar_detect_required + (pmpriv, channel)) { + scan_type = + MLAN_SCAN_TYPE_PASSIVE; + } + } + if (radio_type == BAND_2GHZ) { + if (pmadapter->active_scan_triggered == MFALSE) + if (wlan_bg_scan_type_is_passive + (pmpriv, channel)) { + scan_type = + MLAN_SCAN_TYPE_PASSIVE; + } + } + if (scan_type == MLAN_SCAN_TYPE_PASSIVE) { + (pscan_chan_list + + chan_idx)->chan_scan_mode.passive_scan = MTRUE; + (pscan_chan_list + + chan_idx)->chan_scan_mode.hidden_ssid_report = + MTRUE; + } else { + (pscan_chan_list + + chan_idx)->chan_scan_mode.passive_scan = + MFALSE; + } + + if (puser_scan_in->chan_list[chan_idx].scan_time) { + scan_dur = + (t_u16)puser_scan_in-> + chan_list[chan_idx].scan_time; + } else { + if (scan_type == MLAN_SCAN_TYPE_PASSIVE) { + scan_dur = pmadapter->passive_scan_time; + } else if (*pfiltered_scan) { + scan_dur = + pmadapter->specific_scan_time; + } else { + scan_dur = pmadapter->active_scan_time; + } + } + if (pmadapter->coex_scan && + pmadapter->coex_min_scan_time && + (pmadapter->coex_min_scan_time > scan_dur)) + scan_dur = pmadapter->coex_min_scan_time; + (pscan_chan_list + chan_idx)->min_scan_time = + wlan_cpu_to_le16(scan_dur); + (pscan_chan_list + chan_idx)->max_scan_time = + wlan_cpu_to_le16(scan_dur); + if (*pfiltered_scan) { + (pscan_chan_list + + chan_idx)->chan_scan_mode.disable_chan_filt = + MTRUE; + } + } + + /* Check if we are only scanning the current channel */ + if ((chan_idx == 1) + && (puser_scan_in->chan_list[0].chan_number + == pmpriv->curr_bss_params.bss_descriptor.channel)) { + *pscan_current_only = MTRUE; + PRINTM(MINFO, "Scan: Scanning current channel only\n"); + } + + } else { + PRINTM(MINFO, "Scan: Creating full region channel list\n"); + wlan_scan_create_channel_list(pmpriv, puser_scan_in, + pscan_chan_list, *pfiltered_scan); + } + + LEAVE(); + return ret; +} + +/** + * @brief Inspect the scan response buffer for pointers to expected TLVs + * + * TLVs can be included at the end of the scan response BSS information. + * Parse the data in the buffer for pointers to TLVs that can potentially + * be passed back in the response + * + * @param pmadapter Pointer to the mlan_adapter structure + * @param ptlv Pointer to the start of the TLV buffer to parse + * @param tlv_buf_size Size of the TLV buffer + * @param req_tlv_type Request TLV's type + * @param pptlv Output parameter: Pointer to the request TLV if found + * + * @return N/A + */ +static t_void +wlan_ret_802_11_scan_get_tlv_ptrs(IN pmlan_adapter pmadapter, + IN MrvlIEtypes_Data_t *ptlv, + IN t_u32 tlv_buf_size, + IN t_u32 req_tlv_type, + OUT MrvlIEtypes_Data_t **pptlv) +{ + MrvlIEtypes_Data_t *pcurrent_tlv; + t_u32 tlv_buf_left; + t_u32 tlv_type; + t_u32 tlv_len; + + ENTER(); + + pcurrent_tlv = ptlv; + tlv_buf_left = tlv_buf_size; + *pptlv = MNULL; + + PRINTM(MINFO, "SCAN_RESP: tlv_buf_size = %d\n", tlv_buf_size); + + while (tlv_buf_left >= sizeof(MrvlIEtypesHeader_t)) { + + tlv_type = wlan_le16_to_cpu(pcurrent_tlv->header.type); + tlv_len = wlan_le16_to_cpu(pcurrent_tlv->header.len); + + if (sizeof(ptlv->header) + tlv_len > tlv_buf_left) { + PRINTM(MERROR, "SCAN_RESP: TLV buffer corrupt\n"); + break; + } + + if (req_tlv_type == tlv_type) { + switch (tlv_type) { + case TLV_TYPE_TSFTIMESTAMP: + PRINTM(MINFO, + "SCAN_RESP: TSF Timestamp TLV, len = %d\n", + tlv_len); + *pptlv = (MrvlIEtypes_Data_t *)pcurrent_tlv; + break; + case TLV_TYPE_CHANNELBANDLIST: + PRINTM(MINFO, + "SCAN_RESP: CHANNEL BAND LIST TLV, len = %d\n", + tlv_len); + *pptlv = (MrvlIEtypes_Data_t *)pcurrent_tlv; + break; + case TLV_TYPE_CHANNEL_STATS: + PRINTM(MINFO, + "SCAN_RESP: CHANNEL STATS TLV, len = %d\n", + tlv_len); + *pptlv = (MrvlIEtypes_Data_t *)pcurrent_tlv; + break; + default: + PRINTM(MERROR, + "SCAN_RESP: Unhandled TLV = %d\n", + tlv_type); + /* Give up, this seems corrupted */ + LEAVE(); + return; + } + } + + if (*pptlv) { + /* HEXDUMP("SCAN_RESP: TLV Buf", (t_u8 *)*pptlv+4, tlv_len); */ + break; + } + + tlv_buf_left -= (sizeof(ptlv->header) + tlv_len); + pcurrent_tlv = + (MrvlIEtypes_Data_t *)(pcurrent_tlv->data + tlv_len); + + } /* while */ + + LEAVE(); +} + +/** + * @brief Interpret a BSS scan response returned from the firmware + * + * Parse the various fixed fields and IEs passed back for a BSS probe + * response or beacon from the scan command. Record information as needed + * in the scan table BSSDescriptor_t for that entry. + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pbss_entry Output parameter: Pointer to the BSS Entry + * @param pbeacon_info Pointer to the Beacon information + * @param bytes_left Number of bytes left to parse + * @param ext_scan extended scan + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status +wlan_interpret_bss_desc_with_ie(IN pmlan_adapter pmadapter, + OUT BSSDescriptor_t *pbss_entry, + IN t_u8 **pbeacon_info, + IN t_u32 *bytes_left, IN t_u8 ext_scan) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + IEEEtypes_ElementId_e element_id; + IEEEtypes_FhParamSet_t *pfh_param_set; + IEEEtypes_DsParamSet_t *pds_param_set; + IEEEtypes_CfParamSet_t *pcf_param_set; + IEEEtypes_IbssParamSet_t *pibss_param_set; + IEEEtypes_CapInfo_t *pcap_info; + WLAN_802_11_FIXED_IEs fixed_ie; + t_u8 *pcurrent_ptr; + t_u8 *prate; + t_u8 element_len; + t_u16 total_ie_len; + t_u8 bytes_to_copy; + t_u8 rate_size; + t_u16 beacon_size; + t_u8 found_data_rate_ie; + t_u32 bytes_left_for_current_beacon; + IEEEtypes_ERPInfo_t *perp_info; + + IEEEtypes_VendorSpecific_t *pvendor_ie; + const t_u8 wpa_oui[4] = { 0x00, 0x50, 0xf2, 0x01 }; + const t_u8 wmm_oui[4] = { 0x00, 0x50, 0xf2, 0x02 }; + + IEEEtypes_CountryInfoSet_t *pcountry_info; + + ENTER(); + + found_data_rate_ie = MFALSE; + rate_size = 0; + beacon_size = 0; + + if (*bytes_left >= sizeof(beacon_size)) { + /* Extract & convert beacon size from the command buffer */ + memcpy(pmadapter, &beacon_size, *pbeacon_info, + sizeof(beacon_size)); + beacon_size = wlan_le16_to_cpu(beacon_size); + *bytes_left -= sizeof(beacon_size); + *pbeacon_info += sizeof(beacon_size); + } + + if (!beacon_size || beacon_size > *bytes_left) { + + *pbeacon_info += *bytes_left; + *bytes_left = 0; + + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + /* Initialize the current working beacon pointer for this BSS iteration */ + pcurrent_ptr = *pbeacon_info; + + /* Advance the return beacon pointer past the current beacon */ + *pbeacon_info += beacon_size; + *bytes_left -= beacon_size; + + bytes_left_for_current_beacon = beacon_size; + + if (bytes_left_for_current_beacon < + (MLAN_MAC_ADDR_LENGTH + sizeof(t_u8) + + sizeof(WLAN_802_11_FIXED_IEs))) { + PRINTM(MERROR, "InterpretIE: Not enough bytes left\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + memcpy(pmadapter, pbss_entry->mac_address, pcurrent_ptr, + MLAN_MAC_ADDR_LENGTH); + PRINTM(MINFO, "InterpretIE: AP MAC Addr-" MACSTR "\n", + MAC2STR(pbss_entry->mac_address)); + + pcurrent_ptr += MLAN_MAC_ADDR_LENGTH; + bytes_left_for_current_beacon -= MLAN_MAC_ADDR_LENGTH; + + /* + * Next 4 fields are RSSI (for legacy scan only), time stamp, + * beacon interval, and capability information + */ + if (!ext_scan) { + /* RSSI is 1 byte long */ + pbss_entry->rssi = (t_s32)(*pcurrent_ptr); + PRINTM(MINFO, "InterpretIE: RSSI=%02X\n", *pcurrent_ptr); + pcurrent_ptr += 1; + bytes_left_for_current_beacon -= 1; + } + + /* + * The RSSI is not part of the beacon/probe response. After we have + * advanced pcurrent_ptr past the RSSI field, save the remaining + * data for use at the application layer + */ + pbss_entry->pbeacon_buf = pcurrent_ptr; + pbss_entry->beacon_buf_size = bytes_left_for_current_beacon; + + /* Time stamp is 8 bytes long */ + memcpy(pmadapter, fixed_ie.time_stamp, pcurrent_ptr, 8); + memcpy(pmadapter, pbss_entry->time_stamp, pcurrent_ptr, 8); + pcurrent_ptr += 8; + bytes_left_for_current_beacon -= 8; + + /* Beacon interval is 2 bytes long */ + memcpy(pmadapter, &fixed_ie.beacon_interval, pcurrent_ptr, 2); + pbss_entry->beacon_period = wlan_le16_to_cpu(fixed_ie.beacon_interval); + pcurrent_ptr += 2; + bytes_left_for_current_beacon -= 2; + + /* Capability information is 2 bytes long */ + memcpy(pmadapter, &fixed_ie.capabilities, pcurrent_ptr, 2); + PRINTM(MINFO, "InterpretIE: fixed_ie.capabilities=0x%X\n", + fixed_ie.capabilities); + fixed_ie.capabilities = wlan_le16_to_cpu(fixed_ie.capabilities); + pcap_info = (IEEEtypes_CapInfo_t *)&fixed_ie.capabilities; + memcpy(pmadapter, &pbss_entry->cap_info, pcap_info, + sizeof(IEEEtypes_CapInfo_t)); + pcurrent_ptr += 2; + bytes_left_for_current_beacon -= 2; + + /* Rest of the current buffer are IE's */ + PRINTM(MINFO, "InterpretIE: IELength for this AP = %d\n", + bytes_left_for_current_beacon); + + HEXDUMP("InterpretIE: IE info", (t_u8 *)pcurrent_ptr, + bytes_left_for_current_beacon); + + if (pcap_info->privacy) { + PRINTM(MINFO, "InterpretIE: AP WEP enabled\n"); + pbss_entry->privacy = Wlan802_11PrivFilter8021xWEP; + } else { + pbss_entry->privacy = Wlan802_11PrivFilterAcceptAll; + } + + if (pcap_info->ibss == 1) + pbss_entry->bss_mode = MLAN_BSS_MODE_IBSS; + else + pbss_entry->bss_mode = MLAN_BSS_MODE_INFRA; + + if (pcap_info->spectrum_mgmt == 1) { + PRINTM(MINFO, "InterpretIE: 11h- Spectrum Management " + "capability bit found\n"); + pbss_entry->wlan_11h_bss_info.sensed_11h = 1; + } + + /* Process variable IE */ + while (bytes_left_for_current_beacon >= 2) { + element_id = (IEEEtypes_ElementId_e)(*((t_u8 *)pcurrent_ptr)); + element_len = *((t_u8 *)pcurrent_ptr + 1); + total_ie_len = element_len + sizeof(IEEEtypes_Header_t); + + if (bytes_left_for_current_beacon < total_ie_len) { + PRINTM(MERROR, "InterpretIE: Error in processing IE, " + "bytes left < IE length\n"); + bytes_left_for_current_beacon = 0; + continue; + } + + switch (element_id) { + + case SSID: + if (element_len > MRVDRV_MAX_SSID_LENGTH) { + bytes_left_for_current_beacon = 0; + continue; + } + if (!pbss_entry->ssid.ssid_len) { + pbss_entry->ssid.ssid_len = element_len; + memcpy(pmadapter, pbss_entry->ssid.ssid, + (pcurrent_ptr + 2), element_len); + } + PRINTM(MINFO, "InterpretIE: ssid: %-32s\n", + pbss_entry->ssid.ssid); + break; + + case SUPPORTED_RATES: + if (element_len > WLAN_SUPPORTED_RATES) { + bytes_left_for_current_beacon = 0; + continue; + } + memcpy(pmadapter, pbss_entry->data_rates, + pcurrent_ptr + 2, element_len); + memcpy(pmadapter, pbss_entry->supported_rates, + pcurrent_ptr + 2, element_len); + HEXDUMP("InterpretIE: SupportedRates:", + pbss_entry->supported_rates, element_len); + rate_size = element_len; + found_data_rate_ie = MTRUE; + break; + + case FH_PARAM_SET: + pfh_param_set = (IEEEtypes_FhParamSet_t *)pcurrent_ptr; + pbss_entry->network_type_use = Wlan802_11FH; + memcpy(pmadapter, + &pbss_entry->phy_param_set.fh_param_set, + pfh_param_set, MIN(total_ie_len, + sizeof + (IEEEtypes_FhParamSet_t))); + pbss_entry->phy_param_set.fh_param_set.len = + MIN(element_len, (sizeof(IEEEtypes_FhParamSet_t) + - + sizeof(IEEEtypes_Header_t))); + pbss_entry->phy_param_set.fh_param_set.dwell_time = + wlan_le16_to_cpu(pbss_entry->phy_param_set. + fh_param_set.dwell_time); + break; + + case DS_PARAM_SET: + pds_param_set = (IEEEtypes_DsParamSet_t *)pcurrent_ptr; + + pbss_entry->network_type_use = Wlan802_11DS; + pbss_entry->channel = pds_param_set->current_chan; + + memcpy(pmadapter, + &pbss_entry->phy_param_set.ds_param_set, + pds_param_set, MIN(total_ie_len, + sizeof + (IEEEtypes_DsParamSet_t))); + pbss_entry->phy_param_set.ds_param_set.len = + MIN(element_len, (sizeof(IEEEtypes_DsParamSet_t) + - + sizeof(IEEEtypes_Header_t))); + break; + + case CF_PARAM_SET: + pcf_param_set = (IEEEtypes_CfParamSet_t *)pcurrent_ptr; + memcpy(pmadapter, + &pbss_entry->ss_param_set.cf_param_set, + pcf_param_set, MIN(total_ie_len, + sizeof + (IEEEtypes_CfParamSet_t))); + pbss_entry->ss_param_set.cf_param_set.len = + MIN(element_len, (sizeof(IEEEtypes_CfParamSet_t) + - + sizeof(IEEEtypes_Header_t))); + break; + + case IBSS_PARAM_SET: + pibss_param_set = + (IEEEtypes_IbssParamSet_t *)pcurrent_ptr; + pbss_entry->atim_window = + wlan_le16_to_cpu(pibss_param_set->atim_window); + memcpy(pmadapter, + &pbss_entry->ss_param_set.ibss_param_set, + pibss_param_set, MIN(total_ie_len, + sizeof + (IEEEtypes_IbssParamSet_t))); + pbss_entry->ss_param_set.ibss_param_set.len = + MIN(element_len, + (sizeof(IEEEtypes_IbssParamSet_t) + - sizeof(IEEEtypes_Header_t))); + break; + + /* Handle Country Info IE */ + case COUNTRY_INFO: + pcountry_info = + (IEEEtypes_CountryInfoSet_t *)pcurrent_ptr; + + if (pcountry_info->len < + sizeof(pcountry_info->country_code) || + (unsigned)(pcountry_info->len + 2) > + sizeof(IEEEtypes_CountryInfoFullSet_t)) { + PRINTM(MERROR, + "InterpretIE: 11D- Err " + "country_info len =%d min=%d max=%d\n", + pcountry_info->len, + sizeof(pcountry_info->country_code), + sizeof(IEEEtypes_CountryInfoFullSet_t)); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + memcpy(pmadapter, &pbss_entry->country_info, + pcountry_info, pcountry_info->len + 2); + HEXDUMP("InterpretIE: 11D- country_info:", + (t_u8 *)pcountry_info, + (t_u32)(pcountry_info->len + 2)); + break; + + case ERP_INFO: + perp_info = (IEEEtypes_ERPInfo_t *)pcurrent_ptr; + pbss_entry->erp_flags = perp_info->erp_flags; + break; + + case POWER_CONSTRAINT: + case POWER_CAPABILITY: + case TPC_REPORT: + case CHANNEL_SWITCH_ANN: + case QUIET: + case IBSS_DFS: + case SUPPORTED_CHANNELS: + case TPC_REQUEST: + wlan_11h_process_bss_elem(pmadapter, + &pbss_entry-> + wlan_11h_bss_info, + pcurrent_ptr); + break; + case EXTENDED_SUPPORTED_RATES: + /* + * Only process extended supported rate + * if data rate is already found. + * Data rate IE should come before + * extended supported rate IE + */ + if (found_data_rate_ie) { + if ((element_len + rate_size) > + WLAN_SUPPORTED_RATES) { + bytes_to_copy = + (WLAN_SUPPORTED_RATES - + rate_size); + } else { + bytes_to_copy = element_len; + } + + prate = (t_u8 *)pbss_entry->data_rates; + prate += rate_size; + memcpy(pmadapter, prate, pcurrent_ptr + 2, + bytes_to_copy); + + prate = (t_u8 *)pbss_entry->supported_rates; + prate += rate_size; + memcpy(pmadapter, prate, pcurrent_ptr + 2, + bytes_to_copy); + } + HEXDUMP("InterpretIE: ExtSupportedRates:", + pbss_entry->supported_rates, + element_len + rate_size); + break; + + case VENDOR_SPECIFIC_221: + pvendor_ie = (IEEEtypes_VendorSpecific_t *)pcurrent_ptr; + + if (!memcmp + (pmadapter, pvendor_ie->vend_hdr.oui, wpa_oui, + sizeof(wpa_oui))) { + pbss_entry->pwpa_ie = + (IEEEtypes_VendorSpecific_t *) + pcurrent_ptr; + pbss_entry->wpa_offset = + (t_u16)(pcurrent_ptr - + pbss_entry->pbeacon_buf); + HEXDUMP("InterpretIE: Resp WPA_IE", + (t_u8 *)pbss_entry->pwpa_ie, + ((*(pbss_entry->pwpa_ie)).vend_hdr.len + + sizeof(IEEEtypes_Header_t))); + } else if (!memcmp + (pmadapter, pvendor_ie->vend_hdr.oui, + wmm_oui, sizeof(wmm_oui))) { + if (total_ie_len == + sizeof(IEEEtypes_WmmParameter_t) + || total_ie_len == + sizeof(IEEEtypes_WmmInfo_t)) { + + /* + * Only accept and copy the WMM IE if + * it matches the size expected for the + * WMM Info IE or the WMM Parameter IE. + */ + memcpy(pmadapter, + (t_u8 *)&pbss_entry->wmm_ie, + pcurrent_ptr, total_ie_len); + HEXDUMP("InterpretIE: Resp WMM_IE", + (t_u8 *)&pbss_entry->wmm_ie, + total_ie_len); + } + } + break; + case RSN_IE: + pbss_entry->prsn_ie = + (IEEEtypes_Generic_t *)pcurrent_ptr; + pbss_entry->rsn_offset = + (t_u16)(pcurrent_ptr - pbss_entry->pbeacon_buf); + HEXDUMP("InterpretIE: Resp RSN_IE", + (t_u8 *)pbss_entry->prsn_ie, + (*(pbss_entry->prsn_ie)).ieee_hdr.len + + sizeof(IEEEtypes_Header_t)); + break; + case WAPI_IE: + pbss_entry->pwapi_ie = + (IEEEtypes_Generic_t *)pcurrent_ptr; + pbss_entry->wapi_offset = + (t_u16)(pcurrent_ptr - pbss_entry->pbeacon_buf); + HEXDUMP("InterpretIE: Resp WAPI_IE", + (t_u8 *)pbss_entry->pwapi_ie, + (*(pbss_entry->pwapi_ie)).ieee_hdr.len + + sizeof(IEEEtypes_Header_t)); + break; + case HT_CAPABILITY: + pbss_entry->pht_cap = (IEEEtypes_HTCap_t *)pcurrent_ptr; + pbss_entry->ht_cap_offset = + (t_u16)(pcurrent_ptr - pbss_entry->pbeacon_buf); + HEXDUMP("InterpretIE: Resp HTCAP_IE", + (t_u8 *)pbss_entry->pht_cap, + (*(pbss_entry->pht_cap)).ieee_hdr.len + + sizeof(IEEEtypes_Header_t)); + break; + case HT_OPERATION: + pbss_entry->pht_info = + (IEEEtypes_HTInfo_t *)pcurrent_ptr; + pbss_entry->ht_info_offset = + (t_u16)(pcurrent_ptr - pbss_entry->pbeacon_buf); + HEXDUMP("InterpretIE: Resp HTINFO_IE", + (t_u8 *)pbss_entry->pht_info, + (*(pbss_entry->pht_info)).ieee_hdr.len + + sizeof(IEEEtypes_Header_t)); + break; + case BSSCO_2040: + pbss_entry->pbss_co_2040 = + (IEEEtypes_2040BSSCo_t *)pcurrent_ptr; + pbss_entry->bss_co_2040_offset = + (t_u16)(pcurrent_ptr - pbss_entry->pbeacon_buf); + HEXDUMP("InterpretIE: Resp 2040BSSCOEXISTANCE_IE", + (t_u8 *)pbss_entry->pbss_co_2040, + (*(pbss_entry->pbss_co_2040)).ieee_hdr.len + + sizeof(IEEEtypes_Header_t)); + break; + case EXT_CAPABILITY: + pbss_entry->pext_cap = + (IEEEtypes_ExtCap_t *)pcurrent_ptr; + pbss_entry->ext_cap_offset = + (t_u16)(pcurrent_ptr - pbss_entry->pbeacon_buf); + HEXDUMP("InterpretIE: Resp EXTCAP_IE", + (t_u8 *)pbss_entry->pext_cap, + (*(pbss_entry->pext_cap)).ieee_hdr.len + + sizeof(IEEEtypes_Header_t)); + break; + case OVERLAPBSSSCANPARAM: + pbss_entry->poverlap_bss_scan_param = + (IEEEtypes_OverlapBSSScanParam_t *)pcurrent_ptr; + pbss_entry->overlap_bss_offset = + (t_u16)(pcurrent_ptr - pbss_entry->pbeacon_buf); + HEXDUMP("InterpretIE: Resp OBSS_IE", + (t_u8 *)pbss_entry->poverlap_bss_scan_param, + (*(pbss_entry->poverlap_bss_scan_param)). + ieee_hdr.len + sizeof(IEEEtypes_Header_t)); + break; + case MOBILITY_DOMAIN: + PRINTM(MCMND, "Mobility Domain IE received in Scan\n"); + pbss_entry->pmd_ie = + (IEEEtypes_MobilityDomain_t *)pcurrent_ptr; + pbss_entry->md_offset = + (t_u16)(pcurrent_ptr - pbss_entry->pbeacon_buf); + HEXDUMP("InterpretIE: Resp Mobility Domain IE", + (t_u8 *)pbss_entry->pmd_ie, + (*(pbss_entry->pmd_ie)).ieee_hdr.len + + sizeof(IEEEtypes_Header_t)); + break; + default: + break; + } + + pcurrent_ptr += element_len + 2; + + /* Need to account for IE ID and IE Len */ + bytes_left_for_current_beacon -= (element_len + 2); + + } /* while (bytes_left_for_current_beacon > 2) */ + + LEAVE(); + return ret; +} + +/** + * @brief Adjust ie's position in BSSDescriptor_t + * + * @param pmpriv A pointer to mlan_private structure + * @param pbss_entry A pointer to BSSDescriptor_t structure + * + * @return N/A + */ +static t_void +wlan_adjust_ie_in_bss_entry(IN mlan_private *pmpriv, + IN BSSDescriptor_t *pbss_entry) +{ + ENTER(); + if (pbss_entry->pbeacon_buf) { + if (pbss_entry->pwpa_ie) { + pbss_entry->pwpa_ie = (IEEEtypes_VendorSpecific_t *) + (pbss_entry->pbeacon_buf + + pbss_entry->wpa_offset); + } + if (pbss_entry->prsn_ie) { + pbss_entry->prsn_ie = (IEEEtypes_Generic_t *) + (pbss_entry->pbeacon_buf + + pbss_entry->rsn_offset); + } + if (pbss_entry->pwapi_ie) { + pbss_entry->pwapi_ie = (IEEEtypes_Generic_t *) + (pbss_entry->pbeacon_buf + + pbss_entry->wapi_offset); + } + if (pbss_entry->pmd_ie) { + pbss_entry->pmd_ie = (IEEEtypes_MobilityDomain_t *) + (pbss_entry->pbeacon_buf + + pbss_entry->md_offset); + } + if (pbss_entry->pht_cap) { + pbss_entry->pht_cap = (IEEEtypes_HTCap_t *) + (pbss_entry->pbeacon_buf + + pbss_entry->ht_cap_offset); + } + if (pbss_entry->pht_info) { + pbss_entry->pht_info = (IEEEtypes_HTInfo_t *) + (pbss_entry->pbeacon_buf + + pbss_entry->ht_info_offset); + } + if (pbss_entry->pbss_co_2040) { + pbss_entry->pbss_co_2040 = (IEEEtypes_2040BSSCo_t *) + (pbss_entry->pbeacon_buf + + pbss_entry->bss_co_2040_offset); + } + if (pbss_entry->pext_cap) { + pbss_entry->pext_cap = (IEEEtypes_ExtCap_t *) + (pbss_entry->pbeacon_buf + + pbss_entry->ext_cap_offset); + } + if (pbss_entry->poverlap_bss_scan_param) { + pbss_entry->poverlap_bss_scan_param = + (IEEEtypes_OverlapBSSScanParam_t *) + (pbss_entry->pbeacon_buf + + pbss_entry->overlap_bss_offset); + } + } else { + pbss_entry->pwpa_ie = MNULL; + pbss_entry->wpa_offset = 0; + pbss_entry->prsn_ie = MNULL; + pbss_entry->rsn_offset = 0; + pbss_entry->pwapi_ie = MNULL; + pbss_entry->wapi_offset = 0; + pbss_entry->pmd_ie = MNULL; + pbss_entry->md_offset = 0; + pbss_entry->pht_cap = MNULL; + pbss_entry->ht_cap_offset = 0; + pbss_entry->pht_info = MNULL; + pbss_entry->ht_info_offset = 0; + pbss_entry->pbss_co_2040 = MNULL; + pbss_entry->bss_co_2040_offset = 0; + pbss_entry->pext_cap = MNULL; + pbss_entry->ext_cap_offset = 0; + pbss_entry->poverlap_bss_scan_param = MNULL; + pbss_entry->overlap_bss_offset = 0; + } + LEAVE(); + return; +} + +/** + * @brief Store a beacon or probe response for a BSS returned in the scan + * + * Store a new scan response or an update for a previous scan response. New + * entries need to verify that they do not exceed the total amount of + * memory allocated for the table. + + * Replacement entries need to take into consideration the amount of space + * currently allocated for the beacon/probe response and adjust the entry + * as needed. + * + * A small amount of extra pad (SCAN_BEACON_ENTRY_PAD) is generally reserved + * for an entry in case it is a beacon since a probe response for the + * network will by larger per the standard. This helps to reduce the + * amount of memory copying to fit a new probe response into an entry + * already occupied by a network's previously stored beacon. + * + * @param pmpriv A pointer to mlan_private structure + * @param beacon_idx Index in the scan table to store this entry; may be + * replacing an older duplicate entry for this BSS + * @param num_of_ent Number of entries currently in the table + * @param pnew_beacon Pointer to the new beacon/probe response to save + * + * @return N/A + */ +static t_void +wlan_ret_802_11_scan_store_beacon(IN mlan_private *pmpriv, + IN t_u32 beacon_idx, + IN t_u32 num_of_ent, + IN BSSDescriptor_t *pnew_beacon) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + t_u8 *pbcn_store; + t_u32 new_bcn_size; + t_u32 old_bcn_size; + t_u32 bcn_space; + t_u32 adj_idx; + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u8 *tmp_buf; + t_u16 bcn_size = 0; + t_u32 bcn_offset = 0; + + ENTER(); + + if (pmadapter->pscan_table[beacon_idx].pbeacon_buf) { + + new_bcn_size = pnew_beacon->beacon_buf_size; + old_bcn_size = + pmadapter->pscan_table[beacon_idx].beacon_buf_size; + bcn_space = + pmadapter->pscan_table[beacon_idx].beacon_buf_size_max; + pbcn_store = pmadapter->pscan_table[beacon_idx].pbeacon_buf; + + /* Set the max to be the same as current entry unless changed below */ + pnew_beacon->beacon_buf_size_max = bcn_space; + + if (new_bcn_size == old_bcn_size) { + /* + * Beacon is the same size as the previous entry. + * Replace the previous contents with the scan result + */ + memcpy(pmadapter, pbcn_store, + pnew_beacon->pbeacon_buf, + pnew_beacon->beacon_buf_size); + + } else if (new_bcn_size <= bcn_space) { + /* + * New beacon size will fit in the amount of space + * we have previously allocated for it + */ + + /* Copy the new beacon buffer entry over the old one */ + memcpy(pmadapter, pbcn_store, pnew_beacon->pbeacon_buf, + new_bcn_size); + + /* + * If the old beacon size was less than the + * maximum we had allotted for the entry, and + * the new entry is even smaller, reset the + * max size to the old beacon entry and compress + * the storage space (leaving a new pad space of + * (old_bcn_size - new_bcn_size). + */ + if (old_bcn_size < bcn_space && + new_bcn_size <= old_bcn_size) { + /* + * Old Beacon size is smaller than the + * allotted storage size. Shrink the + * allotted storage space. + */ + PRINTM(MINFO, + "AppControl: Smaller Duplicate Beacon (%d), " + "old = %d, new = %d, space = %d, left = %d\n", + beacon_idx, old_bcn_size, new_bcn_size, + bcn_space, + (pmadapter->bcn_buf_size - + (pmadapter->pbcn_buf_end - + pmadapter->bcn_buf))); + + /* + * memmove (since the memory overlaps) the data + * after the beacon we just stored to the end + * of the current beacon. This cleans up any + * unused space the old larger beacon was using + * in the buffer + */ + memmove(pmadapter, + (void *)((t_ptr)pbcn_store + + (t_ptr)old_bcn_size), + (void *)((t_ptr)pbcn_store + + (t_ptr)bcn_space), + (t_u32)((t_ptr)pmadapter->pbcn_buf_end - + ((t_ptr)pbcn_store + + (t_ptr)bcn_space))); + + /* + * Decrement the end pointer by the difference + * between the old larger size and the new + * smaller size since we are using less space + * due to the new beacon being smaller + */ + pmadapter->pbcn_buf_end -= + (bcn_space - old_bcn_size); + + /* + * Set the maximum storage size to the old + * beacon size + */ + pnew_beacon->beacon_buf_size_max = old_bcn_size; + + /* Adjust beacon buffer pointers that are past the current */ + for (adj_idx = 0; adj_idx < num_of_ent; + adj_idx++) { + if (pmadapter->pscan_table[adj_idx]. + pbeacon_buf > pbcn_store) { + pmadapter->pscan_table[adj_idx]. + pbeacon_buf -= + (bcn_space - + old_bcn_size); + wlan_adjust_ie_in_bss_entry + (pmpriv, + &pmadapter-> + pscan_table[adj_idx]); + } + } + } + } else if (pmadapter->pbcn_buf_end + (new_bcn_size - bcn_space) + < (pmadapter->bcn_buf + pmadapter->bcn_buf_size)) { + /* + * Beacon is larger than space previously allocated + * (bcn_space) and there is enough space left in the + * beaconBuffer to store the additional data + */ + PRINTM(MINFO, + "AppControl: Larger Duplicate Beacon (%d), " + "old = %d, new = %d, space = %d, left = %d\n", + beacon_idx, old_bcn_size, new_bcn_size, + bcn_space, + (pmadapter->bcn_buf_size - + (pmadapter->pbcn_buf_end - + pmadapter->bcn_buf))); + + /* + * memmove (since the memory overlaps) the data + * after the beacon we just stored to the end of + * the current beacon. This moves the data for + * the beacons after this further in memory to + * make space for the new larger beacon we are + * about to copy in. + */ + memmove(pmadapter, + (void *)((t_ptr)pbcn_store + + (t_ptr)new_bcn_size), + (void *)((t_ptr)pbcn_store + (t_ptr)bcn_space), + (t_u32)((t_ptr)pmadapter->pbcn_buf_end - + ((t_ptr)pbcn_store + + (t_ptr)bcn_space))); + + /* Copy the new beacon buffer entry over the old one */ + memcpy(pmadapter, pbcn_store, pnew_beacon->pbeacon_buf, + new_bcn_size); + + /* + * Move the beacon end pointer by the amount of new + * beacon data we are adding + */ + pmadapter->pbcn_buf_end += (new_bcn_size - bcn_space); + + /* + * This entry is bigger than the allotted max space + * previously reserved. Increase the max space to + * be equal to the new beacon size + */ + pnew_beacon->beacon_buf_size_max = new_bcn_size; + + /* Adjust beacon buffer pointers that are past the current */ + for (adj_idx = 0; adj_idx < num_of_ent; adj_idx++) { + if (pmadapter->pscan_table[adj_idx]. + pbeacon_buf > pbcn_store) { + pmadapter->pscan_table[adj_idx]. + pbeacon_buf += + (new_bcn_size - bcn_space); + wlan_adjust_ie_in_bss_entry(pmpriv, + &pmadapter-> + pscan_table + [adj_idx]); + } + } + } else { + /* + * Beacon is larger than the previously allocated + * space, but there is not enough free space to + * store the additional data + */ + PRINTM(MERROR, + "AppControl: Failed: Larger Duplicate Beacon (%d)," + " old = %d, new = %d, space = %d, left = %d\n", + beacon_idx, old_bcn_size, new_bcn_size, + bcn_space, + (pmadapter->bcn_buf_size - + (pmadapter->pbcn_buf_end - + pmadapter->bcn_buf))); + + /* Storage failure, keep old beacon intact */ + pnew_beacon->beacon_buf_size = old_bcn_size; + if (pnew_beacon->pwpa_ie) + pnew_beacon->wpa_offset = + pmadapter->pscan_table[beacon_idx]. + wpa_offset; + if (pnew_beacon->prsn_ie) + pnew_beacon->rsn_offset = + pmadapter->pscan_table[beacon_idx]. + rsn_offset; + if (pnew_beacon->pwapi_ie) + pnew_beacon->wapi_offset = + pmadapter->pscan_table[beacon_idx]. + wapi_offset; + if (pnew_beacon->pmd_ie) + pnew_beacon->md_offset = + pmadapter->pscan_table[beacon_idx]. + md_offset; + if (pnew_beacon->pht_cap) + pnew_beacon->ht_cap_offset = + pmadapter->pscan_table[beacon_idx]. + ht_cap_offset; + if (pnew_beacon->pht_info) + pnew_beacon->ht_info_offset = + pmadapter->pscan_table[beacon_idx]. + ht_info_offset; + if (pnew_beacon->pbss_co_2040) + pnew_beacon->bss_co_2040_offset = + pmadapter->pscan_table[beacon_idx]. + bss_co_2040_offset; + if (pnew_beacon->pext_cap) + pnew_beacon->ext_cap_offset = + pmadapter->pscan_table[beacon_idx]. + ext_cap_offset; + if (pnew_beacon->poverlap_bss_scan_param) + pnew_beacon->overlap_bss_offset = + pmadapter->pscan_table[beacon_idx]. + overlap_bss_offset; + } + /* Point the new entry to its permanent storage space */ + pnew_beacon->pbeacon_buf = pbcn_store; + wlan_adjust_ie_in_bss_entry(pmpriv, pnew_beacon); + } else { + if ((pmadapter->pbcn_buf_end + pnew_beacon->beacon_buf_size + + SCAN_BEACON_ENTRY_PAD > (pmadapter->bcn_buf + + pmadapter->bcn_buf_size)) && + (pmadapter->bcn_buf_size < MAX_SCAN_BEACON_BUFFER)) { + /* no space for this entry, realloc bcn buffer */ + ret = pmadapter->callbacks.moal_malloc(pmadapter-> + pmoal_handle, + pmadapter-> + bcn_buf_size + + DEFAULT_SCAN_BEACON_BUFFER, + MLAN_MEM_DEF, + (t_u8 **) + &tmp_buf); + + if ((ret == MLAN_STATUS_SUCCESS) && (tmp_buf)) { + PRINTM(MCMND, + "Realloc Beacon buffer, old size=%d, new_size=%d\n", + pmadapter->bcn_buf_size, + pmadapter->bcn_buf_size + + DEFAULT_SCAN_BEACON_BUFFER); + bcn_size = + pmadapter->pbcn_buf_end - + pmadapter->bcn_buf; + memcpy(pmadapter, tmp_buf, pmadapter->bcn_buf, + bcn_size); + /* Adjust beacon buffer pointers that are past the current */ + for (adj_idx = 0; adj_idx < num_of_ent; + adj_idx++) { + bcn_offset = + pmadapter->pscan_table[adj_idx]. + pbeacon_buf - + pmadapter->bcn_buf; + pmadapter->pscan_table[adj_idx]. + pbeacon_buf = + tmp_buf + bcn_offset; + wlan_adjust_ie_in_bss_entry(pmpriv, + &pmadapter-> + pscan_table + [adj_idx]); + } + pmadapter->pbcn_buf_end = tmp_buf + bcn_size; + pmadapter->callbacks.moal_mfree(pmadapter-> + pmoal_handle, + (t_u8 *) + pmadapter-> + bcn_buf); + pmadapter->bcn_buf = tmp_buf; + pmadapter->bcn_buf_size += + DEFAULT_SCAN_BEACON_BUFFER; + } + } + /* + * No existing beacon data exists for this entry, check to see + * if we can fit it in the remaining space + */ + if (pmadapter->pbcn_buf_end + pnew_beacon->beacon_buf_size + + SCAN_BEACON_ENTRY_PAD < (pmadapter->bcn_buf + + pmadapter->bcn_buf_size)) { + + /* + * Copy the beacon buffer data from the local entry + * to the adapter dev struct buffer space used to + * store the raw beacon data for each entry in the + * scan table + */ + memcpy(pmadapter, pmadapter->pbcn_buf_end, + pnew_beacon->pbeacon_buf, + pnew_beacon->beacon_buf_size); + + /* + * Update the beacon ptr to point to the table + * save area + */ + pnew_beacon->pbeacon_buf = pmadapter->pbcn_buf_end; + pnew_beacon->beacon_buf_size_max = + (pnew_beacon->beacon_buf_size + + SCAN_BEACON_ENTRY_PAD); + wlan_adjust_ie_in_bss_entry(pmpriv, pnew_beacon); + + /* Increment the end pointer by the size reserved */ + pmadapter->pbcn_buf_end += + pnew_beacon->beacon_buf_size_max; + + PRINTM(MINFO, "AppControl: Beacon[%02d] sz=%03d," + " used = %04d, left = %04d\n", + beacon_idx, + pnew_beacon->beacon_buf_size, + (pmadapter->pbcn_buf_end - pmadapter->bcn_buf), + (pmadapter->bcn_buf_size - + (pmadapter->pbcn_buf_end - + pmadapter->bcn_buf))); + } else { + /* + * No space for new beacon + */ + PRINTM(MCMND, "AppControl: No space beacon (%d): " + MACSTR "; sz=%03d, left=%03d\n", + beacon_idx, + MAC2STR(pnew_beacon->mac_address), + pnew_beacon->beacon_buf_size, + (pmadapter->bcn_buf_size - + (pmadapter->pbcn_buf_end - + pmadapter->bcn_buf))); + + /* + * Storage failure; clear storage records + * for this bcn + */ + pnew_beacon->pbeacon_buf = MNULL; + pnew_beacon->beacon_buf_size = 0; + pnew_beacon->beacon_buf_size_max = 0; + wlan_adjust_ie_in_bss_entry(pmpriv, pnew_beacon); + } + } + + LEAVE(); +} + +/** + * @brief update beacon buffer of the current bss descriptor + * + * @param pmpriv A pointer to mlan_private structure + * + * @return MLAN_STATUS_SUCCESS, otherwise failure + */ +static mlan_status +wlan_update_curr_bcn(IN mlan_private *pmpriv) +{ + BSSDescriptor_t *pcurr_bss = &pmpriv->curr_bss_params.bss_descriptor; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (pmpriv->pcurr_bcn_buf && pmpriv->curr_bcn_size) { + pcurr_bss->pbeacon_buf = pmpriv->pcurr_bcn_buf; + pcurr_bss->beacon_buf_size = pmpriv->curr_bcn_size; + pcurr_bss->beacon_buf_size_max = pmpriv->curr_bcn_size; + + /* adjust the pointers in the current bss descriptor */ + if (pcurr_bss->pwpa_ie) { + pcurr_bss->pwpa_ie = (IEEEtypes_VendorSpecific_t *) + (pcurr_bss->pbeacon_buf + + pcurr_bss->wpa_offset); + } + if (pcurr_bss->prsn_ie) { + pcurr_bss->prsn_ie = (IEEEtypes_Generic_t *) + (pcurr_bss->pbeacon_buf + + pcurr_bss->rsn_offset); + } + if (pcurr_bss->pmd_ie) { + pcurr_bss->pmd_ie = (IEEEtypes_MobilityDomain_t *) + (pcurr_bss->pbeacon_buf + pcurr_bss->md_offset); + } + if (pcurr_bss->pht_cap) { + pcurr_bss->pht_cap = (IEEEtypes_HTCap_t *) + (pcurr_bss->pbeacon_buf + + pcurr_bss->ht_cap_offset); + } + + if (pcurr_bss->pht_info) { + pcurr_bss->pht_info = (IEEEtypes_HTInfo_t *) + (pcurr_bss->pbeacon_buf + + pcurr_bss->ht_info_offset); + } + + if (pcurr_bss->pbss_co_2040) { + pcurr_bss->pbss_co_2040 = (IEEEtypes_2040BSSCo_t *) + (pcurr_bss->pbeacon_buf + + pcurr_bss->bss_co_2040_offset); + } + + if (pcurr_bss->pext_cap) { + pcurr_bss->pext_cap = (IEEEtypes_ExtCap_t *) + (pcurr_bss->pbeacon_buf + + pcurr_bss->ext_cap_offset); + } + + if (pcurr_bss->poverlap_bss_scan_param) { + pcurr_bss->poverlap_bss_scan_param = + (IEEEtypes_OverlapBSSScanParam_t *) + (pcurr_bss->pbeacon_buf + + pcurr_bss->overlap_bss_offset); + } + + PRINTM(MINFO, "current beacon restored %d\n", + pmpriv->curr_bcn_size); + } else { + PRINTM(MERROR, "curr_bcn_buf not saved\n"); + ret = MLAN_STATUS_FAILURE; + } + + LEAVE(); + return ret; +} + +/** + * @brief Post process the scan table after a new scan command has completed + * + * Inspect each entry of the scan table and try to find an entry that + * matches our current associated/joined network from the scan. If + * one is found, update the stored copy of the BSSDescriptor for our + * current network. + * + * Debug dump the current scan table contents if compiled accordingly. + * + * @param pmpriv A pointer to mlan_private structure + * + * @return N/A + */ +static t_void +wlan_scan_process_results(IN mlan_private *pmpriv) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + t_s32 j; + t_u32 i; + mlan_status ret = MLAN_STATUS_SUCCESS; + BSSDescriptor_t *bss_new_entry = MNULL; + pmlan_callbacks pcb = &pmadapter->callbacks; + + ENTER(); + + if (pmpriv->media_connected == MTRUE) { + + j = wlan_find_ssid_in_list(pmpriv, + &pmpriv->curr_bss_params. + bss_descriptor.ssid, + pmpriv->curr_bss_params. + bss_descriptor.mac_address, + pmpriv->bss_mode); + + if (j >= 0) { + pmadapter->callbacks.moal_spin_lock(pmadapter-> + pmoal_handle, + pmpriv-> + curr_bcn_buf_lock); + pmpriv->curr_bss_params.bss_descriptor.pwpa_ie = MNULL; + pmpriv->curr_bss_params.bss_descriptor.wpa_offset = 0; + pmpriv->curr_bss_params.bss_descriptor.prsn_ie = MNULL; + pmpriv->curr_bss_params.bss_descriptor.rsn_offset = 0; + pmpriv->curr_bss_params.bss_descriptor.pwapi_ie = MNULL; + pmpriv->curr_bss_params.bss_descriptor.wapi_offset = 0; + pmpriv->curr_bss_params.bss_descriptor.pmd_ie = MNULL; + pmpriv->curr_bss_params.bss_descriptor.md_offset = 0; + pmpriv->curr_bss_params.bss_descriptor.pht_cap = MNULL; + pmpriv->curr_bss_params.bss_descriptor.ht_cap_offset = + 0; + pmpriv->curr_bss_params.bss_descriptor.pht_info = MNULL; + pmpriv->curr_bss_params.bss_descriptor.ht_info_offset = + 0; + pmpriv->curr_bss_params.bss_descriptor.pbss_co_2040 = + MNULL; + pmpriv->curr_bss_params.bss_descriptor. + bss_co_2040_offset = 0; + pmpriv->curr_bss_params.bss_descriptor.pext_cap = MNULL; + pmpriv->curr_bss_params.bss_descriptor.ext_cap_offset = + 0; + pmpriv->curr_bss_params.bss_descriptor. + poverlap_bss_scan_param = MNULL; + pmpriv->curr_bss_params.bss_descriptor. + overlap_bss_offset = 0; + pmpriv->curr_bss_params.bss_descriptor.pbeacon_buf = + MNULL; + pmpriv->curr_bss_params.bss_descriptor.beacon_buf_size = + 0; + pmpriv->curr_bss_params.bss_descriptor. + beacon_buf_size_max = 0; + + PRINTM(MINFO, + "Found current ssid/bssid in list @ index #%d\n", + j); + /* Make a copy of current BSSID descriptor */ + memcpy(pmadapter, + &pmpriv->curr_bss_params.bss_descriptor, + &pmadapter->pscan_table[j], + sizeof(pmpriv->curr_bss_params.bss_descriptor)); + + pmadapter->callbacks.moal_spin_unlock(pmadapter-> + pmoal_handle, + pmpriv-> + curr_bcn_buf_lock); + wlan_save_curr_bcn(pmpriv); + } else { + //Apend to the end of scan table + if (pmpriv->pcurr_bcn_buf && pmpriv->curr_bcn_size) { + ret = pcb->moal_malloc(pmadapter->pmoal_handle, + sizeof(BSSDescriptor_t), + MLAN_MEM_DEF, + (t_u8 **)&bss_new_entry); + if (ret == MLAN_STATUS_SUCCESS && bss_new_entry) { + memcpy(pmadapter, bss_new_entry, + &pmpriv->curr_bss_params. + bss_descriptor, + sizeof(pmpriv->curr_bss_params. + bss_descriptor)); + if (pmadapter->num_in_scan_table < + MRVDRV_MAX_BSSID_LIST) + pmadapter->num_in_scan_table++; + pmadapter->pscan_table[pmadapter-> + num_in_scan_table + - + 1].pbeacon_buf = + MNULL; + wlan_ret_802_11_scan_store_beacon + (pmpriv, + pmadapter->num_in_scan_table - + 1, + pmadapter->num_in_scan_table, + bss_new_entry); + if (bss_new_entry->pbeacon_buf == MNULL) + pmadapter->num_in_scan_table--; + else + memcpy(pmadapter, + &pmadapter-> + pscan_table[pmadapter-> + num_in_scan_table + - 1], + bss_new_entry, + sizeof(BSSDescriptor_t)); + pcb->moal_mfree(pmadapter->pmoal_handle, + (t_u8 *)bss_new_entry); + } + } + } + + } + + for (i = 0; i < pmadapter->num_in_scan_table; i++) + PRINTM(MINFO, "Scan:(%02d) " MACSTR ", " + "RSSI[%03d], SSID[%s]\n", + i, + MAC2STR(pmadapter->pscan_table[i].mac_address), + (t_s32)pmadapter->pscan_table[i].rssi, + pmadapter->pscan_table[i].ssid.ssid); + + /* + * Prepares domain info from scan table and downloads the + * domain info command to the FW. + */ + wlan_11d_prepare_dnld_domain_info_cmd(pmpriv); + PRINTM(MMSG, "wlan: SCAN COMPLETED: scanned AP count=%d\n", + pmadapter->num_in_scan_table); + LEAVE(); +} + +/** + * @brief Delete a specific indexed entry from the scan table. + * + * Delete the scan table entry indexed by table_idx. Compact the remaining + * entries and adjust any buffering of beacon/probe response data + * if needed. + * + * @param pmpriv A pointer to mlan_private structure + * @param table_idx Scan table entry index to delete from the table + * + * @return N/A + * + * @pre table_idx must be an index to a valid entry + */ +static t_void +wlan_scan_delete_table_entry(IN mlan_private *pmpriv, IN t_s32 table_idx) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + t_u32 del_idx; + t_u32 beacon_buf_adj; + t_u8 *pbeacon_buf; + + ENTER(); + + /* + * Shift the saved beacon buffer data for the scan table back over the + * entry being removed. Update the end of buffer pointer. Save the + * deleted buffer allocation size for pointer adjustments for entries + * compacted after the deleted index. + */ + beacon_buf_adj = pmadapter->pscan_table[table_idx].beacon_buf_size_max; + + PRINTM(MINFO, + "Scan: Delete Entry %d, beacon buffer removal = %d bytes\n", + table_idx, beacon_buf_adj); + + /* Check if the table entry had storage allocated for its beacon */ + if (beacon_buf_adj) { + pbeacon_buf = pmadapter->pscan_table[table_idx].pbeacon_buf; + + /* + * Remove the entry's buffer space, decrement the table + * end pointer by the amount we are removing + */ + pmadapter->pbcn_buf_end -= beacon_buf_adj; + + PRINTM(MINFO, + "Scan: Delete Entry %d, compact data: %p <- %p (sz = %d)\n", + table_idx, + pbeacon_buf, + pbeacon_buf + beacon_buf_adj, + pmadapter->pbcn_buf_end - pbeacon_buf); + + /* + * Compact data storage. Copy all data after the deleted entry's + * end address (pbeacon_buf + beacon_buf_adj) back to the original + * start address (pbeacon_buf). + * + * Scan table entries affected by the move will have their entry + * pointer adjusted below. + * + * Use memmove since the dest/src memory regions overlap. + */ + memmove(pmadapter, pbeacon_buf, + (void *)((t_ptr)pbeacon_buf + (t_ptr)beacon_buf_adj), + (t_u32)((t_ptr)pmadapter->pbcn_buf_end - + (t_ptr)pbeacon_buf)); + } + + PRINTM(MINFO, "Scan: Delete Entry %d, num_in_scan_table = %d\n", + table_idx, pmadapter->num_in_scan_table); + + /* + * Shift all of the entries after the table_idx back by one, compacting + * the table and removing the requested entry + */ + for (del_idx = table_idx; (del_idx + 1) < pmadapter->num_in_scan_table; + del_idx++) { + /* Copy the next entry over this one */ + memcpy(pmadapter, pmadapter->pscan_table + del_idx, + pmadapter->pscan_table + del_idx + 1, + sizeof(BSSDescriptor_t)); + + /* + * Adjust this entry's pointer to its beacon buffer based on the + * removed/compacted entry from the deleted index. Don't decrement + * if the buffer pointer is MNULL (no data stored for this entry). + */ + if (pmadapter->pscan_table[del_idx].pbeacon_buf) { + pmadapter->pscan_table[del_idx].pbeacon_buf -= + beacon_buf_adj; + if (pmadapter->pscan_table[del_idx].pwpa_ie) { + pmadapter->pscan_table[del_idx].pwpa_ie = + (IEEEtypes_VendorSpecific_t *) + (pmadapter->pscan_table[del_idx]. + pbeacon_buf + + pmadapter->pscan_table[del_idx]. + wpa_offset); + } + if (pmadapter->pscan_table[del_idx].prsn_ie) { + pmadapter->pscan_table[del_idx].prsn_ie = + (IEEEtypes_Generic_t *) + (pmadapter->pscan_table[del_idx]. + pbeacon_buf + + pmadapter->pscan_table[del_idx]. + rsn_offset); + } + if (pmadapter->pscan_table[del_idx].pwapi_ie) { + pmadapter->pscan_table[del_idx].pwapi_ie = + (IEEEtypes_Generic_t *) + (pmadapter->pscan_table[del_idx]. + pbeacon_buf + + pmadapter->pscan_table[del_idx]. + wapi_offset); + } + if (pmadapter->pscan_table[del_idx].pmd_ie) { + pmadapter->pscan_table[del_idx].pmd_ie = + (IEEEtypes_MobilityDomain_t *) + (pmadapter->pscan_table[del_idx]. + pbeacon_buf + + pmadapter->pscan_table[del_idx]. + md_offset); + } + if (pmadapter->pscan_table[del_idx].pht_cap) { + pmadapter->pscan_table[del_idx].pht_cap = + (IEEEtypes_HTCap_t *)(pmadapter-> + pscan_table + [del_idx]. + pbeacon_buf + + pmadapter-> + pscan_table + [del_idx]. + ht_cap_offset); + } + + if (pmadapter->pscan_table[del_idx].pht_info) { + pmadapter->pscan_table[del_idx].pht_info = + (IEEEtypes_HTInfo_t *)(pmadapter-> + pscan_table + [del_idx]. + pbeacon_buf + + pmadapter-> + pscan_table + [del_idx]. + ht_info_offset); + } + if (pmadapter->pscan_table[del_idx].pbss_co_2040) { + pmadapter->pscan_table[del_idx].pbss_co_2040 = + (IEEEtypes_2040BSSCo_t *)(pmadapter-> + pscan_table + [del_idx]. + pbeacon_buf + + pmadapter-> + pscan_table + [del_idx]. + bss_co_2040_offset); + } + if (pmadapter->pscan_table[del_idx].pext_cap) { + pmadapter->pscan_table[del_idx].pext_cap = + (IEEEtypes_ExtCap_t *)(pmadapter-> + pscan_table + [del_idx]. + pbeacon_buf + + pmadapter-> + pscan_table + [del_idx]. + ext_cap_offset); + } + if (pmadapter->pscan_table[del_idx]. + poverlap_bss_scan_param) { + pmadapter->pscan_table[del_idx]. + poverlap_bss_scan_param = + (IEEEtypes_OverlapBSSScanParam_t + *)(pmadapter->pscan_table[del_idx]. + pbeacon_buf + + pmadapter->pscan_table[del_idx]. + overlap_bss_offset); + } + + } + } + + /* The last entry is invalid now that it has been deleted or moved back */ + memset(pmadapter, + pmadapter->pscan_table + pmadapter->num_in_scan_table - 1, 0x00, + sizeof(BSSDescriptor_t)); + + pmadapter->num_in_scan_table--; + + LEAVE(); +} + +/** + * @brief Delete all occurrences of a given SSID from the scan table + * + * Iterate through the scan table and delete all entries that match a given + * SSID. Compact the remaining scan table entries. + * + * @param pmpriv A pointer to mlan_private structure + * @param pdel_ssid Pointer to an SSID to be used in deleting all + * matching SSIDs from the scan table + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status +wlan_scan_delete_ssid_table_entry(IN mlan_private *pmpriv, + IN mlan_802_11_ssid *pdel_ssid) +{ + mlan_status ret = MLAN_STATUS_FAILURE; + t_s32 table_idx; + + ENTER(); + + PRINTM(MINFO, "Scan: Delete Ssid Entry: %-32s\n", pdel_ssid->ssid); + + /* + * If the requested SSID is found in the table, delete it. Then keep + * searching the table for multiple entries for the SSID until no + * more are found + */ + while ((table_idx = wlan_find_ssid_in_list(pmpriv, + pdel_ssid, + MNULL, + MLAN_BSS_MODE_AUTO)) >= 0) { + PRINTM(MINFO, "Scan: Delete SSID Entry: Found Idx = %d\n", + table_idx); + ret = MLAN_STATUS_SUCCESS; + wlan_scan_delete_table_entry(pmpriv, table_idx); + } + + LEAVE(); + return ret; +} + +/******************************************************** + Global Functions +********************************************************/ + +/** + * @brief Check if a scanned network compatible with the driver settings + * + * WEP WPA WPA2 ad-hoc encrypt Network + * enabled enabled enabled AES mode Privacy WPA WPA2 Compatible + * 0 0 0 0 NONE 0 0 0 yes No security + * 0 1 0 0 x 1x 1 x yes WPA (disable HT if no AES) + * 0 0 1 0 x 1x x 1 yes WPA2 (disable HT if no AES) + * 0 0 0 1 NONE 1 0 0 yes Ad-hoc AES + * 1 0 0 0 NONE 1 0 0 yes Static WEP (disable HT) + * 0 0 0 0 !=NONE 1 0 0 yes Dynamic WEP + * + * @param pmpriv A pointer to mlan_private + * @param index Index in scan table to check against current driver settings + * @param mode Network mode: Infrastructure or IBSS + * + * @return Index in ScanTable, or negative value if error + */ +t_s32 +wlan_is_network_compatible(IN mlan_private *pmpriv, + IN t_u32 index, IN t_u32 mode) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + BSSDescriptor_t *pbss_desc; + + ENTER(); + + pbss_desc = &pmadapter->pscan_table[index]; + /* Don't check for compatibility if roaming */ + if ((pmpriv->media_connected == MTRUE) + && (pmpriv->bss_mode == MLAN_BSS_MODE_INFRA) + && (pbss_desc->bss_mode == MLAN_BSS_MODE_INFRA)) { + LEAVE(); + return index; + } + + pbss_desc->disable_11n = MFALSE; + + if (pbss_desc->wlan_11h_bss_info.chan_switch_ann.element_id == + CHANNEL_SWITCH_ANN) { + PRINTM(MINFO, + "Don't connect to AP with CHANNEL_SWITCH_ANN IE.\n"); + LEAVE(); + return -1; + } + + if (pmpriv->wps.session_enable == MTRUE) { + PRINTM(MINFO, "Return success directly in WPS period\n"); + LEAVE(); + return index; + } + + if ((pbss_desc->bss_mode == mode) && + (pmpriv->sec_info.ewpa_enabled == MTRUE +#ifdef DRV_EMBEDDED_SUPPLICANT + || supplicantIsEnabled(pmpriv->psapriv) +#endif + )) { + if (((pbss_desc->pwpa_ie) && + ((*(pbss_desc->pwpa_ie)).vend_hdr.element_id == WPA_IE)) || + ((pbss_desc->prsn_ie) && + ((*(pbss_desc->prsn_ie)).ieee_hdr.element_id == RSN_IE))) { + if (((pmpriv->adapter->config_bands & BAND_GN || + pmpriv->adapter->config_bands & BAND_AN) && + pbss_desc->pht_cap) + && (pmpriv->bss_mode == MLAN_BSS_MODE_INFRA) + && !is_wpa_oui_present(pmpriv->adapter, pbss_desc, + CIPHER_SUITE_CCMP) + && !is_rsn_oui_present(pmpriv->adapter, pbss_desc, + CIPHER_SUITE_CCMP)) { + + if (is_wpa_oui_present + (pmpriv->adapter, pbss_desc, + CIPHER_SUITE_TKIP) + || is_rsn_oui_present(pmpriv->adapter, + pbss_desc, + CIPHER_SUITE_TKIP)) { + PRINTM(MINFO, + "Disable 11n if AES is not supported by AP\n"); + pbss_desc->disable_11n = MTRUE; + } else { + LEAVE(); + return -1; + } + } + LEAVE(); + return index; + } else { + PRINTM(MINFO, + "ewpa_enabled: Ignore none WPA/WPA2 AP\n"); + LEAVE(); + return -1; + } + } + + if (pmpriv->sec_info.wapi_enabled && + (pbss_desc->pwapi_ie && + ((*(pbss_desc->pwapi_ie)).ieee_hdr.element_id == WAPI_IE))) { + PRINTM(MINFO, "Return success for WAPI AP\n"); + LEAVE(); + return index; + } + + if (pbss_desc->bss_mode == mode) { + if (pmpriv->sec_info.wep_status == Wlan802_11WEPDisabled + && !pmpriv->sec_info.wpa_enabled + && !pmpriv->sec_info.wpa2_enabled + && ((!pbss_desc->pwpa_ie) || + ((*(pbss_desc->pwpa_ie)).vend_hdr.element_id != WPA_IE)) + && ((!pbss_desc->prsn_ie) || + ((*(pbss_desc->prsn_ie)).ieee_hdr.element_id != RSN_IE)) + && !pmpriv->adhoc_aes_enabled && + pmpriv->sec_info.encryption_mode == + MLAN_ENCRYPTION_MODE_NONE && !pbss_desc->privacy) { + /* No security */ + LEAVE(); + return index; + } else if (pmpriv->sec_info.wep_status == Wlan802_11WEPEnabled + && !pmpriv->sec_info.wpa_enabled + && !pmpriv->sec_info.wpa2_enabled + && !pmpriv->adhoc_aes_enabled + && pbss_desc->privacy) { + /* Static WEP enabled */ + PRINTM(MINFO, "Disable 11n in WEP mode\n"); + pbss_desc->disable_11n = MTRUE; + /* Reject the following cases: */ + /* + * case 1: RSN IE w/o WEP OUI and WPA IE w/o WEP OUI + * case 2: RSN IE w/o WEP OUI and No WPA IE + * case 3: WPA IE w/o WEP OUI and No RSN IE + */ + if (((pbss_desc->prsn_ie) && + ((*(pbss_desc->prsn_ie)).ieee_hdr.element_id == + RSN_IE)) || ((pbss_desc->pwpa_ie) && + ((*(pbss_desc->pwpa_ie)).vend_hdr. + element_id == WPA_IE))) { + if (!is_rsn_oui_present + (pmpriv->adapter, pbss_desc, + CIPHER_SUITE_WEP40) && + !is_rsn_oui_present(pmpriv->adapter, + pbss_desc, + CIPHER_SUITE_WEP104) && + !is_wpa_oui_present(pmpriv->adapter, + pbss_desc, + CIPHER_SUITE_WEP40) && + !is_wpa_oui_present(pmpriv->adapter, + pbss_desc, + CIPHER_SUITE_WEP104)) + index = -1; + } + + LEAVE(); + return index; + } else if (pmpriv->sec_info.wep_status == Wlan802_11WEPDisabled + && pmpriv->sec_info.wpa_enabled + && !pmpriv->sec_info.wpa2_enabled + && ((pbss_desc->pwpa_ie) && + ((*(pbss_desc->pwpa_ie)).vend_hdr.element_id == + WPA_IE)) + && !pmpriv->adhoc_aes_enabled + /* + * Privacy bit may NOT be set in some APs like + * LinkSys WRT54G && pbss_desc->privacy + */ + ) { + /* WPA enabled */ + PRINTM(MINFO, + "wlan_is_network_compatible() WPA: index=%d wpa_ie=%#x " + "rsn_ie=%#x WEP=%s WPA=%s WPA2=%s EncMode=%#x " + "privacy=%#x\n", index, + (pbss_desc->pwpa_ie) ? (*(pbss_desc->pwpa_ie)). + vend_hdr.element_id : 0, + (pbss_desc->prsn_ie) ? (*(pbss_desc->prsn_ie)). + ieee_hdr.element_id : 0, + (pmpriv->sec_info.wep_status == + Wlan802_11WEPEnabled) ? "e" : "d", + (pmpriv->sec_info.wpa_enabled) ? "e" : "d", + (pmpriv->sec_info.wpa2_enabled) ? "e" : "d", + pmpriv->sec_info.encryption_mode, + pbss_desc->privacy); + if (((pmpriv->adapter->config_bands & BAND_GN || + pmpriv->adapter->config_bands & BAND_AN) && + pbss_desc->pht_cap) + && (pmpriv->bss_mode == MLAN_BSS_MODE_INFRA) + && !is_wpa_oui_present(pmpriv->adapter, pbss_desc, + CIPHER_SUITE_CCMP)) { + if (is_wpa_oui_present + (pmpriv->adapter, pbss_desc, + CIPHER_SUITE_TKIP)) { + PRINTM(MINFO, + "Disable 11n if AES is not supported by AP\n"); + pbss_desc->disable_11n = MTRUE; + } else { + LEAVE(); + return -1; + } + } + LEAVE(); + return index; + } else if (pmpriv->sec_info.wep_status == Wlan802_11WEPDisabled + && !pmpriv->sec_info.wpa_enabled + && pmpriv->sec_info.wpa2_enabled + && ((pbss_desc->prsn_ie) && + ((*(pbss_desc->prsn_ie)).ieee_hdr.element_id == + RSN_IE)) + && !pmpriv->adhoc_aes_enabled + /* + * Privacy bit may NOT be set in some APs like + * LinkSys WRT54G && pbss_desc->privacy + */ + ) { + /* WPA2 enabled */ + PRINTM(MINFO, + "wlan_is_network_compatible() WPA2: index=%d wpa_ie=%#x " + "rsn_ie=%#x WEP=%s WPA=%s WPA2=%s EncMode=%#x " + "privacy=%#x\n", index, + (pbss_desc->pwpa_ie) ? (*(pbss_desc->pwpa_ie)). + vend_hdr.element_id : 0, + (pbss_desc->prsn_ie) ? (*(pbss_desc->prsn_ie)). + ieee_hdr.element_id : 0, + (pmpriv->sec_info.wep_status == + Wlan802_11WEPEnabled) ? "e" : "d", + (pmpriv->sec_info.wpa_enabled) ? "e" : "d", + (pmpriv->sec_info.wpa2_enabled) ? "e" : "d", + pmpriv->sec_info.encryption_mode, + pbss_desc->privacy); + if (((pmpriv->adapter->config_bands & BAND_GN || + pmpriv->adapter->config_bands & BAND_AN) && + pbss_desc->pht_cap) + && (pmpriv->bss_mode == MLAN_BSS_MODE_INFRA) + && !is_rsn_oui_present(pmpriv->adapter, pbss_desc, + CIPHER_SUITE_CCMP)) { + if (is_rsn_oui_present + (pmpriv->adapter, pbss_desc, + CIPHER_SUITE_TKIP)) { + PRINTM(MINFO, + "Disable 11n if AES is not supported by AP\n"); + pbss_desc->disable_11n = MTRUE; + } else { + LEAVE(); + return -1; + } + } + LEAVE(); + return index; + } else if (pmpriv->sec_info.wep_status == Wlan802_11WEPDisabled + && !pmpriv->sec_info.wpa_enabled + && !pmpriv->sec_info.wpa2_enabled + && ((!pbss_desc->pwpa_ie) || + ((*(pbss_desc->pwpa_ie)).vend_hdr.element_id != + WPA_IE)) + && ((!pbss_desc->prsn_ie) || + ((*(pbss_desc->prsn_ie)).ieee_hdr.element_id != + RSN_IE)) + && pmpriv->adhoc_aes_enabled && + pmpriv->sec_info.encryption_mode == + MLAN_ENCRYPTION_MODE_NONE && pbss_desc->privacy) { + /* Ad-hoc AES enabled */ + LEAVE(); + return index; + } else if (pmpriv->sec_info.wep_status == Wlan802_11WEPDisabled + && !pmpriv->sec_info.wpa_enabled + && !pmpriv->sec_info.wpa2_enabled + && ((!pbss_desc->pwpa_ie) || + ((*(pbss_desc->pwpa_ie)).vend_hdr.element_id != + WPA_IE)) + && ((!pbss_desc->prsn_ie) || + ((*(pbss_desc->prsn_ie)).ieee_hdr.element_id != + RSN_IE)) + && !pmpriv->adhoc_aes_enabled && + pmpriv->sec_info.encryption_mode != + MLAN_ENCRYPTION_MODE_NONE && pbss_desc->privacy) { + /* Dynamic WEP enabled */ + pbss_desc->disable_11n = MTRUE; + PRINTM(MINFO, + "wlan_is_network_compatible() dynamic WEP: index=%d " + "wpa_ie=%#x rsn_ie=%#x EncMode=%#x privacy=%#x\n", + index, + (pbss_desc->pwpa_ie) ? (*(pbss_desc->pwpa_ie)). + vend_hdr.element_id : 0, + (pbss_desc->prsn_ie) ? (*(pbss_desc->prsn_ie)). + ieee_hdr.element_id : 0, + pmpriv->sec_info.encryption_mode, + pbss_desc->privacy); + LEAVE(); + return index; + } + + /* Security doesn't match */ + PRINTM(MINFO, + "wlan_is_network_compatible() FAILED: index=%d wpa_ie=%#x " + "rsn_ie=%#x WEP=%s WPA=%s WPA2=%s EncMode=%#x privacy=%#x\n", + index, + (pbss_desc->pwpa_ie) ? (*(pbss_desc->pwpa_ie)).vend_hdr. + element_id : 0, + (pbss_desc->prsn_ie) ? (*(pbss_desc->prsn_ie)).ieee_hdr. + element_id : 0, + (pmpriv->sec_info.wep_status == + Wlan802_11WEPEnabled) ? "e" : "d", + (pmpriv->sec_info.wpa_enabled) ? "e" : "d", + (pmpriv->sec_info.wpa2_enabled) ? "e" : "d", + pmpriv->sec_info.encryption_mode, pbss_desc->privacy); + LEAVE(); + return -1; + } + + /* Mode doesn't match */ + LEAVE(); + return -1; +} + +/** + * @brief Internal function used to flush the scan list + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_flush_scan_table(IN pmlan_adapter pmadapter) +{ + t_u8 i = 0; + ENTER(); + + PRINTM(MINFO, "Flushing scan table\n"); + + memset(pmadapter, pmadapter->pscan_table, 0, + (sizeof(BSSDescriptor_t) * MRVDRV_MAX_BSSID_LIST)); + pmadapter->num_in_scan_table = 0; + + memset(pmadapter, pmadapter->bcn_buf, 0, pmadapter->bcn_buf_size); + pmadapter->pbcn_buf_end = pmadapter->bcn_buf; + + for (i = 0; i < pmadapter->num_in_chan_stats; i++) + pmadapter->pchan_stats[i].cca_scan_duration = 0; + pmadapter->idx_chan_stats = 0; + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Internal function used to start a scan based on an input config + * + * Use the input user scan configuration information when provided in + * order to send the appropriate scan commands to firmware to populate or + * update the internal driver scan table + * + * @param pmpriv A pointer to mlan_private structure + * @param pioctl_buf A pointer to MLAN IOCTL Request buffer + * @param puser_scan_in Pointer to the input configuration for the requested + * scan. + * + * @return MLAN_STATUS_SUCCESS or < 0 if error + */ +mlan_status +wlan_scan_networks(IN mlan_private *pmpriv, + IN t_void *pioctl_buf, IN wlan_user_scan_cfg *puser_scan_in) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_adapter *pmadapter = pmpriv->adapter; + mlan_callbacks *pcb = (mlan_callbacks *)&pmadapter->callbacks; + cmd_ctrl_node *pcmd_node = MNULL; + pmlan_ioctl_req pioctl_req = (mlan_ioctl_req *)pioctl_buf; + + wlan_scan_cmd_config_tlv *pscan_cfg_out = MNULL; + MrvlIEtypes_ChanListParamSet_t *pchan_list_out; + t_u32 buf_size; + ChanScanParamSet_t *pscan_chan_list; + + t_u8 keep_previous_scan; + t_u8 filtered_scan; + t_u8 scan_current_chan_only; + t_u8 max_chan_per_scan; + t_u8 i; + + ENTER(); + + ret = pcb->moal_malloc(pmadapter->pmoal_handle, + sizeof(wlan_scan_cmd_config_tlv), MLAN_MEM_DEF, + (t_u8 **)&pscan_cfg_out); + if (ret != MLAN_STATUS_SUCCESS || !pscan_cfg_out) { + PRINTM(MERROR, "Memory allocation for pscan_cfg_out failed!\n"); + if (pioctl_req) + pioctl_req->status_code = MLAN_ERROR_NO_MEM; + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + buf_size = sizeof(ChanScanParamSet_t) * WLAN_USER_SCAN_CHAN_MAX; + ret = pcb->moal_malloc(pmadapter->pmoal_handle, buf_size, MLAN_MEM_DEF, + (t_u8 **)&pscan_chan_list); + if (ret != MLAN_STATUS_SUCCESS || !pscan_chan_list) { + PRINTM(MERROR, "Failed to allocate scan_chan_list\n"); + if (pscan_cfg_out) + pcb->moal_mfree(pmadapter->pmoal_handle, + (t_u8 *)pscan_cfg_out); + if (pioctl_req) + pioctl_req->status_code = MLAN_ERROR_NO_MEM; + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + memset(pmadapter, pscan_chan_list, 0x00, buf_size); + memset(pmadapter, pscan_cfg_out, 0x00, + sizeof(wlan_scan_cmd_config_tlv)); + + keep_previous_scan = MFALSE; + + ret = wlan_scan_setup_scan_config(pmpriv, + puser_scan_in, + &pscan_cfg_out->config, + &pchan_list_out, + pscan_chan_list, + &max_chan_per_scan, + &filtered_scan, + &scan_current_chan_only); + if (ret != MLAN_STATUS_SUCCESS) { + + PRINTM(MERROR, "Failed to setup scan config\n"); + if (pscan_cfg_out) + pcb->moal_mfree(pmadapter->pmoal_handle, + (t_u8 *)pscan_cfg_out); + if (pscan_chan_list) + pcb->moal_mfree(pmadapter->pmoal_handle, + (t_u8 *)pscan_chan_list); + if (pioctl_req) + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + if (puser_scan_in) + keep_previous_scan = puser_scan_in->keep_previous_scan; + + if (keep_previous_scan == MFALSE) { + memset(pmadapter, pmadapter->pscan_table, 0x00, + sizeof(BSSDescriptor_t) * MRVDRV_MAX_BSSID_LIST); + pmadapter->num_in_scan_table = 0; + pmadapter->pbcn_buf_end = pmadapter->bcn_buf; + for (i = 0; i < pmadapter->num_in_chan_stats; i++) + pmadapter->pchan_stats[i].cca_scan_duration = 0; + pmadapter->idx_chan_stats = 0; + } + + ret = wlan_scan_channel_list(pmpriv, + pioctl_buf, + max_chan_per_scan, + filtered_scan, + &pscan_cfg_out->config, + pchan_list_out, pscan_chan_list); + + /* Get scan command from scan_pending_q and put to cmd_pending_q */ + if (ret == MLAN_STATUS_SUCCESS) { + if (pmadapter->ext_scan && pmadapter->ext_scan_enh && + pmadapter->ext_scan_type == EXT_SCAN_ENHANCE) { + wlan_request_cmd_lock(pmadapter); + pmadapter->pscan_ioctl_req = pioctl_req; + pmadapter->scan_processing = MTRUE; + wlan_release_cmd_lock(pmadapter); + } else { + wlan_request_cmd_lock(pmadapter); + if (util_peek_list + (pmadapter->pmoal_handle, + &pmadapter->scan_pending_q, MNULL, MNULL)) { + pcmd_node = + (cmd_ctrl_node *) + util_dequeue_list(pmadapter-> + pmoal_handle, + &pmadapter-> + scan_pending_q, MNULL, + MNULL); + pmadapter->pscan_ioctl_req = pioctl_req; + pmadapter->scan_processing = MTRUE; + wlan_insert_cmd_to_pending_q(pmadapter, + pcmd_node, MTRUE); + } + wlan_release_cmd_lock(pmadapter); + } + } + if (pscan_cfg_out) + pcb->moal_mfree(pmadapter->pmoal_handle, (t_u8 *)pscan_cfg_out); + + if (pscan_chan_list) + pcb->moal_mfree(pmadapter->pmoal_handle, + (t_u8 *)pscan_chan_list); + + LEAVE(); + return ret; +} + +/** + * @brief Prepare a scan command to be sent to the firmware + * + * Use the wlan_scan_cmd_config sent to the command processing module in + * the wlan_prepare_cmd to configure a HostCmd_DS_802_11_SCAN command + * struct to send to firmware. + * + * The fixed fields specifying the BSS type and BSSID filters as well as a + * variable number/length of TLVs are sent in the command to firmware. + * + * @param pmpriv A pointer to mlan_private structure + * @param pcmd A pointer to HostCmd_DS_COMMAND structure to be sent to + * firmware with the HostCmd_DS_801_11_SCAN structure + * @param pdata_buf Void pointer cast of a wlan_scan_cmd_config struct used + * to set the fields/TLVs for the command sent to firmware + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_cmd_802_11_scan(IN mlan_private *pmpriv, + IN HostCmd_DS_COMMAND *pcmd, IN t_void *pdata_buf) +{ + HostCmd_DS_802_11_SCAN *pscan_cmd = &pcmd->params.scan; + wlan_scan_cmd_config *pscan_cfg; + + ENTER(); + + pscan_cfg = (wlan_scan_cmd_config *)pdata_buf; + + /* Set fixed field variables in scan command */ + pscan_cmd->bss_mode = pscan_cfg->bss_mode; + memcpy(pmpriv->adapter, pscan_cmd->bssid, pscan_cfg->specific_bssid, + sizeof(pscan_cmd->bssid)); + memcpy(pmpriv->adapter, pscan_cmd->tlv_buffer, pscan_cfg->tlv_buf, + pscan_cfg->tlv_buf_len); + + pcmd->command = wlan_cpu_to_le16(HostCmd_CMD_802_11_SCAN); + + /* Size is equal to the sizeof(fixed portions) + the TLV len + header */ + pcmd->size = (t_u16)wlan_cpu_to_le16((t_u16)(sizeof(pscan_cmd->bss_mode) + + sizeof(pscan_cmd->bssid) + + pscan_cfg->tlv_buf_len + + S_DS_GEN)); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Check if any hidden SSID found in passive scan channels + * and do specific SSID active scan for those channels + * + * @param pmpriv A pointer to mlan_private structure + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MTRUE/MFALSE + */ + +t_bool +wlan_active_scan_req_for_passive_chan(IN mlan_private *pmpriv, + IN mlan_ioctl_req *pioctl_buf) +{ + t_bool ret = MFALSE; + mlan_adapter *pmadapter = pmpriv->adapter; + t_bool scan_reqd = MFALSE; + t_bool chan_listed = MFALSE; + t_u8 id = 0; + t_u32 bss_idx, i; + t_u8 null_ssid[MLAN_MAX_SSID_LENGTH] = { 0 }; + mlan_callbacks *pcb = (mlan_callbacks *)&pmpriv->adapter->callbacks; + wlan_user_scan_cfg *user_scan_cfg; + mlan_ds_scan *pscan = (mlan_ds_scan *)pioctl_buf->pbuf; + mlan_scan_req *pscan_req = MNULL; + wlan_user_scan_cfg *puser_scan_in = MNULL; + + ENTER(); + + if (pscan->sub_command == MLAN_OID_SCAN_USER_CONFIG) { + puser_scan_in = + (wlan_user_scan_cfg *)pscan->param.user_scan. + scan_cfg_buf; + if (!puser_scan_in->ssid_filter) + goto done; + } + + if (pmadapter->active_scan_triggered) { + pmadapter->active_scan_triggered = MFALSE; + goto done; + } + + if ((pcb-> + moal_malloc(pmadapter->pmoal_handle, sizeof(wlan_user_scan_cfg), + MLAN_MEM_DEF, + (t_u8 **)&user_scan_cfg) != MLAN_STATUS_SUCCESS) || + !user_scan_cfg) { + PRINTM(MERROR, "Memory allocation for user_scan_cfg failed\n"); + goto done; + } + memset(pmadapter, user_scan_cfg, 0, sizeof(wlan_user_scan_cfg)); + for (bss_idx = 0; bss_idx < pmadapter->num_in_scan_table; bss_idx++) { + scan_reqd = MFALSE; + if (!memcmp + (pmadapter, pmadapter->pscan_table[bss_idx].ssid.ssid, + null_ssid, + pmadapter->pscan_table[bss_idx].ssid.ssid_len)) { + if (puser_scan_in && + puser_scan_in->chan_list[0].chan_number) { + for (i = 0; + i < WLAN_USER_SCAN_CHAN_MAX && + puser_scan_in->chan_list[i].chan_number; + i++) { + if (puser_scan_in->chan_list[i]. + chan_number == + pmadapter->pscan_table[bss_idx]. + channel) { + if (puser_scan_in->chan_list[i]. + scan_type == + MLAN_SCAN_TYPE_PASSIVE) + scan_reqd = MTRUE; + break; + } + } + } else if (pmadapter->scan_type == + MLAN_SCAN_TYPE_PASSIVE) { + scan_reqd = MTRUE; + } else { + if ((pmadapter->pscan_table[bss_idx]. + bss_band & BAND_A) && + wlan_11h_radar_detect_required(pmpriv, + pmadapter-> + pscan_table + [bss_idx]. + channel)) + scan_reqd = MTRUE; + if (pmadapter->pscan_table[bss_idx]. + bss_band & (BAND_B | BAND_G) && + wlan_bg_scan_type_is_passive(pmpriv, + pmadapter-> + pscan_table + [bss_idx]. + channel)) + scan_reqd = MTRUE; + } + + if (scan_reqd) { + chan_listed = MFALSE; + for (i = 0; i < id; i++) { + if ((user_scan_cfg->chan_list[i]. + chan_number == + pmadapter->pscan_table[bss_idx]. + channel) + && (user_scan_cfg->chan_list[i]. + radio_type & pmadapter-> + pscan_table[bss_idx]. + bss_band)) { + chan_listed = MTRUE; + break; + } + } + if (chan_listed == MTRUE) + continue; + user_scan_cfg->chan_list[id].chan_number = + pmadapter->pscan_table[bss_idx].channel; + if (pmadapter->pscan_table[bss_idx]. + bss_band & (BAND_B | BAND_G)) + user_scan_cfg->chan_list[id]. + radio_type = BAND_2GHZ; + if (pmadapter->pscan_table[bss_idx]. + bss_band & BAND_A) + user_scan_cfg->chan_list[id]. + radio_type = BAND_5GHZ; + user_scan_cfg->chan_list[id].scan_type = + MLAN_SCAN_TYPE_ACTIVE; + id++; + } + } + } + if (id) { + pmadapter->active_scan_triggered = MTRUE; + if (pscan->sub_command == MLAN_OID_SCAN_USER_CONFIG) { + memcpy(pmpriv->adapter, user_scan_cfg->ssid_list, + puser_scan_in->ssid_list, + sizeof(user_scan_cfg->ssid_list)); + } else { + pscan_req = &pscan->param.scan_req; + memcpy(pmpriv->adapter, + user_scan_cfg->ssid_list[0].ssid, + pscan_req->scan_ssid.ssid, + pscan_req->scan_ssid.ssid_len); + } + user_scan_cfg->keep_previous_scan = MTRUE; + if (MLAN_STATUS_SUCCESS != wlan_scan_networks(pmpriv, + pioctl_buf, + user_scan_cfg)) { + goto done; + } + ret = MTRUE; + } + if (user_scan_cfg) + pcb->moal_mfree(pmadapter->pmoal_handle, (t_u8 *)user_scan_cfg); + +done: + LEAVE(); + return ret; +} + +/** + * @brief This function handles the command response of scan + * + * The response buffer for the scan command has the following + * memory layout: + * + * .-------------------------------------------------------------. + * | Header (4 * sizeof(t_u16)): Standard command response hdr | + * .-------------------------------------------------------------. + * | BufSize (t_u16) : sizeof the BSS Description data | + * .-------------------------------------------------------------. + * | NumOfSet (t_u8) : Number of BSS Descs returned | + * .-------------------------------------------------------------. + * | BSSDescription data (variable, size given in BufSize) | + * .-------------------------------------------------------------. + * | TLV data (variable, size calculated using Header->Size, | + * | BufSize and sizeof the fixed fields above) | + * .-------------------------------------------------------------. + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_ret_802_11_scan(IN mlan_private *pmpriv, + IN HostCmd_DS_COMMAND *resp, IN t_void *pioctl_buf) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_adapter *pmadapter = pmpriv->adapter; + mlan_callbacks *pcb = MNULL; + cmd_ctrl_node *pcmd_node = MNULL; + HostCmd_DS_802_11_SCAN_RSP *pscan_rsp = MNULL; + BSSDescriptor_t *bss_new_entry = MNULL; + MrvlIEtypes_Data_t *ptlv; + MrvlIEtypes_TsfTimestamp_t *ptsf_tlv = MNULL; + MrvlIEtypes_ChannelStats_t *pchanstats_tlv = MNULL; + t_u8 *pbss_info; + t_u32 scan_resp_size; + t_u32 bytes_left; + t_u32 num_in_table; + t_u32 bss_idx; + t_u32 idx; + t_u32 tlv_buf_size; + t_u64 tsf_val; + chan_freq_power_t *cfp; + MrvlIEtypes_ChanBandListParamSet_t *pchan_band_tlv = MNULL; + ChanBandParamSet_t *pchan_band; + t_u8 band; + t_u8 is_bgscan_resp; + t_u32 age_ts_usec; + t_u8 null_ssid[MLAN_MAX_SSID_LENGTH] = { 0 }; + t_u32 status_code = 0; + pmlan_ioctl_req pscan_ioctl_req = MNULL; + + ENTER(); + pcb = (pmlan_callbacks)&pmadapter->callbacks; + + is_bgscan_resp = (resp->command == HostCmd_CMD_802_11_BG_SCAN_QUERY); + if (is_bgscan_resp) + pscan_rsp = &resp->params.bg_scan_query_resp.scan_resp; + else + pscan_rsp = &resp->params.scan_resp; + + if (pscan_rsp->number_of_sets > MRVDRV_MAX_BSSID_LIST) { + PRINTM(MERROR, + "SCAN_RESP: Invalid number of AP returned (%d)!!\n", + pscan_rsp->number_of_sets); + status_code = MLAN_ERROR_CMD_SCAN_FAIL; + ret = MLAN_STATUS_FAILURE; + goto done; + } + + bytes_left = wlan_le16_to_cpu(pscan_rsp->bss_descript_size); + PRINTM(MINFO, "SCAN_RESP: bss_descript_size %d\n", bytes_left); + + scan_resp_size = resp->size; + + PRINTM(MINFO, "SCAN_RESP: returned %d APs before parsing\n", + pscan_rsp->number_of_sets); + + num_in_table = pmadapter->num_in_scan_table; + pbss_info = pscan_rsp->bss_desc_and_tlv_buffer; + + /* + * The size of the TLV buffer is equal to the entire command response + * size (scan_resp_size) minus the fixed fields (sizeof()'s), the + * BSS Descriptions (bss_descript_size as bytesLef) and the command + * response header (S_DS_GEN) + */ + tlv_buf_size = scan_resp_size - (bytes_left + + sizeof(pscan_rsp->bss_descript_size) + + sizeof(pscan_rsp->number_of_sets) + + S_DS_GEN); + if (is_bgscan_resp) + tlv_buf_size -= + sizeof(resp->params.bg_scan_query_resp. + report_condition); + + ptlv = (MrvlIEtypes_Data_t *)(pscan_rsp->bss_desc_and_tlv_buffer + + bytes_left); + + /* + * Search the TLV buffer space in the scan response + * for any valid TLVs + */ + wlan_ret_802_11_scan_get_tlv_ptrs(pmadapter, + ptlv, + tlv_buf_size, + TLV_TYPE_TSFTIMESTAMP, + (MrvlIEtypes_Data_t **)&ptsf_tlv); + + /* + * Search the TLV buffer space in the scan response + * for any valid TLVs + */ + wlan_ret_802_11_scan_get_tlv_ptrs(pmadapter, + ptlv, + tlv_buf_size, + TLV_TYPE_CHANNELBANDLIST, + (MrvlIEtypes_Data_t **) + &pchan_band_tlv); + wlan_ret_802_11_scan_get_tlv_ptrs(pmadapter, ptlv, tlv_buf_size, + TLV_TYPE_CHANNEL_STATS, + (MrvlIEtypes_Data_t **) + &pchanstats_tlv); + + if (pchanstats_tlv) + wlan_update_chan_statistics(pmpriv, pchanstats_tlv); + + /* + * Process each scan response returned (pscan_rsp->number_of_sets). + * Save the information in the bss_new_entry and then insert into + * the driver scan table either as an update to an existing entry + * or as an addition at the end of the table + */ + ret = pcb->moal_malloc(pmadapter->pmoal_handle, sizeof(BSSDescriptor_t), + MLAN_MEM_DEF, (t_u8 **)&bss_new_entry); + + if (ret != MLAN_STATUS_SUCCESS || !bss_new_entry) { + PRINTM(MERROR, "Memory allocation for bss_new_entry failed!\n"); + status_code = MLAN_ERROR_NO_MEM; + ret = MLAN_STATUS_FAILURE; + goto done; + } + + for (idx = 0; idx < pscan_rsp->number_of_sets && bytes_left; idx++) { + /* Zero out the bss_new_entry we are about to store info in */ + memset(pmadapter, bss_new_entry, 0x00, sizeof(BSSDescriptor_t)); + + /* Process the data fields and IEs returned for this BSS */ + if (wlan_interpret_bss_desc_with_ie(pmadapter, + bss_new_entry, + &pbss_info, + &bytes_left, + MFALSE) == + MLAN_STATUS_SUCCESS) { + PRINTM(MINFO, "SCAN_RESP: BSSID = " MACSTR "\n", + MAC2STR(bss_new_entry->mac_address)); + + band = BAND_G; + if (pchan_band_tlv) { + pchan_band = + &pchan_band_tlv->chan_band_param[idx]; + band = radio_type_to_band(pchan_band->bandcfg. + chanBand); + if (!bss_new_entry->channel) + bss_new_entry->channel = + pchan_band->chan_number; + } + /* + * Save the band designation for this entry + * for use in join + */ + bss_new_entry->bss_band = band; + + cfp = wlan_find_cfp_by_band_and_channel(pmadapter, + (t_u8) + bss_new_entry-> + bss_band, + (t_u16) + bss_new_entry-> + channel); + if (cfp) + bss_new_entry->freq = cfp->freq; + else + bss_new_entry->freq = 0; + + /* Skip entry if on blacklisted channel */ + if (cfp && cfp->dynamic.blacklist) { + PRINTM(MINFO, + "SCAN_RESP: dropping entry on blacklist channel.\n"); + continue; + } + + /* + * Search the scan table for the same bssid + */ + for (bss_idx = 0; bss_idx < num_in_table; bss_idx++) { + if (!memcmp + (pmadapter, bss_new_entry->mac_address, + pmadapter->pscan_table[bss_idx]. + mac_address, + sizeof(bss_new_entry->mac_address))) { + /* + * If the SSID matches as well, it is a + * duplicate of this entry. Keep the + * bss_idx set to this entry so we + * replace the old contents in the table + */ + if ((bss_new_entry->ssid.ssid_len == + pmadapter->pscan_table[bss_idx]. + ssid.ssid_len) + && + (!memcmp + (pmadapter, + bss_new_entry->ssid.ssid, + pmadapter->pscan_table[bss_idx]. + ssid.ssid, + bss_new_entry->ssid.ssid_len))) { + PRINTM(MINFO, + "SCAN_RESP: Duplicate of index: %d\n", + bss_idx); + + break; + } + /* + * If the SSID is NULL for same BSSID + * keep the bss_idx set to this entry + * so we replace the old contents in + * the table + */ + if (!memcmp + (pmadapter, + pmadapter->pscan_table[bss_idx]. + ssid.ssid, null_ssid, + pmadapter->pscan_table[bss_idx]. + ssid.ssid_len)) { + PRINTM(MINFO, + "SCAN_RESP: Duplicate of index: %d\n", + bss_idx); + break; + } + } + } + /* + * If the bss_idx is equal to the number of entries + * in the table, the new entry was not a duplicate; + * append it to the scan table + */ + if (bss_idx == num_in_table) { + /* + * Range check the bss_idx, keep it limited + * to the last entry + */ + if (bss_idx == MRVDRV_MAX_BSSID_LIST) + bss_idx--; + else + num_in_table++; + } + + /* + * Save the beacon/probe response returned for later + * application retrieval. Duplicate beacon/probe + * responses are updated if possible + */ + wlan_ret_802_11_scan_store_beacon(pmpriv, + bss_idx, + num_in_table, + bss_new_entry); + if (bss_new_entry->pbeacon_buf == MNULL) { + PRINTM(MCMND, + "No space for beacon, drop this entry\n"); + num_in_table--; + continue; + } + /* + * If the TSF TLV was appended to the scan results, save + * this entry's TSF value in the networkTSF field. The + * networkTSF is the firmware's TSF value at the time + * the beacon or probe response was received. + */ + if (ptsf_tlv) { + memcpy(pmpriv->adapter, &tsf_val, + &ptsf_tlv->tsf_data[idx * TSF_DATA_SIZE], + sizeof(tsf_val)); + tsf_val = wlan_le64_to_cpu(tsf_val); + memcpy(pmpriv->adapter, + &bss_new_entry->network_tsf, &tsf_val, + sizeof(bss_new_entry->network_tsf)); + } + + /* Copy the locally created bss_new_entry to the scan table */ + memcpy(pmadapter, &pmadapter->pscan_table[bss_idx], + bss_new_entry, + sizeof(pmadapter->pscan_table[bss_idx])); + + } else { + /* Error parsing/interpreting the scan response, skipped */ + PRINTM(MERROR, + "SCAN_RESP: wlan_interpret_bss_desc_with_ie returned error\n"); + } + } + + PRINTM(MINFO, "SCAN_RESP: Scanned %2d APs, %d valid, %d total\n", + pscan_rsp->number_of_sets, + num_in_table - pmadapter->num_in_scan_table, num_in_table); + + /* Update the total number of BSSIDs in the scan table */ + pmadapter->num_in_scan_table = num_in_table; + /* Update the age_in_second */ + pmadapter->callbacks.moal_get_system_time(pmadapter->pmoal_handle, + &pmadapter->age_in_secs, + &age_ts_usec); + if (is_bgscan_resp) + goto done; + wlan_request_cmd_lock(pmadapter); + if (!util_peek_list + (pmadapter->pmoal_handle, &pmadapter->scan_pending_q, MNULL, + MNULL)) { + wlan_release_cmd_lock(pmadapter); + if (pmadapter->pscan_ioctl_req) { + if (((mlan_ds_scan *)pmadapter->pscan_ioctl_req->pbuf)-> + sub_command == MLAN_OID_SCAN_SPECIFIC_SSID || + ((mlan_ds_scan *)pmadapter->pscan_ioctl_req->pbuf)-> + sub_command == MLAN_OID_SCAN_USER_CONFIG) { + if (wlan_active_scan_req_for_passive_chan + (pmpriv, pmadapter->pscan_ioctl_req)) { + goto done; + } + } + } + /* + * Process the resulting scan table: + * - Remove any bad ssids + * - Update our current BSS information from scan data + */ + wlan_scan_process_results(pmpriv); + + wlan_request_cmd_lock(pmadapter); + pmadapter->scan_processing = MFALSE; + pscan_ioctl_req = pmadapter->pscan_ioctl_req; + pmadapter->pscan_ioctl_req = MNULL; + /* Need to indicate IOCTL complete */ + if (pscan_ioctl_req) { + pscan_ioctl_req->status_code = MLAN_ERROR_NO_ERROR; + /* Indicate ioctl complete */ + pcb->moal_ioctl_complete(pmadapter->pmoal_handle, + (pmlan_ioctl_req) + pscan_ioctl_req, + MLAN_STATUS_SUCCESS); + } + wlan_release_cmd_lock(pmadapter); + pmadapter->bgscan_reported = MFALSE; + wlan_recv_event(pmpriv, MLAN_EVENT_ID_DRV_SCAN_REPORT, MNULL); + } else { + /* If firmware not ready, do not issue any more scan commands */ + if (pmadapter->hw_status != WlanHardwareStatusReady) { + wlan_release_cmd_lock(pmadapter); + status_code = MLAN_ERROR_FW_NOT_READY; + ret = MLAN_STATUS_FAILURE; + goto done; + } else { + /* Get scan command from scan_pending_q and put to cmd_pending_q */ + pcmd_node = + (cmd_ctrl_node *)util_dequeue_list(pmadapter-> + pmoal_handle, + &pmadapter-> + scan_pending_q, + MNULL, + MNULL); + wlan_insert_cmd_to_pending_q(pmadapter, pcmd_node, + MTRUE); + wlan_release_cmd_lock(pmadapter); + } + } + +done: + if (bss_new_entry) + pcb->moal_mfree(pmadapter->pmoal_handle, (t_u8 *)bss_new_entry); + if (ret) { + /* Flush all pending scan commands */ + wlan_flush_scan_queue(pmadapter); + wlan_request_cmd_lock(pmadapter); + pmadapter->scan_processing = MFALSE; + pscan_ioctl_req = pmadapter->pscan_ioctl_req; + pmadapter->pscan_ioctl_req = MNULL; + if (pscan_ioctl_req) { + pscan_ioctl_req->status_code = status_code; + /* Indicate ioctl complete */ + pcb->moal_ioctl_complete(pmadapter->pmoal_handle, + pscan_ioctl_req, + MLAN_STATUS_FAILURE); + } + wlan_release_cmd_lock(pmadapter); + } + LEAVE(); + return ret; +} + +/** + * @brief Prepare an extended scan command to be sent to the firmware + * + * Use the wlan_scan_cmd_config sent to the command processing module in + * the wlan_prepare_cmd to configure a HostCmd_DS_802_11_SCAN_EXT command + * struct to send to firmware. + * + * @param pmpriv A pointer to mlan_private structure + * @param pcmd A pointer to HostCmd_DS_COMMAND structure to be sent to + * firmware with the HostCmd_DS_802_11_SCAN_EXT structure + * @param pdata_buf Void pointer cast of a wlan_scan_cmd_config struct used + * to set the fields/TLVs for the command sent to firmware + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_cmd_802_11_scan_ext(IN mlan_private *pmpriv, + IN HostCmd_DS_COMMAND *pcmd, IN t_void *pdata_buf) +{ + HostCmd_DS_802_11_SCAN_EXT *pext_scan_cmd = &pcmd->params.ext_scan; + wlan_scan_cmd_config *pscan_cfg = MNULL; + + ENTER(); + + pcmd->command = wlan_cpu_to_le16(HostCmd_CMD_802_11_SCAN_EXT); + + if (pmpriv->adapter->ext_scan_enh == MTRUE) { + if (pdata_buf) { + if (pmpriv->adapter->ext_scan_type == EXT_SCAN_ENHANCE) + pext_scan_cmd->ext_scan_type = EXT_SCAN_ENHANCE; + else + pext_scan_cmd->ext_scan_type = EXT_SCAN_DEFAULT; + } else { + pcmd->size = + wlan_cpu_to_le16((t_u16) + (sizeof + (pext_scan_cmd->ext_scan_type) + + + (t_u16)(sizeof + (pext_scan_cmd-> + reserved)) + + S_DS_GEN)); + pext_scan_cmd->ext_scan_type = EXT_SCAN_CANCEL; + LEAVE(); + return MLAN_STATUS_SUCCESS; + } + } + pscan_cfg = (wlan_scan_cmd_config *)pdata_buf; + + memcpy(pmpriv->adapter, pext_scan_cmd->tlv_buffer, + pscan_cfg->tlv_buf, pscan_cfg->tlv_buf_len); + + /* Size is equal to the sizeof(fixed portions) + the TLV len + header */ + pcmd->size = + wlan_cpu_to_le16((t_u16)(sizeof(pext_scan_cmd->ext_scan_type) + + + (t_u16)sizeof(pext_scan_cmd->reserved) + + pscan_cfg->tlv_buf_len + S_DS_GEN)); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of extended scan + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_ret_802_11_scan_ext(IN mlan_private *pmpriv, + IN HostCmd_DS_COMMAND *resp, IN t_void *pioctl_buf) +{ + HostCmd_DS_802_11_SCAN_EXT *pext_scan_cmd = &(resp->params.ext_scan); + MrvlIEtypesHeader_t *tlv = MNULL; + MrvlIEtypes_ChannelStats_t *tlv_chanstats = MNULL; + t_u16 tlv_buf_left = 0; + t_u16 tlv_type = 0; + t_u16 tlv_len = 0; + t_u32 ext_scan_type; + mlan_callbacks *pcb = (mlan_callbacks *)&pmpriv->adapter->callbacks; + pmlan_ioctl_req pioctl_req = (pmlan_ioctl_req)pioctl_buf; + mlan_adapter *pmadapter = pmpriv->adapter; + ENTER(); + + PRINTM(MINFO, "EXT scan returns successfully\n"); + ext_scan_type = pext_scan_cmd->ext_scan_type; + if (ext_scan_type == EXT_SCAN_CANCEL) { + PRINTM(MCMND, "Cancle scan command completed!\n"); + /* Need to indicate IOCTL complete */ + if (pioctl_req != MNULL) { + pioctl_req->status_code = MLAN_STATUS_SUCCESS; + /* Indicate ioctl complete */ + pcb->moal_ioctl_complete(pmadapter->pmoal_handle, + (pmlan_ioctl_req)pioctl_req, + MLAN_STATUS_SUCCESS); + } + LEAVE(); + return MLAN_STATUS_SUCCESS; + } else if (ext_scan_type == EXT_SCAN_ENHANCE) { + /* Setup the timer after scan command response */ + pcb->moal_start_timer(pmpriv->adapter->pmoal_handle, + pmpriv->adapter->pmlan_cmd_timer, MFALSE, + MRVDRV_TIMER_10S * 2); + pmpriv->adapter->cmd_timer_is_set = MTRUE; + LEAVE(); + return MLAN_STATUS_SUCCESS; + } + tlv = (MrvlIEtypesHeader_t *)pext_scan_cmd->tlv_buffer; + tlv_buf_left = + resp->size - (sizeof(HostCmd_DS_802_11_SCAN_EXT) - 1 + + S_DS_GEN); + while (tlv_buf_left >= sizeof(MrvlIEtypesHeader_t)) { + tlv_type = wlan_le16_to_cpu(tlv->type); + tlv_len = wlan_le16_to_cpu(tlv->len); + if (tlv_buf_left < (tlv_len + sizeof(MrvlIEtypesHeader_t))) { + PRINTM(MERROR, "Error processing scan gap TLV\n"); + break; + } + switch (tlv_type) { + case TLV_TYPE_CHANNEL_STATS: + tlv_chanstats = (MrvlIEtypes_ChannelStats_t *)tlv; + wlan_update_chan_statistics(pmpriv, tlv_chanstats); + break; + default: + break; + } + tlv_buf_left -= tlv_len + sizeof(MrvlIEtypesHeader_t); + tlv = (MrvlIEtypesHeader_t *)((t_u8 *)tlv + tlv_len + + sizeof(MrvlIEtypesHeader_t)); + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function parse and store the extended scan results + * + * @param pmpriv A pointer to mlan_private structure + * @param number_of_sets Number of BSS + * @param pscan_resp A pointer to scan response buffer + * @param scan_resp_size Size of scan response buffer + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status +wlan_parse_ext_scan_result(IN mlan_private *pmpriv, + IN t_u8 number_of_sets, + IN t_u8 *pscan_resp, IN t_u16 scan_resp_size) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_adapter *pmadapter = pmpriv->adapter; + mlan_callbacks *pcb = MNULL; + BSSDescriptor_t *bss_new_entry = MNULL; + t_u8 *pbss_info; + t_u32 bytes_left; + t_u32 bytes_left_for_tlv; + t_u32 num_in_table; + t_u32 bss_idx; + t_u32 idx; + t_u64 tsf_val; + chan_freq_power_t *cfp; + t_u16 tlv_type, tlv_len; + MrvlIEtypes_Data_t *ptlv = MNULL; + MrvlIEtypes_Bss_Scan_Rsp_t *pscan_rsp_tlv = MNULL; + MrvlIEtypes_Bss_Scan_Info_t *pscan_info_tlv = MNULL; + t_u8 band; + t_u32 age_ts_usec; + t_u8 null_ssid[MLAN_MAX_SSID_LENGTH] = { 0 }; + + ENTER(); + pcb = (pmlan_callbacks)&pmadapter->callbacks; + + if (number_of_sets > MRVDRV_MAX_BSSID_LIST) { + PRINTM(MERROR, + "EXT_SCAN: Invalid number of AP returned (%d)!!\n", + number_of_sets); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + bytes_left = scan_resp_size; + PRINTM(MINFO, "EXT_SCAN: bss_descript_size %d\n", scan_resp_size); + PRINTM(MINFO, "EXT_SCAN: returned %d APs before parsing\n", + number_of_sets); + + num_in_table = pmadapter->num_in_scan_table; + ptlv = (MrvlIEtypes_Data_t *)pscan_resp; + + /* + * Process each scan response returned number_of_sets. Save + * the information in the bss_new_entry and then insert into the + * driver scan table either as an update to an existing entry + * or as an addition at the end of the table + */ + ret = pcb->moal_malloc(pmadapter->pmoal_handle, sizeof(BSSDescriptor_t), + MLAN_MEM_DEF, (t_u8 **)&bss_new_entry); + + if (ret != MLAN_STATUS_SUCCESS || !bss_new_entry) { + PRINTM(MERROR, "Memory allocation for bss_new_entry failed!\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + for (idx = 0; idx < number_of_sets && bytes_left > + sizeof(MrvlIEtypesHeader_t); idx++) { + tlv_type = wlan_le16_to_cpu(ptlv->header.type); + tlv_len = wlan_le16_to_cpu(ptlv->header.len); + if (bytes_left < sizeof(MrvlIEtypesHeader_t) + tlv_len) { + PRINTM(MERROR, + "EXT_SCAN: Error bytes left < TLV length\n"); + break; + } + pscan_rsp_tlv = MNULL; + pscan_info_tlv = MNULL; + bytes_left_for_tlv = bytes_left; + /* + * BSS response TLV with beacon or probe response buffer + * at the initial position of each descriptor + */ + if (tlv_type == TLV_TYPE_BSS_SCAN_RSP) { + pbss_info = (t_u8 *)ptlv; + pscan_rsp_tlv = (MrvlIEtypes_Bss_Scan_Rsp_t *)ptlv; + ptlv = (MrvlIEtypes_Data_t *)(ptlv->data + tlv_len); + bytes_left_for_tlv -= + (tlv_len + sizeof(MrvlIEtypesHeader_t)); + } else + break; + + /* Process variable TLV */ + while (bytes_left_for_tlv >= sizeof(MrvlIEtypesHeader_t) && + wlan_le16_to_cpu(ptlv->header.type) != + TLV_TYPE_BSS_SCAN_RSP) { + tlv_type = wlan_le16_to_cpu(ptlv->header.type); + tlv_len = wlan_le16_to_cpu(ptlv->header.len); + if (bytes_left_for_tlv < + sizeof(MrvlIEtypesHeader_t) + tlv_len) { + PRINTM(MERROR, + "EXT_SCAN: Error in processing TLV, " + "bytes left < TLV length\n"); + pscan_rsp_tlv = MNULL; + bytes_left_for_tlv = 0; + continue; + } + switch (tlv_type) { + case TLV_TYPE_BSS_SCAN_INFO: + pscan_info_tlv = + (MrvlIEtypes_Bss_Scan_Info_t *)ptlv; + if (tlv_len != + sizeof(MrvlIEtypes_Bss_Scan_Info_t) - + sizeof(MrvlIEtypesHeader_t)) { + bytes_left_for_tlv = 0; + continue; + } + break; + default: + break; + } + ptlv = (MrvlIEtypes_Data_t *)(ptlv->data + tlv_len); + bytes_left -= (tlv_len + sizeof(MrvlIEtypesHeader_t)); + bytes_left_for_tlv -= + (tlv_len + sizeof(MrvlIEtypesHeader_t)); + } + /* No BSS response TLV */ + if (pscan_rsp_tlv == MNULL) + break; + + /* + * Advance pointer to the beacon buffer length and + * update the bytes count so that the function + * wlan_interpret_bss_desc_with_ie() can handle the + * scan buffer withut any change + */ + pbss_info += sizeof(t_u16); + bytes_left -= sizeof(t_u16); + + /* Zero out the bss_new_entry we are about to store info in */ + memset(pmadapter, bss_new_entry, 0x00, sizeof(BSSDescriptor_t)); + + /* Process the data fields and IEs returned for this BSS */ + if (wlan_interpret_bss_desc_with_ie(pmadapter, + bss_new_entry, + &pbss_info, + &bytes_left, + MTRUE) == + MLAN_STATUS_SUCCESS) { + PRINTM(MINFO, "EXT_SCAN: BSSID = " MACSTR "\n", + MAC2STR(bss_new_entry->mac_address)); + + band = BAND_G; + /* + * If the BSS info TLV was appended to the scan results, + * save this entry's TSF value in the networkTSF field. + * The networkTSF is the firmware's TSF value at the + * time the beacon or probe response was received. + */ + if (pscan_info_tlv) { + /* RSSI is 2 byte long */ + bss_new_entry->rssi = + -(t_s32)(wlan_le16_to_cpu + (pscan_info_tlv->rssi)); + PRINTM(MINFO, "EXT_SCAN: RSSI=%d\n", + bss_new_entry->rssi); + memcpy(pmpriv->adapter, &tsf_val, + &pscan_info_tlv->tsf, sizeof(tsf_val)); + tsf_val = wlan_le64_to_cpu(tsf_val); + memcpy(pmpriv->adapter, + &bss_new_entry->network_tsf, &tsf_val, + sizeof(bss_new_entry->network_tsf)); + band = radio_type_to_band(pscan_info_tlv-> + bandcfg.chanBand); + } + /* Save the band designation for this entry for use in join */ + bss_new_entry->bss_band = band; + + cfp = wlan_find_cfp_by_band_and_channel(pmadapter, + (t_u8) + bss_new_entry-> + bss_band, + (t_u16) + bss_new_entry-> + channel); + if (cfp) + bss_new_entry->freq = cfp->freq; + else + bss_new_entry->freq = 0; + + /* Skip entry if on blacklisted channel */ + if (cfp && cfp->dynamic.blacklist) { + PRINTM(MINFO, + "EXT_SCAN: dropping entry on blacklist channel.\n"); + continue; + } + + /* + * Search the scan table for the same bssid + */ + for (bss_idx = 0; bss_idx < num_in_table; bss_idx++) { + if (!memcmp + (pmadapter, bss_new_entry->mac_address, + pmadapter->pscan_table[bss_idx]. + mac_address, + sizeof(bss_new_entry->mac_address))) { + /* + * If the SSID matches as well, it is a + * duplicate of this entry. Keep the + * bss_idx set to this entry so we + * replace the old contents in the table + */ + if ((bss_new_entry->ssid.ssid_len == + pmadapter->pscan_table[bss_idx]. + ssid.ssid_len) + && + (!memcmp + (pmadapter, + bss_new_entry->ssid.ssid, + pmadapter->pscan_table[bss_idx]. + ssid.ssid, + bss_new_entry->ssid.ssid_len))) { + PRINTM(MINFO, + "EXT_SCAN: Duplicate of index: %d\n", + bss_idx); + break; + } + /* + * If the SSID is NULL for same BSSID + * keep the bss_idx set to this entry + * so we replace the old contents in + * the table + */ + if (!memcmp + (pmadapter, + pmadapter->pscan_table[bss_idx]. + ssid.ssid, null_ssid, + pmadapter->pscan_table[bss_idx]. + ssid.ssid_len)) { + PRINTM(MINFO, + "EXT_SCAN: Duplicate of index: %d\n", + bss_idx); + break; + } + } + } + /* + * If the bss_idx is equal to the number of entries + * in the table, the new entry was not a duplicate; + * append it to the scan table + */ + if (bss_idx == num_in_table) { + /* Range check the bss_idx, keep it limited to the last entry */ + if (bss_idx == MRVDRV_MAX_BSSID_LIST) + bss_idx--; + else + num_in_table++; + } + + /* + * Save the beacon/probe response returned for later + * application retrieval. Duplicate beacon/probe + * responses are updated if possible + */ + wlan_ret_802_11_scan_store_beacon(pmpriv, + bss_idx, + num_in_table, + bss_new_entry); + if (bss_new_entry->pbeacon_buf == MNULL) { + PRINTM(MCMND, + "No space for beacon, drop this entry\n"); + num_in_table--; + continue; + } + + /* Copy the locally created bss_new_entry to the scan table */ + memcpy(pmadapter, &pmadapter->pscan_table[bss_idx], + bss_new_entry, + sizeof(pmadapter->pscan_table[bss_idx])); + } else { + /* Error parsing/interpreting the scan response, skipped */ + PRINTM(MERROR, + "EXT_SCAN: wlan_interpret_bss_desc_with_ie returned error\n"); + } + } + + PRINTM(MINFO, "EXT_SCAN: Scanned %2d APs, %d valid, %d total\n", + number_of_sets, num_in_table - pmadapter->num_in_scan_table, + num_in_table); + + /* Update the total number of BSSIDs in the scan table */ + pmadapter->num_in_scan_table = num_in_table; + /* Update the age_in_second */ + pmadapter->callbacks.moal_get_system_time(pmadapter->pmoal_handle, + &pmadapter->age_in_secs, + &age_ts_usec); + +done: + if (bss_new_entry) + pcb->moal_mfree(pmadapter->pmoal_handle, (t_u8 *)bss_new_entry); + + LEAVE(); + return ret; +} + +/** + * @brief This function handles the event extended scan report + * + * @param pmpriv A pointer to mlan_private structure + * @param pmbuf A pointer to mlan_buffer + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_handle_event_ext_scan_report(IN mlan_private *pmpriv, + IN mlan_buffer *pmbuf) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + mlan_callbacks *pcb = &pmadapter->callbacks; + mlan_ioctl_req *pioctl_req = MNULL; + cmd_ctrl_node *pcmd_node = MNULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + + mlan_event_scan_result *pevent_scan = (pmlan_event_scan_result) + (pmbuf->pbuf + pmbuf->data_offset); + t_u8 *ptlv = (pmbuf->pbuf + pmbuf->data_offset + + sizeof(mlan_event_scan_result)); + t_u16 tlv_buf_left = wlan_le16_to_cpu(pevent_scan->buf_size); + + DBG_HEXDUMP(MCMD_D, "EVENT EXT_SCAN", pmbuf->pbuf + + pmbuf->data_offset, pmbuf->data_len); + wlan_parse_ext_scan_result(pmpriv, pevent_scan->num_of_set, + ptlv, tlv_buf_left); + if (!pevent_scan->more_event + && (pmadapter->ext_scan_type != EXT_SCAN_ENHANCE) + ) { + wlan_request_cmd_lock(pmadapter); + if (!util_peek_list(pmadapter->pmoal_handle, + &pmadapter->scan_pending_q, MNULL, MNULL)) { + wlan_release_cmd_lock(pmadapter); + if (pmadapter->pscan_ioctl_req) { + if (((mlan_ds_scan *)pmadapter-> + pscan_ioctl_req->pbuf)->sub_command == + MLAN_OID_SCAN_SPECIFIC_SSID || + ((mlan_ds_scan *)pmadapter-> + pscan_ioctl_req->pbuf)->sub_command == + MLAN_OID_SCAN_USER_CONFIG) { + if (wlan_active_scan_req_for_passive_chan(pmpriv, pmadapter->pscan_ioctl_req)) { + LEAVE(); + return ret; + } + } + } + /* + * Process the resulting scan table: + * - Remove any bad ssids + * - Update our current BSS information from scan data + */ + wlan_scan_process_results(pmpriv); + wlan_request_cmd_lock(pmadapter); + pmadapter->scan_processing = MFALSE; + pioctl_req = pmadapter->pscan_ioctl_req; + pmadapter->pscan_ioctl_req = MNULL; + /* Need to indicate IOCTL complete */ + if (pioctl_req != MNULL) { + pioctl_req->status_code = MLAN_ERROR_NO_ERROR; + /* Indicate ioctl complete */ + pcb->moal_ioctl_complete(pmadapter-> + pmoal_handle, + (pmlan_ioctl_req) + pioctl_req, + MLAN_STATUS_SUCCESS); + } + wlan_release_cmd_lock(pmadapter); + + pmadapter->bgscan_reported = MFALSE; + wlan_recv_event(pmpriv, MLAN_EVENT_ID_DRV_SCAN_REPORT, + MNULL); + } else { + + /* If firmware not ready, do not issue any more scan commands */ + if (pmadapter->hw_status != WlanHardwareStatusReady) { + wlan_release_cmd_lock(pmadapter); + /* Flush all pending scan commands */ + wlan_flush_scan_queue(pmadapter); + wlan_request_cmd_lock(pmadapter); + pmadapter->scan_processing = MFALSE; + pioctl_req = pmadapter->pscan_ioctl_req; + pmadapter->pscan_ioctl_req = MNULL; + /* Indicate IOCTL complete */ + if (pioctl_req != MNULL) { + pioctl_req->status_code = + MLAN_ERROR_FW_NOT_READY; + + /* Indicate ioctl complete */ + pcb->moal_ioctl_complete(pmadapter-> + pmoal_handle, + (pmlan_ioctl_req) + pioctl_req, + MLAN_STATUS_FAILURE); + } + wlan_release_cmd_lock(pmadapter); + } else { + /* Get scan command from scan_pending_q and put to cmd_pending_q */ + pcmd_node = + (cmd_ctrl_node *) + util_dequeue_list(pmadapter-> + pmoal_handle, + &pmadapter-> + scan_pending_q, MNULL, + MNULL); + wlan_insert_cmd_to_pending_q(pmadapter, + pcmd_node, MTRUE); + wlan_release_cmd_lock(pmadapter); + } + } + } + LEAVE(); + return ret; +} + +/** + * @brief This function handles the event extended scan status + * + * @param pmpriv A pointer to mlan_private structure + * @param pmbuf A pointer to mlan_buffer + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_handle_event_ext_scan_status(IN mlan_private *pmpriv, + IN mlan_buffer *pmbuf) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_event_scan_status *scan_event; + mlan_ioctl_req *pioctl_req; + mlan_callbacks *pcb = (mlan_callbacks *)&pmadapter->callbacks; + t_u16 tlv_buf_left, tlv_len, tlv_type; + MrvlIEtypesHeader_t *tlv; + MrvlIEtypes_ChannelStats_t *tlv_chan_stats; + t_u8 status; + + ENTER(); + + if (pmbuf->data_len < sizeof(mlan_event_scan_status)) { + PRINTM(MERROR, "Wrong ext scan status event data length\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + scan_event = + (pmlan_event_scan_status) (pmbuf->pbuf + pmbuf->data_offset); + DBG_HEXDUMP(MCMD_D, "EVENT: Ext_Scan_Status", scan_event, + pmbuf->data_len); + status = scan_event->scan_status; + PRINTM(MEVENT, "ext_scan_status: status %d (scan %s), buf_len %d\n", + status, status ? "cancelled" : "success", scan_event->buf_len); + + tlv = (MrvlIEtypesHeader_t *)scan_event->event_buf; + tlv_buf_left = pmbuf->data_len - sizeof(mlan_event_scan_status); + while (tlv_buf_left >= sizeof(MrvlIEtypesHeader_t)) { + tlv_type = wlan_le16_to_cpu(tlv->type); + tlv_len = wlan_le16_to_cpu(tlv->len); + if (tlv_buf_left < (tlv_len + sizeof(MrvlIEtypesHeader_t))) { + PRINTM(MERROR, + "Error process scan gap tlv: length %d type 0x%x\n", + tlv_len, tlv_type); + ret = MLAN_STATUS_FAILURE; + goto done; + } + switch (tlv_type) { + case TLV_TYPE_CHANNEL_STATS: + tlv_chan_stats = (MrvlIEtypes_ChannelStats_t *)tlv; + wlan_update_chan_statistics(pmpriv, tlv_chan_stats); + break; + default: + break; + } + tlv_buf_left -= tlv_len + sizeof(MrvlIEtypesHeader_t); + tlv = (MrvlIEtypesHeader_t *)((t_u8 *)(tlv + tlv_len + + sizeof + (MrvlIEtypesHeader_t))); + } + +done: + /* Now we got response from FW, cancel the command timer */ + if (!pmadapter->curr_cmd && pmadapter->cmd_timer_is_set) { + /* Cancel command timeout timer */ + pcb->moal_stop_timer(pmadapter->pmoal_handle, + pmadapter->pmlan_cmd_timer); + /* Cancel command timeout timer */ + pmadapter->cmd_timer_is_set = MFALSE; + } + if (pmadapter->pscan_ioctl_req) { + if (((mlan_ds_scan *)pmadapter->pscan_ioctl_req->pbuf)-> + sub_command == MLAN_OID_SCAN_SPECIFIC_SSID || + ((mlan_ds_scan *)pmadapter->pscan_ioctl_req->pbuf)-> + sub_command == MLAN_OID_SCAN_USER_CONFIG) { + if (wlan_active_scan_req_for_passive_chan + (pmpriv, pmadapter->pscan_ioctl_req)) { + LEAVE(); + return ret; + } + } + } + /* + * Process the resulting scan table: + * - Remove any bad ssids + * - Update our current BSS information from scan data + */ + wlan_scan_process_results(pmpriv); + /** Complete scan ioctl */ + wlan_request_cmd_lock(pmadapter); + pmadapter->scan_processing = MFALSE; + pmadapter->ext_scan_type = EXT_SCAN_DEFAULT; + pioctl_req = pmadapter->pscan_ioctl_req; + pmadapter->pscan_ioctl_req = MNULL; + /* Need to indicate IOCTL complete */ + if (pioctl_req != MNULL) { + pioctl_req->status_code = MLAN_ERROR_NO_ERROR; + /* Indicate ioctl complete */ + pcb->moal_ioctl_complete(pmadapter->pmoal_handle, + pioctl_req, MLAN_STATUS_SUCCESS); + } + wlan_release_cmd_lock(pmadapter); + pmadapter->bgscan_reported = MFALSE; + wlan_recv_event(pmpriv, MLAN_EVENT_ID_DRV_SCAN_REPORT, MNULL); + LEAVE(); + return ret; +} + +/** + * @brief This function prepares command of bg_scan_query. + * + * @param pmpriv A pointer to mlan_private structure + * @param pcmd A pointer to HostCmd_DS_COMMAND structure + * @param pdata_buf Void pointer cast of a wlan_scan_cmd_config struct used + * to set the fields/TLVs for the command sent to firmware + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_cmd_802_11_bg_scan_query(IN mlan_private *pmpriv, + IN HostCmd_DS_COMMAND *pcmd, IN t_void *pdata_buf) +{ + HostCmd_DS_802_11_BG_SCAN_QUERY *bg_query = &pcmd->params.bg_scan_query; + + ENTER(); + + pcmd->command = wlan_cpu_to_le16(HostCmd_CMD_802_11_BG_SCAN_QUERY); + pcmd->size = + wlan_cpu_to_le16(sizeof(HostCmd_DS_802_11_BG_SCAN_QUERY) + + S_DS_GEN); + + bg_query->flush = MTRUE; + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Create a channel list for the driver to scan based on region info + * + * Use the driver region/band information to construct a comprehensive list + * of channels to scan. This routine is used for any scan that is not + * provided a specific channel list to scan. + * + * @param pmpriv A pointer to mlan_private structure + * @param pbg_scan_in pointer to scan configuration parameters + * @param tlv_chan_list A pointer to structure MrvlIEtypes_ChanListParamSet_t + * + * @return channel number + */ +static t_u8 +wlan_bgscan_create_channel_list(IN mlan_private *pmpriv, + IN const wlan_bgscan_cfg *pbg_scan_in, + MrvlIEtypes_ChanListParamSet_t *tlv_chan_list) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + region_chan_t *pscan_region; + chan_freq_power_t *cfp; + t_u32 region_idx; + t_u32 chan_idx = 0; + t_u32 next_chan; + t_u8 scan_type; + t_u8 radio_type; + + ENTER(); + + for (region_idx = 0; + region_idx < NELEMENTS(pmadapter->region_channel); region_idx++) { + + if (wlan_11d_is_enabled(pmpriv) && + pmpriv->media_connected != MTRUE) { + /* Scan all the supported chan for the first scan */ + if (!pmadapter->universal_channel[region_idx].valid) + continue; + pscan_region = + &pmadapter->universal_channel[region_idx]; + } else { + if (!pmadapter->region_channel[region_idx].valid) + continue; + pscan_region = &pmadapter->region_channel[region_idx]; + } + + if (pbg_scan_in && !pbg_scan_in->chan_list[0].chan_number && + pbg_scan_in->chan_list[0].radio_type & BAND_SPECIFIED) { + radio_type = + pbg_scan_in->chan_list[0]. + radio_type & ~BAND_SPECIFIED; + if (!radio_type && (pscan_region->band != BAND_B) && + (pscan_region->band != BAND_G)) + continue; + if (radio_type && (pscan_region->band != BAND_A)) + continue; + } + if (!wlan_is_band_compatible + (pmpriv->config_bands | pmadapter->adhoc_start_band, + pscan_region->band)) + continue; + for (next_chan = 0; + next_chan < pscan_region->num_cfp; + next_chan++, chan_idx++) { + if (chan_idx >= WLAN_BG_SCAN_CHAN_MAX) + break; + /* + * Set the default scan type to ACTIVE SCAN type, will + * later be changed to passive on a per channel basis + * if restricted by regulatory requirements (11d or 11h) + */ + scan_type = MLAN_SCAN_TYPE_ACTIVE; + cfp = pscan_region->pcfp + next_chan; + + switch (pscan_region->band) { + case BAND_A: + tlv_chan_list->chan_scan_param[chan_idx]. + bandcfg.chanBand = BAND_5GHZ; + /* Passive scan on DFS channels */ + if (wlan_11h_radar_detect_required + (pmpriv, (t_u8)cfp->channel)) + scan_type = MLAN_SCAN_TYPE_PASSIVE; + break; + case BAND_B: + case BAND_G: + if (wlan_bg_scan_type_is_passive + (pmpriv, (t_u8)cfp->channel)) + scan_type = MLAN_SCAN_TYPE_PASSIVE; + tlv_chan_list->chan_scan_param[chan_idx]. + bandcfg.chanBand = BAND_2GHZ; + break; + default: + tlv_chan_list->chan_scan_param[chan_idx]. + bandcfg.chanBand = BAND_2GHZ; + break; + } + + if (pbg_scan_in && pbg_scan_in->chan_list[0].scan_time) { + tlv_chan_list->chan_scan_param[chan_idx]. + max_scan_time = + wlan_cpu_to_le16((t_u16)pbg_scan_in-> + chan_list[0]. + scan_time); + tlv_chan_list->chan_scan_param[chan_idx]. + min_scan_time = + wlan_cpu_to_le16((t_u16)pbg_scan_in-> + chan_list[0]. + scan_time); + } else if (scan_type == MLAN_SCAN_TYPE_PASSIVE) { + tlv_chan_list->chan_scan_param[chan_idx]. + max_scan_time = + wlan_cpu_to_le16(pmadapter-> + passive_scan_time); + tlv_chan_list->chan_scan_param[chan_idx]. + min_scan_time = + wlan_cpu_to_le16(pmadapter-> + passive_scan_time); + } else { + tlv_chan_list->chan_scan_param[chan_idx]. + max_scan_time = + wlan_cpu_to_le16(pmadapter-> + specific_scan_time); + tlv_chan_list->chan_scan_param[chan_idx]. + min_scan_time = + wlan_cpu_to_le16(pmadapter-> + specific_scan_time); + } + + if (scan_type == MLAN_SCAN_TYPE_PASSIVE) { + tlv_chan_list->chan_scan_param[chan_idx]. + chan_scan_mode.passive_scan = MTRUE; + } else { + tlv_chan_list->chan_scan_param[chan_idx]. + chan_scan_mode.passive_scan = MFALSE; + } + + tlv_chan_list->chan_scan_param[chan_idx].chan_number = + (t_u8)cfp->channel; + tlv_chan_list->chan_scan_param[chan_idx].chan_scan_mode. + disable_chan_filt = MTRUE; + } + } + + LEAVE(); + return chan_idx; +} + +/** + * @brief This function prepares command of bg_scan_config + * + * @param pmpriv A pointer to mlan_private structure + * @param pcmd A pointer to HostCmd_DS_COMMAND structure + * @param pdata_buf Void pointer cast of a wlan_scan_cmd_config struct used + * to set the fields/TLVs for the command sent to firmware + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_cmd_bgscan_config(IN mlan_private *pmpriv, + IN HostCmd_DS_COMMAND *pcmd, IN t_void *pdata_buf) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + HostCmd_DS_802_11_BG_SCAN_CONFIG *bg_scan = + &pcmd->params.bg_scan_config; + wlan_bgscan_cfg *bg_scan_in = (wlan_bgscan_cfg *)pdata_buf; + t_u16 cmd_size = 0; + MrvlIEtypes_NumProbes_t *pnum_probes_tlv = MNULL; + MrvlIEtypes_BeaconLowRssiThreshold_t *rssi_tlv = MNULL; + MrvlIEtypes_BeaconLowSnrThreshold_t *snr_tlv = MNULL; + MrvlIEtypes_WildCardSsIdParamSet_t *pwildcard_ssid_tlv = MNULL; + MrvlIEtypes_ChanListParamSet_t *tlv_chan_list = MNULL; + MrvlIEtypes_StartLater_t *tlv_start_later = MNULL; + MrvlIEtypes_RepeatCount_t *tlv_repeat = MNULL; + t_u8 *tlv = MNULL; + t_u16 num_probes = 0; + t_u32 ssid_idx; + t_u32 ssid_len = 0; + t_u32 chan_idx; + t_u32 chan_num; + t_u8 radio_type; + t_u16 scan_dur; + t_u8 scan_type; + + ENTER(); + + pcmd->command = wlan_cpu_to_le16(HostCmd_CMD_802_11_BG_SCAN_CONFIG); + bg_scan->action = wlan_cpu_to_le16(bg_scan_in->action); + bg_scan->enable = bg_scan_in->enable; + bg_scan->bss_type = bg_scan_in->bss_type; + cmd_size = sizeof(HostCmd_DS_802_11_BG_SCAN_CONFIG) + S_DS_GEN; + if (bg_scan_in->scan_interval) + bg_scan->scan_interval = + wlan_cpu_to_le32(bg_scan_in->scan_interval); + else + bg_scan->scan_interval = + wlan_cpu_to_le32(DEFAULT_BGSCAN_INTERVAL); + bg_scan->report_condition = + wlan_cpu_to_le32(bg_scan_in->report_condition); + + if ((bg_scan_in->action == BG_SCAN_ACT_GET) || + (bg_scan_in->action == BG_SCAN_ACT_GET_PPS_UAPSD) || + (!bg_scan->enable)) + goto done; + + tlv = (t_u8 *)bg_scan + sizeof(HostCmd_DS_802_11_BG_SCAN_CONFIG); + num_probes = (bg_scan_in->num_probes ? bg_scan_in->num_probes : + pmadapter->scan_probes); + if (num_probes) { + pnum_probes_tlv = (MrvlIEtypes_NumProbes_t *)tlv; + pnum_probes_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_NUMPROBES); + pnum_probes_tlv->header.len = + wlan_cpu_to_le16(sizeof(pnum_probes_tlv->num_probes)); + pnum_probes_tlv->num_probes = + wlan_cpu_to_le16((t_u16)num_probes); + tlv += sizeof(MrvlIEtypes_NumProbes_t); + cmd_size += sizeof(MrvlIEtypes_NumProbes_t); + } + if (bg_scan_in->rssi_threshold) { + rssi_tlv = (MrvlIEtypes_BeaconLowRssiThreshold_t *)tlv; + rssi_tlv->header.type = wlan_cpu_to_le16(TLV_TYPE_RSSI_LOW); + rssi_tlv->header.len = + wlan_cpu_to_le16(sizeof + (MrvlIEtypes_BeaconLowRssiThreshold_t) + - sizeof(MrvlIEtypesHeader_t)); + rssi_tlv->value = bg_scan_in->rssi_threshold; + rssi_tlv->frequency = 0; + tlv += sizeof(MrvlIEtypes_BeaconLowRssiThreshold_t); + cmd_size += sizeof(MrvlIEtypes_BeaconLowRssiThreshold_t); + } + if (bg_scan_in->snr_threshold) { + snr_tlv = (MrvlIEtypes_BeaconLowSnrThreshold_t *)tlv; + snr_tlv->header.type = wlan_cpu_to_le16(TLV_TYPE_SNR_LOW); + snr_tlv->header.len = + wlan_cpu_to_le16(sizeof + (MrvlIEtypes_BeaconLowSnrThreshold_t) - + sizeof(MrvlIEtypesHeader_t)); + snr_tlv->value = bg_scan_in->snr_threshold; + snr_tlv->frequency = 0; + tlv += sizeof(MrvlIEtypes_BeaconLowRssiThreshold_t); + cmd_size += sizeof(MrvlIEtypes_BeaconLowRssiThreshold_t); + } + if (bg_scan_in->repeat_count) { + tlv_repeat = (MrvlIEtypes_RepeatCount_t *)tlv; + tlv_repeat->header.type = + wlan_cpu_to_le16(TLV_TYPE_REPEAT_COUNT); + tlv_repeat->header.len = + wlan_cpu_to_le16(sizeof(MrvlIEtypes_RepeatCount_t) - + sizeof(MrvlIEtypesHeader_t)); + tlv_repeat->repeat_count = + wlan_cpu_to_le16(bg_scan_in->repeat_count); + tlv += sizeof(MrvlIEtypes_RepeatCount_t); + cmd_size += sizeof(MrvlIEtypes_RepeatCount_t); + } + for (ssid_idx = 0; ((ssid_idx < NELEMENTS(bg_scan_in->ssid_list)) + && (*bg_scan_in->ssid_list[ssid_idx].ssid || + bg_scan_in->ssid_list[ssid_idx].max_len)); + ssid_idx++) { + ssid_len = + wlan_strlen((char *)bg_scan_in->ssid_list[ssid_idx]. + ssid); + pwildcard_ssid_tlv = (MrvlIEtypes_WildCardSsIdParamSet_t *)tlv; + pwildcard_ssid_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_WILDCARDSSID); + pwildcard_ssid_tlv->header.len = + (t_u16)(ssid_len + + sizeof(pwildcard_ssid_tlv->max_ssid_length)); + pwildcard_ssid_tlv->max_ssid_length = + bg_scan_in->ssid_list[ssid_idx].max_len; + memcpy(pmadapter, pwildcard_ssid_tlv->ssid, + bg_scan_in->ssid_list[ssid_idx].ssid, + MIN(MLAN_MAX_SSID_LENGTH, ssid_len)); + tlv += sizeof(pwildcard_ssid_tlv->header) + + pwildcard_ssid_tlv->header.len; + cmd_size += + sizeof(pwildcard_ssid_tlv->header) + + pwildcard_ssid_tlv->header.len; + pwildcard_ssid_tlv->header.len = + wlan_cpu_to_le16(pwildcard_ssid_tlv->header.len); + PRINTM(MINFO, "Scan: ssid_list[%d]: %s, %d\n", ssid_idx, + pwildcard_ssid_tlv->ssid, + pwildcard_ssid_tlv->max_ssid_length); + } + if (bg_scan_in->chan_list[0].chan_number) { + tlv_chan_list = (MrvlIEtypes_ChanListParamSet_t *)tlv; + PRINTM(MINFO, "Scan: Using supplied channel list\n"); + chan_num = 0; + for (chan_idx = 0; chan_idx < WLAN_BG_SCAN_CHAN_MAX + && bg_scan_in->chan_list[chan_idx].chan_number; + chan_idx++) { + radio_type = bg_scan_in->chan_list[chan_idx].radio_type; + if (!wlan_is_band_compatible + (pmpriv->config_bands | pmadapter->adhoc_start_band, + radio_type_to_band(radio_type))) + continue; + scan_type = bg_scan_in->chan_list[chan_idx].scan_type; + /* Prevent active scanning on a radar controlled channel */ + if (radio_type == BAND_5GHZ) { + if (wlan_11h_radar_detect_required + (pmpriv, + bg_scan_in->chan_list[chan_idx]. + chan_number)) { + scan_type = MLAN_SCAN_TYPE_PASSIVE; + } + } + if (radio_type == BAND_2GHZ) { + if (wlan_bg_scan_type_is_passive + (pmpriv, + bg_scan_in->chan_list[chan_idx]. + chan_number)) { + scan_type = MLAN_SCAN_TYPE_PASSIVE; + } + } + tlv_chan_list->chan_scan_param[chan_num].chan_number = + bg_scan_in->chan_list[chan_idx].chan_number; + tlv_chan_list->chan_scan_param[chan_num].bandcfg. + chanBand = + bg_scan_in->chan_list[chan_idx].radio_type; + + if (scan_type == MLAN_SCAN_TYPE_PASSIVE) { + tlv_chan_list->chan_scan_param[chan_num]. + chan_scan_mode.passive_scan = MTRUE; + } else { + tlv_chan_list->chan_scan_param[chan_num]. + chan_scan_mode.passive_scan = MFALSE; + } + if (bg_scan_in->chan_list[chan_idx].scan_time) { + scan_dur = + (t_u16)bg_scan_in->chan_list[chan_idx]. + scan_time; + } else { + if (scan_type == MLAN_SCAN_TYPE_PASSIVE) { + scan_dur = pmadapter->passive_scan_time; + } else { + scan_dur = + pmadapter->specific_scan_time; + } + } + tlv_chan_list->chan_scan_param[chan_num].min_scan_time = + wlan_cpu_to_le16(scan_dur); + tlv_chan_list->chan_scan_param[chan_num].max_scan_time = + wlan_cpu_to_le16(scan_dur); + chan_num++; + } + tlv_chan_list->header.type = + wlan_cpu_to_le16(TLV_TYPE_CHANLIST); + tlv_chan_list->header.len = + wlan_cpu_to_le16(sizeof(ChanScanParamSet_t) * chan_num); + tlv += sizeof(MrvlIEtypesHeader_t) + + sizeof(ChanScanParamSet_t) * chan_num; + cmd_size += + sizeof(MrvlIEtypesHeader_t) + + sizeof(ChanScanParamSet_t) * chan_num; + } else { + tlv_chan_list = (MrvlIEtypes_ChanListParamSet_t *)tlv; + chan_num = + wlan_bgscan_create_channel_list(pmpriv, bg_scan_in, + tlv_chan_list); + tlv_chan_list->header.type = + wlan_cpu_to_le16(TLV_TYPE_CHANLIST); + tlv_chan_list->header.len = + wlan_cpu_to_le16(sizeof(ChanScanParamSet_t) * chan_num); + tlv += sizeof(MrvlIEtypesHeader_t) + + sizeof(ChanScanParamSet_t) * chan_num; + cmd_size += + sizeof(MrvlIEtypesHeader_t) + + sizeof(ChanScanParamSet_t) * chan_num; + } + if (bg_scan_in->chan_per_scan) { + bg_scan->chan_per_scan = bg_scan_in->chan_per_scan; + } else { + if (bg_scan_in->report_condition & BG_SCAN_WAIT_ALL_CHAN_DONE) + bg_scan->chan_per_scan = chan_num; + else + bg_scan->chan_per_scan = + MRVDRV_MAX_CHANNELS_PER_SPECIFIC_SCAN; + } + + tlv_start_later = (MrvlIEtypes_StartLater_t *)tlv; + tlv_start_later->header.type = + wlan_cpu_to_le16(TLV_TYPE_STARTBGSCANLATER); + tlv_start_later->header.len = + wlan_cpu_to_le16(sizeof(MrvlIEtypes_StartLater_t) - + sizeof(MrvlIEtypesHeader_t)); + tlv_start_later->value = wlan_cpu_to_le16(bg_scan_in->start_later); + tlv += sizeof(MrvlIEtypes_StartLater_t); + cmd_size += sizeof(MrvlIEtypes_StartLater_t); + +done: + pcmd->size = wlan_cpu_to_le16(cmd_size); + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of extended scan + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_ret_bgscan_config(IN mlan_private *pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf) +{ + mlan_ds_scan *pscan = MNULL; + HostCmd_DS_802_11_BG_SCAN_CONFIG *bg_scan = + &resp->params.bg_scan_config; + wlan_bgscan_cfg *bg_scan_out = MNULL; + + ENTER(); + if (pioctl_buf) { + pscan = (mlan_ds_scan *)pioctl_buf->pbuf; + bg_scan_out = + (wlan_bgscan_cfg *)pscan->param.user_scan.scan_cfg_buf; + bg_scan_out->action = wlan_le16_to_cpu(bg_scan->action); + if ((bg_scan_out->action == BG_SCAN_ACT_GET) && + (bg_scan_out->action == BG_SCAN_ACT_GET_PPS_UAPSD)) { + bg_scan_out->enable = bg_scan->enable; + bg_scan_out->bss_type = bg_scan->bss_type; + bg_scan_out->chan_per_scan = bg_scan->chan_per_scan; + bg_scan_out->scan_interval = + wlan_le32_to_cpu(bg_scan->scan_interval); + bg_scan_out->report_condition = + wlan_le32_to_cpu(bg_scan->report_condition); + pioctl_buf->data_read_written = + sizeof(mlan_ds_scan) + MLAN_SUB_COMMAND_SIZE; + } + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of bgscan_query + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_ret_802_11_bgscan_query(IN mlan_private *pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf) +{ + mlan_ds_scan *pscan = MNULL; + mlan_adapter *pmadapter = pmpriv->adapter; + ENTER(); + wlan_ret_802_11_scan(pmpriv, resp, MNULL); + if (pioctl_buf) { + pscan = (mlan_ds_scan *)pioctl_buf->pbuf; + pscan->param.scan_resp.pscan_table = + (t_u8 *)pmadapter->pscan_table; + pscan->param.scan_resp.num_in_scan_table = + pmadapter->num_in_scan_table; + pscan->param.scan_resp.age_in_secs = pmadapter->age_in_secs; + pscan->param.scan_resp.pchan_stats = + (t_u8 *)pmadapter->pchan_stats; + pscan->param.scan_resp.num_in_chan_stats = + pmadapter->num_in_chan_stats; + + pioctl_buf->data_read_written = sizeof(mlan_scan_resp) + + MLAN_SUB_COMMAND_SIZE; + + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function finds ssid in ssid list. + * + * @param pmpriv A pointer to mlan_private structure + * @param ssid SSID to find in the list + * @param bssid BSSID to qualify the SSID selection (if provided) + * @param mode Network mode: Infrastructure or IBSS + * + * @return index in BSSID list or < 0 if error + */ +t_s32 +wlan_find_ssid_in_list(IN mlan_private *pmpriv, + IN mlan_802_11_ssid *ssid, IN t_u8 *bssid, IN t_u32 mode) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + t_s32 net = -1, j; + t_u8 best_rssi = 0; + t_u32 i; + + ENTER(); + PRINTM(MINFO, "Num of entries in scan table = %d\n", + pmadapter->num_in_scan_table); + + /* + * Loop through the table until the maximum is reached or until a match + * is found based on the bssid field comparison + */ + for (i = 0; + i < pmadapter->num_in_scan_table && (!bssid || (bssid && net < 0)); + i++) { + if (!wlan_ssid_cmp + (pmadapter, &pmadapter->pscan_table[i].ssid, ssid) && + (!bssid || + !memcmp(pmadapter, pmadapter->pscan_table[i].mac_address, + bssid, MLAN_MAC_ADDR_LENGTH))) { + + if ((mode == MLAN_BSS_MODE_INFRA) && + !wlan_is_band_compatible(pmpriv->config_bands, + pmadapter->pscan_table[i]. + bss_band)) + continue; + + switch (mode) { + case MLAN_BSS_MODE_INFRA: + case MLAN_BSS_MODE_IBSS: + j = wlan_is_network_compatible(pmpriv, i, mode); + + if (j >= 0) { + if (SCAN_RSSI + (pmadapter->pscan_table[i].rssi) > + best_rssi) { + best_rssi = + SCAN_RSSI(pmadapter-> + pscan_table + [i].rssi); + net = i; + } + } else { + if (net == -1) + net = j; + } + break; + case MLAN_BSS_MODE_AUTO: + default: + /* + * Do not check compatibility if the mode requested is + * Auto/Unknown. Allows generic find to work without + * verifying against the Adapter security settings + */ + if (SCAN_RSSI(pmadapter->pscan_table[i].rssi) > + best_rssi) { + best_rssi = + SCAN_RSSI(pmadapter-> + pscan_table[i].rssi); + net = i; + } + break; + } + } + } + + LEAVE(); + return net; +} + +/** + * @brief This function finds a specific compatible BSSID in the scan list + * + * @param pmpriv A pointer to mlan_private structure + * @param bssid BSSID to find in the scan list + * @param mode Network mode: Infrastructure or IBSS + * + * @return index in BSSID list or < 0 if error + */ +t_s32 +wlan_find_bssid_in_list(IN mlan_private *pmpriv, IN t_u8 *bssid, IN t_u32 mode) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + t_s32 net = -1; + t_u32 i; + + ENTER(); + + if (!bssid) { + LEAVE(); + return -1; + } + + PRINTM(MINFO, "FindBSSID: Num of BSSIDs = %d\n", + pmadapter->num_in_scan_table); + + /* + * Look through the scan table for a compatible match. The ret return + * variable will be equal to the index in the scan table (greater + * than zero) if the network is compatible. The loop will continue + * past a matched bssid that is not compatible in case there is an + * AP with multiple SSIDs assigned to the same BSSID + */ + for (i = 0; net < 0 && i < pmadapter->num_in_scan_table; i++) { + if (!memcmp + (pmadapter, pmadapter->pscan_table[i].mac_address, bssid, + MLAN_MAC_ADDR_LENGTH)) { + if ((mode == MLAN_BSS_MODE_INFRA) && + !wlan_is_band_compatible(pmpriv->config_bands, + pmadapter->pscan_table[i]. + bss_band)) + continue; + switch (mode) { + case MLAN_BSS_MODE_INFRA: + case MLAN_BSS_MODE_IBSS: + net = wlan_is_network_compatible(pmpriv, i, + mode); + break; + default: + net = i; + break; + } + } + } + + LEAVE(); + return net; +} + +/** + * @brief Compare two SSIDs + * + * @param pmadapter A pointer to mlan_adapter structure + * @param ssid1 A pointer to ssid to compare + * @param ssid2 A pointer to ssid to compare + * + * @return 0--ssid is same, otherwise is different + */ +t_s32 +wlan_ssid_cmp(IN pmlan_adapter pmadapter, + IN mlan_802_11_ssid *ssid1, IN mlan_802_11_ssid *ssid2) +{ + ENTER(); + + if (!ssid1 || !ssid2) { + LEAVE(); + return -1; + } + + if (ssid1->ssid_len != ssid2->ssid_len) { + LEAVE(); + return -1; + } + + LEAVE(); + return memcmp(pmadapter, ssid1->ssid, ssid2->ssid, ssid1->ssid_len); +} + +/** + * @brief Find the AP with specific ssid in the scan list + * + * @param pmpriv A pointer to mlan_private structure + * @param preq_ssid_bssid A pointer to AP's ssid returned + * + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +mlan_status +wlan_find_best_network(IN mlan_private *pmpriv, + OUT mlan_ssid_bssid *preq_ssid_bssid) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + mlan_status ret = MLAN_STATUS_SUCCESS; + BSSDescriptor_t *preq_bss; + t_s32 i; + + ENTER(); + + memset(pmadapter, preq_ssid_bssid, 0, sizeof(mlan_ssid_bssid)); + + i = wlan_find_best_network_in_list(pmpriv); + + if (i >= 0) { + preq_bss = &pmadapter->pscan_table[i]; + memcpy(pmadapter, &preq_ssid_bssid->ssid, &preq_bss->ssid, + sizeof(mlan_802_11_ssid)); + memcpy(pmadapter, (t_u8 *)&preq_ssid_bssid->bssid, + (t_u8 *)&preq_bss->mac_address, MLAN_MAC_ADDR_LENGTH); + + /* Make sure we are in the right mode */ + if (pmpriv->bss_mode == MLAN_BSS_MODE_AUTO) + pmpriv->bss_mode = preq_bss->bss_mode; + preq_ssid_bssid->channel = (t_u16)preq_bss->channel; + if (preq_bss->pmd_ie + && wlan_ft_akm_is_used(pmpriv, (t_u8 *)preq_bss->prsn_ie) + ) { + preq_ssid_bssid->ft_md = preq_bss->pmd_ie->mdid; + preq_ssid_bssid->ft_cap = preq_bss->pmd_ie->ft_cap; + } + preq_ssid_bssid->bss_band = preq_bss->bss_band; + } + + if (!preq_ssid_bssid->ssid.ssid_len) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + PRINTM(MINFO, "Best network found = [%s], " + "[" MACSTR "]\n", + preq_ssid_bssid->ssid.ssid, MAC2STR(preq_ssid_bssid->bssid)); + +done: + LEAVE(); + return ret; +} + +/** + * @brief Send a scan command for all available channels filtered on a spec + * + * @param pmpriv A pointer to mlan_private structure + * @param pioctl_buf A pointer to MLAN IOCTL Request buffer + * @param preq_ssid A pointer to AP's ssid returned + * + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +mlan_status +wlan_scan_specific_ssid(IN mlan_private *pmpriv, + IN t_void *pioctl_buf, IN mlan_802_11_ssid *preq_ssid) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_callbacks *pcb = (mlan_callbacks *)&pmpriv->adapter->callbacks; + wlan_user_scan_cfg *pscan_cfg; + pmlan_ioctl_req pioctl_req = (mlan_ioctl_req *)pioctl_buf; + + ENTER(); + + if (!preq_ssid) { + if (pioctl_req) + pioctl_req->status_code = MLAN_ERROR_CMD_SCAN_FAIL; + ret = MLAN_STATUS_FAILURE; + goto done; + } + wlan_scan_delete_ssid_table_entry(pmpriv, preq_ssid); + + ret = pcb->moal_malloc(pmpriv->adapter->pmoal_handle, + sizeof(wlan_user_scan_cfg), MLAN_MEM_DEF, + (t_u8 **)&pscan_cfg); + + if (ret != MLAN_STATUS_SUCCESS || !pscan_cfg) { + PRINTM(MERROR, "Memory allocation for pscan_cfg failed!\n"); + if (pioctl_req) + pioctl_req->status_code = MLAN_ERROR_NO_MEM; + ret = MLAN_STATUS_FAILURE; + goto done; + } + + memset(pmpriv->adapter, pscan_cfg, 0x00, sizeof(wlan_user_scan_cfg)); + + memcpy(pmpriv->adapter, pscan_cfg->ssid_list[0].ssid, + preq_ssid->ssid, preq_ssid->ssid_len); + pscan_cfg->keep_previous_scan = MTRUE; + + ret = wlan_scan_networks(pmpriv, pioctl_buf, pscan_cfg); + + if (pscan_cfg) + pcb->moal_mfree(pmpriv->adapter->pmoal_handle, + (t_u8 *)pscan_cfg); + +done: + LEAVE(); + return ret; +} + +/** + * @brief Save a beacon buffer of the current bss descriptor + * Save the current beacon buffer to restore in the following cases that + * makes the bcn_buf not to contain the current ssid's beacon buffer. + * - the current ssid was not found somehow in the last scan. + * - the current ssid was the last entry of the scan table and overloaded. + * + * @param pmpriv A pointer to mlan_private structure + * + * @return N/A + */ +t_void +wlan_save_curr_bcn(IN mlan_private *pmpriv) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + mlan_callbacks *pcb = (pmlan_callbacks)&pmadapter->callbacks; + BSSDescriptor_t *pcurr_bss = &pmpriv->curr_bss_params.bss_descriptor; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + pcb->moal_spin_lock(pmadapter->pmoal_handle, pmpriv->curr_bcn_buf_lock); + /* save the beacon buffer if it is not saved or updated */ + if ((pmpriv->pcurr_bcn_buf == MNULL) || + (pmpriv->curr_bcn_size != pcurr_bss->beacon_buf_size) || + (memcmp + (pmpriv->adapter, pmpriv->pcurr_bcn_buf, pcurr_bss->pbeacon_buf, + pcurr_bss->beacon_buf_size))) { + + if (pmpriv->pcurr_bcn_buf) { + pcb->moal_mfree(pmadapter->pmoal_handle, + pmpriv->pcurr_bcn_buf); + pmpriv->pcurr_bcn_buf = MNULL; + } + pmpriv->curr_bcn_size = pcurr_bss->beacon_buf_size; + + if (pmpriv->curr_bcn_size) { + ret = pcb->moal_malloc(pmadapter->pmoal_handle, + pcurr_bss->beacon_buf_size, + MLAN_MEM_DEF, + &pmpriv->pcurr_bcn_buf); + + if ((ret == MLAN_STATUS_SUCCESS) && + pmpriv->pcurr_bcn_buf) { + memcpy(pmpriv->adapter, pmpriv->pcurr_bcn_buf, + pcurr_bss->pbeacon_buf, + pcurr_bss->beacon_buf_size); + PRINTM(MINFO, "current beacon saved %d\n", + pmpriv->curr_bcn_size); + } else { + PRINTM(MERROR, + "Fail to allocate curr_bcn_buf\n"); + } + } + } + wlan_update_curr_bcn(pmpriv); + pcb->moal_spin_unlock(pmadapter->pmoal_handle, + pmpriv->curr_bcn_buf_lock); + + LEAVE(); +} + +/** + * @brief Free a beacon buffer of the current bss descriptor + * + * @param pmpriv A pointer to mlan_private structure + * + * @return N/A + */ +t_void +wlan_free_curr_bcn(IN mlan_private *pmpriv) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + mlan_callbacks *pcb = (pmlan_callbacks)&pmadapter->callbacks; + + ENTER(); + if (pmpriv->pcurr_bcn_buf) { + pcb->moal_mfree(pmadapter->pmoal_handle, pmpriv->pcurr_bcn_buf); + pmpriv->pcurr_bcn_buf = MNULL; + } + LEAVE(); +} diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_sdio.c b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_sdio.c new file mode 100644 index 000000000000..a5a7a646ad76 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_sdio.c @@ -0,0 +1,2470 @@ +/** @file mlan_sdio.c + * + * @brief This file contains SDIO specific code + * + * Copyright (C) 2008-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/******************************************************** +Change log: + 10/27/2008: initial version +********************************************************/ + +#include "mlan.h" +#ifdef STA_SUPPORT +#include "mlan_join.h" +#endif +#include "mlan_util.h" +#include "mlan_fw.h" +#include "mlan_main.h" +#include "mlan_init.h" +#include "mlan_wmm.h" +#include "mlan_11n.h" +#include "mlan_sdio.h" + +/******************************************************** + Local Variables +********************************************************/ + +/******************************************************** + Global Variables +********************************************************/ + +/******************************************************** + Local Functions +********************************************************/ + +/** + * @brief This function initialize the SDIO port + * + * @param pmadapter A pointer to mlan_adapter structure + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status +wlan_sdio_init_ioport(mlan_adapter *pmadapter) +{ + t_u32 reg; + pmlan_callbacks pcb = &pmadapter->callbacks; + + t_u8 host_int_rsr_reg = HOST_INT_RSR_REG; + t_u8 host_int_rsr_mask = HOST_INT_RSR_MASK; + t_u8 card_misc_cfg_reg = CARD_MISC_CFG_REG; + t_u8 card_config_2_1_reg = CARD_CONFIG_2_1_REG; + t_u8 cmd_config_0 = CMD_CONFIG_0; + t_u8 cmd_config_1 = CMD_CONFIG_1; + + ENTER(); + pmadapter->ioport = 0; + + pmadapter->ioport = MEM_PORT; + + PRINTM(MINFO, "SDIO FUNC1 IO port: 0x%x\n", pmadapter->ioport); + + /* enable sdio cmd53 new mode */ + if (MLAN_STATUS_SUCCESS == pcb->moal_read_reg(pmadapter->pmoal_handle, + card_config_2_1_reg, + ®)) { + pcb->moal_write_reg(pmadapter->pmoal_handle, + card_config_2_1_reg, reg | CMD53_NEW_MODE); + } else { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + /* configure cmd port */ + /* enable reading rx length from the register */ + if (MLAN_STATUS_SUCCESS == pcb->moal_read_reg(pmadapter->pmoal_handle, + cmd_config_0, ®)) { + pcb->moal_write_reg(pmadapter->pmoal_handle, + cmd_config_0, reg | CMD_PORT_RD_LEN_EN); + } else { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + /* enable Dnld/Upld ready auto reset for cmd port + * after cmd53 is completed */ + if (MLAN_STATUS_SUCCESS == pcb->moal_read_reg(pmadapter->pmoal_handle, + cmd_config_1, ®)) { + pcb->moal_write_reg(pmadapter->pmoal_handle, + cmd_config_1, reg | CMD_PORT_AUTO_EN); + } else { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + if ((pmadapter->init_para.int_mode == INT_MODE_GPIO) && + (pmadapter->init_para.gpio_pin == GPIO_INT_NEW_MODE)) { + PRINTM(MMSG, "Enable GPIO-1 int mode\n"); + pcb->moal_write_reg(pmadapter->pmoal_handle, SCRATCH_REG_32, + ENABLE_GPIO_1_INT_MODE); + } + /* Set Host interrupt reset to read to clear */ + if (MLAN_STATUS_SUCCESS == + pcb->moal_read_reg(pmadapter->pmoal_handle, host_int_rsr_reg, + ®)) { + pcb->moal_write_reg(pmadapter->pmoal_handle, host_int_rsr_reg, + reg | host_int_rsr_mask); + } else { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + /* Dnld/Upld ready set to auto reset */ + if (MLAN_STATUS_SUCCESS == + pcb->moal_read_reg(pmadapter->pmoal_handle, card_misc_cfg_reg, + ®)) { + pcb->moal_write_reg(pmadapter->pmoal_handle, card_misc_cfg_reg, + reg | AUTO_RE_ENABLE_INT); + } else { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function sends data to the card. + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pmbuf A pointer to mlan_buffer (pmbuf->data_len should include SDIO header) + * @param port Port + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status +wlan_write_data_sync(mlan_adapter *pmadapter, mlan_buffer *pmbuf, t_u32 port) +{ + t_u32 i = 0; + pmlan_callbacks pcb = &pmadapter->callbacks; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + do { + ret = pcb->moal_write_data_sync(pmadapter->pmoal_handle, pmbuf, + port, 0); + if (ret != MLAN_STATUS_SUCCESS) { + i++; + PRINTM(MERROR, + "host_to_card, write iomem (%d) failed: %d\n", i, + ret); + if (MLAN_STATUS_SUCCESS != + pcb->moal_write_reg(pmadapter->pmoal_handle, + HOST_TO_CARD_EVENT_REG, + HOST_TERM_CMD53)) { + PRINTM(MERROR, "write CFG reg failed\n"); + } + ret = MLAN_STATUS_FAILURE; + if (i > MAX_WRITE_IOMEM_RETRY) { + pmbuf->status_code = MLAN_ERROR_DATA_TX_FAIL; + goto exit; + } + } + } while (ret == MLAN_STATUS_FAILURE); +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function gets available SDIO port for reading cmd/data + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pport A pointer to port number + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status +wlan_get_rd_port(mlan_adapter *pmadapter, t_u8 *pport) +{ + t_u32 rd_bitmap = pmadapter->mp_rd_bitmap; + t_u8 max_ports = MAX_PORT; + + ENTER(); + + PRINTM(MIF_D, "wlan_get_rd_port: mp_rd_bitmap=0x%08x\n", rd_bitmap); + + if (!(rd_bitmap & (DATA_PORT_MASK))) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + if (pmadapter->mp_rd_bitmap & (1 << pmadapter->curr_rd_port)) { + pmadapter->mp_rd_bitmap &= + (t_u32)(~(1 << pmadapter->curr_rd_port)); + *pport = pmadapter->curr_rd_port; + + /* hw rx wraps round only after port (MAX_PORT-1) */ + if (++pmadapter->curr_rd_port == max_ports) + /* port 0 is not reserved for cmd port */ + pmadapter->curr_rd_port = 0; + } else { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + PRINTM(MIF_D, "port=%d mp_rd_bitmap=0x%08x -> 0x%08x\n", + *pport, rd_bitmap, pmadapter->mp_rd_bitmap); + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function gets available SDIO port for writing data + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pport A pointer to port number + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status +wlan_get_wr_port_data(mlan_adapter *pmadapter, t_u8 *pport) +{ + t_u32 wr_bitmap = pmadapter->mp_wr_bitmap; + + ENTER(); + + PRINTM(MIF_D, "wlan_get_wr_port_data: mp_wr_bitmap=0x%08x\n", + wr_bitmap); + + if (!(wr_bitmap & pmadapter->mp_data_port_mask)) { + pmadapter->data_sent = MTRUE; + LEAVE(); + return MLAN_STATUS_RESOURCE; + } + + if (pmadapter->mp_wr_bitmap & (1 << pmadapter->curr_wr_port)) { + pmadapter->mp_wr_bitmap &= + (t_u32)(~(1 << pmadapter->curr_wr_port)); + *pport = pmadapter->curr_wr_port; + if (++pmadapter->curr_wr_port == pmadapter->mp_end_port) + pmadapter->curr_wr_port = 0; + } else { + pmadapter->data_sent = MTRUE; + LEAVE(); + return MLAN_STATUS_RESOURCE; + } + + PRINTM(MIF_D, "port=%d mp_wr_bitmap=0x%08x -> 0x%08x\n", + *pport, wr_bitmap, pmadapter->mp_wr_bitmap); + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function polls the card status register. + * + * @param pmadapter A pointer to mlan_adapter structure + * @param bits the bit mask + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status +wlan_sdio_poll_card_status(mlan_adapter *pmadapter, t_u8 bits) +{ + pmlan_callbacks pcb = &pmadapter->callbacks; + t_u32 tries; + t_u32 cs = 0; + + ENTER(); + + for (tries = 0; tries < MAX_POLL_TRIES; tries++) { + if (pcb->moal_read_reg(pmadapter->pmoal_handle, + CARD_TO_HOST_EVENT_REG, + &cs) != MLAN_STATUS_SUCCESS) + break; + else if ((cs & bits) == bits) { + LEAVE(); + return MLAN_STATUS_SUCCESS; + } + wlan_udelay(pmadapter, 10); + } + + PRINTM(MERROR, + "wlan_sdio_poll_card_status failed, tries = %d, cs = 0x%x\n", + tries, cs); + LEAVE(); + return MLAN_STATUS_FAILURE; +} + +/** + * @brief This function reads firmware status registers + * + * @param pmadapter A pointer to mlan_adapter structure + * @param dat A pointer to keep returned data + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status +wlan_sdio_read_fw_status(mlan_adapter *pmadapter, t_u16 *dat) +{ + pmlan_callbacks pcb = &pmadapter->callbacks; + t_u32 fws0 = 0, fws1 = 0; + + ENTER(); + if (MLAN_STATUS_SUCCESS != pcb->moal_read_reg(pmadapter->pmoal_handle, + CARD_FW_STATUS0_REG, + &fws0)) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + if (MLAN_STATUS_SUCCESS != pcb->moal_read_reg(pmadapter->pmoal_handle, + CARD_FW_STATUS1_REG, + &fws1)) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + *dat = (t_u16)((fws1 << 8) | fws0); + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** @brief This function disables the host interrupts mask. + * + * @param pmadapter A pointer to mlan_adapter structure + * @param mask the interrupt mask + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status +wlan_sdio_disable_host_int_mask(pmlan_adapter pmadapter, t_u8 mask) +{ + t_u32 host_int_mask = 0; + pmlan_callbacks pcb = &pmadapter->callbacks; + + ENTER(); + + /* Read back the host_int_mask register */ + if (MLAN_STATUS_SUCCESS != pcb->moal_read_reg(pmadapter->pmoal_handle, + HOST_INT_MASK_REG, + &host_int_mask)) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + /* Update with the mask and write back to the register */ + host_int_mask &= ~mask; + + if (MLAN_STATUS_SUCCESS != pcb->moal_write_reg(pmadapter->pmoal_handle, + HOST_INT_MASK_REG, + host_int_mask)) { + PRINTM(MWARN, "Disable host interrupt failed\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function enables the host interrupts mask + * + * @param pmadapter A pointer to mlan_adapter structure + * @param mask the interrupt mask + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status +wlan_sdio_enable_host_int_mask(pmlan_adapter pmadapter, t_u8 mask) +{ + pmlan_callbacks pcb = &pmadapter->callbacks; + + ENTER(); + + /* Simply write the mask to the register */ + if (MLAN_STATUS_SUCCESS != pcb->moal_write_reg(pmadapter->pmoal_handle, + HOST_INT_MASK_REG, + mask)) { + PRINTM(MWARN, "Enable host interrupt failed\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function reads data from the card. + * + * @param pmadapter A pointer to mlan_adapter structure + * @param type A pointer to keep type as data or command + * @param nb A pointer to keep the data/cmd length returned in buffer + * @param pmbuf A pointer to the SDIO data/cmd buffer + * @param npayload the length of data/cmd buffer + * @param ioport the SDIO ioport + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status +wlan_sdio_card_to_host(mlan_adapter *pmadapter, + t_u32 *type, t_u32 *nb, pmlan_buffer pmbuf, + t_u32 npayload, t_u32 ioport) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_callbacks pcb = &pmadapter->callbacks; + t_u32 i = 0; + + ENTER(); + + if (!pmbuf) { + PRINTM(MWARN, "pmbuf is NULL!\n"); + ret = MLAN_STATUS_FAILURE; + goto exit; + } + do { + ret = pcb->moal_read_data_sync(pmadapter->pmoal_handle, pmbuf, + ioport, 0); + + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, + "wlan: cmd53 read failed: %d ioport=0x%x retry=%d\n", + ret, ioport, i); + i++; + if (MLAN_STATUS_SUCCESS != + pcb->moal_write_reg(pmadapter->pmoal_handle, + HOST_TO_CARD_EVENT_REG, + HOST_TERM_CMD53)) { + PRINTM(MERROR, "Set Term cmd53 failed\n"); + } + if (i > MAX_WRITE_IOMEM_RETRY) { + pmbuf->status_code = MLAN_ERROR_DATA_RX_FAIL; + ret = MLAN_STATUS_FAILURE; + goto exit; + } + } + } while (ret == MLAN_STATUS_FAILURE); + *nb = wlan_le16_to_cpu(*(t_u16 *)(pmbuf->pbuf + pmbuf->data_offset)); + if (*nb > npayload) { + PRINTM(MERROR, "invalid packet, *nb=%d, npayload=%d\n", *nb, + npayload); + pmbuf->status_code = MLAN_ERROR_PKT_SIZE_INVALID; + ret = MLAN_STATUS_FAILURE; + goto exit; + } + + DBG_HEXDUMP(MIF_D, "SDIO Blk Rd", pmbuf->pbuf + pmbuf->data_offset, + MIN(*nb, MAX_DATA_DUMP_LEN)); + + *type = wlan_le16_to_cpu(*(t_u16 *) + (pmbuf->pbuf + pmbuf->data_offset + 2)); + +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function downloads FW blocks to device + * + * @param pmadapter A pointer to mlan_adapter + * @param firmware A pointer to firmware image + * @param firmwarelen firmware len + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status +wlan_prog_fw_w_helper(IN pmlan_adapter pmadapter, t_u8 *fw, t_u32 fw_len) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_callbacks pcb = &pmadapter->callbacks; + t_u8 *firmware = fw; + t_u32 firmwarelen = fw_len; + t_u32 offset = 0; + t_u32 base0, base1; + t_void *tmpfwbuf = MNULL; + t_u32 tmpfwbufsz; + t_u8 *fwbuf; + mlan_buffer mbuf; + t_u16 len = 0; + t_u32 txlen = 0, tx_blocks = 0, tries = 0; + t_u32 i = 0; + t_u32 read_base_0_reg = READ_BASE_0_REG; + t_u32 read_base_1_reg = READ_BASE_1_REG; + + ENTER(); + + if (!firmware && !pcb->moal_get_fw_data) { + PRINTM(MMSG, "No firmware image found! Terminating download\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + PRINTM(MINFO, "WLAN: Downloading FW image (%d bytes)\n", firmwarelen); + + tmpfwbufsz = ALIGN_SZ(WLAN_UPLD_SIZE, DMA_ALIGNMENT); + ret = pcb->moal_malloc(pmadapter->pmoal_handle, tmpfwbufsz, + MLAN_MEM_DEF | MLAN_MEM_DMA, (t_u8 **)&tmpfwbuf); + if ((ret != MLAN_STATUS_SUCCESS) || !tmpfwbuf) { + PRINTM(MERROR, + "Unable to allocate buffer for firmware. Terminating download\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + memset(pmadapter, tmpfwbuf, 0, tmpfwbufsz); + /* Ensure 8-byte aligned firmware buffer */ + fwbuf = (t_u8 *)ALIGN_ADDR(tmpfwbuf, DMA_ALIGNMENT); + + /* Perform firmware data transfer */ + do { + /* The host polls for the DN_LD_CARD_RDY and CARD_IO_READY bits */ + ret = wlan_sdio_poll_card_status(pmadapter, + CARD_IO_READY | + DN_LD_CARD_RDY); + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MFATAL, + "WLAN: FW download with helper poll status timeout @ %d\n", + offset); + goto done; + } + + /* More data? */ + if (firmwarelen && offset >= firmwarelen) + break; + + for (tries = 0; tries < MAX_POLL_TRIES; tries++) { + ret = pcb->moal_read_reg(pmadapter->pmoal_handle, + read_base_0_reg, &base0); + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "Dev BASE0 register read failed:" + " base0=0x%04X(%d). Terminating download\n", + base0, base0); + goto done; + } + ret = pcb->moal_read_reg(pmadapter->pmoal_handle, + read_base_1_reg, &base1); + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "Dev BASE1 register read failed:" + " base1=0x%04X(%d). Terminating download\n", + base1, base1); + goto done; + } + len = (t_u16)(((base1 & 0xff) << 8) | (base0 & 0xff)); + + if (len) + break; + wlan_udelay(pmadapter, 10); + } + + if (!len) + break; + else if (len > WLAN_UPLD_SIZE) { + PRINTM(MFATAL, + "WLAN: FW download failure @ %d, invalid length %d\n", + offset, len); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* Ignore CRC check before download the 1st packet */ + if (offset == 0 && (len & MBIT(0))) { + len &= ~MBIT(0); + } + + txlen = len; + + if (len & MBIT(0)) { + i++; + if (i > MAX_WRITE_IOMEM_RETRY) { + PRINTM(MFATAL, + "WLAN: FW download failure @ %d, over max retry count\n", + offset); + ret = MLAN_STATUS_FAILURE; + goto done; + } + PRINTM(MERROR, + "WLAN: FW CRC error indicated by the helper:" + " len = 0x%04X, txlen = %d\n", len, txlen); + len &= ~MBIT(0); + + PRINTM(MERROR, "WLAN: retry: %d, offset %d\n", i, + offset); + DBG_HEXDUMP(MERROR, "WLAN: FW block:", fwbuf, len); + + /* Setting this to 0 to resend from same offset */ + txlen = 0; + } else { + i = 0; + + /* Set blocksize to transfer - checking + * for last block */ + if (firmwarelen && firmwarelen - offset < txlen) + txlen = firmwarelen - offset; + PRINTM(MINFO, "."); + + tx_blocks = + (txlen + MLAN_SDIO_BLOCK_SIZE_FW_DNLD - + 1) / MLAN_SDIO_BLOCK_SIZE_FW_DNLD; + + /* Copy payload to buffer */ + if (firmware) + memmove(pmadapter, fwbuf, &firmware[offset], + txlen); + else + pcb->moal_get_fw_data(pmadapter->pmoal_handle, + offset, txlen, fwbuf); + } + + /* Send data */ + memset(pmadapter, &mbuf, 0, sizeof(mlan_buffer)); + mbuf.pbuf = (t_u8 *)fwbuf; + mbuf.data_len = tx_blocks * MLAN_SDIO_BLOCK_SIZE_FW_DNLD; + + ret = pcb->moal_write_data_sync(pmadapter->pmoal_handle, &mbuf, + pmadapter->ioport, 0); + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, + "WLAN: FW download, write iomem (%d) failed @ %d\n", + i, offset); + if (pcb-> + moal_write_reg(pmadapter->pmoal_handle, + HOST_TO_CARD_EVENT_REG, + HOST_TERM_CMD53) != + MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "write CFG reg failed\n"); + } + ret = MLAN_STATUS_FAILURE; + goto done; + } + + offset += txlen; + } while (MTRUE); + + PRINTM(MMSG, "Wlan: FW download over, firmwarelen=%d downloaded %d\n", + firmwarelen, offset); + + ret = MLAN_STATUS_SUCCESS; +done: + if (tmpfwbuf) + pcb->moal_mfree(pmadapter->pmoal_handle, (t_u8 *)tmpfwbuf); + + LEAVE(); + return ret; +} + +/** + * @brief This function disables the host interrupts. + * + * @param pmadapter A pointer to mlan_adapter structure + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status +wlan_disable_host_int(pmlan_adapter pmadapter) +{ + mlan_status ret; + + ENTER(); + ret = wlan_sdio_disable_host_int_mask(pmadapter, HIM_DISABLE); + LEAVE(); + return ret; +} + +/** + * @brief This function decodes the rx packet & + * calls corresponding handlers according to the packet type + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pmbuf A pointer to the SDIO data/cmd buffer + * @param upld_typ Type of rx packet + * @param lock_flag flag for spin_lock. + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_decode_rx_packet(mlan_adapter *pmadapter, mlan_buffer *pmbuf, + t_u32 upld_typ, t_u8 lock_flag) +{ + t_u8 *cmd_buf; + t_u32 event; + + ENTER(); + + switch (upld_typ) { + case MLAN_TYPE_SPA_DATA: + PRINTM(MINFO, "--- Rx: SPA Data packet ---\n"); + pmbuf->data_len = pmadapter->upld_len; + if (pmadapter->rx_work_flag) { + pmbuf->buf_type = MLAN_BUF_TYPE_SPA_DATA; + if (lock_flag) + pmadapter->callbacks.moal_spin_lock(pmadapter-> + pmoal_handle, + pmadapter-> + rx_data_queue. + plock); + util_enqueue_list_tail(pmadapter->pmoal_handle, + &pmadapter->rx_data_queue, + (pmlan_linked_list)pmbuf, MNULL, + MNULL); + pmadapter->rx_pkts_queued++; + if (lock_flag) + pmadapter->callbacks. + moal_spin_unlock(pmadapter-> + pmoal_handle, + pmadapter-> + rx_data_queue.plock); + } else { + wlan_decode_spa_buffer(pmadapter, + pmbuf->pbuf + pmbuf->data_offset, + pmbuf->data_len); + wlan_free_mlan_buffer(pmadapter, pmbuf); + } + pmadapter->data_received = MTRUE; + break; + case MLAN_TYPE_DATA: + PRINTM(MINFO, "--- Rx: Data packet ---\n"); + pmbuf->data_len = (pmadapter->upld_len - INTF_HEADER_LEN); + pmbuf->data_offset += INTF_HEADER_LEN; + if (pmadapter->rx_work_flag) { + if (lock_flag) + pmadapter->callbacks.moal_spin_lock(pmadapter-> + pmoal_handle, + pmadapter-> + rx_data_queue. + plock); + util_enqueue_list_tail(pmadapter->pmoal_handle, + &pmadapter->rx_data_queue, + (pmlan_linked_list)pmbuf, MNULL, + MNULL); + pmadapter->rx_pkts_queued++; + if (lock_flag) + pmadapter->callbacks. + moal_spin_unlock(pmadapter-> + pmoal_handle, + pmadapter-> + rx_data_queue.plock); + } else { + wlan_handle_rx_packet(pmadapter, pmbuf); + } + pmadapter->data_received = MTRUE; + break; + + case MLAN_TYPE_CMD: + PRINTM(MINFO, "--- Rx: Cmd Response ---\n"); + /* take care of curr_cmd = NULL case */ + if (!pmadapter->curr_cmd) { + cmd_buf = pmadapter->upld_buf; + if (pmadapter->ps_state == PS_STATE_SLEEP_CFM) { + wlan_process_sleep_confirm_resp(pmadapter, + pmbuf->pbuf + + pmbuf-> + data_offset + + INTF_HEADER_LEN, + pmadapter-> + upld_len - + INTF_HEADER_LEN); + } + pmadapter->upld_len -= INTF_HEADER_LEN; + memcpy(pmadapter, cmd_buf, + pmbuf->pbuf + pmbuf->data_offset + + INTF_HEADER_LEN, MIN(MRVDRV_SIZE_OF_CMD_BUFFER, + pmadapter->upld_len - + INTF_HEADER_LEN)); + wlan_free_mlan_buffer(pmadapter, pmbuf); + } else { + pmadapter->cmd_resp_received = MTRUE; + pmadapter->upld_len -= INTF_HEADER_LEN; + pmbuf->data_len = pmadapter->upld_len; + pmbuf->data_offset += INTF_HEADER_LEN; + pmadapter->curr_cmd->respbuf = pmbuf; + if (pmadapter->upld_len >= MRVDRV_SIZE_OF_CMD_BUFFER) { + PRINTM(MMSG, "Invalid CmdResp len=%d\n", + pmadapter->upld_len); + DBG_HEXDUMP(MERROR, "Invalid CmdResp", + pmbuf->pbuf + pmbuf->data_offset, + MAX_DATA_DUMP_LEN); + } + } + break; + + case MLAN_TYPE_EVENT: + PRINTM(MINFO, "--- Rx: Event ---\n"); + event = *(t_u32 *)&pmbuf->pbuf[pmbuf->data_offset + + INTF_HEADER_LEN]; + pmadapter->event_cause = wlan_le32_to_cpu(event); + if ((pmadapter->upld_len > MLAN_EVENT_HEADER_LEN) && + ((pmadapter->upld_len - MLAN_EVENT_HEADER_LEN) < + MAX_EVENT_SIZE)) { + memcpy(pmadapter, pmadapter->event_body, + pmbuf->pbuf + pmbuf->data_offset + + MLAN_EVENT_HEADER_LEN, + pmadapter->upld_len - MLAN_EVENT_HEADER_LEN); + } + + /* event cause has been saved to adapter->event_cause */ + pmadapter->event_received = MTRUE; + pmbuf->data_len = pmadapter->upld_len; + pmadapter->pmlan_buffer_event = pmbuf; + + /* remove SDIO header */ + pmbuf->data_offset += INTF_HEADER_LEN; + pmbuf->data_len -= INTF_HEADER_LEN; + break; + + default: + PRINTM(MERROR, "SDIO unknown upload type = 0x%x\n", upld_typ); + wlan_free_mlan_buffer(pmadapter, pmbuf); + break; + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +#ifdef SDIO_MULTI_PORT_RX_AGGR +/** + * @brief This function receives single packet + * + * @param pmadapter A pointer to mlan_adapter structure + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_receive_single_packet(mlan_adapter *pmadapter) +{ + mlan_buffer *pmbuf; + t_u8 port; + t_u16 rx_len; + t_u32 pkt_type = 0; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + pmbuf = pmadapter->mpa_rx.mbuf_arr[0]; + port = pmadapter->mpa_rx.start_port; + rx_len = pmadapter->mpa_rx.len_arr[0]; + if (MLAN_STATUS_SUCCESS != wlan_sdio_card_to_host(pmadapter, &pkt_type, + (t_u32 *)&pmadapter-> + upld_len, pmbuf, + rx_len, + pmadapter->ioport + + port)) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + if (pkt_type != MLAN_TYPE_DATA && pkt_type != MLAN_TYPE_SPA_DATA) { + PRINTM(MERROR, + "receive a wrong pkt from DATA PORT: type=%d, len=%dd\n", + pkt_type, pmbuf->data_len); + pmbuf->status_code = MLAN_ERROR_DATA_RX_FAIL; + ret = MLAN_STATUS_FAILURE; + goto done; + } + pmadapter->mpa_rx_count[0]++; + wlan_decode_rx_packet(pmadapter, pmbuf, pkt_type, MTRUE); +done: + if (ret != MLAN_STATUS_SUCCESS) + wlan_free_mlan_buffer(pmadapter, pmbuf); + MP_RX_AGGR_BUF_RESET(pmadapter); + LEAVE(); + return ret; +} + +/** + * @brief This function receives data from the card in aggregate mode. + * + * @param pmadapter A pointer to mlan_adapter structure + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_receive_mp_aggr_buf(mlan_adapter *pmadapter) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_callbacks pcb = &pmadapter->callbacks; + mlan_buffer mbuf_aggr; + mlan_buffer *mbuf_deaggr; + t_u32 pind = 0; + t_u32 pkt_len, pkt_type = 0; + t_u8 *curr_ptr; + t_u32 cmd53_port = 0; + t_u32 i = 0; + t_u32 port_count = 0; + + /* do aggr RX now */ + PRINTM(MINFO, "do_rx_aggr: num of packets: %d\n", + pmadapter->mpa_rx.pkt_cnt); + + memset(pmadapter, &mbuf_aggr, 0, sizeof(mlan_buffer)); + + if (pmadapter->mpa_rx.pkt_cnt == 1) + return wlan_receive_single_packet(pmadapter); + if (!pmadapter->mpa_rx.buf) { + mbuf_aggr.data_len = pmadapter->mpa_rx.buf_len; + mbuf_aggr.pnext = mbuf_aggr.pprev = &mbuf_aggr; + mbuf_aggr.use_count = 0; + for (pind = 0; pind < pmadapter->mpa_rx.pkt_cnt; pind++) { + pmadapter->mpa_rx.mbuf_arr[pind]->data_len = + pmadapter->mpa_rx.len_arr[pind]; + wlan_link_buf_to_aggr(&mbuf_aggr, + pmadapter->mpa_rx.mbuf_arr[pind]); + } + } else { + mbuf_aggr.pbuf = (t_u8 *)pmadapter->mpa_rx.buf; + mbuf_aggr.data_len = pmadapter->mpa_rx.buf_len; + } + + port_count = bitcount(pmadapter->mpa_rx.ports) - 1; + /* port_count = pmadapter->mpa_rx.pkt_cnt - 1; */ + cmd53_port = + (pmadapter->ioport | SDIO_MPA_ADDR_BASE | (port_count << 8)) + + pmadapter->mpa_rx.start_port; + do { + ret = pcb->moal_read_data_sync(pmadapter->pmoal_handle, + &mbuf_aggr, cmd53_port, 0); + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, + "wlan: sdio mp cmd53 read failed: %d ioport=0x%x retry=%d\n", + ret, cmd53_port, i); + i++; + if (MLAN_STATUS_SUCCESS != + pcb->moal_write_reg(pmadapter->pmoal_handle, + HOST_TO_CARD_EVENT_REG, + HOST_TERM_CMD53)) { + PRINTM(MERROR, "Set Term cmd53 failed\n"); + } + if (i > MAX_WRITE_IOMEM_RETRY) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + } + } while (ret == MLAN_STATUS_FAILURE); + if (pmadapter->rx_work_flag) + pmadapter->callbacks.moal_spin_lock(pmadapter->pmoal_handle, + pmadapter->rx_data_queue. + plock); + if (!pmadapter->mpa_rx.buf && pmadapter->mpa_rx.pkt_cnt > 1) { + for (pind = 0; pind < pmadapter->mpa_rx.pkt_cnt; pind++) { + mbuf_deaggr = pmadapter->mpa_rx.mbuf_arr[pind]; + pkt_len = + wlan_le16_to_cpu(*(t_u16 *) + (mbuf_deaggr->pbuf + + mbuf_deaggr->data_offset)); + pkt_type = + wlan_le16_to_cpu(*(t_u16 *) + (mbuf_deaggr->pbuf + + mbuf_deaggr->data_offset + + 2)); + pmadapter->upld_len = pkt_len; + wlan_decode_rx_packet(pmadapter, mbuf_deaggr, pkt_type, + MFALSE); + } + } else { + DBG_HEXDUMP(MIF_D, "SDIO MP-A Blk Rd", pmadapter->mpa_rx.buf, + MIN(pmadapter->mpa_rx.buf_len, MAX_DATA_DUMP_LEN)); + + curr_ptr = pmadapter->mpa_rx.buf; + + for (pind = 0; pind < pmadapter->mpa_rx.pkt_cnt; pind++) { + + /* get curr PKT len & type */ + pkt_len = wlan_le16_to_cpu(*(t_u16 *)&curr_ptr[0]); + pkt_type = wlan_le16_to_cpu(*(t_u16 *)&curr_ptr[2]); + + PRINTM(MINFO, "RX: [%d] pktlen: %d pkt_type: 0x%x\n", + pind, pkt_len, pkt_type); + + /* copy pkt to deaggr buf */ + mbuf_deaggr = pmadapter->mpa_rx.mbuf_arr[pind]; + if ((pkt_type == MLAN_TYPE_DATA + || pkt_type == MLAN_TYPE_SPA_DATA) && + (pkt_len <= pmadapter->mpa_rx.len_arr[pind])) { + memcpy(pmadapter, + mbuf_deaggr->pbuf + + mbuf_deaggr->data_offset, curr_ptr, + pkt_len); + pmadapter->upld_len = pkt_len; + /* Process de-aggr packet */ + wlan_decode_rx_packet(pmadapter, mbuf_deaggr, + pkt_type, MFALSE); + } else { + PRINTM(MERROR, + "Wrong aggr packet: type=%d, len=%d, max_len=%d\n", + pkt_type, pkt_len, + pmadapter->mpa_rx.len_arr[pind]); + wlan_free_mlan_buffer(pmadapter, mbuf_deaggr); + } + curr_ptr += pmadapter->mpa_rx.len_arr[pind]; + } + } + if (pmadapter->rx_work_flag) + pmadapter->callbacks.moal_spin_unlock(pmadapter->pmoal_handle, + pmadapter->rx_data_queue. + plock); + pmadapter->mpa_rx_count[pmadapter->mpa_rx.pkt_cnt - 1]++; + MP_RX_AGGR_BUF_RESET(pmadapter); +done: + return ret; +} + +/** + * @brief This function receives data from the card in aggregate mode. + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pmbuf A pointer to the SDIO data/cmd buffer + * @param port Current port on which packet needs to be rxed + * @param rx_len Length of received packet + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status +wlan_sdio_card_to_host_mp_aggr(mlan_adapter *pmadapter, mlan_buffer + *pmbuf, t_u8 port, t_u16 rx_len) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + t_s32 f_do_rx_aggr = 0; + t_s32 f_do_rx_cur = 0; + t_s32 f_aggr_cur = 0; + t_s32 f_post_aggr_cur = 0; + t_u32 pind = 0; + t_u32 pkt_type = 0; + + ENTER(); + + if (!pmadapter->mpa_rx.enabled) { + PRINTM(MINFO, + "card_2_host_mp_aggr: rx aggregation disabled !\n"); + + f_do_rx_cur = 1; + goto rx_curr_single; + } + + if (pmadapter->mp_rd_bitmap & DATA_PORT_MASK) { + /* Some more data RX pending */ + PRINTM(MINFO, "card_2_host_mp_aggr: Not last packet\n"); + + if (MP_RX_AGGR_IN_PROGRESS(pmadapter)) { + if (MP_RX_AGGR_BUF_HAS_ROOM(pmadapter, rx_len)) { + f_aggr_cur = 1; + } else { + /* No room in Aggr buf, do rx aggr now */ + f_do_rx_aggr = 1; + f_post_aggr_cur = 1; + } + } else { + /* Rx aggr not in progress */ + f_aggr_cur = 1; + } + + } else { + /* No more data RX pending */ + PRINTM(MINFO, "card_2_host_mp_aggr: Last packet\n"); + + if (MP_RX_AGGR_IN_PROGRESS(pmadapter)) { + f_do_rx_aggr = 1; + if (MP_RX_AGGR_BUF_HAS_ROOM(pmadapter, rx_len)) { + f_aggr_cur = 1; + } else { + /* No room in Aggr buf, do rx aggr now */ + f_do_rx_cur = 1; + } + } else { + f_do_rx_cur = 1; + } + + } + + if (f_aggr_cur) { + PRINTM(MINFO, "Current packet aggregation.\n"); + /* Curr pkt can be aggregated */ + MP_RX_AGGR_SETUP(pmadapter, pmbuf, port, rx_len); + + if (MP_RX_AGGR_PKT_LIMIT_REACHED(pmadapter) || + MP_RX_AGGR_PORT_LIMIT_REACHED(pmadapter) + ) { + PRINTM(MINFO, + "card_2_host_mp_aggr: Aggregation Packet limit reached\n"); + /* No more pkts allowed in Aggr buf, rx it */ + f_do_rx_aggr = 1; + } + } + + if (f_do_rx_aggr) { + /* do aggr RX now */ + if (MLAN_STATUS_SUCCESS != wlan_receive_mp_aggr_buf(pmadapter)) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + } +rx_curr_single: + if (f_do_rx_cur) { + PRINTM(MINFO, "RX: f_do_rx_cur: port: %d rx_len: %d\n", port, + rx_len); + + if (MLAN_STATUS_SUCCESS != + wlan_sdio_card_to_host(pmadapter, &pkt_type, + (t_u32 *)&pmadapter->upld_len, pmbuf, + rx_len, pmadapter->ioport + port)) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + if (pkt_type != MLAN_TYPE_DATA + && pkt_type != MLAN_TYPE_SPA_DATA) { + PRINTM(MERROR, + "receive a wrong pkt from DATA PORT: type=%d, len=%dd\n", + pkt_type, pmbuf->data_len); + pmbuf->status_code = MLAN_ERROR_DATA_RX_FAIL; + ret = MLAN_STATUS_FAILURE; + goto done; + } + + pmadapter->mpa_rx_count[0]++; + + wlan_decode_rx_packet(pmadapter, pmbuf, pkt_type, MTRUE); + } + if (f_post_aggr_cur) { + PRINTM(MINFO, "Current packet aggregation.\n"); + /* Curr pkt can be aggregated */ + MP_RX_AGGR_SETUP(pmadapter, pmbuf, port, rx_len); + } +done: + if (ret == MLAN_STATUS_FAILURE) { + if (MP_RX_AGGR_IN_PROGRESS(pmadapter)) { + /* MP-A transfer failed - cleanup */ + for (pind = 0; pind < pmadapter->mpa_rx.pkt_cnt; pind++) { + wlan_free_mlan_buffer(pmadapter, + pmadapter->mpa_rx. + mbuf_arr[pind]); + } + MP_RX_AGGR_BUF_RESET(pmadapter); + } + + if (f_do_rx_cur) { + /* Single Transfer pending */ + /* Free curr buff also */ + wlan_free_mlan_buffer(pmadapter, pmbuf); + } + } + + LEAVE(); + return ret; + +} +#endif + +#ifdef SDIO_MULTI_PORT_TX_AGGR +/** + * @brief This function sends aggr buf + * + * @param pmadapter A pointer to mlan_adapter structure + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_send_mp_aggr_buf(mlan_adapter *pmadapter) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u32 cmd53_port = 0; + t_u32 port_count = 0; + mlan_buffer mbuf_aggr; + t_u8 i = 0; + t_u8 mp_aggr_pkt_limit = SDIO_MP_AGGR_DEF_PKT_LIMIT; + + ENTER(); + + if (!pmadapter->mpa_tx.pkt_cnt) { + LEAVE(); + return ret; + } + PRINTM(MINFO, "host_2_card_mp_aggr: Send aggregation buffer." + "%d %d\n", pmadapter->mpa_tx.start_port, + pmadapter->mpa_tx.ports); + + memset(pmadapter, &mbuf_aggr, 0, sizeof(mlan_buffer)); + + if (!pmadapter->mpa_tx.buf && pmadapter->mpa_tx.pkt_cnt > 1) { + mbuf_aggr.data_len = pmadapter->mpa_tx.buf_len; + mbuf_aggr.pnext = mbuf_aggr.pprev = &mbuf_aggr; + mbuf_aggr.use_count = 0; + for (i = 0; i < pmadapter->mpa_tx.pkt_cnt; i++) + wlan_link_buf_to_aggr(&mbuf_aggr, + pmadapter->mpa_tx.mbuf_arr[i]); + } else { + mbuf_aggr.pbuf = (t_u8 *)pmadapter->mpa_tx.buf; + mbuf_aggr.data_len = pmadapter->mpa_tx.buf_len; + } + + port_count = bitcount(pmadapter->mpa_tx.ports) - 1; + cmd53_port = + (pmadapter->ioport | SDIO_MPA_ADDR_BASE | (port_count << 8)) + + pmadapter->mpa_tx.start_port; + + if (pmadapter->mpa_tx.pkt_cnt == 1) + cmd53_port = pmadapter->ioport + pmadapter->mpa_tx.start_port; + /** only one packet */ + if (!pmadapter->mpa_tx.buf && pmadapter->mpa_tx.pkt_cnt == 1) + ret = wlan_write_data_sync(pmadapter, + pmadapter->mpa_tx.mbuf_arr[0], + cmd53_port); + else + ret = wlan_write_data_sync(pmadapter, &mbuf_aggr, cmd53_port); + if (!pmadapter->mpa_tx.buf) { + /** free mlan buffer */ + for (i = 0; i < pmadapter->mpa_tx.pkt_cnt; i++) { + wlan_write_data_complete(pmadapter, + pmadapter->mpa_tx.mbuf_arr[i], + MLAN_STATUS_SUCCESS); + } + } + if (!(pmadapter->mp_wr_bitmap & (1 << pmadapter->curr_wr_port)) + && (pmadapter->mpa_tx.pkt_cnt < mp_aggr_pkt_limit)) + pmadapter->mpa_sent_no_ports++; + pmadapter->mpa_tx_count[pmadapter->mpa_tx.pkt_cnt - 1]++; + pmadapter->last_mp_wr_bitmap[pmadapter->last_mp_index] = + pmadapter->mp_wr_bitmap; + pmadapter->last_mp_wr_ports[pmadapter->last_mp_index] = cmd53_port; + pmadapter->last_mp_wr_len[pmadapter->last_mp_index] = + pmadapter->mpa_tx.buf_len; + pmadapter->last_curr_wr_port[pmadapter->last_mp_index] = + pmadapter->curr_wr_port; + memcpy(pmadapter, + (t_u8 *)&pmadapter->last_mp_wr_info[pmadapter->last_mp_index * + mp_aggr_pkt_limit], + (t_u8 *)pmadapter->mpa_tx.mp_wr_info, + sizeof(pmadapter->mpa_tx.mp_wr_info) + ); + pmadapter->last_mp_index++; + if (pmadapter->last_mp_index >= SDIO_MP_DBG_NUM) + pmadapter->last_mp_index = 0; + MP_TX_AGGR_BUF_RESET(pmadapter); + LEAVE(); + return ret; +} + +/** + * @brief This function sends data to the card in SDIO aggregated mode. + * + * @param pmadapter A pointer to mlan_adapter structure + * @param mbuf A pointer to the SDIO data/cmd buffer + * @param port current port for aggregation + * @param next_pkt_len Length of next packet used for multiport aggregation + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status +wlan_host_to_card_mp_aggr(mlan_adapter *pmadapter, mlan_buffer *mbuf, t_u8 port, + t_u32 next_pkt_len) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + t_s32 f_send_aggr_buf = 0; + t_s32 f_send_cur_buf = 0; + t_s32 f_precopy_cur_buf = 0; + t_s32 f_postcopy_cur_buf = 0; + t_u8 aggr_sg = 0; + t_u8 mp_aggr_pkt_limit = SDIO_MP_AGGR_DEF_PKT_LIMIT; + + ENTER(); + + PRINTM(MIF_D, "host_2_card_mp_aggr: next_pkt_len: %d curr_port:%d\n", + next_pkt_len, port); + + if (!pmadapter->mpa_tx.enabled) { + PRINTM(MINFO, + "host_2_card_mp_aggr: tx aggregation disabled !\n"); + f_send_cur_buf = 1; + goto tx_curr_single; + } + + if (next_pkt_len) { + /* More pkt in TX queue */ + PRINTM(MINFO, "host_2_card_mp_aggr: More packets in Queue.\n"); + + if (MP_TX_AGGR_IN_PROGRESS(pmadapter)) { + if (MP_TX_AGGR_BUF_HAS_ROOM + (pmadapter, mbuf, mbuf->data_len)) { + f_precopy_cur_buf = 1; + + if (! + (pmadapter-> + mp_wr_bitmap & (1 << pmadapter-> + curr_wr_port)) || + !MP_TX_AGGR_BUF_HAS_ROOM(pmadapter, mbuf, + mbuf->data_len + + next_pkt_len)) { + f_send_aggr_buf = 1; + } + } else { + /* No room in Aggr buf, send it */ + f_send_aggr_buf = 1; + + if (! + (pmadapter-> + mp_wr_bitmap & (1 << pmadapter-> + curr_wr_port))) { + f_send_cur_buf = 1; + } else { + f_postcopy_cur_buf = 1; + } + } + } else { + if (MP_TX_AGGR_BUF_HAS_ROOM + (pmadapter, mbuf, mbuf->data_len) && + (pmadapter-> + mp_wr_bitmap & (1 << pmadapter->curr_wr_port))) + f_precopy_cur_buf = 1; + else + f_send_cur_buf = 1; + } + } else { + /* Last pkt in TX queue */ + PRINTM(MINFO, + "host_2_card_mp_aggr: Last packet in Tx Queue.\n"); + + if (MP_TX_AGGR_IN_PROGRESS(pmadapter)) { + /* some packs in Aggr buf already */ + f_send_aggr_buf = 1; + + if (MP_TX_AGGR_BUF_HAS_ROOM + (pmadapter, mbuf, mbuf->data_len)) { + f_precopy_cur_buf = 1; + } else { + /* No room in Aggr buf, send it */ + f_send_cur_buf = 1; + } + } else { + f_send_cur_buf = 1; + } + pmadapter->mpa_sent_last_pkt++; + } + + if (f_precopy_cur_buf) { + PRINTM(MINFO, "host_2_card_mp_aggr: Precopy current buffer\n"); + if (pmadapter->mpa_buf) + memcpy(pmadapter, pmadapter->mpa_buf + + (pmadapter->last_mp_index * mp_aggr_pkt_limit + + pmadapter->mpa_tx.pkt_cnt) * + MLAN_SDIO_BLOCK_SIZE, + mbuf->pbuf + mbuf->data_offset, + MLAN_SDIO_BLOCK_SIZE); + if (!pmadapter->mpa_tx.buf) { + MP_TX_AGGR_BUF_PUT_SG(pmadapter, mbuf, port); + aggr_sg = MTRUE; + } else { + + MP_TX_AGGR_BUF_PUT(pmadapter, mbuf, port); + } + if (MP_TX_AGGR_PKT_LIMIT_REACHED(pmadapter) || + MP_TX_AGGR_PORT_LIMIT_REACHED(pmadapter) + ) { + PRINTM(MIF_D, + "host_2_card_mp_aggr: Aggregation Pkt limit reached\n"); + /* No more pkts allowed in Aggr buf, send it */ + f_send_aggr_buf = 1; + } + } + + if (f_send_aggr_buf) + ret = wlan_send_mp_aggr_buf(pmadapter); + +tx_curr_single: + if (f_send_cur_buf) { + PRINTM(MINFO, "host_2_card_mp_aggr: writing to port #%d\n", + port); + ret = wlan_write_data_sync(pmadapter, mbuf, + pmadapter->ioport + port); + if (!(pmadapter->mp_wr_bitmap & (1 << pmadapter->curr_wr_port))) + pmadapter->mpa_sent_no_ports++; + pmadapter->last_mp_wr_bitmap[pmadapter->last_mp_index] = + pmadapter->mp_wr_bitmap; + pmadapter->last_mp_wr_ports[pmadapter->last_mp_index] = + pmadapter->ioport + port; + pmadapter->last_mp_wr_len[pmadapter->last_mp_index] = + mbuf->data_len; + memset(pmadapter, + (t_u8 *)&pmadapter->last_mp_wr_info[pmadapter-> + last_mp_index * + mp_aggr_pkt_limit], + 0, sizeof(t_u16) * mp_aggr_pkt_limit); + pmadapter->last_mp_wr_info[pmadapter->last_mp_index * + mp_aggr_pkt_limit] = + *(t_u16 *)(mbuf->pbuf + mbuf->data_offset); + pmadapter->last_curr_wr_port[pmadapter->last_mp_index] = + pmadapter->curr_wr_port; + if (pmadapter->mpa_buf) + memcpy(pmadapter, + pmadapter->mpa_buf + + (pmadapter->last_mp_index * mp_aggr_pkt_limit * + MLAN_SDIO_BLOCK_SIZE), + mbuf->pbuf + mbuf->data_offset, + MLAN_SDIO_BLOCK_SIZE); + pmadapter->last_mp_index++; + if (pmadapter->last_mp_index >= SDIO_MP_DBG_NUM) + pmadapter->last_mp_index = 0; + pmadapter->mpa_tx_count[0]++; + } + if (f_postcopy_cur_buf) { + PRINTM(MINFO, "host_2_card_mp_aggr: Postcopy current buffer\n"); + if (pmadapter->mpa_buf) + memcpy(pmadapter, pmadapter->mpa_buf + + (pmadapter->last_mp_index * mp_aggr_pkt_limit + + pmadapter->mpa_tx.pkt_cnt) * + MLAN_SDIO_BLOCK_SIZE, + mbuf->pbuf + mbuf->data_offset, + MLAN_SDIO_BLOCK_SIZE); + if (!pmadapter->mpa_tx.buf) { + MP_TX_AGGR_BUF_PUT_SG(pmadapter, mbuf, port); + aggr_sg = MTRUE; + } else { + MP_TX_AGGR_BUF_PUT(pmadapter, mbuf, port); + } + } + /* Always return PENDING in SG mode */ + if (aggr_sg) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} +#endif /* SDIO_MULTI_PORT_TX_AGGR */ + +/******************************************************** + Global functions +********************************************************/ + +/** + * @brief This function checks if the interface is ready to download + * or not while other download interface is present + * + * @param pmadapter A pointer to mlan_adapter structure + * @param val Winner status (0: winner) + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + * + */ +mlan_status +wlan_check_winner_status(mlan_adapter *pmadapter, t_u32 *val) +{ + t_u32 winner = 0; + pmlan_callbacks pcb; + t_u8 card_fw_status0_reg = CARD_FW_STATUS0_REG; + + ENTER(); + + pcb = &pmadapter->callbacks; + + if (MLAN_STATUS_SUCCESS != + pcb->moal_read_reg(pmadapter->pmoal_handle, card_fw_status0_reg, + &winner)) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + *val = winner; + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function checks if the firmware is ready to accept + * command or not. + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pollnum Maximum polling number + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_check_fw_status(mlan_adapter *pmadapter, t_u32 pollnum) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u16 firmwarestat = 0; + t_u32 tries; + + ENTER(); + + /* Wait for firmware initialization event */ + for (tries = 0; tries < pollnum; tries++) { + ret = wlan_sdio_read_fw_status(pmadapter, &firmwarestat); + if (MLAN_STATUS_SUCCESS != ret) + continue; + if (firmwarestat == FIRMWARE_READY) { + ret = MLAN_STATUS_SUCCESS; + break; + } else { + wlan_mdelay(pmadapter, 100); + ret = MLAN_STATUS_FAILURE; + } + } + + if (ret != MLAN_STATUS_SUCCESS) { + if (pollnum > 1) + PRINTM(MERROR, + "Fail to poll firmware status: firmwarestat=0x%x\n", + firmwarestat); + goto done; + } + +done: + LEAVE(); + return ret; +} + +/** + * @brief This function downloads firmware to card + * + * @param pmadapter A pointer to mlan_adapter + * @param pmfw A pointer to firmware image + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_dnld_fw(IN pmlan_adapter pmadapter, IN pmlan_fw_image pmfw) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + /* Download the firmware image via helper */ + ret = wlan_prog_fw_w_helper(pmadapter, pmfw->pfw_buf, pmfw->fw_len); + if (ret != MLAN_STATUS_SUCCESS) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + LEAVE(); + return ret; +} + +/** + * @brief This function probes the driver + * + * @param pmadapter A pointer to mlan_adapter structure + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_sdio_probe(pmlan_adapter pmadapter) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u32 sdio_ireg = 0; + pmlan_callbacks pcb = &pmadapter->callbacks; + + ENTER(); + /* + * Read the HOST_INT_STATUS_REG for ACK the first interrupt got + * from the bootloader. If we don't do this we get a interrupt + * as soon as we register the irq. + */ + pcb->moal_read_reg(pmadapter->pmoal_handle, + HOST_INT_STATUS_REG, &sdio_ireg); + + /* Disable host interrupt mask register for SDIO */ + ret = wlan_disable_host_int(pmadapter); + if (ret != MLAN_STATUS_SUCCESS) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + /* Get SDIO ioport */ + ret = wlan_sdio_init_ioport(pmadapter); + LEAVE(); + return ret; +} + +/** + * @brief This function gets interrupt status. + * + * @param pmadapter A pointer to mlan_adapter structure + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_interrupt(pmlan_adapter pmadapter) +{ + pmlan_callbacks pcb = &pmadapter->callbacks; + mlan_buffer mbuf; + t_u32 sdio_ireg = 0; + + t_u8 max_mp_regs = MAX_MP_REGS; + t_u8 host_int_status_reg = HOST_INT_STATUS_REG; + + ENTER(); + + memset(pmadapter, &mbuf, 0, sizeof(mlan_buffer)); + mbuf.pbuf = pmadapter->mp_regs; + mbuf.data_len = max_mp_regs; + + if (MLAN_STATUS_SUCCESS != + pcb->moal_read_data_sync(pmadapter->pmoal_handle, &mbuf, + REG_PORT | MLAN_SDIO_BYTE_MODE_MASK, 0)) { + PRINTM(MERROR, "moal_read_data_sync: read registers failed\n"); + pmadapter->dbg.num_int_read_failure++; + goto done; + } + + DBG_HEXDUMP(MIF_D, "SDIO MP Registers", pmadapter->mp_regs, + max_mp_regs); + sdio_ireg = pmadapter->mp_regs[host_int_status_reg]; + pmadapter->dbg.last_int_status = pmadapter->sdio_ireg | sdio_ireg; + if (sdio_ireg) { + /* + * DN_LD_HOST_INT_STATUS and/or UP_LD_HOST_INT_STATUS + * DN_LD_CMD_PORT_HOST_INT_STATUS and/or + * UP_LD_CMD_PORT_HOST_INT_STATUS + * Clear the interrupt status register + */ + PRINTM(MINTR, "wlan_interrupt: sdio_ireg = 0x%x\n", sdio_ireg); + pmadapter->num_of_irq++; + pcb->moal_spin_lock(pmadapter->pmoal_handle, + pmadapter->pint_lock); + pmadapter->sdio_ireg |= sdio_ireg; + pcb->moal_spin_unlock(pmadapter->pmoal_handle, + pmadapter->pint_lock); + + if (!pmadapter->pps_uapsd_mode && + pmadapter->ps_state == PS_STATE_SLEEP) { + pmadapter->pm_wakeup_fw_try = MFALSE; + pmadapter->ps_state = PS_STATE_AWAKE; + pmadapter->pm_wakeup_card_req = MFALSE; + } + } else { + PRINTM(MMSG, "wlan_interrupt: sdio_ireg = 0x%x\n", sdio_ireg); + } +done: + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function enables the host interrupts. + * + * @param pmadapter A pointer to mlan_adapter structure + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_enable_host_int(pmlan_adapter pmadapter) +{ + mlan_status ret; + t_u8 mask = HIM_ENABLE; + + ENTER(); + ret = wlan_sdio_enable_host_int_mask(pmadapter, mask); + LEAVE(); + return ret; +} + +#if defined(SDIO_MULTI_PORT_RX_AGGR) +/** + * @brief This function try to read the packet when fail to alloc rx buffer + * + * @param pmadapter A pointer to mlan_adapter structure + * @param port Current port on which packet needs to be rxed + * @param rx_len Length of received packet + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status +wlan_sdio_card_to_host_recovery(mlan_adapter *pmadapter, t_u8 port, + t_u16 rx_len) +{ + mlan_buffer mbuf; + t_u32 pkt_type = 0; + mlan_status ret = MLAN_STATUS_FAILURE; + ENTER(); + if (MP_RX_AGGR_IN_PROGRESS(pmadapter)) { + PRINTM(MDATA, "Recovery:do Rx Aggr\n"); + /* do aggr RX now */ + wlan_receive_mp_aggr_buf(pmadapter); + } + memset(pmadapter, &mbuf, 0, sizeof(mlan_buffer)); + mbuf.pbuf = pmadapter->rx_buf; + mbuf.data_len = rx_len; + + PRINTM(MDATA, "Recovery: Try read port=%d rx_len=%d\n", port, rx_len); + if (MLAN_STATUS_SUCCESS != wlan_sdio_card_to_host(pmadapter, &pkt_type, + (t_u32 *)&pmadapter-> + upld_len, &mbuf, + rx_len, + pmadapter->ioport + + port)) { + PRINTM(MERROR, "Recovery: Fail to do cmd53\n"); + } + if (pkt_type != MLAN_TYPE_DATA && pkt_type != MLAN_TYPE_SPA_DATA) { + PRINTM(MERROR, + "Recovery: Receive a wrong pkt: type=%d, len=%d\n", + pkt_type, pmadapter->upld_len); + goto done; + } + if (pkt_type == MLAN_TYPE_DATA) { + //TODO fill the hole in Rx reorder table + PRINTM(MDATA, "Recovery: Drop Data packet\n"); + pmadapter->dbg.num_pkt_dropped++; + } else if (pkt_type == MLAN_TYPE_SPA_DATA) { + PRINTM(MDATA, "Recovery: SPA Data packet len=%d\n", + pmadapter->upld_len); + wlan_decode_spa_buffer(pmadapter, pmadapter->rx_buf, + pmadapter->upld_len); + pmadapter->data_received = MTRUE; + } + PRINTM(MMSG, "wlan: Success handle rx port=%d, rx_len=%d \n", port, + rx_len); + ret = MLAN_STATUS_SUCCESS; +done: + LEAVE(); + return ret; +} +#endif + +/** + * @brief This function checks the interrupt status and handle it accordingly. + * + * @param pmadapter A pointer to mlan_adapter structure + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_process_int_status(mlan_adapter *pmadapter) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_callbacks pcb = &pmadapter->callbacks; + t_u8 sdio_ireg; + mlan_buffer *pmbuf = MNULL; + + t_u8 port = 0; + t_u32 len_reg_l, len_reg_u; + t_u32 rx_blocks; + t_u8 bit_count = 0; + t_u32 ps_state = pmadapter->ps_state; + t_u16 rx_len; + t_u32 upld_typ = 0; + t_u32 cr = 0; + t_u8 rd_len_p0_l = RD_LEN_P0_L; + t_u8 rd_len_p0_u = RD_LEN_P0_U; + t_u8 cmd_rd_len_0 = CMD_RD_LEN_0; + t_u8 cmd_rd_len_1 = CMD_RD_LEN_1; + + ENTER(); + + pcb->moal_spin_lock(pmadapter->pmoal_handle, pmadapter->pint_lock); + sdio_ireg = pmadapter->sdio_ireg; + pmadapter->sdio_ireg = 0; + pcb->moal_spin_unlock(pmadapter->pmoal_handle, pmadapter->pint_lock); + + if (!sdio_ireg) + goto done; + /* check the command port */ + if (sdio_ireg & DN_LD_CMD_PORT_HOST_INT_STATUS) { + if (pmadapter->cmd_sent) + pmadapter->cmd_sent = MFALSE; + PRINTM(MINFO, "cmd_sent=%d\n", pmadapter->cmd_sent); + } + + if (sdio_ireg & UP_LD_CMD_PORT_HOST_INT_STATUS) { + /* read the len of control packet */ + rx_len = ((t_u16)pmadapter->mp_regs[cmd_rd_len_1]) << 8; + rx_len |= (t_u16)pmadapter->mp_regs[cmd_rd_len_0]; + PRINTM(MINFO, "RX: cmd port rx_len=%u\n", rx_len); + rx_blocks = + (rx_len + MLAN_SDIO_BLOCK_SIZE - + 1) / MLAN_SDIO_BLOCK_SIZE; + if (rx_len <= INTF_HEADER_LEN || + (rx_blocks * MLAN_SDIO_BLOCK_SIZE) > ALLOC_BUF_SIZE) { + PRINTM(MERROR, "invalid rx_len=%d\n", rx_len); + ret = MLAN_STATUS_FAILURE; + goto done; + } + rx_len = (t_u16)(rx_blocks * MLAN_SDIO_BLOCK_SIZE); + pmbuf = wlan_alloc_mlan_buffer(pmadapter, rx_len, 0, + MOAL_MALLOC_BUFFER); + if (pmbuf == MNULL) { + PRINTM(MERROR, "Failed to allocate 'mlan_buffer'\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + PRINTM(MINFO, "cmd rx buffer rx_len = %d\n", rx_len); + + /* Transfer data from card */ + if (MLAN_STATUS_SUCCESS != + wlan_sdio_card_to_host(pmadapter, &upld_typ, + (t_u32 *)&pmadapter->upld_len, pmbuf, + rx_len, + pmadapter->ioport | CMD_PORT_SLCT)) { + pmadapter->dbg.num_cmdevt_card_to_host_failure++; + PRINTM(MERROR, + "Card-to-host cmd failed: int status=0x%x\n", + sdio_ireg); + wlan_free_mlan_buffer(pmadapter, pmbuf); + ret = MLAN_STATUS_FAILURE; + goto term_cmd53; + } + + if ((upld_typ != MLAN_TYPE_CMD) && + (upld_typ != MLAN_TYPE_EVENT)) + PRINTM(MERROR, + "receive a wrong packet from CMD PORT. type =0x%d\n", + upld_typ); + + wlan_decode_rx_packet(pmadapter, pmbuf, upld_typ, MFALSE); + + /* We might receive data/sleep_cfm at the same time */ + /* reset data_receive flag to avoid ps_state change */ + if ((ps_state == PS_STATE_SLEEP_CFM) && + (pmadapter->ps_state == PS_STATE_SLEEP)) + pmadapter->data_received = MFALSE; + } + + if (sdio_ireg & DN_LD_HOST_INT_STATUS) { + if (pmadapter->mp_wr_bitmap & pmadapter->mp_data_port_mask) + pmadapter->mp_invalid_update++; + pmadapter->mp_wr_bitmap = + (t_u32)pmadapter->mp_regs[WR_BITMAP_L]; + pmadapter->mp_wr_bitmap |= + ((t_u32)pmadapter->mp_regs[WR_BITMAP_U]) << 8; + pmadapter->mp_wr_bitmap |= + ((t_u32)pmadapter->mp_regs[WR_BITMAP_1L]) << 16; + pmadapter->mp_wr_bitmap |= + ((t_u32)pmadapter->mp_regs[WR_BITMAP_1U]) << 24; + bit_count = + bitcount(pmadapter->mp_wr_bitmap & pmadapter-> + mp_data_port_mask); + if (bit_count) { + pmadapter->mp_update[bit_count - 1]++; + if (pmadapter->mp_update[bit_count - 1] == 0xffffffff) + memset(pmadapter, pmadapter->mp_update, 0, + sizeof(pmadapter->mp_update)); + } + + pmadapter->last_recv_wr_bitmap = pmadapter->mp_wr_bitmap; + PRINTM(MINTR, "DNLD: wr_bitmap=0x%08x\n", + pmadapter->mp_wr_bitmap); + if (pmadapter->data_sent && + (pmadapter-> + mp_wr_bitmap & (1 << pmadapter->curr_wr_port))) { + PRINTM(MINFO, " <--- Tx DONE Interrupt --->\n"); + pmadapter->data_sent = MFALSE; + } + } + + if (sdio_ireg & UP_LD_HOST_INT_STATUS) { + pmadapter->mp_rd_bitmap = + (t_u32)pmadapter->mp_regs[RD_BITMAP_L]; + pmadapter->mp_rd_bitmap |= + ((t_u32)pmadapter->mp_regs[RD_BITMAP_U]) << 8; + pmadapter->mp_rd_bitmap |= + ((t_u32)pmadapter->mp_regs[RD_BITMAP_1L]) << 16; + pmadapter->mp_rd_bitmap |= + ((t_u32)pmadapter->mp_regs[RD_BITMAP_1U]) << 24; + PRINTM(MINTR, "UPLD: rd_bitmap=0x%08x\n", + pmadapter->mp_rd_bitmap); + + while (MTRUE) { + ret = wlan_get_rd_port(pmadapter, &port); + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MINFO, + "no more rd_port to be handled\n"); + break; + } + len_reg_l = rd_len_p0_l + (port << 1); + len_reg_u = rd_len_p0_u + (port << 1); + rx_len = ((t_u16)pmadapter->mp_regs[len_reg_u]) << 8; + rx_len |= (t_u16)pmadapter->mp_regs[len_reg_l]; + PRINTM(MINFO, "RX: port=%d rx_len=%u\n", port, rx_len); + rx_blocks = + (rx_len + MLAN_SDIO_BLOCK_SIZE - + 1) / MLAN_SDIO_BLOCK_SIZE; +#if defined(SDIO_MULTI_PORT_RX_AGGR) + if (rx_len <= INTF_HEADER_LEN || + (rx_blocks * MLAN_SDIO_BLOCK_SIZE) > + pmadapter->mpa_rx.buf_size) { +#else + if (rx_len <= INTF_HEADER_LEN || + (rx_blocks * MLAN_SDIO_BLOCK_SIZE) > + ALLOC_BUF_SIZE) { +#endif + PRINTM(MERROR, "invalid rx_len=%d\n", rx_len); + ret = MLAN_STATUS_FAILURE; + goto done; + } + rx_len = (t_u16)(rx_blocks * MLAN_SDIO_BLOCK_SIZE); + if (rx_len > MLAN_TX_DATA_BUF_SIZE_2K + && !pmadapter->enable_net_mon) + pmbuf = wlan_alloc_mlan_buffer(pmadapter, + rx_len, 0, + MOAL_MALLOC_BUFFER); + else + pmbuf = wlan_alloc_mlan_buffer(pmadapter, + rx_len, + MLAN_RX_HEADER_LEN, + MOAL_ALLOC_MLAN_BUFFER); + if (pmbuf == MNULL) { + PRINTM(MERROR, + "Failed to allocate 'mlan_buffer'\n"); + pmadapter->dbg.num_alloc_buffer_failure++; +#if defined(SDIO_MULTI_PORT_RX_AGGR) + if (MLAN_STATUS_SUCCESS == + wlan_sdio_card_to_host_recovery(pmadapter, + port, + rx_len)) + continue; +#endif + ret = MLAN_STATUS_FAILURE; + goto done; + } + PRINTM(MINFO, "rx_len = %d\n", rx_len); +#ifdef SDIO_MULTI_PORT_RX_AGGR + if (MLAN_STATUS_SUCCESS != + wlan_sdio_card_to_host_mp_aggr(pmadapter, pmbuf, + port, rx_len)) { +#else + /* Transfer data from card */ + if (MLAN_STATUS_SUCCESS != + wlan_sdio_card_to_host(pmadapter, &upld_typ, + (t_u32 *)&pmadapter-> + upld_len, pmbuf, rx_len, + pmadapter->ioport + port)) { +#endif /* SDIO_MULTI_PORT_RX_AGGR */ + + pmadapter->dbg.num_rx_card_to_host_failure++; + + PRINTM(MERROR, + "Card to host failed: int status=0x%x\n", + sdio_ireg); +#ifndef SDIO_MULTI_PORT_RX_AGGR + wlan_free_mlan_buffer(pmadapter, pmbuf); +#endif + ret = MLAN_STATUS_FAILURE; + goto term_cmd53; + } +#ifndef SDIO_MULTI_PORT_RX_AGGR + wlan_decode_rx_packet(pmadapter, pmbuf, upld_typ, + MTRUE); +#endif + } + /* We might receive data/sleep_cfm at the same time */ + /* reset data_receive flag to avoid ps_state change */ + if ((ps_state == PS_STATE_SLEEP_CFM) && + (pmadapter->ps_state == PS_STATE_SLEEP)) + pmadapter->data_received = MFALSE; + } + + ret = MLAN_STATUS_SUCCESS; + goto done; + +term_cmd53: + /* terminate cmd53 */ + if (MLAN_STATUS_SUCCESS != pcb->moal_read_reg(pmadapter->pmoal_handle, + HOST_TO_CARD_EVENT_REG, + &cr)) + PRINTM(MERROR, "read CFG reg failed\n"); + PRINTM(MINFO, "Config Reg val = %d\n", cr); + if (MLAN_STATUS_SUCCESS != pcb->moal_write_reg(pmadapter->pmoal_handle, + HOST_TO_CARD_EVENT_REG, + (cr | HOST_TERM_CMD53))) + PRINTM(MERROR, "write CFG reg failed\n"); + PRINTM(MINFO, "write success\n"); + if (MLAN_STATUS_SUCCESS != pcb->moal_read_reg(pmadapter->pmoal_handle, + HOST_TO_CARD_EVENT_REG, + &cr)) + PRINTM(MERROR, "read CFG reg failed\n"); + PRINTM(MINFO, "Config reg val =%x\n", cr); + +done: + LEAVE(); + return ret; +} + +/** + * @brief This function sends data to the card. + * + * @param pmadapter A pointer to mlan_adapter structure + * @param type data or command + * @param pmbuf A pointer to mlan_buffer (pmbuf->data_len should include SDIO header) + * @param tx_param A pointer to mlan_tx_param + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_sdio_host_to_card(mlan_adapter *pmadapter, t_u8 type, mlan_buffer *pmbuf, + mlan_tx_param *tx_param) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u32 buf_block_len; + t_u32 blksz; + t_u8 port = 0; + t_u32 cmd53_port = 0; + t_u8 *payload = pmbuf->pbuf + pmbuf->data_offset; + + ENTER(); + + /* Allocate buffer and copy payload */ + blksz = MLAN_SDIO_BLOCK_SIZE; + buf_block_len = (pmbuf->data_len + blksz - 1) / blksz; + *(t_u16 *)&payload[0] = wlan_cpu_to_le16((t_u16)pmbuf->data_len); + *(t_u16 *)&payload[2] = wlan_cpu_to_le16(type); + + /* + * This is SDIO specific header + * t_u16 length, + * t_u16 type (MLAN_TYPE_DATA = 0, + * MLAN_TYPE_CMD = 1, MLAN_TYPE_EVENT = 3) + */ + if (type == MLAN_TYPE_DATA) { + ret = wlan_get_wr_port_data(pmadapter, &port); + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, + "no wr_port available: wr_bitmap=0x%08x curr_wr_port=%d\n", + pmadapter->mp_wr_bitmap, + pmadapter->curr_wr_port); + goto exit; + } + /* Transfer data to card */ + pmbuf->data_len = buf_block_len * blksz; + +#ifdef SDIO_MULTI_PORT_TX_AGGR + if (tx_param) + ret = wlan_host_to_card_mp_aggr(pmadapter, pmbuf, port, + tx_param->next_pkt_len); + else + ret = wlan_host_to_card_mp_aggr(pmadapter, pmbuf, port, + 0); +#else + ret = wlan_write_data_sync(pmadapter, pmbuf, + pmadapter->ioport + port); +#endif /* SDIO_MULTI_PORT_TX_AGGR */ + + } else { + /*Type must be MLAN_TYPE_CMD */ + pmadapter->cmd_sent = MTRUE; + if (pmbuf->data_len <= INTF_HEADER_LEN || + pmbuf->data_len > WLAN_UPLD_SIZE) + PRINTM(MWARN, + "wlan_sdio_host_to_card(): Error: payload=%p, nb=%d\n", + payload, pmbuf->data_len); + /* Transfer data to card */ + pmbuf->data_len = buf_block_len * blksz; + cmd53_port = (pmadapter->ioport) | CMD_PORT_SLCT; + ret = wlan_write_data_sync(pmadapter, pmbuf, cmd53_port); + } + + if (ret == MLAN_STATUS_FAILURE) { + if (type == MLAN_TYPE_CMD) + pmadapter->cmd_sent = MFALSE; + if (type == MLAN_TYPE_DATA) + pmadapter->data_sent = MFALSE; + } else { + if (type == MLAN_TYPE_DATA) { + if (! + (pmadapter-> + mp_wr_bitmap & (1 << pmadapter->curr_wr_port))) + pmadapter->data_sent = MTRUE; + else + pmadapter->data_sent = MFALSE; + } + DBG_HEXDUMP(MIF_D, "SDIO Blk Wr", + pmbuf->pbuf + pmbuf->data_offset, + MIN(pmbuf->data_len, MAX_DATA_DUMP_LEN)); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief Deaggregate single port aggregation packet + * + * @param pmadapter A pointer to mlan_adapter structure + * @param buf A pointer to aggregated data packet + * @param len + * + * @return N/A + */ +void +wlan_decode_spa_buffer(mlan_adapter *pmadapter, t_u8 *buf, t_u32 len) +{ + int total_pkt_len; + t_u8 block_num = 0; + t_u16 block_size = 0; + t_u8 *data; + t_u32 pkt_len, pkt_type = 0; + mlan_buffer *mbuf_deaggr = MNULL; + + ENTER(); + + data = (t_u8 *)buf; + total_pkt_len = len; + if (total_pkt_len < pmadapter->sdio_rx_block_size) { + PRINTM(MERROR, "Invalid sp aggr packet size=%d\n", + total_pkt_len); + goto done; + } + while (total_pkt_len >= (OFFSET_OF_SDIO_HEADER + INTF_HEADER_LEN)) { + block_num = *(data + OFFSET_OF_BLOCK_NUMBER); + block_size = pmadapter->sdio_rx_block_size * block_num; + if (block_size > total_pkt_len) { + PRINTM(MERROR, + "Error in pkt, block_num=%d, pkt_len=%d\n", + block_num, total_pkt_len); + break; + } + pkt_len = + wlan_le16_to_cpu(*(t_u16 *) + (data + OFFSET_OF_SDIO_HEADER)); + pkt_type = + wlan_le16_to_cpu(*(t_u16 *) + (data + OFFSET_OF_SDIO_HEADER + 2)); + if ((pkt_len + OFFSET_OF_SDIO_HEADER) > block_size) { + PRINTM(MERROR, + "Error in pkt, pkt_len=%d, block_size=%d\n", + pkt_len, block_size); + break; + } + mbuf_deaggr = + wlan_alloc_mlan_buffer(pmadapter, + pkt_len - INTF_HEADER_LEN, + MLAN_RX_HEADER_LEN, + MOAL_ALLOC_MLAN_BUFFER); + if (mbuf_deaggr == MNULL) { + PRINTM(MERROR, "Error allocating daggr mlan_buffer\n"); + break; + } + memcpy(pmadapter, mbuf_deaggr->pbuf + mbuf_deaggr->data_offset, + data + OFFSET_OF_SDIO_HEADER + INTF_HEADER_LEN, + pkt_len - INTF_HEADER_LEN); + mbuf_deaggr->data_len = pkt_len - INTF_HEADER_LEN; + wlan_handle_rx_packet(pmadapter, mbuf_deaggr); + data += block_size; + total_pkt_len -= block_size; + if (total_pkt_len < pmadapter->sdio_rx_block_size) + break; + } +done: + LEAVE(); + return; +} + +/** + * @brief This function deaggr rx pkt + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pmbuf A pointer to the SDIO mpa data + * @return N/A + */ +t_void +wlan_sdio_deaggr_rx_pkt(IN pmlan_adapter pmadapter, mlan_buffer *pmbuf) +{ + if (pmbuf->buf_type == MLAN_BUF_TYPE_SPA_DATA) { + wlan_decode_spa_buffer(pmadapter, + pmbuf->pbuf + pmbuf->data_offset, + pmbuf->data_len); + wlan_free_mlan_buffer(pmadapter, pmbuf); + } else + wlan_handle_rx_packet(pmadapter, pmbuf); +} + +#if defined(SDIO_MULTI_PORT_TX_AGGR) || defined(SDIO_MULTI_PORT_RX_AGGR) +/** + * @brief This function allocates buffer for the SDIO aggregation buffer + * related members of adapter structure + * + * @param pmadapter A pointer to mlan_adapter structure + * @param mpa_tx_buf_size Tx buffer size to allocate + * @param mpa_rx_buf_size Rx buffer size to allocate + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_alloc_sdio_mpa_buffers(IN mlan_adapter *pmadapter, + t_u32 mpa_tx_buf_size, t_u32 mpa_rx_buf_size) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_callbacks pcb = &pmadapter->callbacks; + t_u8 mp_aggr_pkt_limit = SDIO_MP_AGGR_DEF_PKT_LIMIT; + + ENTER(); + +#ifdef SDIO_MULTI_PORT_TX_AGGR + if ((pmadapter->max_segs < mp_aggr_pkt_limit) || + (pmadapter->max_seg_size < pmadapter->max_sp_tx_size)) { + ret = pcb->moal_malloc(pmadapter->pmoal_handle, + mpa_tx_buf_size + DMA_ALIGNMENT, + MLAN_MEM_DEF | MLAN_MEM_DMA, + (t_u8 **)&pmadapter->mpa_tx.head_ptr); + if (ret != MLAN_STATUS_SUCCESS || !pmadapter->mpa_tx.head_ptr) { + PRINTM(MERROR, + "Could not allocate buffer for SDIO MP TX aggr\n"); + ret = MLAN_STATUS_FAILURE; + goto error; + } + pmadapter->mpa_tx.buf = + (t_u8 *)ALIGN_ADDR(pmadapter->mpa_tx.head_ptr, + DMA_ALIGNMENT); + } else { + PRINTM(MMSG, "wlan: Enable TX SG mode\n"); + pmadapter->mpa_tx.head_ptr = MNULL; + pmadapter->mpa_tx.buf = MNULL; + } + pmadapter->mpa_tx.buf_size = mpa_tx_buf_size; +#endif /* SDIO_MULTI_PORT_TX_AGGR */ + +#ifdef SDIO_MULTI_PORT_RX_AGGR + if ((pmadapter->max_segs < mp_aggr_pkt_limit) || + (pmadapter->max_seg_size < pmadapter->max_sp_rx_size)) { + ret = pcb->moal_malloc(pmadapter->pmoal_handle, + mpa_rx_buf_size + DMA_ALIGNMENT, + MLAN_MEM_DEF | MLAN_MEM_DMA, + (t_u8 **)&pmadapter->mpa_rx.head_ptr); + if (ret != MLAN_STATUS_SUCCESS || !pmadapter->mpa_rx.head_ptr) { + PRINTM(MERROR, + "Could not allocate buffer for SDIO MP RX aggr\n"); + ret = MLAN_STATUS_FAILURE; + goto error; + } + pmadapter->mpa_rx.buf = + (t_u8 *)ALIGN_ADDR(pmadapter->mpa_rx.head_ptr, + DMA_ALIGNMENT); + } else { + PRINTM(MMSG, "wlan: Enable RX SG mode\n"); + pmadapter->mpa_rx.head_ptr = MNULL; + pmadapter->mpa_rx.buf = MNULL; + } + pmadapter->mpa_rx.buf_size = mpa_rx_buf_size; +#endif /* SDIO_MULTI_PORT_RX_AGGR */ +error: + if (ret != MLAN_STATUS_SUCCESS) + wlan_free_sdio_mpa_buffers(pmadapter); + + LEAVE(); + return ret; +} + +/** + * @brief This function frees buffers for the SDIO aggregation + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_free_sdio_mpa_buffers(IN mlan_adapter *pmadapter) +{ + pmlan_callbacks pcb = &pmadapter->callbacks; + + ENTER(); + +#ifdef SDIO_MULTI_PORT_TX_AGGR + if (pmadapter->mpa_tx.buf) { + pcb->moal_mfree(pmadapter->pmoal_handle, + (t_u8 *)pmadapter->mpa_tx.head_ptr); + pmadapter->mpa_tx.head_ptr = MNULL; + pmadapter->mpa_tx.buf = MNULL; + pmadapter->mpa_tx.buf_size = 0; + } +#endif /* SDIO_MULTI_PORT_TX_AGGR */ + +#ifdef SDIO_MULTI_PORT_RX_AGGR + if (pmadapter->mpa_rx.buf) { + pcb->moal_mfree(pmadapter->pmoal_handle, + (t_u8 *)pmadapter->mpa_rx.head_ptr); + pmadapter->mpa_rx.head_ptr = MNULL; + pmadapter->mpa_rx.buf = MNULL; + pmadapter->mpa_rx.buf_size = 0; + } +#endif /* SDIO_MULTI_PORT_RX_AGGR */ + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +#if defined(SDIO_MULTI_PORT_RX_AGGR) +/** + * @brief This function re-allocate rx mpa buffer + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_re_alloc_sdio_rx_mpa_buffer(IN mlan_adapter *pmadapter) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_callbacks pcb = &pmadapter->callbacks; + t_u8 mp_aggr_pkt_limit = SDIO_MP_AGGR_DEF_PKT_LIMIT; + + t_u32 mpa_rx_buf_size = SDIO_MP_RX_AGGR_DEF_BUF_SIZE; + +#ifdef SDIO_MULTI_PORT_RX_AGGR + if (pmadapter->mpa_rx.buf) { + pcb->moal_mfree(pmadapter->pmoal_handle, + (t_u8 *)pmadapter->mpa_rx.head_ptr); + pmadapter->mpa_rx.head_ptr = MNULL; + pmadapter->mpa_rx.buf = MNULL; + pmadapter->mpa_rx.buf_size = 0; + } +#endif /* SDIO_MULTI_PORT_RX_AGGR */ + if (pmadapter->sdio_rx_aggr_enable) + mpa_rx_buf_size = MAX(mpa_rx_buf_size, SDIO_CMD53_MAX_SIZE); + if ((pmadapter->max_segs < mp_aggr_pkt_limit) || + (pmadapter->max_seg_size < pmadapter->max_sp_rx_size)) { + ret = pcb->moal_malloc(pmadapter->pmoal_handle, + mpa_rx_buf_size + DMA_ALIGNMENT, + MLAN_MEM_DEF | MLAN_MEM_DMA, + (t_u8 **)&pmadapter->mpa_rx.head_ptr); + if (ret != MLAN_STATUS_SUCCESS || !pmadapter->mpa_rx.head_ptr) { + PRINTM(MERROR, + "Could not allocate buffer for SDIO MP RX aggr\n"); + ret = MLAN_STATUS_FAILURE; + goto error; + } + pmadapter->mpa_rx.buf = + (t_u8 *)ALIGN_ADDR(pmadapter->mpa_rx.head_ptr, + DMA_ALIGNMENT); + } else { + PRINTM(MMSG, "wlan: Enable RX SG mode\n"); + pmadapter->mpa_rx.head_ptr = MNULL; + pmadapter->mpa_rx.buf = MNULL; + } + pmadapter->mpa_rx.buf_size = mpa_rx_buf_size; + PRINTM(MMSG, "mpa_rx_buf_size=%d\n", mpa_rx_buf_size); +error: + return ret; +} +#endif + +#endif /* SDIO_MULTI_PORT_TX_AGGR || SDIO_MULTI_PORT_RX_AGGR */ + +/** + * @brief This function issues commands to initialize firmware + * + * @param priv A pointer to mlan_private structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_set_sdio_gpio_int(IN pmlan_private priv) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_adapter pmadapter = MNULL; + HostCmd_DS_SDIO_GPIO_INT_CONFIG sdio_int_cfg; + + if (!priv) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + pmadapter = priv->adapter; + + ENTER(); + + if (pmadapter->int_mode == INT_MODE_GPIO) { + if (pmadapter->gpio_pin != GPIO_INT_NEW_MODE) { + PRINTM(MINFO, + "SDIO_GPIO_INT_CONFIG: interrupt mode is GPIO\n"); + sdio_int_cfg.action = HostCmd_ACT_GEN_SET; + sdio_int_cfg.gpio_pin = pmadapter->gpio_pin; + sdio_int_cfg.gpio_int_edge = INT_FALLING_EDGE; + sdio_int_cfg.gpio_pulse_width = DELAY_1_US; + ret = wlan_prepare_cmd(priv, + HostCmd_CMD_SDIO_GPIO_INT_CONFIG, + HostCmd_ACT_GEN_SET, 0, MNULL, + &sdio_int_cfg); + + if (ret) { + PRINTM(MERROR, + "SDIO_GPIO_INT_CONFIG: send command fail\n"); + ret = MLAN_STATUS_FAILURE; + } + } + } else { + PRINTM(MINFO, "SDIO_GPIO_INT_CONFIG: interrupt mode is SDIO\n"); + } + + LEAVE(); + return ret; +} + +/** + * @brief This function prepares command of SDIO GPIO interrupt + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action The action: GET or SET + * @param pdata_buf A pointer to data buffer + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_cmd_sdio_gpio_int(pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, IN t_void *pdata_buf) +{ + HostCmd_DS_SDIO_GPIO_INT_CONFIG *psdio_gpio_int = + &cmd->params.sdio_gpio_int; + + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_SDIO_GPIO_INT_CONFIG); + cmd->size = + wlan_cpu_to_le16((sizeof(HostCmd_DS_SDIO_GPIO_INT_CONFIG)) + + S_DS_GEN); + + memset(pmpriv->adapter, psdio_gpio_int, 0, + sizeof(HostCmd_DS_SDIO_GPIO_INT_CONFIG)); + if (cmd_action == HostCmd_ACT_GEN_SET) { + memcpy(pmpriv->adapter, psdio_gpio_int, pdata_buf, + sizeof(HostCmd_DS_SDIO_GPIO_INT_CONFIG)); + psdio_gpio_int->action = + wlan_cpu_to_le16(psdio_gpio_int->action); + psdio_gpio_int->gpio_pin = + wlan_cpu_to_le16(psdio_gpio_int->gpio_pin); + psdio_gpio_int->gpio_int_edge = + wlan_cpu_to_le16(psdio_gpio_int->gpio_int_edge); + psdio_gpio_int->gpio_pulse_width = + wlan_cpu_to_le16(psdio_gpio_int->gpio_pulse_width); + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +#define FW_RESET_REG 0x0EE +#define FW_RESET_VAL 0x99 +mlan_status +wlan_reset_fw(pmlan_adapter pmadapter) +{ + t_u32 tries = 0; + t_u32 value = 1; + t_u32 reset_reg = FW_RESET_REG; + t_u8 reset_val = FW_RESET_VAL; + pmlan_callbacks pcb = &pmadapter->callbacks; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + wlan_pm_wakeup_card(pmadapter); + + /** wait SOC fully wake up */ + for (tries = 0; tries < MAX_POLL_TRIES; ++tries) { + if (MLAN_STATUS_SUCCESS == + pcb->moal_write_reg(pmadapter->pmoal_handle, reset_reg, + 0xba)) { + pcb->moal_read_reg(pmadapter->pmoal_handle, reset_reg, + &value); + if (value == 0xba) { + PRINTM(MMSG, "FW wake up\n"); + break; + } + } + pcb->moal_udelay(pmadapter->pmoal_handle, 1000); + } + /* Write register to notify FW */ + if (MLAN_STATUS_FAILURE == + pcb->moal_write_reg(pmadapter->pmoal_handle, reset_reg, + reset_val)) { + PRINTM(MERROR, "Failed to write register.\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + pcb->moal_read_reg(pmadapter->pmoal_handle, HOST_TO_CARD_EVENT_REG, + &value); + pcb->moal_write_reg(pmadapter->pmoal_handle, HOST_TO_CARD_EVENT_REG, + value | HOST_POWER_UP); + /* Poll register around 100 ms */ + for (tries = 0; tries < MAX_POLL_TRIES; ++tries) { + pcb->moal_read_reg(pmadapter->pmoal_handle, reset_reg, &value); + if (value == 0) + /* FW is ready */ + break; + pcb->moal_udelay(pmadapter->pmoal_handle, 1000); + } + + if (value) { + PRINTM(MERROR, "Failed to poll FW reset register %X=0x%x\n", + reset_reg, value); + ret = MLAN_STATUS_FAILURE; + goto done; + } + PRINTM(MMSG, "FW Reset success\n"); + ret = wlan_sdio_probe(pmadapter); +done: + LEAVE(); + return ret; +} diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_sdio.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_sdio.h new file mode 100644 index 000000000000..0280024ac9ee --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_sdio.h @@ -0,0 +1,436 @@ +/** @file mlan_sdio.h + * + * @brief This file contains definitions for SDIO interface. + * driver. + * + * Copyright (C) 2008-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ +/**************************************************** +Change log: +****************************************************/ + +#ifndef _MLAN_SDIO_H +#define _MLAN_SDIO_H + +/** Block mode */ +#ifndef BLOCK_MODE +#define BLOCK_MODE 1 +#endif + +/** Fixed address mode */ +#ifndef FIXED_ADDRESS +#define FIXED_ADDRESS 0 +#endif + +/* Host Control Registers */ +/** Host Control Registers : Host to Card Event */ +#define HOST_TO_CARD_EVENT_REG 0x00 +/** Host Control Registers : Host terminates Command 53 */ +#define HOST_TERM_CMD53 (0x1U << 2) +/** Host Control Registers : Host without Command 53 finish host */ +#define HOST_WO_CMD53_FINISH_HOST (0x1U << 2) +/** Host Control Registers : Host power up */ +#define HOST_POWER_UP (0x1U << 1) +/** Host Control Registers : Host power down */ +#define HOST_POWER_DOWN (0x1U << 0) + +/** Host Control Registers : Host interrupt RSR */ +#define HOST_INT_RSR_REG 0x04 + +/** Host Control Registers : Upload host interrupt RSR */ +#define UP_LD_HOST_INT_RSR (0x1U) +#define HOST_INT_RSR_MASK 0xFF + +/** Host Control Registers : Host interrupt mask */ +#define HOST_INT_MASK_REG 0x08 + +/** Host Control Registers : Upload host interrupt mask */ +#define UP_LD_HOST_INT_MASK (0x1U) +/** Host Control Registers : Download host interrupt mask */ +#define DN_LD_HOST_INT_MASK (0x2U) +/** Host Control Registers : Cmd port upload interrupt mask */ +#define CMD_PORT_UPLD_INT_MASK (0x1U << 6) +/** Host Control Registers : Cmd port download interrupt mask */ +#define CMD_PORT_DNLD_INT_MASK (0x1U << 7) +/** Enable Host interrupt mask */ +#define HIM_ENABLE (UP_LD_HOST_INT_MASK | \ + DN_LD_HOST_INT_MASK | \ + CMD_PORT_UPLD_INT_MASK | \ + CMD_PORT_DNLD_INT_MASK) +/** Disable Host interrupt mask */ +#define HIM_DISABLE 0xff + +/** Host Control Registers : Host interrupt status */ +#define HOST_INT_STATUS_REG 0x0C + +/** Host Control Registers : Upload command port host interrupt status */ +#define UP_LD_CMD_PORT_HOST_INT_STATUS (0x40U) +/** Host Control Registers : Download command port host interrupt status */ +#define DN_LD_CMD_PORT_HOST_INT_STATUS (0x80U) + +/** Host Control Registers : Upload host interrupt status */ +#define UP_LD_HOST_INT_STATUS (0x1U) +/** Host Control Registers : Download host interrupt status */ +#define DN_LD_HOST_INT_STATUS (0x2U) + +/** Port for registers */ +#define REG_PORT 0 +/** Port for memory */ +#define MEM_PORT 0x10000 +/** LSB of read bitmap */ +#define RD_BITMAP_L 0x10 +/** MSB of read bitmap */ +#define RD_BITMAP_U 0x11 +/** LSB of read bitmap second word */ +#define RD_BITMAP_1L 0x12 +/** MSB of read bitmap second word */ +#define RD_BITMAP_1U 0x13 +/** LSB of write bitmap */ +#define WR_BITMAP_L 0x14 +/** MSB of write bitmap */ +#define WR_BITMAP_U 0x15 +/** LSB of write bitmap second word */ +#define WR_BITMAP_1L 0x16 +/** MSB of write bitmap second word */ +#define WR_BITMAP_1U 0x17 +/** LSB of read length for port 0 */ +#define RD_LEN_P0_L 0x18 +/** MSB of read length for port 0 */ +#define RD_LEN_P0_U 0x19 + +/* Card Control Registers : Command port read length 0 */ +#define CMD_RD_LEN_0 0xC0 +/* Card Control Registers : Command port read length 1 */ +#define CMD_RD_LEN_1 0xC1 +/* Card Control Registers : Command port read length 2 (reserved) */ +#define CMD_RD_LEN_2 0xC2 +/* Card Control Registers : Command port read length 3 */ +#define CMD_RD_LEN_3 0xC3 +/* Card Control Registers : Command port configuration 0 */ +#define CMD_CONFIG_0 0xC4 +#define CMD_PORT_RD_LEN_EN (0x1U << 2) +/* Card Control Registers : Command port configuration 1 */ +#define CMD_CONFIG_1 0xC5 +/* Card Control Registers : cmd port auto enable */ +#define CMD_PORT_AUTO_EN (0x1U << 0) +/* Card Control Registers : Command port configuration 2 (reserved) */ +#define CMD_CONFIG_2 0xC6 +/* Card Control Registers : Command port configuration 3 (reserved) */ +#define CMD_CONFIG_3 0xC7 + +/* Command port */ +#define CMD_PORT_SLCT 0x8000 +/** Data port mask */ +#define DATA_PORT_MASK 0xffffffff + +/** Misc. Config Register : Auto Re-enable interrupts */ +#define AUTO_RE_ENABLE_INT MBIT(4) + +/** Host Control Registers : Host transfer status */ +#define HOST_RESTART_REG 0x58 +/** Host Control Registers : Download CRC error */ +#define DN_LD_CRC_ERR (0x1U << 2) +/** Host Control Registers : Upload restart */ +#define UP_LD_RESTART (0x1U << 1) +/** Host Control Registers : Download restart */ +#define DN_LD_RESTART (0x1U << 0) + +/* Card Control Registers */ +/** Card Control Registers : Card to host event */ +#define CARD_TO_HOST_EVENT_REG 0x5C +/** Card Control Registers : Command port upload ready */ +#define UP_LD_CP_RDY (0x1U << 6) +/** Card Control Registers : Command port download ready */ +#define DN_LD_CP_RDY (0x1U << 7) +/** Card Control Registers : Card I/O ready */ +#define CARD_IO_READY (0x1U << 3) +/** Card Control Registers : CIS card ready */ +#define CIS_CARD_RDY (0x1U << 2) +/** Card Control Registers : Upload card ready */ +#define UP_LD_CARD_RDY (0x1U << 1) +/** Card Control Registers : Download card ready */ +#define DN_LD_CARD_RDY (0x1U << 0) + +/** Card Control Registers : Host interrupt mask register */ +#define HOST_INTERRUPT_MASK_REG 0x60 +/** Card Control Registers : Host power interrupt mask */ +#define HOST_POWER_INT_MASK (0x1U << 3) +/** Card Control Registers : Abort card interrupt mask */ +#define ABORT_CARD_INT_MASK (0x1U << 2) +/** Card Control Registers : Upload card interrupt mask */ +#define UP_LD_CARD_INT_MASK (0x1U << 1) +/** Card Control Registers : Download card interrupt mask */ +#define DN_LD_CARD_INT_MASK (0x1U << 0) + +/** Card Control Registers : Card interrupt status register */ +#define CARD_INTERRUPT_STATUS_REG 0x64 +/** Card Control Registers : Power up interrupt */ +#define POWER_UP_INT (0x1U << 4) +/** Card Control Registers : Power down interrupt */ +#define POWER_DOWN_INT (0x1U << 3) + +/** Card Control Registers : Card interrupt RSR register */ +#define CARD_INTERRUPT_RSR_REG 0x68 +/** Card Control Registers : Power up RSR */ +#define POWER_UP_RSR (0x1U << 4) +/** Card Control Registers : Power down RSR */ +#define POWER_DOWN_RSR (0x1U << 3) + +/** Card Control Registers : SQ Read base address 0 register */ +#define READ_BASE_0_REG 0xf8 +/** Card Control Registers : SQ Read base address 1 register */ +#define READ_BASE_1_REG 0xf9 +/** Enable GPIO-1 as a duplicated signal of interrupt as appear of SDIO_DAT1*/ +#define ENABLE_GPIO_1_INT_MODE 0x88 +/** Scratch reg 3 2 : Configure GPIO-1 INT*/ +#define SCRATCH_REG_32 0xEE + +/** Card Control Registers : Card revision register */ +#define CARD_REVISION_REG 0xC8 + +/** Firmware status 0 register (SCRATCH0_0) */ +#define CARD_FW_STATUS0_REG 0xe8 +/** Firmware status 1 register (SCRATCH0_1) */ +#define CARD_FW_STATUS1_REG 0xe9 +/** Rx length register (SCRATCH0_2) */ +#define CARD_RX_LEN_REG 0xea +/** Rx unit register (SCRATCH0_3) */ +#define CARD_RX_UNIT_REG 0xeb + +/** Card Control Registers : Card OCR 0 register */ +#define CARD_OCR_0_REG 0xD4 +/** Card Control Registers : Card OCR 1 register */ +#define CARD_OCR_1_REG 0xD5 +/** Card Control Registers : Card OCR 3 register */ +#define CARD_OCR_3_REG 0xD6 +/** Card Control Registers : Card config register */ +#define CARD_CONFIG_REG 0xD7 +/** Card Control Registers : Miscellaneous Configuration Register */ +#define CARD_MISC_CFG_REG 0xD8 + +/** Card Control Registers : sdio new mode register 1 */ +#define CARD_CONFIG_2_1_REG 0xD9 +/** Card Control Registers : cmd53 new mode */ +#define CMD53_NEW_MODE (0x1U << 0) +/** Card Control Registers : cmd53 tx len format 1 (0x10) */ +#define CMD53_TX_LEN_FORMAT_1 (0x1U << 4) +/** Card Control Registers : cmd53 tx len format 2 (0x20)*/ +#define CMD53_TX_LEN_FORMAT_2 (0x1U << 5) +/** Card Control Registers : cmd53 rx len format 1 (0x40) */ +#define CMD53_RX_LEN_FORMAT_1 (0x1U << 6) +/** Card Control Registers : cmd53 rx len format 2 (0x80)*/ +#define CMD53_RX_LEN_FORMAT_2 (0x1U << 7) + +/** Card Control Registers : sdio new mode register 2 */ +#define CARD_CONFIG_2_2_REG 0xDA +/** Card Control Registers : test data out (0x01) */ +#define TEST_DATA_OUT_1 (0x1U << 0) +/** Card Control Registers : test data out (0x02) */ +#define TEST_DATA_OUT_2 (0x1U << 1) +/** Card Control Registers : test data out (0x04) */ +#define TEST_DATA_OUT_3 (0x1U << 2) +/** Card Control Registers : test data out (0x08) */ +#define TEST_DATA_OUT_4 (0x1U << 3) +/** Card Control Registers : test cmd out (0x10) */ +#define TEST_CMD_OUT (0x1U << 4) + +/** Card Control Registers : sdio new mode register 3 */ +#define CARD_CONFIG_2_3_REG 0xDB +/** Card Control Registers : test data enable (0x01) */ +#define TEST_DATA_EN_1 (0x1U << 0) +/** Card Control Registers : test data enable (0x02) */ +#define TEST_DATA_EN_2 (0x1U << 1) +/** Card Control Registers : test data enable (0x04) */ +#define TEST_DATA_EN_3 (0x1U << 2) +/** Card Control Registers : test data enable (0x08) */ +#define TEST_DATA_EN_4 (0x1U << 3) +/** Card Control Registers : test cmd enable (0x10) */ +#define TEST_CMD_EN (0x1U << 4) +/** Card Control Registers : test mode (0x20) */ +#define TEST_MODE (0x1U << 5) + +/** Card Control Registers : Debug 0 register */ +#define DEBUG_0_REG 0xDC +/** Card Control Registers : SD test BUS 0 */ +#define SD_TESTBUS0 (0x1U) +/** Card Control Registers : Debug 1 register */ +#define DEBUG_1_REG 0xDD +/** Card Control Registers : SD test BUS 1 */ +#define SD_TESTBUS1 (0x1U) +/** Card Control Registers : Debug 2 register */ +#define DEBUG_2_REG 0xDE +/** Card Control Registers : SD test BUS 2 */ +#define SD_TESTBUS2 (0x1U) +/** Card Control Registers : Debug 3 register */ +#define DEBUG_3_REG 0xDF +/** Card Control Registers : SD test BUS 3 */ +#define SD_TESTBUS3 (0x1U) + +/** Host Control Registers : I/O port 0 */ +#define IO_PORT_0_REG 0xE4 +/** Host Control Registers : I/O port 1 */ +#define IO_PORT_1_REG 0xE5 +/** Host Control Registers : I/O port 2 */ +#define IO_PORT_2_REG 0xE6 + +#define FW_RESET_REG 0x0EE +#define FW_RESET_VAL 0x99 + +/** Event header Len*/ +#define MLAN_EVENT_HEADER_LEN 8 + +/** SDIO byte mode size */ +#define MAX_BYTE_MODE_SIZE 512 + +#if defined(SDIO_MULTI_PORT_TX_AGGR) || defined(SDIO_MULTI_PORT_RX_AGGR) +/** The base address for packet with multiple ports aggregation */ +#define SDIO_MPA_ADDR_BASE 0x1000 +#endif + +#ifdef SDIO_MULTI_PORT_TX_AGGR + +/** SDIO Tx aggregation in progress ? */ +#define MP_TX_AGGR_IN_PROGRESS(a) (a->mpa_tx.pkt_cnt > 0) + +/** SDIO Tx aggregation buffer room for next packet ? */ +#define MP_TX_AGGR_BUF_HAS_ROOM(a, mbuf, len) \ + (((a->mpa_tx.buf_len) + len) <= (a->mpa_tx.buf_size)) + +/** Copy current packet (SDIO Tx aggregation buffer) to SDIO buffer */ +#define MP_TX_AGGR_BUF_PUT(a, mbuf, port) do { \ + pmadapter->callbacks.moal_memmove(a->pmoal_handle, \ + &a->mpa_tx.buf[a->mpa_tx.buf_len], \ + mbuf->pbuf+mbuf->data_offset, mbuf->data_len);\ + a->mpa_tx.buf_len += mbuf->data_len; \ + a->mpa_tx.mp_wr_info[a->mpa_tx.pkt_cnt] = \ + *(t_u16 *)(mbuf->pbuf+mbuf->data_offset); \ + if (!a->mpa_tx.pkt_cnt) { \ + a->mpa_tx.start_port = port; \ + } \ + a->mpa_tx.ports |= (1 << port); \ + a->mpa_tx.pkt_cnt++; \ +} while (0) + +#define MP_TX_AGGR_BUF_PUT_SG(a, mbuf, port) do { \ + a->mpa_tx.buf_len += mbuf->data_len; \ + a->mpa_tx.mp_wr_info[a->mpa_tx.pkt_cnt] = \ + *(t_u16 *)(mbuf->pbuf+mbuf->data_offset); \ + a->mpa_tx.mbuf_arr[a->mpa_tx.pkt_cnt] = mbuf; \ + if (!a->mpa_tx.pkt_cnt) { \ + a->mpa_tx.start_port = port; \ + } \ + a->mpa_tx.ports |= (1 << port); \ + a->mpa_tx.pkt_cnt++; \ +} while (0) +/** SDIO Tx aggregation limit ? */ +#define MP_TX_AGGR_PKT_LIMIT_REACHED(a) ((a->mpa_tx.pkt_cnt) \ + == (a->mpa_tx.pkt_aggr_limit)) + +/** SDIO Tx aggregation port limit ? */ +#define MP_TX_AGGR_PORT_LIMIT_REACHED(a) (MFALSE) + +/** Reset SDIO Tx aggregation buffer parameters */ +#define MP_TX_AGGR_BUF_RESET(a) do { \ + memset(a, a->mpa_tx.mp_wr_info, 0, sizeof(a->mpa_tx.mp_wr_info)); \ + a->mpa_tx.pkt_cnt = 0; \ + a->mpa_tx.buf_len = 0; \ + a->mpa_tx.ports = 0; \ + a->mpa_tx.start_port = 0; \ +} while (0) + +#endif /* SDIO_MULTI_PORT_TX_AGGR */ + +#ifdef SDIO_MULTI_PORT_RX_AGGR + +/** SDIO Rx aggregation limit ? */ +#define MP_RX_AGGR_PKT_LIMIT_REACHED(a) (a->mpa_rx.pkt_cnt \ + == a->mpa_rx.pkt_aggr_limit) + +/** SDIO Rx aggregation port limit ? */ +/** this is for test only, because port 0 is reserved for control port */ +/* #define MP_RX_AGGR_PORT_LIMIT_REACHED(a) (a->curr_rd_port == 1) */ + +/* receive packets aggregated up to a half of mp_end_port */ +/* note: hw rx wraps round only after port (MAX_PORT-1) */ +#define MP_RX_AGGR_PORT_LIMIT_REACHED(a) \ + (((a->curr_rd_port < a->mpa_rx.start_port) && \ + (((MAX_PORT - a->mpa_rx.start_port) + a->curr_rd_port) \ + >= (a->mp_end_port >> 1))) || \ + ((a->curr_rd_port - a->mpa_rx.start_port) >= \ + (a->mp_end_port >> 1))) + +/** SDIO Rx aggregation in progress ? */ +#define MP_RX_AGGR_IN_PROGRESS(a) (a->mpa_rx.pkt_cnt > 0) + +/** SDIO Rx aggregation buffer room for next packet ? */ +#define MP_RX_AGGR_BUF_HAS_ROOM(a, rx_len) \ + ((a->mpa_rx.buf_len + rx_len) <= a->mpa_rx.buf_size) + +/** Prepare to copy current packet from card to SDIO Rx aggregation buffer */ +#define MP_RX_AGGR_SETUP(a, mbuf, port, rx_len) do { \ + a->mpa_rx.buf_len += rx_len; \ + if (!a->mpa_rx.pkt_cnt) { \ + a->mpa_rx.start_port = port; \ + } \ + a->mpa_rx.ports |= (1 << port); \ + a->mpa_rx.mbuf_arr[a->mpa_rx.pkt_cnt] = mbuf; \ + a->mpa_rx.len_arr[a->mpa_rx.pkt_cnt] = rx_len; \ + a->mpa_rx.pkt_cnt++; \ +} while (0) + +/** Reset SDIO Rx aggregation buffer parameters */ +#define MP_RX_AGGR_BUF_RESET(a) do { \ + a->mpa_rx.pkt_cnt = 0; \ + a->mpa_rx.buf_len = 0; \ + a->mpa_rx.ports = 0; \ + a->mpa_rx.start_port = 0; \ +} while (0) + +#endif /* SDIO_MULTI_PORT_RX_AGGR */ + +/** Enable host interrupt */ +mlan_status wlan_enable_host_int(pmlan_adapter pmadapter); +/** Probe and initialization function */ +mlan_status wlan_sdio_probe(pmlan_adapter pmadapter); +/** multi interface download check */ +mlan_status wlan_check_winner_status(mlan_adapter *pmadapter, t_u32 *val); + +#ifdef SDIO_MULTI_PORT_TX_AGGR +mlan_status wlan_send_mp_aggr_buf(mlan_adapter *pmadapter); +#endif + +#if defined(SDIO_MULTI_PORT_RX_AGGR) +mlan_status wlan_re_alloc_sdio_rx_mpa_buffer(IN mlan_adapter *pmadapter); +#endif + +void wlan_decode_spa_buffer(mlan_adapter *pmadapter, t_u8 *buf, t_u32 len); +t_void wlan_sdio_deaggr_rx_pkt(IN pmlan_adapter pmadapter, mlan_buffer *pmbuf); +/** Firmware status check */ +mlan_status wlan_check_fw_status(mlan_adapter *pmadapter, t_u32 pollnum); +/** Read interrupt status */ +mlan_status wlan_interrupt(pmlan_adapter pmadapter); +/** Process Interrupt Status */ +mlan_status wlan_process_int_status(mlan_adapter *pmadapter); +/** Transfer data to card */ +mlan_status wlan_sdio_host_to_card(mlan_adapter *pmadapter, t_u8 type, + mlan_buffer *mbuf, mlan_tx_param *tx_param); +mlan_status wlan_set_sdio_gpio_int(IN pmlan_private priv); +mlan_status wlan_cmd_sdio_gpio_int(pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, IN t_void *pdata_buf); +mlan_status wlan_reset_fw(pmlan_adapter pmadapter); +#endif /* _MLAN_SDIO_H */ diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_shim.c b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_shim.c new file mode 100644 index 000000000000..4086a02f4f57 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_shim.c @@ -0,0 +1,1418 @@ +/** @file mlan_shim.c + * + * @brief This file contains APIs to MOAL module. + * + * Copyright (C) 2008-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/******************************************************** +Change log: + 10/13/2008: initial version +********************************************************/ + +#include "mlan.h" +#ifdef STA_SUPPORT +#include "mlan_join.h" +#endif +#include "mlan_util.h" +#include "mlan_fw.h" +#include "mlan_main.h" +#include "mlan_wmm.h" +#include "mlan_sdio.h" +#ifdef UAP_SUPPORT +#include "mlan_uap.h" +#endif +#include "mlan_11h.h" +#include "mlan_11n_rxreorder.h" + +/******************************************************** + Local Variables +********************************************************/ + +/******************************************************** + Global Variables +********************************************************/ +#ifdef STA_SUPPORT +mlan_operations mlan_sta_ops = { + /* init cmd handler */ + wlan_ops_sta_init_cmd, + /* ioctl handler */ + wlan_ops_sta_ioctl, + /* cmd handler */ + wlan_ops_sta_prepare_cmd, + /* cmdresp handler */ + wlan_ops_sta_process_cmdresp, + /* rx handler */ + wlan_ops_sta_process_rx_packet, + /* Event handler */ + wlan_ops_sta_process_event, + /* txpd handler */ + wlan_ops_sta_process_txpd, + /* BSS role: STA */ + MLAN_BSS_ROLE_STA, +}; +#endif +#ifdef UAP_SUPPORT +mlan_operations mlan_uap_ops = { + /* init cmd handler */ + wlan_ops_uap_init_cmd, + /* ioctl handler */ + wlan_ops_uap_ioctl, + /* cmd handler */ + wlan_ops_uap_prepare_cmd, + /* cmdresp handler */ + wlan_ops_uap_process_cmdresp, + /* rx handler */ + wlan_ops_uap_process_rx_packet, + /* Event handler */ + wlan_ops_uap_process_event, + /* txpd handler */ + wlan_ops_uap_process_txpd, + /* BSS role: uAP */ + MLAN_BSS_ROLE_UAP, +}; +#endif + +/** mlan function table */ +mlan_operations *mlan_ops[] = { +#ifdef STA_SUPPORT + &mlan_sta_ops, +#endif +#ifdef UAP_SUPPORT + &mlan_uap_ops, +#endif + MNULL, +}; + +/** Global moal_assert callback */ +t_void (*assert_callback) (IN t_void *pmoal_handle, IN t_u32 cond) = MNULL; +#ifdef DEBUG_LEVEL1 +#ifdef DEBUG_LEVEL2 +#define DEFAULT_DEBUG_MASK (0xffffffff) +#else +#define DEFAULT_DEBUG_MASK (MMSG | MFATAL | MERROR) +#endif + +/** Global moal_print callback */ +t_void (*print_callback) (IN t_void *pmoal_handle, + IN t_u32 level, IN char *pformat, IN ... + ) = MNULL; + +/** Global moal_get_system_time callback */ +mlan_status (*get_sys_time_callback) (IN t_void *pmoal_handle, + OUT t_u32 *psec, + OUT t_u32 *pusec) = MNULL; + +/** Global driver debug mit masks */ +t_u32 mlan_drvdbg = DEFAULT_DEBUG_MASK; +#endif + +/******************************************************** + Local Functions +*******************************************************/ +/** + * @brief This function process pending ioctl + * + * @param pmadapter A pointer to mlan_adapter structure + * + */ +void +wlan_process_pending_ioctl(mlan_adapter *pmadapter) +{ + pmlan_ioctl_req pioctl_buf; + mlan_status status = MLAN_STATUS_SUCCESS; + pmlan_callbacks pcb; +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) + mlan_ds_bss *bss = MNULL; +#endif +#ifdef STA_SUPPORT + mlan_ds_misc_cfg *misc = MNULL; +#endif + ENTER(); + + pcb = &pmadapter->callbacks; + + while ((pioctl_buf = + (pmlan_ioctl_req)util_dequeue_list(pmadapter->pmoal_handle, + &pmadapter->ioctl_pending_q, + pcb->moal_spin_lock, + pcb->moal_spin_unlock))) { + switch (pioctl_buf->req_id) { +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) + case MLAN_IOCTL_BSS: + bss = (mlan_ds_bss *)pioctl_buf->pbuf; + if (bss->sub_command == MLAN_OID_BSS_ROLE) { + PRINTM(MCMND, "Role switch ioctl\n"); + status = wlan_bss_ioctl_bss_role(pmadapter, + pioctl_buf); + } + break; +#endif +#ifdef STA_SUPPORT + case MLAN_IOCTL_MISC_CFG: + misc = (mlan_ds_misc_cfg *)pioctl_buf->pbuf; + if (misc->sub_command == MLAN_OID_MISC_WARM_RESET) { + PRINTM(MCMND, "Warm Reset ioctl\n"); + status = wlan_misc_ioctl_warm_reset(pmadapter, + pioctl_buf); + } + break; +#endif + default: + break; + } + if (status != MLAN_STATUS_PENDING) + pcb->moal_ioctl_complete(pmadapter->pmoal_handle, + pioctl_buf, status); + } + LEAVE(); +} + +/******************************************************** + Global Functions +********************************************************/ + +/** + * @brief This function registers MOAL to MLAN module. + * + * @param pmdevice A pointer to a mlan_device structure + * allocated in MOAL + * @param ppmlan_adapter A pointer to a t_void pointer to store + * mlan_adapter structure pointer as the context + * + * @return MLAN_STATUS_SUCCESS + * The registration succeeded. + * MLAN_STATUS_FAILURE + * The registration failed. + * + * mlan_status mlan_register ( + * IN pmlan_device pmdevice, + * OUT t_void **ppmlan_adapter + * ); + * + * Comments + * MOAL constructs mlan_device data structure to pass moal_handle and + * mlan_callback table to MLAN. MLAN returns mlan_adapter pointer to + * the ppmlan_adapter buffer provided by MOAL. + * Headers: + * declared in mlan_decl.h + * See Also + * mlan_unregister + */ +mlan_status +mlan_register(IN pmlan_device pmdevice, OUT t_void **ppmlan_adapter) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_adapter pmadapter = MNULL; + pmlan_callbacks pcb = MNULL; + t_u8 i = 0; + t_u32 j = 0; + + if (!pmdevice || !ppmlan_adapter) { + return MLAN_STATUS_FAILURE; + } + MASSERT(ppmlan_adapter); + MASSERT(pmdevice->callbacks.moal_print); +#ifdef DEBUG_LEVEL1 + print_callback = pmdevice->callbacks.moal_print; + get_sys_time_callback = pmdevice->callbacks.moal_get_system_time; +#endif + assert_callback = pmdevice->callbacks.moal_assert; + + ENTER(); + + MASSERT(pmdevice->callbacks.moal_malloc); + MASSERT(pmdevice->callbacks.moal_memset); + MASSERT(pmdevice->callbacks.moal_memmove); + + if (!pmdevice->callbacks.moal_malloc || + !pmdevice->callbacks.moal_memset || + !pmdevice->callbacks.moal_memmove) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + /* Allocate memory for adapter structure */ + if (pmdevice->callbacks.moal_vmalloc && pmdevice->callbacks.moal_vfree) + ret = pmdevice->callbacks.moal_vmalloc(pmdevice->pmoal_handle, + sizeof(mlan_adapter), + (t_u8 **)&pmadapter); + else + ret = pmdevice->callbacks.moal_malloc(pmdevice->pmoal_handle, + sizeof(mlan_adapter), + MLAN_MEM_DEF, + (t_u8 **)&pmadapter); + if ((ret != MLAN_STATUS_SUCCESS) || !pmadapter) { + ret = MLAN_STATUS_FAILURE; + goto exit_register; + } + + pmdevice->callbacks.moal_memset(pmdevice->pmoal_handle, pmadapter, + 0, sizeof(mlan_adapter)); + + pcb = &pmadapter->callbacks; + + /* Save callback functions */ + pmdevice->callbacks.moal_memmove(pmadapter->pmoal_handle, pcb, + &pmdevice->callbacks, + sizeof(mlan_callbacks)); + + /* Assertion for all callback functions */ + MASSERT(pcb->moal_get_hw_spec_complete); + MASSERT(pcb->moal_init_fw_complete); + MASSERT(pcb->moal_shutdown_fw_complete); + MASSERT(pcb->moal_send_packet_complete); + MASSERT(pcb->moal_recv_packet); + MASSERT(pcb->moal_recv_event); + MASSERT(pcb->moal_ioctl_complete); + + MASSERT(pcb->moal_write_reg); + MASSERT(pcb->moal_read_reg); + MASSERT(pcb->moal_alloc_mlan_buffer); + MASSERT(pcb->moal_free_mlan_buffer); + MASSERT(pcb->moal_write_data_sync); + MASSERT(pcb->moal_read_data_sync); + MASSERT(pcb->moal_mfree); + MASSERT(pcb->moal_memcpy); + MASSERT(pcb->moal_memcmp); + MASSERT(pcb->moal_get_system_time); + MASSERT(pcb->moal_init_timer); + MASSERT(pcb->moal_free_timer); + MASSERT(pcb->moal_start_timer); + MASSERT(pcb->moal_stop_timer); + MASSERT(pcb->moal_init_lock); + MASSERT(pcb->moal_free_lock); + MASSERT(pcb->moal_spin_lock); + MASSERT(pcb->moal_spin_unlock); + MASSERT(pcb->moal_hist_data_add); + MASSERT(pcb->moal_updata_peer_signal); + MASSERT(pcb->moal_do_div); + /* Save pmoal_handle */ + pmadapter->pmoal_handle = pmdevice->pmoal_handle; + + if ((pmdevice->int_mode == INT_MODE_GPIO) && (pmdevice->gpio_pin == 0)) { + PRINTM(MERROR, "SDIO_GPIO_INT_CONFIG: Invalid GPIO Pin\n"); + ret = MLAN_STATUS_FAILURE; + goto error; + } + pmadapter->init_para.int_mode = pmdevice->int_mode; + pmadapter->init_para.gpio_pin = pmdevice->gpio_pin; + /* card specific probing has been deferred until now .. */ + ret = wlan_sdio_probe(pmadapter); + if (MLAN_STATUS_SUCCESS != ret) { + ret = MLAN_STATUS_FAILURE; + goto error; + } +#ifdef DEBUG_LEVEL1 + mlan_drvdbg = pmdevice->drvdbg; +#endif + +#ifdef MFG_CMD_SUPPORT + pmadapter->init_para.mfg_mode = pmdevice->mfg_mode; +#endif +#if defined(SDIO_MULTI_PORT_TX_AGGR) || defined(SDIO_MULTI_PORT_RX_AGGR) + pmadapter->max_segs = pmdevice->max_segs; + pmadapter->max_seg_size = pmdevice->max_seg_size; +#endif + +#ifdef SDIO_MULTI_PORT_TX_AGGR + pmadapter->init_para.mpa_tx_cfg = pmdevice->mpa_tx_cfg; +#endif +#ifdef SDIO_MULTI_PORT_RX_AGGR + pmadapter->init_para.mpa_rx_cfg = pmdevice->mpa_rx_cfg; +#endif + pmadapter->sdio_rx_aggr_enable = pmdevice->sdio_rx_aggr_enable; + pmadapter->init_para.auto_ds = pmdevice->auto_ds; + pmadapter->init_para.ps_mode = pmdevice->ps_mode; + if (pmdevice->max_tx_buf == MLAN_TX_DATA_BUF_SIZE_2K || + pmdevice->max_tx_buf == MLAN_TX_DATA_BUF_SIZE_4K || + pmdevice->max_tx_buf == MLAN_TX_DATA_BUF_SIZE_8K) + pmadapter->init_para.max_tx_buf = pmdevice->max_tx_buf; +#ifdef STA_SUPPORT + pmadapter->init_para.cfg_11d = pmdevice->cfg_11d; +#else + pmadapter->init_para.cfg_11d = 0; +#endif + pmadapter->init_para.dfs_master_radar_det_en = + DFS_MASTER_RADAR_DETECT_EN; + pmadapter->init_para.dfs_slave_radar_det_en = DFS_SLAVE_RADAR_DETECT_EN; + pmadapter->init_para.dev_cap_mask = pmdevice->dev_cap_mask; + pmadapter->init_para.indrstcfg = pmdevice->indrstcfg; + pmadapter->rx_work_flag = pmdevice->rx_work; + + pmadapter->fixed_beacon_buffer = pmdevice->fixed_beacon_buffer; + + pmadapter->multiple_dtim = pmdevice->multi_dtim; + pmadapter->inact_tmo = pmdevice->inact_tmo; + pmadapter->init_para.drcs_chantime_mode = pmdevice->drcs_chantime_mode; + pmadapter->init_para.fw_region = pmdevice->fw_region; + pmadapter->hs_wake_interval = pmdevice->hs_wake_interval; + if (pmdevice->indication_gpio != 0xff) { + pmadapter->ind_gpio = pmdevice->indication_gpio & 0x0f; + pmadapter->level = (pmdevice->indication_gpio & 0xf0) >> 4; + if (pmadapter->level != 0 && pmadapter->level != 1) { + PRINTM(MERROR, + "Indication GPIO level is wrong and will use default value 0.\n"); + pmadapter->level = 0; + } + } + + pmadapter->priv_num = 0; + pmadapter->priv[0] = MNULL; + + if (pcb->moal_vmalloc && pcb->moal_vfree) + ret = pcb->moal_vmalloc(pmadapter->pmoal_handle, + sizeof(mlan_private), + (t_u8 **)&pmadapter->priv[0]); + else + ret = pcb->moal_malloc(pmadapter->pmoal_handle, + sizeof(mlan_private), MLAN_MEM_DEF, + (t_u8 **)&pmadapter->priv[0]); + if (ret != MLAN_STATUS_SUCCESS || !pmadapter->priv[0]) { + ret = MLAN_STATUS_FAILURE; + goto error; + } + + pmadapter->priv_num++; + memset(pmadapter, pmadapter->priv[0], 0, sizeof(mlan_private)); + + pmadapter->priv[0]->adapter = pmadapter; + pmadapter->priv[0]->bss_type = (t_u8)pmdevice->bss_attr[0].bss_type; + pmadapter->priv[0]->frame_type = (t_u8)pmdevice->bss_attr[0].frame_type; + pmadapter->priv[0]->bss_priority = + (t_u8)pmdevice->bss_attr[0].bss_priority; + if (pmdevice->bss_attr[0].bss_type == MLAN_BSS_TYPE_STA) + pmadapter->priv[0]->bss_role = MLAN_BSS_ROLE_STA; + else if (pmdevice->bss_attr[0].bss_type == MLAN_BSS_TYPE_UAP) + pmadapter->priv[0]->bss_role = MLAN_BSS_ROLE_UAP; +#ifdef WIFI_DIRECT_SUPPORT + else if (pmdevice->bss_attr[0].bss_type == MLAN_BSS_TYPE_WIFIDIRECT) { + pmadapter->priv[0]->bss_role = MLAN_BSS_ROLE_STA; + if (pmdevice->bss_attr[0].bss_virtual) + pmadapter->priv[0]->bss_virtual = MTRUE; + } +#endif + /* Save bss_index and bss_num */ + pmadapter->priv[0]->bss_index = 0; + pmadapter->priv[0]->bss_num = (t_u8)pmdevice->bss_attr[0].bss_num; + + /* init function table */ + for (j = 0; mlan_ops[j]; j++) { + if (mlan_ops[j]->bss_role == GET_BSS_ROLE(pmadapter->priv[0])) { + memcpy(pmadapter, &pmadapter->priv[0]->ops, mlan_ops[j], + sizeof(mlan_operations)); + } + } + /** back up bss_attr table */ + memcpy(pmadapter, pmadapter->bss_attr, pmdevice->bss_attr, + sizeof(pmadapter->bss_attr)); + + /* Initialize lock variables */ + if (wlan_init_lock_list(pmadapter) != MLAN_STATUS_SUCCESS) { + ret = MLAN_STATUS_FAILURE; + goto error; + } + + /** init lock varible for first priv */ + if (wlan_init_priv_lock_list(pmadapter, 0) != MLAN_STATUS_SUCCESS) { + ret = MLAN_STATUS_FAILURE; + goto error; + } + + /* Allocate memory for member of adapter structure */ + if (wlan_allocate_adapter(pmadapter)) { + ret = MLAN_STATUS_FAILURE; + goto error; + } + + /* Initialize timers */ + if (wlan_init_timer(pmadapter) != MLAN_STATUS_SUCCESS) { + ret = MLAN_STATUS_FAILURE; + goto error; + } + /* Return pointer of mlan_adapter to MOAL */ + *ppmlan_adapter = pmadapter; + + goto exit_register; + +error: + PRINTM(MINFO, "Leave mlan_register with error\n"); + /* Free timers */ + wlan_free_timer(pmadapter); + /* Free adapter structure */ + wlan_free_adapter(pmadapter); + /* Free lock variables */ + wlan_free_lock_list(pmadapter); + for (i = 0; i < MLAN_MAX_BSS_NUM; i++) { + if (pmadapter->priv[i]) { + if (pcb->moal_vmalloc && pcb->moal_vfree) + pcb->moal_vfree(pmadapter->pmoal_handle, + (t_u8 *)pmadapter->priv[i]); + else + pcb->moal_mfree(pmadapter->pmoal_handle, + (t_u8 *)pmadapter->priv[i]); + } + } + if (pcb->moal_vmalloc && pcb->moal_vfree) + pcb->moal_vfree(pmadapter->pmoal_handle, (t_u8 *)pmadapter); + else + pcb->moal_mfree(pmadapter->pmoal_handle, (t_u8 *)pmadapter); + +exit_register: + LEAVE(); + return ret; +} + +/** + * @brief This function unregisters MOAL from MLAN module. + * + * @param pmlan_adapter A pointer to a mlan_device structure + * allocated in MOAL + * + * @return MLAN_STATUS_SUCCESS + * The deregistration succeeded. + */ +mlan_status +mlan_unregister(IN t_void *pmlan_adapter + ) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_adapter *pmadapter = (mlan_adapter *)pmlan_adapter; + pmlan_callbacks pcb; + t_s32 i = 0; + + MASSERT(pmlan_adapter); + + ENTER(); + + pcb = &pmadapter->callbacks; + /* Free adapter structure */ + wlan_free_adapter(pmadapter); + + /* Free timers */ + wlan_free_timer(pmadapter); + + /* Free lock variables */ + wlan_free_lock_list(pmadapter); + + /* Free private structures */ + for (i = 0; i < pmadapter->priv_num; i++) { + if (pmadapter->priv[i]) { + if (pcb->moal_vmalloc && pcb->moal_vfree) + pcb->moal_vfree(pmadapter->pmoal_handle, + (t_u8 *)pmadapter->priv[i]); + else + pcb->moal_mfree(pmadapter->pmoal_handle, + (t_u8 *)pmadapter->priv[i]); + } + } + + /* Free mlan_adapter */ + if (pcb->moal_vmalloc && pcb->moal_vfree) + pcb->moal_vfree(pmadapter->pmoal_handle, (t_u8 *)pmadapter); + else + pcb->moal_mfree(pmadapter->pmoal_handle, (t_u8 *)pmadapter); + + LEAVE(); + return ret; +} + +/** + * @brief This function downloads the firmware + * + * @param pmlan_adapter A pointer to a t_void pointer to store + * mlan_adapter structure pointer + * @param pmfw A pointer to firmware image + * + * @return MLAN_STATUS_SUCCESS + * The firmware download succeeded. + * MLAN_STATUS_FAILURE + * The firmware download failed. + */ +mlan_status +mlan_dnld_fw(IN t_void *pmlan_adapter, IN pmlan_fw_image pmfw) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_adapter *pmadapter = (mlan_adapter *)pmlan_adapter; + t_u32 poll_num = 1; + t_u32 winner = 0; + + ENTER(); + MASSERT(pmlan_adapter); + +/*when using GPIO wakeup, don't run the below code. + *if using GPIO wakeup, host will do handshake with FW + *to check if FW wake up and pull up SDIO line, then reload driver. + *So when using GPIO wakeup, don't need driver to do check wakeup status again. + *when using SDIO interface wakeup, run the below code; + *if using SDIO interface wakeup, driver need to do check wakeup status with FW. + */ + + /* Card specific probing */ + ret = wlan_sdio_probe(pmadapter); + if (ret == MLAN_STATUS_FAILURE) { + PRINTM(MERROR, "WLAN SDIO probe failed\n", ret); + LEAVE(); + return ret; + } + + /* Check if firmware is already running */ + ret = wlan_check_fw_status(pmadapter, poll_num); + if (ret == MLAN_STATUS_SUCCESS) { + if (pmfw->fw_reload) { + ret = wlan_reset_fw(pmadapter); + if (ret == MLAN_STATUS_FAILURE) { + PRINTM(MERROR, "FW reset failure!"); + LEAVE(); + return ret; + } + } else { + PRINTM(MMSG, + "WLAN FW already running! Skip FW download\n"); + goto done; + } + } + poll_num = MAX_FIRMWARE_POLL_TRIES; + + /* Check if other interface is downloading */ + ret = wlan_check_winner_status(pmadapter, &winner); + if (ret == MLAN_STATUS_FAILURE) { + PRINTM(MFATAL, "WLAN read winner status failed!\n"); + goto done; + } + if (winner) { + PRINTM(MMSG, + "WLAN is not the winner (0x%x). Skip FW download\n", + winner); + poll_num = MAX_MULTI_INTERFACE_POLL_TRIES; + goto poll_fw; + } + + if (pmfw) { + /* Download helper/firmware */ + ret = wlan_dnld_fw(pmadapter, pmfw); + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "wlan_dnld_fw fail ret=0x%x\n", ret); + LEAVE(); + return ret; + } + } + +poll_fw: + /* Check if the firmware is downloaded successfully or not */ + ret = wlan_check_fw_status(pmadapter, poll_num); + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MFATAL, "FW failed to be active in time!\n"); + ret = MLAN_STATUS_FAILURE; + LEAVE(); + return ret; + } +done: + + /* re-enable host interrupt for mlan after fw dnld is successful */ + wlan_enable_host_int(pmadapter); + + LEAVE(); + return ret; +} + +/** + * @brief This function pass init param to MLAN + * + * @param pmlan_adapter A pointer to a t_void pointer to store + * mlan_adapter structure pointer + * @param pparam A pointer to mlan_init_param structure + * + * @return MLAN_STATUS_SUCCESS + * + */ +mlan_status +mlan_set_init_param(IN t_void *pmlan_adapter, IN pmlan_init_param pparam) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_adapter *pmadapter = (mlan_adapter *)pmlan_adapter; + + ENTER(); + MASSERT(pmlan_adapter); + + /** Save DPD data in MLAN */ + if ((pparam->pdpd_data_buf) && (pparam->dpd_data_len > 0)) { + pmadapter->pdpd_data = pparam->pdpd_data_buf; + pmadapter->dpd_data_len = pparam->dpd_data_len; + } + if (pparam->ptxpwr_data_buf && (pparam->txpwr_data_len > 0)) { + pmadapter->ptxpwr_data = pparam->ptxpwr_data_buf; + pmadapter->txpwr_data_len = pparam->txpwr_data_len; + } + /** Save cal data in MLAN */ + if ((pparam->pcal_data_buf) && (pparam->cal_data_len > 0)) { + pmadapter->pcal_data = pparam->pcal_data_buf; + pmadapter->cal_data_len = pparam->cal_data_len; + } + + LEAVE(); + return ret; +} + +/** + * @brief This function initializes the firmware + * + * @param pmlan_adapter A pointer to a t_void pointer to store + * mlan_adapter structure pointer + * + * @return MLAN_STATUS_SUCCESS + * The firmware initialization succeeded. + * MLAN_STATUS_PENDING + * The firmware initialization is pending. + * MLAN_STATUS_FAILURE + * The firmware initialization failed. + */ +mlan_status +mlan_init_fw(IN t_void *pmlan_adapter + ) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_adapter *pmadapter = (mlan_adapter *)pmlan_adapter; + + ENTER(); + MASSERT(pmlan_adapter); + + pmadapter->hw_status = WlanHardwareStatusGetHwSpec; + + /* Initialize firmware, may return PENDING */ + ret = wlan_init_fw(pmadapter); + PRINTM(MINFO, "wlan_init_fw returned ret=0x%x\n", ret); + + LEAVE(); + return ret; +} + +/** + * @brief Shutdown firmware + * + * @param pmlan_adapter A pointer to mlan_adapter structure + * + * @return MLAN_STATUS_SUCCESS + * The firmware shutdown call succeeded. + * MLAN_STATUS_PENDING + * The firmware shutdown call is pending. + * MLAN_STATUS_FAILURE + * The firmware shutdown call failed. + */ +mlan_status +mlan_shutdown_fw(IN t_void *pmlan_adapter + ) +{ + mlan_status ret = MLAN_STATUS_PENDING; + mlan_adapter *pmadapter = (mlan_adapter *)pmlan_adapter; + pmlan_buffer pmbuf; + pmlan_ioctl_req pioctl_buf; + pmlan_callbacks pcb; + t_s32 i = 0; + + ENTER(); + + MASSERT(pmlan_adapter); + /* MLAN already shutdown */ + if (pmadapter->hw_status == WlanHardwareStatusNotReady) { + LEAVE(); + return MLAN_STATUS_SUCCESS; + } + + pmadapter->hw_status = WlanHardwareStatusClosing; + /* Wait for mlan_process to complete */ + if (pmadapter->mlan_processing) { + PRINTM(MWARN, "MLAN main processing is still running\n"); + LEAVE(); + return ret; + } + + /* Shut down MLAN */ + PRINTM(MINFO, "Shutdown MLAN...\n"); + + /* Cancel all pending commands and complete ioctls */ + wlan_cancel_all_pending_cmd(pmadapter); + + /* Clean up priv structures */ + for (i = 0; i < pmadapter->priv_num; i++) { + if (pmadapter->priv[i]) + wlan_free_priv(pmadapter->priv[i]); + } + + pcb = &pmadapter->callbacks; + /** cancel pending ioctl */ + while ((pioctl_buf = + (pmlan_ioctl_req)util_dequeue_list(pmadapter->pmoal_handle, + &pmadapter->ioctl_pending_q, + pcb->moal_spin_lock, + pcb->moal_spin_unlock))) { + pioctl_buf->status_code = MLAN_ERROR_CMD_CANCEL; + pcb->moal_ioctl_complete(pmadapter->pmoal_handle, pioctl_buf, + MLAN_STATUS_FAILURE); + } + + while ((pmbuf = (pmlan_buffer)util_dequeue_list(pmadapter->pmoal_handle, + &pmadapter-> + rx_data_queue, + pcb->moal_spin_lock, + pcb-> + moal_spin_unlock))) { + wlan_free_mlan_buffer(pmadapter, pmbuf); + } + pmadapter->rx_pkts_queued = 0; + + /* Notify completion */ + ret = wlan_shutdown_fw_complete(pmadapter); + + LEAVE(); + return ret; +} + +/** + * @brief queue main work + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return N/A + */ +static t_void +mlan_queue_main_work(mlan_adapter *pmadapter) +{ + pmlan_callbacks pcb = &pmadapter->callbacks; + ENTER(); + pcb->moal_spin_lock(pmadapter->pmoal_handle, + pmadapter->pmain_proc_lock); + + /* Check if already processing */ + if (pmadapter->mlan_processing) { + pmadapter->more_task_flag = MTRUE; + pcb->moal_spin_unlock(pmadapter->pmoal_handle, + pmadapter->pmain_proc_lock); + } else { + pcb->moal_spin_unlock(pmadapter->pmoal_handle, + pmadapter->pmain_proc_lock); + wlan_recv_event(wlan_get_priv(pmadapter, MLAN_BSS_ROLE_ANY), + MLAN_EVENT_ID_DRV_DEFER_HANDLING, MNULL); + } + LEAVE(); + return; +} + +/** + * @brief queue rx_work + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return N/A + */ +static t_void +mlan_queue_rx_work(mlan_adapter *pmadapter) +{ + pmlan_callbacks pcb = &pmadapter->callbacks; + ENTER(); + + pcb->moal_spin_lock(pmadapter->pmoal_handle, pmadapter->prx_proc_lock); + + /* Check if already processing */ + if (pmadapter->mlan_rx_processing) { + pcb->moal_spin_unlock(pmadapter->pmoal_handle, + pmadapter->prx_proc_lock); + } else { + pcb->moal_spin_unlock(pmadapter->pmoal_handle, + pmadapter->prx_proc_lock); + wlan_recv_event(wlan_get_priv(pmadapter, MLAN_BSS_ROLE_ANY), + MLAN_EVENT_ID_DRV_DEFER_RX_WORK, MNULL); + } + LEAVE(); + return; +} + +/** + * @brief block main process + * + * @param pmadapter A pointer to mlan_adapter structure + * @param block MTRUE/MFALSE + * + * @return N/A + */ +void +mlan_block_main_process(mlan_adapter *pmadapter, t_u8 block) +{ + pmlan_callbacks pcb = &pmadapter->callbacks; + pcb->moal_spin_lock(pmadapter->pmoal_handle, + pmadapter->pmain_proc_lock); + if (!block) { + pmadapter->main_lock_flag = MFALSE; + pcb->moal_spin_unlock(pmadapter->pmoal_handle, + pmadapter->pmain_proc_lock); + } else { + pmadapter->main_lock_flag = MTRUE; + if (pmadapter->mlan_processing) { + pcb->moal_spin_unlock(pmadapter->pmoal_handle, + pmadapter->pmain_proc_lock); + PRINTM(MEVENT, "wlan: wait main work done...\n"); + wlan_recv_event(wlan_get_priv + (pmadapter, MLAN_BSS_ROLE_ANY), + MLAN_EVENT_ID_DRV_FLUSH_MAIN_WORK, + MNULL); + } else { + pcb->moal_spin_unlock(pmadapter->pmoal_handle, + pmadapter->pmain_proc_lock); + } + } +} + +/** + * @brief block rx process + * + * @param pmadapter A pointer to mlan_adapter structure + * @param block MTRUE/MFALSE; + * + * @return N/A + */ +void +mlan_block_rx_process(mlan_adapter *pmadapter, t_u8 block) +{ + pmlan_callbacks pcb = &pmadapter->callbacks; + pcb->moal_spin_lock(pmadapter->pmoal_handle, pmadapter->prx_proc_lock); + if (!block) { + pmadapter->rx_lock_flag = MFALSE; + pcb->moal_spin_unlock(pmadapter->pmoal_handle, + pmadapter->prx_proc_lock); + } else { + pmadapter->rx_lock_flag = MTRUE; + if (pmadapter->mlan_rx_processing) { + pcb->moal_spin_unlock(pmadapter->pmoal_handle, + pmadapter->prx_proc_lock); + PRINTM(MEVENT, "wlan: wait rx work done...\n"); + wlan_recv_event(wlan_get_priv + (pmadapter, MLAN_BSS_ROLE_ANY), + MLAN_EVENT_ID_DRV_FLUSH_RX_WORK, MNULL); + } else { + pcb->moal_spin_unlock(pmadapter->pmoal_handle, + pmadapter->prx_proc_lock); + } + } +} + +/** + * @brief The receive process + * + * @param pmlan_adapter A pointer to mlan_adapter structure + * @param rx_pkts A pointer to save receive pkts number + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +mlan_rx_process(IN t_void *pmlan_adapter, IN t_u8 *rx_pkts) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_adapter *pmadapter = (mlan_adapter *)pmlan_adapter; + pmlan_callbacks pcb; + pmlan_buffer pmbuf; + t_u8 limit = 0; + t_u8 rx_num = 0; + + ENTER(); + + MASSERT(pmlan_adapter); + pcb = &pmadapter->callbacks; + pcb->moal_spin_lock(pmadapter->pmoal_handle, pmadapter->prx_proc_lock); + if (pmadapter->mlan_rx_processing || pmadapter->rx_lock_flag) { + pcb->moal_spin_unlock(pmadapter->pmoal_handle, + pmadapter->prx_proc_lock); + goto exit_rx_proc; + } else { + pmadapter->mlan_rx_processing = MTRUE; + pcb->moal_spin_unlock(pmadapter->pmoal_handle, + pmadapter->prx_proc_lock); + } + if (rx_pkts) + limit = *rx_pkts; + /* Check for Rx data */ + while (MTRUE) { + if (pmadapter->flush_data) { + pmadapter->flush_data = MFALSE; + wlan_flush_rxreorder_tbl(pmadapter); + } + pmadapter->callbacks.moal_spin_lock(pmadapter->pmoal_handle, + pmadapter->rx_data_queue. + plock); + pmbuf = (pmlan_buffer)util_dequeue_list(pmadapter->pmoal_handle, + &pmadapter-> + rx_data_queue, MNULL, + MNULL); + if (!pmbuf) { + pmadapter->callbacks.moal_spin_unlock(pmadapter-> + pmoal_handle, + pmadapter-> + rx_data_queue. + plock); + break; + } + pmadapter->rx_pkts_queued--; + rx_num++; + pmadapter->callbacks.moal_spin_unlock(pmadapter->pmoal_handle, + pmadapter->rx_data_queue. + plock); + if (pmadapter->delay_task_flag && + (pmadapter->rx_pkts_queued < LOW_RX_PENDING)) { + PRINTM(MEVENT, "Run\n"); + pmadapter->delay_task_flag = MFALSE; + mlan_queue_main_work(pmadapter); + } + wlan_sdio_deaggr_rx_pkt(pmadapter, pmbuf); + if (limit && rx_num >= limit) + break; + } + if (rx_pkts) + *rx_pkts = rx_num; + pcb->moal_spin_lock(pmadapter->pmoal_handle, pmadapter->prx_proc_lock); + pmadapter->mlan_rx_processing = MFALSE; + pcb->moal_spin_unlock(pmadapter->pmoal_handle, + pmadapter->prx_proc_lock); +exit_rx_proc: + LEAVE(); + return ret; +} + +/** + * @brief The main process + * + * @param pmlan_adapter A pointer to mlan_adapter structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +mlan_main_process(IN t_void *pmlan_adapter + ) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_adapter *pmadapter = (mlan_adapter *)pmlan_adapter; + pmlan_callbacks pcb; + + ENTER(); + + MASSERT(pmlan_adapter); + + pcb = &pmadapter->callbacks; + + pcb->moal_spin_lock(pmadapter->pmoal_handle, + pmadapter->pmain_proc_lock); + + /* Check if already processing */ + if (pmadapter->mlan_processing || pmadapter->main_lock_flag) { + pmadapter->more_task_flag = MTRUE; + pcb->moal_spin_unlock(pmadapter->pmoal_handle, + pmadapter->pmain_proc_lock); + goto exit_main_proc; + } else { + pmadapter->mlan_processing = MTRUE; + pmadapter->main_process_cnt++; + pcb->moal_spin_unlock(pmadapter->pmoal_handle, + pmadapter->pmain_proc_lock); + } +process_start: + do { + /* Is MLAN shutting down or not ready? */ + if ((pmadapter->hw_status == WlanHardwareStatusClosing) || + (pmadapter->hw_status == WlanHardwareStatusNotReady)) + break; + if (pmadapter->pending_ioctl) { + wlan_process_pending_ioctl(pmadapter); + pmadapter->pending_ioctl = MFALSE; + } + if (pmadapter->rx_pkts_queued > HIGH_RX_PENDING) { + PRINTM(MEVENT, "Pause\n"); + pmadapter->delay_task_flag = MTRUE; + mlan_queue_rx_work(pmadapter); + break; + } + /* Handle pending SDIO interrupts if any */ + if (pmadapter->sdio_ireg) { + if (pmadapter->hs_activated == MTRUE) + wlan_process_hs_config(pmadapter); + wlan_process_int_status(pmadapter); + if (pmadapter->data_received && pmadapter->rx_work_flag) + mlan_queue_rx_work(pmadapter); + } + + /* Need to wake up the card ? */ + if ((pmadapter->ps_state == PS_STATE_SLEEP) && + (pmadapter->pm_wakeup_card_req && + !pmadapter->pm_wakeup_fw_try) && + (wlan_is_cmd_pending(pmadapter) + || !wlan_bypass_tx_list_empty(pmadapter) + || !wlan_wmm_lists_empty(pmadapter) + )) { + wlan_pm_wakeup_card(pmadapter); + pmadapter->pm_wakeup_fw_try = MTRUE; + continue; + } + if (IS_CARD_RX_RCVD(pmadapter)) { + pmadapter->data_received = MFALSE; + if (pmadapter->hs_activated == MTRUE) { + pmadapter->is_hs_configured = MFALSE; + wlan_host_sleep_activated_event(wlan_get_priv + (pmadapter, + MLAN_BSS_ROLE_ANY), + MFALSE); + } + pmadapter->pm_wakeup_fw_try = MFALSE; + if (pmadapter->ps_state == PS_STATE_SLEEP) + pmadapter->ps_state = PS_STATE_AWAKE; + } else { + /* We have tried to wakeup the card already */ + if (pmadapter->pm_wakeup_fw_try) + break; + /* Check if we need to confirm Sleep Request received previously */ + if (pmadapter->ps_state == PS_STATE_PRE_SLEEP) + if (!pmadapter->cmd_sent && + !pmadapter->curr_cmd) + wlan_check_ps_cond(pmadapter); + if (pmadapter->ps_state != PS_STATE_AWAKE || + (pmadapter->tx_lock_flag == MTRUE)) + break; + + if (pmadapter->data_sent + || wlan_is_tdls_link_chan_switching(pmadapter-> + tdls_status) + || (wlan_bypass_tx_list_empty(pmadapter) && + wlan_wmm_lists_empty(pmadapter)) + || wlan_11h_radar_detected_tx_blocked(pmadapter) + ) { + if (pmadapter->cmd_sent || pmadapter->curr_cmd + || !wlan_is_send_cmd_allowed(pmadapter-> + tdls_status) || + !wlan_is_cmd_pending(pmadapter)) { + break; + } + } + } + + /* Check for Cmd Resp */ + if (pmadapter->cmd_resp_received) { + pmadapter->cmd_resp_received = MFALSE; + wlan_process_cmdresp(pmadapter); + + /* call moal back when init_fw is done */ + if (pmadapter->hw_status == WlanHardwareStatusInitdone) { + pmadapter->hw_status = WlanHardwareStatusReady; + wlan_init_fw_complete(pmadapter); + } else if (pmadapter->hw_status == + WlanHardwareStatusGetHwSpecdone) { + pmadapter->hw_status = + WlanHardwareStatusInitializing; + wlan_get_hw_spec_complete(pmadapter); + } + } + + /* Check for event */ + if (pmadapter->event_received) { + pmadapter->event_received = MFALSE; + wlan_process_event(pmadapter); + } + + /* Check if we need to confirm Sleep Request received previously */ + if (pmadapter->ps_state == PS_STATE_PRE_SLEEP) + if (!pmadapter->cmd_sent && !pmadapter->curr_cmd) + wlan_check_ps_cond(pmadapter); + + /* + * The ps_state may have been changed during processing of + * Sleep Request event. + */ + if ((pmadapter->ps_state == PS_STATE_SLEEP) + || (pmadapter->ps_state == PS_STATE_PRE_SLEEP) + || (pmadapter->ps_state == PS_STATE_SLEEP_CFM) + || (pmadapter->tx_lock_flag == MTRUE) + ) { + continue; + } + + if (!pmadapter->cmd_sent && !pmadapter->curr_cmd + && wlan_is_send_cmd_allowed(pmadapter->tdls_status) + ) { + if (wlan_exec_next_cmd(pmadapter) == + MLAN_STATUS_FAILURE) { + ret = MLAN_STATUS_FAILURE; + break; + } + } + + if (!pmadapter->data_sent && + !wlan_11h_radar_detected_tx_blocked(pmadapter) && + !wlan_is_tdls_link_chan_switching(pmadapter->tdls_status) && + !wlan_bypass_tx_list_empty(pmadapter)) { + PRINTM(MINFO, "mlan_send_pkt(): deq(bybass_txq)\n"); + wlan_process_bypass_tx(pmadapter); + if (pmadapter->hs_activated == MTRUE) { + pmadapter->is_hs_configured = MFALSE; + wlan_host_sleep_activated_event(wlan_get_priv + (pmadapter, + MLAN_BSS_ROLE_ANY), + MFALSE); + } + } + + if (!pmadapter->data_sent && !wlan_wmm_lists_empty(pmadapter) + && !wlan_11h_radar_detected_tx_blocked(pmadapter) + && !wlan_is_tdls_link_chan_switching(pmadapter->tdls_status) + ) { + wlan_wmm_process_tx(pmadapter); + if (pmadapter->hs_activated == MTRUE) { + pmadapter->is_hs_configured = MFALSE; + wlan_host_sleep_activated_event(wlan_get_priv + (pmadapter, + MLAN_BSS_ROLE_ANY), + MFALSE); + } + } + +#ifdef STA_SUPPORT + if (pmadapter->delay_null_pkt && !pmadapter->cmd_sent && + !pmadapter->curr_cmd && !wlan_is_cmd_pending(pmadapter) && + wlan_bypass_tx_list_empty(pmadapter) && + wlan_wmm_lists_empty(pmadapter)) { + if (wlan_send_null_packet + (wlan_get_priv(pmadapter, MLAN_BSS_ROLE_STA), + MRVDRV_TxPD_POWER_MGMT_NULL_PACKET | + MRVDRV_TxPD_POWER_MGMT_LAST_PACKET) + == MLAN_STATUS_SUCCESS) { + pmadapter->delay_null_pkt = MFALSE; + } + break; + } +#endif + + } while (MTRUE); + + pcb->moal_spin_lock(pmadapter->pmoal_handle, + pmadapter->pmain_proc_lock); + if (pmadapter->more_task_flag == MTRUE) { + pmadapter->more_task_flag = MFALSE; + pcb->moal_spin_unlock(pmadapter->pmoal_handle, + pmadapter->pmain_proc_lock); + goto process_start; + } + pmadapter->mlan_processing = MFALSE; + pcb->moal_spin_unlock(pmadapter->pmoal_handle, + pmadapter->pmain_proc_lock); + +exit_main_proc: + if (pmadapter->hw_status == WlanHardwareStatusClosing) + mlan_shutdown_fw(pmadapter); + LEAVE(); + return ret; +} + +/** + * @brief Function to send packet + * + * @param pmlan_adapter A pointer to mlan_adapter structure + * @param pmbuf A pointer to mlan_buffer structure + * + * @return MLAN_STATUS_PENDING + */ +mlan_status +mlan_send_packet(IN t_void *pmlan_adapter, IN pmlan_buffer pmbuf) +{ + mlan_status ret = MLAN_STATUS_PENDING; + mlan_adapter *pmadapter = (mlan_adapter *)pmlan_adapter; + mlan_private *pmpriv; + t_u16 eth_type = 0; + t_u8 ra[MLAN_MAC_ADDR_LENGTH]; + tdlsStatus_e tdls_status; + + ENTER(); + MASSERT(pmlan_adapter &&pmbuf); + + if (!pmlan_adapter ||!pmbuf) { + return MLAN_STATUS_FAILURE; + } + + MASSERT(pmbuf->bss_index < pmadapter->priv_num); + pmbuf->flags |= MLAN_BUF_FLAG_MOAL_TX_BUF; + pmpriv = pmadapter->priv[pmbuf->bss_index]; + + eth_type = + mlan_ntohs(*(t_u16 *)&pmbuf-> + pbuf[pmbuf->data_offset + + MLAN_ETHER_PKT_TYPE_OFFSET]); + if (((pmadapter->priv[pmbuf->bss_index]->port_ctrl_mode == MTRUE) && + ((eth_type == MLAN_ETHER_PKT_TYPE_EAPOL) + || (eth_type == MLAN_ETHER_PKT_TYPE_WAPI) + )) + || (eth_type == MLAN_ETHER_PKT_TYPE_TDLS_ACTION) + || (pmbuf->buf_type == MLAN_BUF_TYPE_RAW_DATA) + + ) { + if (eth_type == MLAN_ETHER_PKT_TYPE_TDLS_ACTION) { + memcpy(pmadapter, ra, pmbuf->pbuf + pmbuf->data_offset, + MLAN_MAC_ADDR_LENGTH); + tdls_status = wlan_get_tdls_link_status(pmpriv, ra); + if (MTRUE == wlan_is_tdls_link_setup(tdls_status) || + !pmpriv->media_connected) + pmbuf->flags |= MLAN_BUF_FLAG_TDLS; + } + PRINTM(MINFO, "mlan_send_pkt(): enq(bybass_txq)\n"); + wlan_add_buf_bypass_txqueue(pmadapter, pmbuf); + } else { + /* Transmit the packet */ + wlan_wmm_add_buf_txqueue(pmadapter, pmbuf); + } + + LEAVE(); + return ret; +} + +/** + * @brief MLAN ioctl handler + * + * @param adapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING --success, otherwise fail + */ +mlan_status +mlan_ioctl(IN t_void *adapter, IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_adapter pmadapter = (pmlan_adapter)adapter; + pmlan_private pmpriv = MNULL; + + ENTER(); + + if (pioctl_req == MNULL) { + PRINTM(MMSG, "Cancel all pending cmd!\n"); + wlan_cancel_all_pending_cmd(pmadapter); + goto exit; + } + if (pioctl_req->action == MLAN_ACT_CANCEL) { + wlan_cancel_pending_ioctl(pmadapter, pioctl_req); + ret = MLAN_STATUS_SUCCESS; + goto exit; + } + pmpriv = pmadapter->priv[pioctl_req->bss_index]; + ret = pmpriv->ops.ioctl(adapter, pioctl_req); +exit: + LEAVE(); + return ret; +} + +/** + * @brief Packet receive completion callback handler + * + * @param pmlan_adapter A pointer to mlan_adapter structure + * @param pmbuf A pointer to mlan_buffer structure + * @param status Callback status + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +mlan_recv_packet_complete(IN t_void *pmlan_adapter, + IN pmlan_buffer pmbuf, IN mlan_status status) +{ + mlan_adapter *pmadapter = (mlan_adapter *)pmlan_adapter; + + ENTER(); + wlan_recv_packet_complete(pmadapter, pmbuf, status); + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief select wmm queue + * + * @param pmlan_adapter A pointer to mlan_adapter structure + * @param bss_num BSS number + * @param tid TID + * + * @return wmm queue priority (0 - 3) + */ +t_u8 +mlan_select_wmm_queue(IN t_void *pmlan_adapter, IN t_u8 bss_num, IN t_u8 tid) +{ + mlan_adapter *pmadapter = (mlan_adapter *)pmlan_adapter; + pmlan_private pmpriv = pmadapter->priv[bss_num]; + t_u8 ret; + ENTER(); + ret = wlan_wmm_select_queue(pmpriv, tid); + LEAVE(); + return ret; +} + +/** + * @brief This function gets interrupt status. + * + */ +/** + * @param adapter A pointer to mlan_adapter structure + * @return N/A + */ +mlan_status +mlan_interrupt(IN t_void *adapter) +{ + mlan_adapter *pmadapter = (mlan_adapter *)adapter; + mlan_status ret; + + ENTER(); + ret = wlan_interrupt(pmadapter); + LEAVE(); + return ret; +} + +/** + * @brief This function wakeup firmware. + * + * @param adapter A pointer to mlan_adapter structure + * @return N/A + */ +t_void +mlan_pm_wakeup_card(IN t_void *adapter) +{ + mlan_adapter *pmadapter = (mlan_adapter *)adapter; + ENTER(); + wlan_pm_wakeup_card(pmadapter); + LEAVE(); +} + +/** + * @brief This function check main_process status. + * + * @param adapter A pointer to mlan_adapter structure + * @return MTRUE/MFALSE + */ +t_u8 +mlan_is_main_process_running(IN t_void *adapter) +{ + mlan_adapter *pmadapter = (mlan_adapter *)adapter; + pmlan_callbacks pcb = &pmadapter->callbacks; + t_u8 ret = MFALSE; + ENTER(); + pcb->moal_spin_lock(pmadapter->pmoal_handle, + pmadapter->pmain_proc_lock); + + /* Check if already processing */ + if (pmadapter->mlan_processing) { + pmadapter->more_task_flag = MTRUE; + ret = MTRUE; + } + pcb->moal_spin_unlock(pmadapter->pmoal_handle, + pmadapter->pmain_proc_lock); + LEAVE(); + return ret; +} diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_sta_cmd.c b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_sta_cmd.c new file mode 100644 index 000000000000..e1585240e013 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_sta_cmd.c @@ -0,0 +1,3438 @@ +/** @file mlan_sta_cmd.c + * + * @brief This file contains the handling of command. + * it prepares command and sends it to firmware when + * it is ready. + * + * Copyright (C) 2008-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +/****************************************************** +Change log: + 10/21/2008: initial version +******************************************************/ + +#include "mlan.h" +#include "mlan_join.h" +#include "mlan_util.h" +#include "mlan_fw.h" +#include "mlan_main.h" +#include "mlan_wmm.h" +#include "mlan_11n.h" +#include "mlan_11h.h" +#include "mlan_sdio.h" +#include "mlan_meas.h" + +/******************************************************** + Local Variables +********************************************************/ + +/******************************************************** + Global Variables +********************************************************/ + +/******************************************************** + Local Functions +********************************************************/ + +/** + * @brief This function prepares command of RSSI info. + * + * @param pmpriv A pointer to mlan_private structure + * @param pcmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action Command action + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_cmd_802_11_rssi_info(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *pcmd, IN t_u16 cmd_action) +{ + ENTER(); + + pcmd->command = wlan_cpu_to_le16(HostCmd_CMD_RSSI_INFO); + pcmd->size = + wlan_cpu_to_le16(sizeof(HostCmd_DS_802_11_RSSI_INFO) + + S_DS_GEN); + pcmd->params.rssi_info.action = wlan_cpu_to_le16(cmd_action); + pcmd->params.rssi_info.ndata = + wlan_cpu_to_le16(pmpriv->data_avg_factor); + pcmd->params.rssi_info.nbcn = wlan_cpu_to_le16(pmpriv->bcn_avg_factor); + + /* Reset SNR/NF/RSSI values in private structure */ + pmpriv->data_rssi_last = 0; + pmpriv->data_nf_last = 0; + pmpriv->data_rssi_avg = 0; + pmpriv->data_nf_avg = 0; + pmpriv->bcn_rssi_last = 0; + pmpriv->bcn_nf_last = 0; + pmpriv->bcn_rssi_avg = 0; + pmpriv->bcn_nf_avg = 0; + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of rssi_info_ext + * + * @param pmpriv A pointer to mlan_private structure + * @param pcmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action Command action + * @param pdata_buf A pointer to command information buffer + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_cmd_802_11_rssi_info_ext(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *pcmd, + IN t_u16 cmd_action, IN t_void *pdata_buf) +{ + HostCmd_DS_802_11_RSSI_INFO_EXT *rssi_info_ext_cmd = + &pcmd->params.rssi_info_ext; + mlan_ds_get_info *info = (mlan_ds_get_info *)pdata_buf; + MrvlIEtypes_RSSI_EXT_t *signal_info_tlv = MNULL; + t_u8 *pos = MNULL; + + ENTER(); + + pcmd->command = wlan_cpu_to_le16(HostCmd_CMD_RSSI_INFO_EXT); + pcmd->size = sizeof(HostCmd_DS_802_11_RSSI_INFO_EXT) + S_DS_GEN; + rssi_info_ext_cmd->action = wlan_cpu_to_le16(cmd_action); + rssi_info_ext_cmd->ndata = 0; + rssi_info_ext_cmd->nbcn = 0; + + if (info->param.path_id) { + pos = (t_u8 *)rssi_info_ext_cmd + + sizeof(HostCmd_DS_802_11_RSSI_INFO_EXT); + signal_info_tlv = (MrvlIEtypes_RSSI_EXT_t *) pos; + signal_info_tlv->header.len = + wlan_cpu_to_le16(sizeof(MrvlIEtypes_RSSI_EXT_t) - + sizeof(MrvlIEtypesHeader_t)); + signal_info_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_RSSI_INFO); + signal_info_tlv->path_id = + wlan_cpu_to_le16(info->param.path_id); + pcmd->size += sizeof(MrvlIEtypes_RSSI_EXT_t); + } + pcmd->size = wlan_cpu_to_le16(pcmd->size); + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of snmp_mib. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action The action: GET or SET + * @param cmd_oid OID: ENABLE or DISABLE + * @param pdata_buf A pointer to command information buffer + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_cmd_802_11_snmp_mib(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, + IN t_u32 cmd_oid, IN t_void *pdata_buf) +{ + HostCmd_DS_802_11_SNMP_MIB *psnmp_mib = &cmd->params.smib; + t_u32 ul_temp; + + ENTER(); + PRINTM(MINFO, "SNMP_CMD: cmd_oid = 0x%x\n", cmd_oid); + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_802_11_SNMP_MIB); + cmd->size = sizeof(HostCmd_DS_802_11_SNMP_MIB) - 1 + S_DS_GEN; + + if (cmd_action == HostCmd_ACT_GEN_GET) { + psnmp_mib->query_type = wlan_cpu_to_le16(HostCmd_ACT_GEN_GET); + psnmp_mib->buf_size = wlan_cpu_to_le16(MAX_SNMP_BUF_SIZE); + cmd->size += MAX_SNMP_BUF_SIZE; + } + + switch (cmd_oid) { + case DtimPeriod_i: + psnmp_mib->oid = wlan_cpu_to_le16((t_u16)DtimPeriod_i); + if (cmd_action == HostCmd_ACT_GEN_SET) { + psnmp_mib->query_type = + wlan_cpu_to_le16(HostCmd_ACT_GEN_SET); + psnmp_mib->buf_size = wlan_cpu_to_le16(sizeof(t_u8)); + ul_temp = *((t_u32 *)pdata_buf); + psnmp_mib->value[0] = (t_u8)ul_temp; + cmd->size += sizeof(t_u8); + } + break; + case FragThresh_i: + psnmp_mib->oid = wlan_cpu_to_le16((t_u16)FragThresh_i); + if (cmd_action == HostCmd_ACT_GEN_SET) { + psnmp_mib->query_type = + wlan_cpu_to_le16(HostCmd_ACT_GEN_SET); + psnmp_mib->buf_size = wlan_cpu_to_le16(sizeof(t_u16)); + ul_temp = *((t_u32 *)pdata_buf); + *((t_u16 *)(psnmp_mib->value)) = + wlan_cpu_to_le16((t_u16)ul_temp); + cmd->size += sizeof(t_u16); + } + break; + case RtsThresh_i: + psnmp_mib->oid = wlan_cpu_to_le16((t_u16)RtsThresh_i); + if (cmd_action == HostCmd_ACT_GEN_SET) { + psnmp_mib->query_type = + wlan_cpu_to_le16(HostCmd_ACT_GEN_SET); + psnmp_mib->buf_size = wlan_cpu_to_le16(sizeof(t_u16)); + ul_temp = *((t_u32 *)pdata_buf); + *(t_u16 *)(psnmp_mib->value) = + wlan_cpu_to_le16((t_u16)ul_temp); + cmd->size += sizeof(t_u16); + } + break; + + case ShortRetryLim_i: + psnmp_mib->oid = wlan_cpu_to_le16((t_u16)ShortRetryLim_i); + if (cmd_action == HostCmd_ACT_GEN_SET) { + psnmp_mib->query_type = + wlan_cpu_to_le16(HostCmd_ACT_GEN_SET); + psnmp_mib->buf_size = wlan_cpu_to_le16(sizeof(t_u16)); + ul_temp = (*(t_u32 *)pdata_buf); + *((t_u16 *)(psnmp_mib->value)) = + wlan_cpu_to_le16((t_u16)ul_temp); + cmd->size += sizeof(t_u16); + } + break; + case Dot11D_i: + psnmp_mib->oid = wlan_cpu_to_le16((t_u16)Dot11D_i); + if (cmd_action == HostCmd_ACT_GEN_SET) { + psnmp_mib->query_type = + wlan_cpu_to_le16(HostCmd_ACT_GEN_SET); + psnmp_mib->buf_size = wlan_cpu_to_le16(sizeof(t_u16)); + ul_temp = *(t_u32 *)pdata_buf; + *((t_u16 *)(psnmp_mib->value)) = + wlan_cpu_to_le16((t_u16)ul_temp); + cmd->size += sizeof(t_u16); + } + break; + case Dot11H_i: + psnmp_mib->oid = wlan_cpu_to_le16((t_u16)Dot11H_i); + if (cmd_action == HostCmd_ACT_GEN_SET) { + psnmp_mib->query_type = + wlan_cpu_to_le16(HostCmd_ACT_GEN_SET); + psnmp_mib->buf_size = wlan_cpu_to_le16(sizeof(t_u16)); + ul_temp = *(t_u32 *)pdata_buf; + *((t_u16 *)(psnmp_mib->value)) = + wlan_cpu_to_le16((t_u16)ul_temp); + cmd->size += sizeof(t_u16); + } + break; + case WwsMode_i: + psnmp_mib->oid = wlan_cpu_to_le16((t_u16)WwsMode_i); + if (cmd_action == HostCmd_ACT_GEN_SET) { + psnmp_mib->query_type = + wlan_cpu_to_le16(HostCmd_ACT_GEN_SET); + psnmp_mib->buf_size = wlan_cpu_to_le16(sizeof(t_u16)); + ul_temp = *((t_u32 *)pdata_buf); + *((t_u16 *)(psnmp_mib->value)) = + wlan_cpu_to_le16((t_u16)ul_temp); + cmd->size += sizeof(t_u16); + } + break; + case Thermal_i: + psnmp_mib->oid = wlan_cpu_to_le16((t_u16)Thermal_i); + break; + case NullPktPeriod_i: + /** keep alive null data pkt interval in full power mode */ + psnmp_mib->oid = wlan_cpu_to_le16((t_u16)NullPktPeriod_i); + if (cmd_action == HostCmd_ACT_GEN_SET) { + psnmp_mib->query_type = + wlan_cpu_to_le16(HostCmd_ACT_GEN_SET); + psnmp_mib->buf_size = wlan_cpu_to_le16(sizeof(t_u32)); + ul_temp = *((t_u32 *)pdata_buf); + ul_temp = wlan_cpu_to_le32(ul_temp); + memcpy(pmpriv->adapter, psnmp_mib->value, &ul_temp, + sizeof(t_u32)); + cmd->size += sizeof(t_u32); + } + break; + case ECSAEnable_i: + psnmp_mib->oid = wlan_cpu_to_le16((t_u16)ECSAEnable_i); + if (cmd_action == HostCmd_ACT_GEN_SET) { + psnmp_mib->query_type = + wlan_cpu_to_le16(HostCmd_ACT_GEN_SET); + psnmp_mib->buf_size = wlan_cpu_to_le16(sizeof(t_u8)); + psnmp_mib->value[0] = *((t_u8 *)pdata_buf); + cmd->size += sizeof(t_u8); + } + break; + case SignalextEnable_i: + psnmp_mib->oid = wlan_cpu_to_le16((t_u16)SignalextEnable_i); + if (cmd_action == HostCmd_ACT_GEN_SET) { + psnmp_mib->query_type = + wlan_cpu_to_le16(HostCmd_ACT_GEN_SET); + psnmp_mib->buf_size = wlan_cpu_to_le16(sizeof(t_u8)); + psnmp_mib->value[0] = *(t_u8 *)pdata_buf; + cmd->size += sizeof(t_u8); + } + break; + default: + break; + } + cmd->size = wlan_cpu_to_le16(cmd->size); + PRINTM(MINFO, + "SNMP_CMD: Action=0x%x, OID=0x%x, OIDSize=0x%x, Value=0x%x\n", + cmd_action, cmd_oid, wlan_le16_to_cpu(psnmp_mib->buf_size), + wlan_le16_to_cpu(*(t_u16 *)psnmp_mib->value)); + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of get_log. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_cmd_802_11_get_log(IN pmlan_private pmpriv, IN HostCmd_DS_COMMAND *cmd) +{ + ENTER(); + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_802_11_GET_LOG); + cmd->size = + wlan_cpu_to_le16(sizeof(HostCmd_DS_802_11_GET_LOG) + S_DS_GEN); + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of tx_power_cfg. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action The action: GET or SET + * @param pdata_buf A pointer to data buffer + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_cmd_tx_power_cfg(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, IN t_void *pdata_buf) +{ + MrvlTypes_Power_Group_t *ppg_tlv = MNULL; + HostCmd_DS_TXPWR_CFG *ptxp = MNULL; + HostCmd_DS_TXPWR_CFG *ptxp_cfg = &cmd->params.txp_cfg; + + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_TXPWR_CFG); + cmd->size = wlan_cpu_to_le16(S_DS_GEN + sizeof(HostCmd_DS_TXPWR_CFG)); + switch (cmd_action) { + case HostCmd_ACT_GEN_SET: + ptxp = (HostCmd_DS_TXPWR_CFG *)pdata_buf; + if (ptxp->mode) { + ppg_tlv = + (MrvlTypes_Power_Group_t *)((t_u8 *)pdata_buf + + sizeof + (HostCmd_DS_TXPWR_CFG)); + memmove(pmpriv->adapter, ptxp_cfg, pdata_buf, + sizeof(HostCmd_DS_TXPWR_CFG) + + sizeof(MrvlTypes_Power_Group_t) + + ppg_tlv->length); + + ppg_tlv = (MrvlTypes_Power_Group_t *)((t_u8 *)ptxp_cfg + + sizeof + (HostCmd_DS_TXPWR_CFG)); + cmd->size += + wlan_cpu_to_le16(sizeof(MrvlTypes_Power_Group_t) + + ppg_tlv->length); + ppg_tlv->type = wlan_cpu_to_le16(ppg_tlv->type); + ppg_tlv->length = wlan_cpu_to_le16(ppg_tlv->length); + } else { + memmove(pmpriv->adapter, ptxp_cfg, pdata_buf, + sizeof(HostCmd_DS_TXPWR_CFG)); + } + ptxp_cfg->action = wlan_cpu_to_le16(cmd_action); + ptxp_cfg->cfg_index = wlan_cpu_to_le16(ptxp_cfg->cfg_index); + ptxp_cfg->mode = wlan_cpu_to_le32(ptxp_cfg->mode); + break; + case HostCmd_ACT_GEN_GET: + ptxp_cfg->action = wlan_cpu_to_le16(cmd_action); + break; + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of rf_tx_power. + * + * @param pmpriv A pointer to wlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action the action: GET or SET + * @param pdata_buf A pointer to data buffer + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_cmd_802_11_rf_tx_power(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, IN t_void *pdata_buf) +{ + + HostCmd_DS_802_11_RF_TX_POWER *prtp = &cmd->params.txp; + + ENTER(); + + cmd->size = wlan_cpu_to_le16((sizeof(HostCmd_DS_802_11_RF_TX_POWER)) + + S_DS_GEN); + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_802_11_RF_TX_POWER); + prtp->action = cmd_action; + + PRINTM(MINFO, "RF_TX_POWER_CMD: Size:%d Cmd:0x%x Act:%d\n", + cmd->size, cmd->command, prtp->action); + + switch (cmd_action) { + case HostCmd_ACT_GEN_GET: + prtp->action = wlan_cpu_to_le16(HostCmd_ACT_GEN_GET); + prtp->current_level = 0; + break; + + case HostCmd_ACT_GEN_SET: + prtp->action = wlan_cpu_to_le16(HostCmd_ACT_GEN_SET); + prtp->current_level = wlan_cpu_to_le16(*((t_s16 *)pdata_buf)); + break; + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +#ifdef WIFI_DIRECT_SUPPORT +/** + * @brief Check if any p2p interface is conencted + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return MTRUE/MFALSE; + */ +static t_u8 +wlan_is_p2p_connected(IN pmlan_adapter pmadapter) +{ + int j; + pmlan_private priv; + ENTER(); + for (j = 0; j < pmadapter->priv_num; ++j) { + priv = pmadapter->priv[j]; + if (priv) { + if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT) { + if ((priv->bss_role == MLAN_BSS_ROLE_STA) && + (priv->media_connected == MTRUE)) { + LEAVE(); + return MTRUE; + } + if ((priv->bss_role == MLAN_BSS_ROLE_UAP) && + (priv->uap_bss_started == MTRUE)) { + LEAVE(); + return MTRUE; + } + } + } + } + LEAVE(); + return MFALSE; +} +#endif + +/** + * @brief This function prepares command of hs_cfg. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action The action: GET or SET + * @param pdata_buf A pointer to data buffer + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_cmd_802_11_hs_cfg(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, IN hs_config_param *pdata_buf) +{ + pmlan_adapter pmadapter = pmpriv->adapter; + HostCmd_DS_802_11_HS_CFG_ENH *phs_cfg = &cmd->params.opt_hs_cfg; + t_u16 hs_activate = MFALSE; + t_u8 *tlv = (t_u8 *)phs_cfg + sizeof(HostCmd_DS_802_11_HS_CFG_ENH); + MrvlIEtypes_HsWakeHoldoff_t *holdoff_tlv = MNULL; + MrvlIEtypes_PsParamsInHs_t *psparam_tlv = MNULL; + MrvlIEtypes_WakeupSourceGPIO_t *gpio_tlv = MNULL; + MrvlIEtypes_MgmtFrameFilter_t *mgmt_filter_tlv = MNULL; + MrvlIEtypes_WakeupExtend_t *ext_tlv = MNULL; + + ENTER(); + + if (pdata_buf == MNULL) { + /* New Activate command */ + hs_activate = MTRUE; + } + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_802_11_HS_CFG_ENH); + + if (!hs_activate && (pdata_buf->conditions != HOST_SLEEP_CFG_CANCEL) + && ((pmadapter->arp_filter_size > 0) + && (pmadapter->arp_filter_size <= ARP_FILTER_MAX_BUF_SIZE))) { + PRINTM(MINFO, "Attach %d bytes ArpFilter to HSCfg cmd\n", + pmadapter->arp_filter_size); + memcpy(pmpriv->adapter, + ((t_u8 *)phs_cfg) + sizeof(HostCmd_DS_802_11_HS_CFG_ENH), + pmadapter->arp_filter, pmadapter->arp_filter_size); + cmd->size = + pmadapter->arp_filter_size + + sizeof(HostCmd_DS_802_11_HS_CFG_ENH) + S_DS_GEN; + tlv = (t_u8 *)phs_cfg + sizeof(HostCmd_DS_802_11_HS_CFG_ENH) + + pmadapter->arp_filter_size; + } else + cmd->size = S_DS_GEN + sizeof(HostCmd_DS_802_11_HS_CFG_ENH); + + if (hs_activate) { + cmd->size = wlan_cpu_to_le16(cmd->size); + phs_cfg->action = wlan_cpu_to_le16(HS_ACTIVATE); + phs_cfg->params.hs_activate.resp_ctrl = + wlan_cpu_to_le16(RESP_NEEDED); + } else { + phs_cfg->action = wlan_cpu_to_le16(HS_CONFIGURE); +#ifdef WIFI_DIRECT_SUPPORT + if (wlan_is_p2p_connected(pmadapter)) + phs_cfg->params.hs_config.conditions = + wlan_cpu_to_le32(pdata_buf-> + conditions | + HOST_SLEEP_COND_MULTICAST_DATA); + else +#endif + phs_cfg->params.hs_config.conditions = + wlan_cpu_to_le32(pdata_buf->conditions); + phs_cfg->params.hs_config.gpio = pdata_buf->gpio; + phs_cfg->params.hs_config.gap = pdata_buf->gap; + if (pmadapter->min_wake_holdoff) { + cmd->size += sizeof(MrvlIEtypes_HsWakeHoldoff_t); + holdoff_tlv = (MrvlIEtypes_HsWakeHoldoff_t *)tlv; + holdoff_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_HS_WAKE_HOLDOFF); + holdoff_tlv->header.len = + wlan_cpu_to_le16(sizeof + (MrvlIEtypes_HsWakeHoldoff_t) - + sizeof(MrvlIEtypesHeader_t)); + holdoff_tlv->min_wake_holdoff = + wlan_cpu_to_le16(pmadapter->min_wake_holdoff); + tlv += sizeof(MrvlIEtypes_HsWakeHoldoff_t); + PRINTM(MCMND, "min_wake_holdoff=%d\n", + pmadapter->min_wake_holdoff); + } + if (pmadapter->hs_wake_interval && pmpriv->media_connected && + (pmpriv->bss_type == MLAN_BSS_TYPE_STA)) { + cmd->size += sizeof(MrvlIEtypes_PsParamsInHs_t); + psparam_tlv = (MrvlIEtypes_PsParamsInHs_t *) tlv; + psparam_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_PS_PARAMS_IN_HS); + psparam_tlv->header.len = + wlan_cpu_to_le16(sizeof + (MrvlIEtypes_PsParamsInHs_t) - + sizeof(MrvlIEtypesHeader_t)); + psparam_tlv->hs_wake_interval = + wlan_cpu_to_le32(pmadapter->hs_wake_interval); + psparam_tlv->hs_inactivity_timeout = + wlan_cpu_to_le32(pmadapter-> + hs_inactivity_timeout); + tlv += sizeof(MrvlIEtypes_PsParamsInHs_t); + PRINTM(MCMND, "hs_wake_interval=%d\n", + pmadapter->hs_wake_interval); + PRINTM(MCMND, "hs_inactivity_timeout=%d\n", + pmadapter->hs_inactivity_timeout); + } + if (pmadapter->param_type_ind == 1) { + cmd->size += sizeof(MrvlIEtypes_WakeupSourceGPIO_t); + gpio_tlv = (MrvlIEtypes_WakeupSourceGPIO_t *) tlv; + gpio_tlv->header.type = + wlan_cpu_to_le16 + (TLV_TYPE_HS_WAKEUP_SOURCE_GPIO); + gpio_tlv->header.len = + wlan_cpu_to_le16(sizeof + (MrvlIEtypes_WakeupSourceGPIO_t) + - sizeof(MrvlIEtypesHeader_t)); + gpio_tlv->ind_gpio = (t_u8)pmadapter->ind_gpio; + gpio_tlv->level = (t_u8)pmadapter->level; + tlv += sizeof(MrvlIEtypes_WakeupSourceGPIO_t); + } + if (pmadapter->param_type_ext == 2) { + cmd->size += sizeof(MrvlIEtypes_WakeupExtend_t); + ext_tlv = (MrvlIEtypes_WakeupExtend_t *) tlv; + ext_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_WAKEUP_EXTEND); + ext_tlv->header.len = + wlan_cpu_to_le16(sizeof + (MrvlIEtypes_WakeupExtend_t) - + sizeof(MrvlIEtypesHeader_t)); + ext_tlv->event_force_ignore = + wlan_cpu_to_le32(pmadapter->event_force_ignore); + ext_tlv->event_use_ext_gap = + wlan_cpu_to_le32(pmadapter->event_use_ext_gap); + ext_tlv->ext_gap = pmadapter->ext_gap; + ext_tlv->gpio_wave = pmadapter->gpio_wave; + tlv += sizeof(MrvlIEtypes_WakeupExtend_t); + } + if (pmadapter->mgmt_filter[0].type) { + int i = 0; + mgmt_frame_filter mgmt_filter[MAX_MGMT_FRAME_FILTER]; + memset(pmadapter, mgmt_filter, 0, + MAX_MGMT_FRAME_FILTER * + sizeof(mgmt_frame_filter)); + mgmt_filter_tlv = (MrvlIEtypes_MgmtFrameFilter_t *) tlv; + mgmt_filter_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_MGMT_FRAME_WAKEUP); + tlv += sizeof(MrvlIEtypesHeader_t); + while (i < MAX_MGMT_FRAME_FILTER && + pmadapter->mgmt_filter[i].type) { + mgmt_filter[i].action = + (t_u8)pmadapter->mgmt_filter[i].action; + mgmt_filter[i].type = + (t_u8)pmadapter->mgmt_filter[i].type; + mgmt_filter[i].frame_mask = + wlan_cpu_to_le32(pmadapter-> + mgmt_filter[i]. + frame_mask); + i++; + } + memcpy(pmadapter, (t_u8 *)mgmt_filter_tlv->filter, + (t_u8 *)mgmt_filter, + i * sizeof(mgmt_frame_filter)); + tlv += i * sizeof(mgmt_frame_filter); + mgmt_filter_tlv->header.len = + wlan_cpu_to_le16(i * sizeof(mgmt_frame_filter)); + cmd->size += + i * sizeof(mgmt_frame_filter) + + sizeof(MrvlIEtypesHeader_t); + } + cmd->size = wlan_cpu_to_le16(cmd->size); + PRINTM(MCMND, + "HS_CFG_CMD: condition:0x%x gpio:0x%x gap:0x%x holdoff=%d wake_interval=%d inactivity_timeout=%d\n", + phs_cfg->params.hs_config.conditions, + phs_cfg->params.hs_config.gpio, + phs_cfg->params.hs_config.gap, + pmadapter->min_wake_holdoff, pmadapter->hs_wake_interval, + pmadapter->hs_inactivity_timeout); + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of mac_address. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action The action: GET or SET + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_cmd_802_11_mac_address(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, IN t_u16 cmd_action) +{ + ENTER(); + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_802_11_MAC_ADDRESS); + cmd->size = wlan_cpu_to_le16(sizeof(HostCmd_DS_802_11_MAC_ADDRESS) + + S_DS_GEN); + cmd->result = 0; + + cmd->params.mac_addr.action = wlan_cpu_to_le16(cmd_action); + + if (cmd_action == HostCmd_ACT_GEN_SET) { + memcpy(pmpriv->adapter, cmd->params.mac_addr.mac_addr, + pmpriv->curr_addr, MLAN_MAC_ADDR_LENGTH); + /* HEXDUMP("SET_CMD: MAC ADDRESS-", priv->CurrentAddr, 6); */ + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of sleep_period. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action The action: GET or SET + * @param pdata_buf A pointer to data buffer + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_cmd_802_11_sleep_period(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, IN t_u16 *pdata_buf) +{ + HostCmd_DS_802_11_SLEEP_PERIOD *pcmd_sleep_pd = &cmd->params.sleep_pd; + + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_802_11_SLEEP_PERIOD); + cmd->size = + wlan_cpu_to_le16(sizeof(HostCmd_DS_802_11_SLEEP_PERIOD) + + S_DS_GEN); + if (cmd_action == HostCmd_ACT_GEN_SET) { + pcmd_sleep_pd->sleep_pd = wlan_cpu_to_le16(*(t_u16 *)pdata_buf); + } + pcmd_sleep_pd->action = wlan_cpu_to_le16(cmd_action); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of sleep_params. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action The action: GET or SET + * @param pdata_buf A pointer to data buffer + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_cmd_802_11_sleep_params(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, IN t_u16 *pdata_buf) +{ + HostCmd_DS_802_11_SLEEP_PARAMS *pcmd_sp = &cmd->params.sleep_param; + mlan_ds_sleep_params *psp = (mlan_ds_sleep_params *)pdata_buf; + + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_802_11_SLEEP_PARAMS); + cmd->size = + wlan_cpu_to_le16(sizeof(HostCmd_DS_802_11_SLEEP_PARAMS) + + S_DS_GEN); + if (cmd_action == HostCmd_ACT_GEN_SET) { + pcmd_sp->reserved = (t_u16)psp->reserved; + pcmd_sp->error = (t_u16)psp->error; + pcmd_sp->offset = (t_u16)psp->offset; + pcmd_sp->stable_time = (t_u16)psp->stable_time; + pcmd_sp->cal_control = (t_u8)psp->cal_control; + pcmd_sp->external_sleep_clk = (t_u8)psp->ext_sleep_clk; + + pcmd_sp->reserved = wlan_cpu_to_le16(pcmd_sp->reserved); + pcmd_sp->error = wlan_cpu_to_le16(pcmd_sp->error); + pcmd_sp->offset = wlan_cpu_to_le16(pcmd_sp->offset); + pcmd_sp->stable_time = wlan_cpu_to_le16(pcmd_sp->stable_time); + } + pcmd_sp->action = wlan_cpu_to_le16(cmd_action); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of mac_multicast_adr. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action The action: GET or SET + * @param pdata_buf A pointer to data buffer + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_cmd_mac_multicast_adr(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, IN t_void *pdata_buf) +{ + mlan_multicast_list *pmcast_list = (mlan_multicast_list *)pdata_buf; + HostCmd_DS_MAC_MULTICAST_ADR *pmc_addr = &cmd->params.mc_addr; + + ENTER(); + cmd->size = + wlan_cpu_to_le16(sizeof(HostCmd_DS_MAC_MULTICAST_ADR) + + S_DS_GEN); + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_MAC_MULTICAST_ADR); + + pmc_addr->action = wlan_cpu_to_le16(cmd_action); + pmc_addr->num_of_adrs = + wlan_cpu_to_le16((t_u16)pmcast_list->num_multicast_addr); + memcpy(pmpriv->adapter, pmc_addr->mac_list, pmcast_list->mac_list, + pmcast_list->num_multicast_addr * MLAN_MAC_ADDR_LENGTH); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of deauthenticate/disassociate. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd_no Command number + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param pdata_buf A pointer to data buffer + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_cmd_802_11_deauthenticate(IN pmlan_private pmpriv, + IN t_u16 cmd_no, + IN HostCmd_DS_COMMAND *cmd, IN t_void *pdata_buf) +{ + HostCmd_DS_802_11_DEAUTHENTICATE *pdeauth = &cmd->params.deauth; + + ENTER(); + + cmd->command = wlan_cpu_to_le16(cmd_no); + cmd->size = + wlan_cpu_to_le16(sizeof(HostCmd_DS_802_11_DEAUTHENTICATE) + + S_DS_GEN); + + /* Set AP MAC address */ + memcpy(pmpriv->adapter, pdeauth, (t_u8 *)pdata_buf, sizeof(*pdeauth)); + if (cmd_no == HostCmd_CMD_802_11_DEAUTHENTICATE) + PRINTM(MCMND, "Deauth: " MACSTR "\n", + MAC2STR(pdeauth->mac_addr)); + else + PRINTM(MCMND, "Disassociate: " MACSTR "\n", + MAC2STR(pdeauth->mac_addr)); + + if (pmpriv->adapter->state_11h.recvd_chanswann_event) { +/** Reason code 36 = Requested from peer station as it is leaving the BSS */ +#define REASON_CODE_PEER_STA_LEAVING 36 + pdeauth->reason_code = + wlan_cpu_to_le16(REASON_CODE_PEER_STA_LEAVING); + } else { + pdeauth->reason_code = wlan_cpu_to_le16(pdeauth->reason_code); + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of ad_hoc_stop. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_cmd_802_11_ad_hoc_stop(IN pmlan_private pmpriv, IN HostCmd_DS_COMMAND *cmd) +{ + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_802_11_AD_HOC_STOP); + cmd->size = wlan_cpu_to_le16(S_DS_GEN); + + if (wlan_11h_is_active(pmpriv)) + wlan_11h_activate(pmpriv, MNULL, MFALSE); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of key_material. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action The action: GET or SET + * @param cmd_oid OID: ENABLE or DISABLE + * @param pdata_buf A pointer to data buffer + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status +wlan_cmd_802_11_key_material(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, + IN t_u32 cmd_oid, IN t_void *pdata_buf) +{ + HostCmd_DS_802_11_KEY_MATERIAL *pkey_material = + &cmd->params.key_material; + mlan_ds_encrypt_key *pkey = (mlan_ds_encrypt_key *)pdata_buf; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + if (!pkey) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_802_11_KEY_MATERIAL); + pkey_material->action = wlan_cpu_to_le16(cmd_action); + if (cmd_action == HostCmd_ACT_GEN_GET) { + PRINTM(MCMND, "GET Key\n"); + pkey_material->key_param_set.key_idx = + pkey->key_index & KEY_INDEX_MASK; + pkey_material->key_param_set.type = + wlan_cpu_to_le16(TLV_TYPE_KEY_PARAM_V2); + pkey_material->key_param_set.length = + wlan_cpu_to_le16(KEY_PARAMS_FIXED_LEN); + memcpy(pmpriv->adapter, pkey_material->key_param_set.mac_addr, + pkey->mac_addr, MLAN_MAC_ADDR_LENGTH); + if (pkey->key_flags & KEY_FLAG_GROUP_KEY) + pkey_material->key_param_set.key_info |= + KEY_INFO_MCAST_KEY; + else + pkey_material->key_param_set.key_info |= + KEY_INFO_UCAST_KEY; + if (pkey->key_flags & KEY_FLAG_AES_MCAST_IGTK) + pkey_material->key_param_set.key_info = + KEY_INFO_CMAC_AES_KEY; + pkey_material->key_param_set.key_info = + wlan_cpu_to_le16(pkey_material->key_param_set.key_info); + cmd->size = + wlan_cpu_to_le16(sizeof(MrvlIEtypesHeader_t) + + S_DS_GEN + KEY_PARAMS_FIXED_LEN + + sizeof(pkey_material->action)); + goto done; + } + memset(pmpriv->adapter, &pkey_material->key_param_set, 0, + sizeof(MrvlIEtype_KeyParamSetV2_t)); + if (pkey->key_flags & KEY_FLAG_REMOVE_KEY) { + pkey_material->action = + wlan_cpu_to_le16(HostCmd_ACT_GEN_REMOVE); + pkey_material->key_param_set.type = + wlan_cpu_to_le16(TLV_TYPE_KEY_PARAM_V2); + pkey_material->key_param_set.length = + wlan_cpu_to_le16(KEY_PARAMS_FIXED_LEN); + pkey_material->key_param_set.key_idx = + pkey->key_index & KEY_INDEX_MASK; + pkey_material->key_param_set.key_info = + wlan_cpu_to_le16(KEY_INFO_MCAST_KEY | + KEY_INFO_UCAST_KEY); + memcpy(pmpriv->adapter, pkey_material->key_param_set.mac_addr, + pkey->mac_addr, MLAN_MAC_ADDR_LENGTH); + cmd->size = + wlan_cpu_to_le16(sizeof(MrvlIEtypesHeader_t) + + S_DS_GEN + KEY_PARAMS_FIXED_LEN + + sizeof(pkey_material->action)); + PRINTM(MCMND, "Remove Key\n"); + goto done; + } + pkey_material->action = wlan_cpu_to_le16(HostCmd_ACT_GEN_SET); + pkey_material->key_param_set.key_idx = pkey->key_index & KEY_INDEX_MASK; + pkey_material->key_param_set.type = + wlan_cpu_to_le16(TLV_TYPE_KEY_PARAM_V2); + pkey_material->key_param_set.key_info = KEY_INFO_ENABLE_KEY; + memcpy(pmpriv->adapter, pkey_material->key_param_set.mac_addr, + pkey->mac_addr, MLAN_MAC_ADDR_LENGTH); + if (pkey->key_len <= MAX_WEP_KEY_SIZE) { + pkey_material->key_param_set.length = + wlan_cpu_to_le16(KEY_PARAMS_FIXED_LEN + + sizeof(wep_param_t)); + pkey_material->key_param_set.key_type = KEY_TYPE_ID_WEP; + if (pkey->is_current_wep_key) { + pkey_material->key_param_set.key_info |= + KEY_INFO_MCAST_KEY | KEY_INFO_UCAST_KEY; + if (pkey_material->key_param_set.key_idx == + (pmpriv->wep_key_curr_index & KEY_INDEX_MASK)) + pkey_material->key_param_set.key_info |= + KEY_INFO_DEFAULT_KEY; + } else { + if (pkey->key_flags & KEY_FLAG_GROUP_KEY) + pkey_material->key_param_set.key_info |= + KEY_INFO_MCAST_KEY; + else + pkey_material->key_param_set.key_info |= + KEY_INFO_UCAST_KEY; + if (pkey->key_flags & KEY_FLAG_SET_TX_KEY) + pkey_material->key_param_set.key_info |= + KEY_INFO_DEFAULT_KEY; + } + pkey_material->key_param_set.key_info = + wlan_cpu_to_le16(pkey_material->key_param_set.key_info); + pkey_material->key_param_set.key_params.wep.key_len = + wlan_cpu_to_le16(pkey->key_len); + memcpy(pmpriv->adapter, + pkey_material->key_param_set.key_params.wep.key, + pkey->key_material, pkey->key_len); + cmd->size = + wlan_cpu_to_le16(sizeof(MrvlIEtypesHeader_t) + + S_DS_GEN + KEY_PARAMS_FIXED_LEN + + sizeof(wep_param_t) + + sizeof(pkey_material->action)); + PRINTM(MCMND, "Set WEP Key\n"); + goto done; + } + if (pkey->key_flags & KEY_FLAG_GROUP_KEY) + pkey_material->key_param_set.key_info |= KEY_INFO_MCAST_KEY; + else + pkey_material->key_param_set.key_info |= KEY_INFO_UCAST_KEY; + if (pkey->key_flags & KEY_FLAG_SET_TX_KEY) + pkey_material->key_param_set.key_info |= + KEY_INFO_TX_KEY | KEY_INFO_RX_KEY; + else + pkey_material->key_param_set.key_info |= KEY_INFO_RX_KEY; + if (pkey->is_wapi_key) { + pkey_material->key_param_set.key_type = KEY_TYPE_ID_WAPI; + memcpy(pmpriv->adapter, + pkey_material->key_param_set.key_params.wapi.pn, + pkey->pn, PN_SIZE); + pkey_material->key_param_set.key_params.wapi.key_len = + wlan_cpu_to_le16(pkey->key_len); + memcpy(pmpriv->adapter, + pkey_material->key_param_set.key_params.wapi.key, + pkey->key_material, pkey->key_len); + if (!pmpriv->sec_info.wapi_key_on) + pkey_material->key_param_set.key_info |= + KEY_INFO_DEFAULT_KEY; + if (pkey->key_flags & KEY_FLAG_GROUP_KEY) + pmpriv->sec_info.wapi_key_on = MTRUE; + pkey_material->key_param_set.key_info = + wlan_cpu_to_le16(pkey_material->key_param_set.key_info); + pkey_material->key_param_set.length = + wlan_cpu_to_le16(KEY_PARAMS_FIXED_LEN + + sizeof(wapi_param)); + cmd->size = + wlan_cpu_to_le16(sizeof(MrvlIEtypesHeader_t) + + S_DS_GEN + KEY_PARAMS_FIXED_LEN + + sizeof(wapi_param) + + sizeof(pkey_material->action)); + PRINTM(MCMND, "Set WAPI Key\n"); + goto done; + } + if (pmpriv->bss_mode == MLAN_BSS_MODE_INFRA) { + /* Enable default key for WPA/WPA2 */ + if (!pmpriv->wpa_is_gtk_set) + pkey_material->key_param_set.key_info |= + KEY_INFO_DEFAULT_KEY; + } else { + pkey_material->key_param_set.key_info |= KEY_INFO_DEFAULT_KEY; + /* Enable unicast bit for WPA-NONE/ADHOC_AES */ + if ((!pmpriv->sec_info.wpa2_enabled) && + (pkey->key_flags & KEY_FLAG_SET_TX_KEY)) + pkey_material->key_param_set.key_info |= + KEY_INFO_UCAST_KEY; + } + pkey_material->key_param_set.key_info = + wlan_cpu_to_le16(pkey_material->key_param_set.key_info); + if (pkey->key_len == WPA_AES_KEY_LEN && + !(pkey->key_flags & KEY_FLAG_AES_MCAST_IGTK)) { + if (pkey-> + key_flags & (KEY_FLAG_RX_SEQ_VALID | KEY_FLAG_TX_SEQ_VALID)) + memcpy(pmpriv->adapter, + pkey_material->key_param_set.key_params.aes.pn, + pkey->pn, SEQ_MAX_SIZE); + pkey_material->key_param_set.key_type = KEY_TYPE_ID_AES; + pkey_material->key_param_set.key_params.aes.key_len = + wlan_cpu_to_le16(pkey->key_len); + memcpy(pmpriv->adapter, + pkey_material->key_param_set.key_params.aes.key, + pkey->key_material, pkey->key_len); + pkey_material->key_param_set.length = + wlan_cpu_to_le16(KEY_PARAMS_FIXED_LEN + + sizeof(aes_param)); + cmd->size = + wlan_cpu_to_le16(sizeof(MrvlIEtypesHeader_t) + + S_DS_GEN + KEY_PARAMS_FIXED_LEN + + sizeof(aes_param) + + sizeof(pkey_material->action)); + PRINTM(MCMND, "Set AES Key\n"); + goto done; + } + if (pkey->key_len == WPA_IGTK_KEY_LEN && + (pkey->key_flags & KEY_FLAG_AES_MCAST_IGTK)) { + if (pkey-> + key_flags & (KEY_FLAG_RX_SEQ_VALID | KEY_FLAG_TX_SEQ_VALID)) + memcpy(pmpriv->adapter, + pkey_material->key_param_set.key_params.cmac_aes. + ipn, pkey->pn, SEQ_MAX_SIZE); + pkey_material->key_param_set.key_info &= + ~(wlan_cpu_to_le16(KEY_INFO_MCAST_KEY)); + pkey_material->key_param_set.key_info |= + wlan_cpu_to_le16(KEY_INFO_AES_MCAST_IGTK); + pkey_material->key_param_set.key_type = KEY_TYPE_ID_AES_CMAC; + pkey_material->key_param_set.key_params.cmac_aes.key_len = + wlan_cpu_to_le16(pkey->key_len); + memcpy(pmpriv->adapter, + pkey_material->key_param_set.key_params.cmac_aes.key, + pkey->key_material, pkey->key_len); + pkey_material->key_param_set.length = + wlan_cpu_to_le16(KEY_PARAMS_FIXED_LEN + + sizeof(cmac_aes_param)); + cmd->size = + wlan_cpu_to_le16(sizeof(MrvlIEtypesHeader_t) + + S_DS_GEN + KEY_PARAMS_FIXED_LEN + + sizeof(cmac_aes_param) + + sizeof(pkey_material->action)); + PRINTM(MCMND, "Set CMAC AES Key\n"); + goto done; + } + if (pkey->key_len == WPA_TKIP_KEY_LEN) { + if (pkey-> + key_flags & (KEY_FLAG_RX_SEQ_VALID | KEY_FLAG_TX_SEQ_VALID)) + memcpy(pmpriv->adapter, + pkey_material->key_param_set.key_params.tkip.pn, + pkey->pn, SEQ_MAX_SIZE); + pkey_material->key_param_set.key_type = KEY_TYPE_ID_TKIP; + pkey_material->key_param_set.key_params.tkip.key_len = + wlan_cpu_to_le16(pkey->key_len); + memcpy(pmpriv->adapter, + pkey_material->key_param_set.key_params.tkip.key, + pkey->key_material, pkey->key_len); + pkey_material->key_param_set.length = + wlan_cpu_to_le16(KEY_PARAMS_FIXED_LEN + + sizeof(tkip_param)); + cmd->size = + wlan_cpu_to_le16(sizeof(MrvlIEtypesHeader_t) + + S_DS_GEN + KEY_PARAMS_FIXED_LEN + + sizeof(tkip_param) + + sizeof(pkey_material->action)); + PRINTM(MCMND, "Set TKIP Key\n"); + } +done: + LEAVE(); + return ret; +} + +/** + * @brief This function prepares command of gtk rekey offload + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action The action: GET or SET + * @param cmd_oid OID: ENABLE or DISABLE + * @param pdata_buf A pointer to data buffer + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status +wlan_cmd_gtk_rekey_offload(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, + IN t_u32 cmd_oid, IN t_void *pdata_buf) +{ + HostCmd_DS_GTK_REKEY_PARAMS *rekey = &cmd->params.gtk_rekey; + mlan_ds_misc_gtk_rekey_data *data = + (mlan_ds_misc_gtk_rekey_data *) pdata_buf; + t_u64 rekey_ctr; + + ENTER(); + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_GTK_REKEY_OFFLOAD_CFG); + cmd->size = wlan_cpu_to_le16(sizeof(*rekey) + S_DS_GEN); + + rekey->action = wlan_cpu_to_le16(cmd_action); + if (cmd_action == HostCmd_ACT_GEN_SET) { + memcpy(pmpriv->adapter, rekey->kek, data->kek, MLAN_KEK_LEN); + memcpy(pmpriv->adapter, rekey->kck, data->kck, MLAN_KCK_LEN); + rekey_ctr = + wlan_le64_to_cpu(swap_byte_64 + (*(t_u64 *)data->replay_ctr)); + rekey->replay_ctr_low = wlan_cpu_to_le32((t_u32)rekey_ctr); + rekey->replay_ctr_high = + wlan_cpu_to_le32((t_u64)rekey_ctr >> 32); + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function send eapol pkt to FW + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action the action: GET or SET + * @param pdata_buf A pointer to data buffer + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_cmd_eapol_pkt(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, IN t_void *pdata_buf) +{ + HostCmd_DS_EAPOL_PKT *eapol_pkt = &cmd->params.eapol_pkt; + mlan_buffer *pmbuf = (mlan_buffer *)pdata_buf; + + ENTER(); + eapol_pkt->action = wlan_cpu_to_le16(HostCmd_ACT_GEN_SET); + cmd->size = sizeof(HostCmd_DS_EAPOL_PKT) + S_DS_GEN; + cmd->command = wlan_cpu_to_le16(cmd->command); + + eapol_pkt->tlv_eapol.header.type = wlan_cpu_to_le16(TLV_TYPE_EAPOL_PKT); + eapol_pkt->tlv_eapol.header.len = wlan_cpu_to_le16(pmbuf->data_len); + memcpy(pmpriv->adapter, eapol_pkt->tlv_eapol.pkt_buf, + pmbuf->pbuf + pmbuf->data_offset, pmbuf->data_len); + cmd->size += pmbuf->data_len; + cmd->size = wlan_cpu_to_le16(cmd->size); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Handle the supplicant profile command + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action The action: GET or SET + * @param pdata_buf A pointer to data buffer + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_cmd_802_11_supplicant_profile(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, IN t_void *pdata_buf) +{ + HostCmd_DS_802_11_SUPPLICANT_PROFILE *sup_profile = + (HostCmd_DS_802_11_SUPPLICANT_PROFILE *)&(cmd->params. + esupplicant_profile); + MrvlIEtypes_EncrProto_t *encr_proto_tlv = MNULL; + MrvlIEtypes_Cipher_t *pcipher_tlv = MNULL; + t_u8 *ptlv_buffer = (t_u8 *)sup_profile->tlv_buf; + mlan_ds_esupp_mode *esupp = MNULL; + + ENTER(); + + cmd->size = + wlan_cpu_to_le16(sizeof(HostCmd_DS_802_11_SUPPLICANT_PROFILE) + + S_DS_GEN - 1); + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_SUPPLICANT_PROFILE); + sup_profile->action = wlan_cpu_to_le16(cmd_action); + if ((cmd_action == HostCmd_ACT_GEN_SET) && pdata_buf) { + esupp = (mlan_ds_esupp_mode *)pdata_buf; + if (esupp->rsn_mode) { + encr_proto_tlv = (MrvlIEtypes_EncrProto_t *)ptlv_buffer; + encr_proto_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_ENCRYPTION_PROTO); + encr_proto_tlv->header.len = + (t_u16)sizeof(encr_proto_tlv->rsn_mode); + encr_proto_tlv->rsn_mode = + wlan_cpu_to_le16(esupp->rsn_mode); + ptlv_buffer += + (encr_proto_tlv->header.len + + sizeof(MrvlIEtypesHeader_t)); + cmd->size += + (encr_proto_tlv->header.len + + sizeof(MrvlIEtypesHeader_t)); + encr_proto_tlv->header.len = + wlan_cpu_to_le16(encr_proto_tlv->header.len); + } + if (esupp->act_paircipher || esupp->act_groupcipher) { + pcipher_tlv = (MrvlIEtypes_Cipher_t *)ptlv_buffer; + pcipher_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_CIPHER); + pcipher_tlv->header.len = + (t_u16)(sizeof(pcipher_tlv->pair_cipher) + + sizeof(pcipher_tlv->group_cipher)); + if (esupp->act_paircipher) { + pcipher_tlv->pair_cipher = + esupp->act_paircipher & 0xff; + } + if (esupp->act_groupcipher) { + pcipher_tlv->group_cipher = + esupp->act_groupcipher & 0xff; + } + ptlv_buffer += + (pcipher_tlv->header.len + + sizeof(MrvlIEtypesHeader_t)); + cmd->size += + (pcipher_tlv->header.len + + sizeof(MrvlIEtypesHeader_t)); + pcipher_tlv->header.len = + wlan_cpu_to_le16(pcipher_tlv->header.len); + } + } + cmd->size = wlan_cpu_to_le16(cmd->size); + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of rf_channel. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action The action: GET or SET + * @param pdata_buf A pointer to data buffer + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_cmd_802_11_rf_channel(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, IN t_void *pdata_buf) +{ + HostCmd_DS_802_11_RF_CHANNEL *prf_chan = &cmd->params.rf_channel; + + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_802_11_RF_CHANNEL); + cmd->size = wlan_cpu_to_le16(sizeof(HostCmd_DS_802_11_RF_CHANNEL) + + S_DS_GEN); + + if (cmd_action == HostCmd_ACT_GEN_SET) { + if ((pmpriv->adapter->adhoc_start_band & BAND_A) + || (pmpriv->adapter->adhoc_start_band & BAND_AN) + ) + prf_chan->rf_type.bandcfg.chanBand = BAND_5GHZ; + prf_chan->rf_type.bandcfg.chanWidth = + pmpriv->adapter->chan_bandwidth; + prf_chan->current_channel = + wlan_cpu_to_le16(*((t_u16 *)pdata_buf)); + } + prf_chan->action = wlan_cpu_to_le16(cmd_action); + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of ibss_coalescing_status. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action The action: GET or SET + * @param pdata_buf A pointer to data buffer or MNULL + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_cmd_ibss_coalescing_status(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, IN t_void *pdata_buf) +{ + HostCmd_DS_802_11_IBSS_STATUS *pibss_coal = + &(cmd->params.ibss_coalescing); + t_u16 enable = 0; + + ENTER(); + + cmd->command = + wlan_cpu_to_le16(HostCmd_CMD_802_11_IBSS_COALESCING_STATUS); + cmd->size = + wlan_cpu_to_le16(sizeof(HostCmd_DS_802_11_IBSS_STATUS) + + S_DS_GEN); + cmd->result = 0; + pibss_coal->action = wlan_cpu_to_le16(cmd_action); + + switch (cmd_action) { + case HostCmd_ACT_GEN_SET: + if (pdata_buf != MNULL) + enable = *(t_u16 *)pdata_buf; + pibss_coal->enable = wlan_cpu_to_le16(enable); + break; + + /* In other case.. Nothing to do */ + case HostCmd_ACT_GEN_GET: + default: + break; + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of mgmt IE list. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action The action: GET or SET + * @param pdata_buf A pointer to data buffer + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_cmd_mgmt_ie_list(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, IN t_void *pdata_buf) +{ + t_u16 req_len = 0, travel_len = 0; + custom_ie *cptr = MNULL; + mlan_ds_misc_custom_ie *cust_ie = MNULL; + HostCmd_DS_MGMT_IE_LIST_CFG *pmgmt_ie_list = + &(cmd->params.mgmt_ie_list); + + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_MGMT_IE_LIST); + cmd->size = sizeof(HostCmd_DS_MGMT_IE_LIST_CFG) + S_DS_GEN; + cmd->result = 0; + pmgmt_ie_list->action = wlan_cpu_to_le16(cmd_action); + + cust_ie = (mlan_ds_misc_custom_ie *)pdata_buf; + pmgmt_ie_list->ds_mgmt_ie.type = wlan_cpu_to_le16(cust_ie->type); + pmgmt_ie_list->ds_mgmt_ie.len = wlan_cpu_to_le16(cust_ie->len); + + if (pmgmt_ie_list->ds_mgmt_ie.ie_data_list && cust_ie->ie_data_list) { + req_len = cust_ie->len; + travel_len = 0; + /* conversion for index, mask, len */ + if (req_len == sizeof(t_u16)) + cust_ie->ie_data_list[0].ie_index = + wlan_cpu_to_le16(cust_ie->ie_data_list[0]. + ie_index); + + while (req_len > sizeof(t_u16)) { + cptr = (custom_ie *)(((t_u8 *)cust_ie->ie_data_list) + + travel_len); + travel_len += + cptr->ie_length + sizeof(custom_ie) - + MAX_IE_SIZE; + req_len -= + cptr->ie_length + sizeof(custom_ie) - + MAX_IE_SIZE; + cptr->ie_index = wlan_cpu_to_le16(cptr->ie_index); + cptr->mgmt_subtype_mask = + wlan_cpu_to_le16(cptr->mgmt_subtype_mask); + cptr->ie_length = wlan_cpu_to_le16(cptr->ie_length); + } + if (cust_ie->len) + memcpy(pmpriv->adapter, + pmgmt_ie_list->ds_mgmt_ie.ie_data_list, + cust_ie->ie_data_list, cust_ie->len); + } + + cmd->size -= + (MAX_MGMT_IE_INDEX_TO_FW * sizeof(custom_ie)) + + sizeof(tlvbuf_max_mgmt_ie); + cmd->size += cust_ie->len; + cmd->size = wlan_cpu_to_le16(cmd->size); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of TDLS configuration. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action The action: GET or SET + * @param pdata_buf A pointer to data buffer + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status +wlan_cmd_tdls_config(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, IN t_void *pdata_buf) +{ + t_u16 travel_len = 0; + mlan_ds_misc_tdls_config *tdls_config = MNULL; + tdls_all_config *tdls_all_cfg = MNULL; + HostCmd_DS_TDLS_CONFIG *ptdls_config_data = + &(cmd->params.tdls_config_data); + t_u8 zero_mac[] = { 0, 0, 0, 0, 0, 0 }; + + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_TDLS_CONFIG); + cmd->size = sizeof(HostCmd_DS_TDLS_CONFIG) + S_DS_GEN; + cmd->result = 0; + + tdls_config = (mlan_ds_misc_tdls_config *)pdata_buf; + ptdls_config_data->tdls_info.tdls_action = + wlan_cpu_to_le16(tdls_config->tdls_action); + + tdls_all_cfg = (tdls_all_config *)tdls_config->tdls_data; + + switch (tdls_config->tdls_action) { + case WLAN_TDLS_CONFIG: + travel_len = sizeof(tdls_all_cfg->u.tdls_config); + tdls_all_cfg->u.tdls_config.enable = + wlan_cpu_to_le16(tdls_all_cfg->u.tdls_config.enable); + memcpy(pmpriv->adapter, ptdls_config_data->tdls_info.tdls_data, + &tdls_all_cfg->u.tdls_setup, travel_len); + break; + + case WLAN_TDLS_SET_INFO: + travel_len = tdls_all_cfg->u.tdls_set.tlv_length; + if ((travel_len + sizeof(t_u16)) > MAX_TDLS_DATA_LEN) { + PRINTM(MERROR, "TDLS configuration overflow\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + memcpy(pmpriv->adapter, ptdls_config_data->tdls_info.tdls_data, + (t_u8 *)&tdls_all_cfg->u.tdls_set.cap_info, + sizeof(t_u16)); + memcpy(pmpriv->adapter, + (t_u8 *)ptdls_config_data->tdls_info.tdls_data + + sizeof(t_u16), &tdls_all_cfg->u.tdls_set.tlv_buffer, + travel_len); + travel_len += sizeof(t_u16); + break; + case WLAN_TDLS_DISCOVERY_REQ: + travel_len = MLAN_MAC_ADDR_LENGTH; + memcpy(pmpriv->adapter, ptdls_config_data->tdls_info.tdls_data, + tdls_all_cfg->u.tdls_discovery.peer_mac_addr, + travel_len); + break; + + case WLAN_TDLS_SETUP_REQ: + travel_len = sizeof(tdls_all_cfg->u.tdls_setup); + tdls_all_cfg->u.tdls_setup.setup_timeout = + wlan_cpu_to_le32(tdls_all_cfg->u.tdls_setup. + setup_timeout); + tdls_all_cfg->u.tdls_setup.key_lifetime = + wlan_cpu_to_le32(tdls_all_cfg->u.tdls_setup. + key_lifetime); + memcpy(pmpriv->adapter, ptdls_config_data->tdls_info.tdls_data, + &tdls_all_cfg->u.tdls_setup, travel_len); + break; + + case WLAN_TDLS_TEAR_DOWN_REQ: + travel_len = sizeof(tdls_all_cfg->u.tdls_tear_down); + tdls_all_cfg->u.tdls_tear_down.reason_code = + wlan_cpu_to_le16(tdls_all_cfg->u.tdls_tear_down. + reason_code); + memcpy(pmpriv->adapter, ptdls_config_data->tdls_info.tdls_data, + &tdls_all_cfg->u.tdls_tear_down, travel_len); + break; + case WLAN_TDLS_STOP_CHAN_SWITCH: + travel_len = MLAN_MAC_ADDR_LENGTH; + memcpy(pmpriv->adapter, ptdls_config_data->tdls_info.tdls_data, + tdls_all_cfg->u.tdls_stop_chan_switch.peer_mac_addr, + travel_len); + break; + case WLAN_TDLS_INIT_CHAN_SWITCH: + travel_len = sizeof(tdls_all_cfg->u.tdls_chan_switch); + tdls_all_cfg->u.tdls_chan_switch.switch_time = + wlan_cpu_to_le16(tdls_all_cfg->u.tdls_chan_switch. + switch_time); + tdls_all_cfg->u.tdls_chan_switch.switch_timeout = + wlan_cpu_to_le16(tdls_all_cfg->u.tdls_chan_switch. + switch_timeout); + memcpy(pmpriv->adapter, ptdls_config_data->tdls_info.tdls_data, + &tdls_all_cfg->u.tdls_chan_switch, travel_len); + break; + case WLAN_TDLS_CS_PARAMS: + travel_len = sizeof(tdls_all_cfg->u.tdls_cs_params); + memcpy(pmpriv->adapter, ptdls_config_data->tdls_info.tdls_data, + &tdls_all_cfg->u.tdls_cs_params, travel_len); + break; + case WLAN_TDLS_CS_DISABLE: + travel_len = sizeof(tdls_all_cfg->u.tdls_disable_cs); + tdls_all_cfg->u.tdls_disable_cs.data = + wlan_cpu_to_le16(tdls_all_cfg->u.tdls_disable_cs.data); + memcpy(pmpriv->adapter, ptdls_config_data->tdls_info.tdls_data, + &tdls_all_cfg->u.tdls_disable_cs, travel_len); + break; + case WLAN_TDLS_POWER_MODE: + travel_len = sizeof(tdls_all_cfg->u.tdls_power_mode); + tdls_all_cfg->u.tdls_power_mode.power_mode = + wlan_cpu_to_le16(tdls_all_cfg->u.tdls_power_mode. + power_mode); + memcpy(pmpriv->adapter, ptdls_config_data->tdls_info.tdls_data, + &tdls_all_cfg->u.tdls_power_mode, travel_len); + break; + + case WLAN_TDLS_LINK_STATUS: + travel_len = 0; + if (memcmp + (pmpriv->adapter, + tdls_all_cfg->u.tdls_link_status_req.peer_mac_addr, + zero_mac, sizeof(zero_mac))) { + travel_len = + sizeof(tdls_all_cfg->u.tdls_link_status_req); + memcpy(pmpriv->adapter, + ptdls_config_data->tdls_info.tdls_data, + tdls_all_cfg->u.tdls_link_status_req. + peer_mac_addr, travel_len); + } + break; + + case WLAN_TDLS_DEBUG_ALLOW_WEAK_SECURITY: + case WLAN_TDLS_DEBUG_SETUP_SAME_LINK: + case WLAN_TDLS_DEBUG_FAIL_SETUP_CONFIRM: + case WLAN_TDLS_DEBUG_WRONG_BSS: + case WLAN_TDLS_DEBUG_SETUP_PROHIBITED: + case WLAN_TDLS_DEBUG_HIGHER_LOWER_MAC: + case WLAN_TDLS_DEBUG_IGNORE_KEY_EXPIRY: + case WLAN_TDLS_DEBUG_STOP_RX: + case WLAN_TDLS_DEBUG_CS_RET_IM: + travel_len = sizeof(tdls_all_cfg->u.tdls_debug_data); + tdls_all_cfg->u.tdls_debug_data.debug_data = + wlan_cpu_to_le16(tdls_all_cfg->u.tdls_debug_data. + debug_data); + memcpy(pmpriv->adapter, ptdls_config_data->tdls_info.tdls_data, + &tdls_all_cfg->u.tdls_debug_data, travel_len); + break; + + default: + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + cmd->size += travel_len; + cmd->size -= MAX_TDLS_DATA_LEN; + cmd->size = wlan_cpu_to_le16(cmd->size); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of TDLS create/config/delete + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action The action: GET or SET + * @param pdata_buf A pointer to data buffer + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status +wlan_cmd_tdls_oper(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, IN t_void *pdata_buf) +{ + t_u16 travel_len = 0; + mlan_ds_misc_tdls_oper *tdls_oper = MNULL; + HostCmd_DS_TDLS_OPER *ptdls_oper = &(cmd->params.tdls_oper_data); + sta_node *sta_ptr; + t_u8 *pos; + MrvlIEtypes_RatesParamSet_t *Rate_tlv = MNULL; + MrvlIETypes_HTCap_t *HTcap_tlv = MNULL; + MrvlIETypes_HTInfo_t *HTInfo_tlv = MNULL; + MrvlIETypes_2040BSSCo_t *BSSCo = MNULL; + MrvlIETypes_ExtCap_t *ExCap = MNULL; + MrvlIEtypes_RsnParamSet_t *Rsn_ie = MNULL; + MrvlIETypes_qosinfo_t *qos_info = MNULL; + MrvlIETypes_LinkIDElement_t *LinkID = MNULL; + BSSDescriptor_t *pbss_desc = &pmpriv->curr_bss_params.bss_descriptor; + MrvlIEtypes_TDLS_Idle_Timeout_t *TdlsIdleTimeout = MNULL; + + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_TDLS_OPERATION); + cmd->size = sizeof(HostCmd_DS_TDLS_OPER) + S_DS_GEN; + cmd->result = 0; + + tdls_oper = (mlan_ds_misc_tdls_oper *)pdata_buf; + ptdls_oper->reason = 0; + memcpy(pmpriv->adapter, ptdls_oper->peer_mac, tdls_oper->peer_mac, + MLAN_MAC_ADDR_LENGTH); + sta_ptr = wlan_get_station_entry(pmpriv, tdls_oper->peer_mac); + pos = (t_u8 *)ptdls_oper + sizeof(HostCmd_DS_TDLS_OPER); + switch (tdls_oper->tdls_action) { + case WLAN_TDLS_CREATE_LINK: + if (sta_ptr) + sta_ptr->status = TDLS_SETUP_INPROGRESS; + ptdls_oper->tdls_action = wlan_cpu_to_le16(TDLS_CREATE); + break; + case WLAN_TDLS_CONFIG_LINK: + if (sta_ptr) { + ptdls_oper->tdls_action = wlan_cpu_to_le16(TDLS_CONFIG); + /*capability */ + *(t_u16 *)pos = wlan_cpu_to_le16(sta_ptr->capability); + travel_len += sizeof(sta_ptr->capability); + + /*supported rate */ + Rate_tlv = + (MrvlIEtypes_RatesParamSet_t *)(pos + + travel_len); + Rate_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_RATES); + Rate_tlv->header.len = + wlan_cpu_to_le16(sta_ptr->rate_len); + memcpy(pmpriv->adapter, + pos + travel_len + sizeof(MrvlIEtypesHeader_t), + sta_ptr->support_rate, Rate_tlv->header.len); + travel_len += + sizeof(MrvlIEtypesHeader_t) + sta_ptr->rate_len; + + /*Extended capability */ + if (sta_ptr->ExtCap.ieee_hdr.element_id == + EXT_CAPABILITY) { + ExCap = (MrvlIETypes_ExtCap_t *)(pos + + travel_len); + ExCap->header.type = + wlan_cpu_to_le16(TLV_TYPE_EXTCAP); + ExCap->header.len = + wlan_cpu_to_le16(sta_ptr->ExtCap. + ieee_hdr.len); + memcpy(pmpriv->adapter, &ExCap->ext_cap, + &sta_ptr->ExtCap.ext_cap, + sta_ptr->ExtCap.ieee_hdr.len); + travel_len += + sta_ptr->ExtCap.ieee_hdr.len + + sizeof(MrvlIEtypesHeader_t); + } + if (ExCap) { + if (pmpriv->host_tdls_uapsd_support && + ISSUPP_EXTCAP_TDLS_UAPSD(ExCap->ext_cap)) { + /* qos_info */ + qos_info = + (MrvlIETypes_qosinfo_t *)(pos + + travel_len); + qos_info->header.type = + wlan_cpu_to_le16(QOS_INFO); + qos_info->header.len = + wlan_cpu_to_le16(sizeof(t_u8)); + qos_info->qos_info = sta_ptr->qos_info; + travel_len += + sizeof(MrvlIETypes_qosinfo_t); + } else { + RESET_EXTCAP_TDLS_UAPSD(ExCap->ext_cap); + } + + if (! + (pmpriv->host_tdls_cs_support && + ISSUPP_EXTCAP_TDLS_CHAN_SWITCH(ExCap-> + ext_cap))) + RESET_EXTCAP_TDLS_CHAN_SWITCH(ExCap-> + ext_cap); + } + + /*RSN ie */ + if (sta_ptr->rsn_ie.ieee_hdr.element_id == RSN_IE) { + Rsn_ie = (MrvlIEtypes_RsnParamSet_t *)(pos + + travel_len); + Rsn_ie->header.type = + wlan_cpu_to_le16(sta_ptr->rsn_ie. + ieee_hdr.element_id); + Rsn_ie->header.len = + wlan_cpu_to_le16(sta_ptr->rsn_ie. + ieee_hdr.len); + memcpy(pmpriv->adapter, Rsn_ie->rsn_ie, + sta_ptr->rsn_ie.data, + sta_ptr->rsn_ie.ieee_hdr.len); + travel_len += + sta_ptr->rsn_ie.ieee_hdr.len + + sizeof(MrvlIEtypesHeader_t); + } + /*Link ID */ + if (sta_ptr->link_ie.element_id == LINK_ID) { + LinkID = (MrvlIETypes_LinkIDElement_t *)(pos + + travel_len); + LinkID->header.type = wlan_cpu_to_le16(LINK_ID); + LinkID->header.len = + wlan_cpu_to_le16(sta_ptr->link_ie.len); + memcpy(pmpriv->adapter, &LinkID->bssid, + &sta_ptr->link_ie.bssid, + sta_ptr->link_ie.len); + travel_len += + sta_ptr->link_ie.len + + sizeof(MrvlIEtypesHeader_t); + } + /*HT capability */ + if (sta_ptr->HTcap.ieee_hdr.element_id == HT_CAPABILITY) { + HTcap_tlv = + (MrvlIETypes_HTCap_t *)(pos + + travel_len); + HTcap_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_HT_CAP); + HTcap_tlv->header.len = + wlan_cpu_to_le16(sta_ptr->HTcap. + ieee_hdr.len); + memcpy(pmpriv->adapter, &HTcap_tlv->ht_cap, + &sta_ptr->HTcap.ht_cap, + sta_ptr->HTcap.ieee_hdr.len); + travel_len += + sta_ptr->HTcap.ieee_hdr.len + + sizeof(MrvlIEtypesHeader_t); + } + if (HTcap_tlv) { + if (pmpriv->host_tdls_cs_support && + (pmpriv->adapter->fw_bands & BAND_A)) + wlan_fill_ht_cap_tlv(pmpriv, HTcap_tlv, + BAND_A, MFALSE); + else + wlan_fill_ht_cap_tlv(pmpriv, HTcap_tlv, + pbss_desc-> + bss_band, MFALSE); + DBG_HEXDUMP(MCMD_D, "FW htcap", + (t_u8 *)HTcap_tlv, + sizeof(MrvlIETypes_HTCap_t)); + } + + /*HT info */ + if (sta_ptr->HTInfo.ieee_hdr.element_id == HT_OPERATION) { + HTInfo_tlv = + (MrvlIETypes_HTInfo_t *)(pos + + travel_len); + HTInfo_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_HT_INFO); + HTInfo_tlv->header.len = + wlan_cpu_to_le16(sta_ptr->HTInfo. + ieee_hdr.len); + memcpy(pmpriv->adapter, &HTInfo_tlv->ht_info, + &sta_ptr->HTInfo.ht_info, + sta_ptr->HTInfo.ieee_hdr.len); + travel_len += + sta_ptr->HTInfo.ieee_hdr.len + + sizeof(MrvlIEtypesHeader_t); + DBG_HEXDUMP(MCMD_D, "HT Info", + (t_u8 *)HTInfo_tlv, + sizeof(MrvlIETypes_HTInfo_t)); + } + /*20/40 BSS co-exist */ + if (sta_ptr->BSSCO_20_40.ieee_hdr.element_id == + BSSCO_2040) { + BSSCo = (MrvlIETypes_2040BSSCo_t *)(pos + + travel_len); + BSSCo->header.type = + wlan_cpu_to_le16 + (TLV_TYPE_2040BSS_COEXISTENCE); + BSSCo->header.len = + wlan_cpu_to_le16(sta_ptr->BSSCO_20_40. + ieee_hdr.len); + memcpy(pmpriv->adapter, &BSSCo->bss_co_2040, + &sta_ptr->BSSCO_20_40.bss_co_2040, + sta_ptr->BSSCO_20_40.ieee_hdr.len); + travel_len += + sta_ptr->BSSCO_20_40.ieee_hdr.len + + sizeof(MrvlIEtypesHeader_t); + } + TdlsIdleTimeout = + (MrvlIEtypes_TDLS_Idle_Timeout_t *)(pos + + travel_len); + TdlsIdleTimeout->header.type = + wlan_cpu_to_le16(TLV_TYPE_TDLS_IDLE_TIMEOUT); + TdlsIdleTimeout->header.len = + sizeof(TdlsIdleTimeout->value); + TdlsIdleTimeout->header.len = + wlan_cpu_to_le16(TdlsIdleTimeout->header.len); + TdlsIdleTimeout->value = + wlan_cpu_to_le16(pmpriv->tdls_idle_time); + travel_len += sizeof(MrvlIEtypes_TDLS_Idle_Timeout_t); + } + break; + case WLAN_TDLS_DISABLE_LINK: + ptdls_oper->tdls_action = wlan_cpu_to_le16(TDLS_DELETE); + break; + default: + break; + } + cmd->size += travel_len; + cmd->size = wlan_cpu_to_le16(cmd->size); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares system clock cfg command + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action The action: GET or SET + * @param pdata_buf A pointer to data buffer + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_cmd_sysclock_cfg(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, IN t_void *pdata_buf) +{ + HostCmd_DS_ECL_SYSTEM_CLOCK_CONFIG *cfg = &cmd->params.sys_clock_cfg; + mlan_ds_misc_sys_clock *clk_cfg = (mlan_ds_misc_sys_clock *)pdata_buf; + int i = 0; + + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_ECL_SYSTEM_CLOCK_CONFIG); + cmd->size = + wlan_cpu_to_le16(sizeof(HostCmd_DS_ECL_SYSTEM_CLOCK_CONFIG) + + S_DS_GEN); + + cfg->action = wlan_cpu_to_le16(cmd_action); + cfg->cur_sys_clk = wlan_cpu_to_le16(clk_cfg->cur_sys_clk); + cfg->sys_clk_type = wlan_cpu_to_le16(clk_cfg->sys_clk_type); + cfg->sys_clk_len = + wlan_cpu_to_le16(clk_cfg->sys_clk_num) * sizeof(t_u16); + for (i = 0; i < clk_cfg->sys_clk_num; i++) + cfg->sys_clk[i] = wlan_cpu_to_le16(clk_cfg->sys_clk[i]); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of subscribe event. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action the action: GET or SET + * @param pdata_buf A pointer to data buffer + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_cmd_subscribe_event(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, IN t_void *pdata_buf) +{ + mlan_ds_subscribe_evt *sub_evt = (mlan_ds_subscribe_evt *)pdata_buf; + HostCmd_DS_SUBSCRIBE_EVENT *evt = + (HostCmd_DS_SUBSCRIBE_EVENT *)&cmd->params.subscribe_event; + t_u16 cmd_size = 0; + t_u8 *tlv = MNULL; + MrvlIEtypes_BeaconLowRssiThreshold_t *rssi_low = MNULL; + MrvlIEtypes_BeaconLowSnrThreshold_t *snr_low = MNULL; + MrvlIEtypes_FailureCount_t *fail_count = MNULL; + MrvlIEtypes_BeaconsMissed_t *beacon_missed = MNULL; + MrvlIEtypes_BeaconHighRssiThreshold_t *rssi_high = MNULL; + MrvlIEtypes_BeaconHighSnrThreshold_t *snr_high = MNULL; + MrvlIEtypes_DataLowRssiThreshold_t *data_rssi_low = MNULL; + MrvlIEtypes_DataLowSnrThreshold_t *data_snr_low = MNULL; + MrvlIEtypes_DataHighRssiThreshold_t *data_rssi_high = MNULL; + MrvlIEtypes_DataHighSnrThreshold_t *data_snr_high = MNULL; + MrvlIEtypes_LinkQualityThreshold_t *link_quality = MNULL; + MrvlIETypes_PreBeaconMissed_t *pre_bcn_missed = MNULL; + + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_802_11_SUBSCRIBE_EVENT); + evt->action = wlan_cpu_to_le16(cmd_action); + cmd_size = sizeof(HostCmd_DS_SUBSCRIBE_EVENT) + S_DS_GEN; + if (cmd_action == HostCmd_ACT_GEN_GET) + goto done; + evt->action = wlan_cpu_to_le16(sub_evt->evt_action); + evt->event_bitmap = wlan_cpu_to_le16(sub_evt->evt_bitmap); + tlv = (t_u8 *)cmd + cmd_size; + if (sub_evt->evt_bitmap & SUBSCRIBE_EVT_RSSI_LOW) { + rssi_low = (MrvlIEtypes_BeaconLowRssiThreshold_t *)tlv; + rssi_low->header.type = wlan_cpu_to_le16(TLV_TYPE_RSSI_LOW); + rssi_low->header.len = + wlan_cpu_to_le16(sizeof + (MrvlIEtypes_BeaconLowRssiThreshold_t) + - sizeof(MrvlIEtypesHeader_t)); + rssi_low->value = sub_evt->low_rssi; + rssi_low->frequency = sub_evt->low_rssi_freq; + tlv += sizeof(MrvlIEtypes_BeaconLowRssiThreshold_t); + cmd_size += sizeof(MrvlIEtypes_BeaconLowRssiThreshold_t); + } + if (sub_evt->evt_bitmap & SUBSCRIBE_EVT_SNR_LOW) { + snr_low = (MrvlIEtypes_BeaconLowSnrThreshold_t *)tlv; + snr_low->header.type = wlan_cpu_to_le16(TLV_TYPE_SNR_LOW); + snr_low->header.len = + wlan_cpu_to_le16(sizeof + (MrvlIEtypes_BeaconLowSnrThreshold_t) - + sizeof(MrvlIEtypesHeader_t)); + snr_low->value = sub_evt->low_snr; + snr_low->frequency = sub_evt->low_snr_freq; + tlv += sizeof(MrvlIEtypes_BeaconLowSnrThreshold_t); + cmd_size += sizeof(MrvlIEtypes_BeaconLowSnrThreshold_t); + } + if (sub_evt->evt_bitmap & SUBSCRIBE_EVT_MAX_FAIL) { + fail_count = (MrvlIEtypes_FailureCount_t *)tlv; + fail_count->header.type = wlan_cpu_to_le16(TLV_TYPE_FAILCOUNT); + fail_count->header.len = + wlan_cpu_to_le16(sizeof(MrvlIEtypes_FailureCount_t) - + sizeof(MrvlIEtypesHeader_t)); + fail_count->value = sub_evt->failure_count; + fail_count->frequency = sub_evt->failure_count_freq; + tlv += sizeof(MrvlIEtypes_FailureCount_t); + cmd_size += sizeof(MrvlIEtypes_FailureCount_t); + } + if (sub_evt->evt_bitmap & SUBSCRIBE_EVT_BEACON_MISSED) { + beacon_missed = (MrvlIEtypes_BeaconsMissed_t *)tlv; + beacon_missed->header.type = wlan_cpu_to_le16(TLV_TYPE_BCNMISS); + beacon_missed->header.len = + wlan_cpu_to_le16(sizeof(MrvlIEtypes_BeaconsMissed_t) - + sizeof(MrvlIEtypesHeader_t)); + beacon_missed->value = sub_evt->beacon_miss; + beacon_missed->frequency = sub_evt->beacon_miss_freq; + tlv += sizeof(MrvlIEtypes_BeaconsMissed_t); + cmd_size += sizeof(MrvlIEtypes_BeaconsMissed_t); + } + if (sub_evt->evt_bitmap & SUBSCRIBE_EVT_RSSI_HIGH) { + rssi_high = (MrvlIEtypes_BeaconHighRssiThreshold_t *)tlv; + rssi_high->header.type = wlan_cpu_to_le16(TLV_TYPE_RSSI_HIGH); + rssi_high->header.len = + wlan_cpu_to_le16(sizeof + (MrvlIEtypes_BeaconHighRssiThreshold_t) + - sizeof(MrvlIEtypesHeader_t)); + rssi_high->value = sub_evt->high_rssi; + rssi_high->frequency = sub_evt->high_rssi_freq; + tlv += sizeof(MrvlIEtypes_BeaconHighRssiThreshold_t); + cmd_size += sizeof(MrvlIEtypes_BeaconHighRssiThreshold_t); + } + if (sub_evt->evt_bitmap & SUBSCRIBE_EVT_SNR_HIGH) { + snr_high = (MrvlIEtypes_BeaconHighSnrThreshold_t *)tlv; + snr_high->header.type = wlan_cpu_to_le16(TLV_TYPE_SNR_HIGH); + snr_high->header.len = + wlan_cpu_to_le16(sizeof + (MrvlIEtypes_BeaconHighSnrThreshold_t) + - sizeof(MrvlIEtypesHeader_t)); + snr_high->value = sub_evt->high_snr; + snr_high->frequency = sub_evt->high_snr_freq; + tlv += sizeof(MrvlIEtypes_BeaconHighSnrThreshold_t); + cmd_size += sizeof(MrvlIEtypes_BeaconHighSnrThreshold_t); + } + if (sub_evt->evt_bitmap & SUBSCRIBE_EVT_DATA_RSSI_LOW) { + data_rssi_low = (MrvlIEtypes_DataLowRssiThreshold_t *)tlv; + data_rssi_low->header.type = + wlan_cpu_to_le16(TLV_TYPE_RSSI_LOW_DATA); + data_rssi_low->header.len = + wlan_cpu_to_le16(sizeof + (MrvlIEtypes_DataLowRssiThreshold_t) - + sizeof(MrvlIEtypesHeader_t)); + data_rssi_low->value = sub_evt->data_low_rssi; + data_rssi_low->frequency = sub_evt->data_low_rssi_freq; + tlv += sizeof(MrvlIEtypes_DataLowRssiThreshold_t); + cmd_size += sizeof(MrvlIEtypes_DataLowRssiThreshold_t); + } + if (sub_evt->evt_bitmap & SUBSCRIBE_EVT_DATA_SNR_LOW) { + data_snr_low = (MrvlIEtypes_DataLowSnrThreshold_t *)tlv; + data_snr_low->header.type = + wlan_cpu_to_le16(TLV_TYPE_SNR_LOW_DATA); + data_snr_low->header.len = + wlan_cpu_to_le16(sizeof + (MrvlIEtypes_DataLowSnrThreshold_t) - + sizeof(MrvlIEtypesHeader_t)); + data_snr_low->value = sub_evt->data_low_snr; + data_snr_low->frequency = sub_evt->data_low_snr_freq; + tlv += sizeof(MrvlIEtypes_DataLowSnrThreshold_t); + cmd_size += sizeof(MrvlIEtypes_DataLowSnrThreshold_t); + } + if (sub_evt->evt_bitmap & SUBSCRIBE_EVT_DATA_RSSI_HIGH) { + data_rssi_high = (MrvlIEtypes_DataHighRssiThreshold_t *)tlv; + data_rssi_high->header.type = + wlan_cpu_to_le16(TLV_TYPE_RSSI_HIGH_DATA); + data_rssi_high->header.len = + wlan_cpu_to_le16(sizeof + (MrvlIEtypes_DataHighRssiThreshold_t) - + sizeof(MrvlIEtypesHeader_t)); + data_rssi_high->value = sub_evt->data_high_rssi; + data_rssi_high->frequency = sub_evt->data_high_rssi_freq; + tlv += sizeof(MrvlIEtypes_DataHighRssiThreshold_t); + cmd_size += sizeof(MrvlIEtypes_DataHighRssiThreshold_t); + } + if (sub_evt->evt_bitmap & SUBSCRIBE_EVT_DATA_SNR_HIGH) { + data_snr_high = (MrvlIEtypes_DataHighSnrThreshold_t *)tlv; + data_snr_high->header.type = + wlan_cpu_to_le16(TLV_TYPE_SNR_HIGH_DATA); + data_snr_high->header.len = + wlan_cpu_to_le16(sizeof + (MrvlIEtypes_DataHighSnrThreshold_t) - + sizeof(MrvlIEtypesHeader_t)); + data_snr_high->value = sub_evt->data_high_snr; + data_snr_high->frequency = sub_evt->data_high_snr_freq; + tlv += sizeof(MrvlIEtypes_DataHighSnrThreshold_t); + cmd_size += sizeof(MrvlIEtypes_DataHighSnrThreshold_t); + } + if (sub_evt->evt_bitmap & SUBSCRIBE_EVT_LINK_QUALITY) { + link_quality = (MrvlIEtypes_LinkQualityThreshold_t *)tlv; + link_quality->header.type = + wlan_cpu_to_le16(TLV_TYPE_LINK_QUALITY); + link_quality->header.len = + wlan_cpu_to_le16(sizeof + (MrvlIEtypes_LinkQualityThreshold_t) - + sizeof(MrvlIEtypesHeader_t)); + link_quality->link_snr = wlan_cpu_to_le16(sub_evt->link_snr); + link_quality->link_snr_freq = + wlan_cpu_to_le16(sub_evt->link_snr_freq); + link_quality->link_rate = wlan_cpu_to_le16(sub_evt->link_rate); + link_quality->link_rate_freq = + wlan_cpu_to_le16(sub_evt->link_rate_freq); + link_quality->link_tx_latency = + wlan_cpu_to_le16(sub_evt->link_tx_latency); + link_quality->link_tx_lantency_freq = + wlan_cpu_to_le16(sub_evt->link_tx_lantency_freq); + tlv += sizeof(MrvlIEtypes_LinkQualityThreshold_t); + cmd_size += sizeof(MrvlIEtypes_LinkQualityThreshold_t); + } + if (sub_evt->evt_bitmap & SUBSCRIBE_EVT_PRE_BEACON_LOST) { + pre_bcn_missed = (MrvlIETypes_PreBeaconMissed_t *)tlv; + pre_bcn_missed->header.type = + wlan_cpu_to_le16(TLV_TYPE_PRE_BCNMISS); + pre_bcn_missed->header.len = + wlan_cpu_to_le16(sizeof(MrvlIETypes_PreBeaconMissed_t) - + sizeof(MrvlIEtypesHeader_t)); + pre_bcn_missed->value = sub_evt->pre_beacon_miss; + pre_bcn_missed->frequency = 0; + tlv += sizeof(MrvlIETypes_PreBeaconMissed_t); + cmd_size += sizeof(MrvlIETypes_PreBeaconMissed_t); + } +done: + cmd->size = wlan_cpu_to_le16(cmd_size); + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of OTP user data. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action the action: GET or SET + * @param pdata_buf A pointer to data buffer + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_cmd_otp_user_data(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, IN t_void *pdata_buf) +{ + mlan_ds_misc_otp_user_data *user_data = + (mlan_ds_misc_otp_user_data *)pdata_buf; + HostCmd_DS_OTP_USER_DATA *cmd_user_data = + (HostCmd_DS_OTP_USER_DATA *)&cmd->params.otp_user_data; + t_u16 cmd_size = 0; + + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_OTP_READ_USER_DATA); + cmd_size = sizeof(HostCmd_DS_OTP_USER_DATA) + S_DS_GEN - 1; + + cmd_user_data->action = wlan_cpu_to_le16(cmd_action); + cmd_user_data->reserved = 0; + cmd_user_data->user_data_length = + wlan_cpu_to_le16(user_data->user_data_length); + cmd_size += user_data->user_data_length; + cmd->size = wlan_cpu_to_le16(cmd_size); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares inactivity timeout command + * + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action the action: GET or SET + * @param pdata_buf A pointer to data buffer + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_cmd_inactivity_timeout(IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, IN t_void *pdata_buf) +{ + pmlan_ds_inactivity_to inac_to; + HostCmd_DS_INACTIVITY_TIMEOUT_EXT *cmd_inac_to = + &cmd->params.inactivity_to; + + ENTER(); + + inac_to = (mlan_ds_inactivity_to *)pdata_buf; + + cmd->size = + wlan_cpu_to_le16(sizeof(HostCmd_DS_INACTIVITY_TIMEOUT_EXT) + + S_DS_GEN); + cmd->command = wlan_cpu_to_le16(cmd->command); + cmd_inac_to->action = wlan_cpu_to_le16(cmd_action); + if (cmd_action == HostCmd_ACT_GEN_SET) { + cmd_inac_to->timeout_unit = + wlan_cpu_to_le16((t_u16)inac_to->timeout_unit); + cmd_inac_to->unicast_timeout = + wlan_cpu_to_le16((t_u16)inac_to->unicast_timeout); + cmd_inac_to->mcast_timeout = + wlan_cpu_to_le16((t_u16)inac_to->mcast_timeout); + cmd_inac_to->ps_entry_timeout = + wlan_cpu_to_le16((t_u16)inac_to->ps_entry_timeout); + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares network monitor command + * + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action the action: GET or SET + * @param pdata_buf A pointer to data buffer + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_cmd_net_monitor(IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, IN t_void *pdata_buf) +{ + mlan_ds_misc_net_monitor *net_mon; + HostCmd_DS_802_11_NET_MONITOR *cmd_net_mon = &cmd->params.net_mon; + ChanBandParamSet_t *pchan_band = MNULL; + t_u32 bw_offset = 0; + + ENTER(); + + net_mon = (mlan_ds_misc_net_monitor *)pdata_buf; + + cmd->size = + wlan_cpu_to_le16(S_DS_GEN + + sizeof(HostCmd_DS_802_11_NET_MONITOR)); + cmd->command = wlan_cpu_to_le16(cmd->command); + cmd_net_mon->action = wlan_cpu_to_le16(cmd_action); + if (cmd_action == HostCmd_ACT_GEN_SET) { + cmd_net_mon->enable_net_mon = + wlan_cpu_to_le16((t_u16)net_mon->enable_net_mon); + if (net_mon->enable_net_mon) { + pchan_band = + &cmd_net_mon->monitor_chan.chan_band_param[0]; + cmd_net_mon->filter_flag = + wlan_cpu_to_le16((t_u16)net_mon->filter_flag); + cmd_net_mon->monitor_chan.header.type = + wlan_cpu_to_le16(TLV_TYPE_CHANNELBANDLIST); + cmd_net_mon->monitor_chan.header.len = + wlan_cpu_to_le16(sizeof(ChanBandParamSet_t)); + pchan_band->chan_number = (t_u8)net_mon->channel; + pchan_band->bandcfg.chanBand = + wlan_band_to_radio_type((t_u8)net_mon->band); + + if (net_mon->band & BAND_GN || net_mon->band & BAND_AN) { + bw_offset = net_mon->chan_bandwidth; + if (bw_offset == CHANNEL_BW_40MHZ_ABOVE) { + pchan_band->bandcfg.chan2Offset = + SEC_CHAN_ABOVE; + pchan_band->bandcfg.chanWidth = + CHAN_BW_40MHZ; + } else if (bw_offset == CHANNEL_BW_40MHZ_BELOW) { + pchan_band->bandcfg.chan2Offset = + SEC_CHAN_BELOW; + pchan_band->bandcfg.chanWidth = + CHAN_BW_40MHZ; + } + } + + } + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares DFS repeater mode configuration + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action the action: GET or SET + * @param pdata_buf A pointer to data buffer + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_cmd_dfs_repeater_cfg(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, IN t_void *pdata_buf) +{ + mlan_ds_misc_dfs_repeater *dfs_repeater = MNULL; + HostCmd_DS_DFS_REPEATER_MODE *cmd_dfs_repeater = + &cmd->params.dfs_repeater; + + ENTER(); + + cmd->size = S_DS_GEN + sizeof(HostCmd_DS_DFS_REPEATER_MODE); + + dfs_repeater = (mlan_ds_misc_dfs_repeater *)pdata_buf; + cmd->size = wlan_cpu_to_le16(cmd->size); + cmd->command = wlan_cpu_to_le16(cmd->command); + cmd_dfs_repeater->action = wlan_cpu_to_le16(cmd_action); + + if (cmd_action == HostCmd_ACT_GEN_SET) + cmd_dfs_repeater->mode = wlan_cpu_to_le16(dfs_repeater->mode); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of coalesce_config. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action The action: GET or SET + * @param pdata_buf A pointer to data buffer + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_cmd_coalesce_config(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, IN t_void *pdata_buf) +{ + HostCmd_DS_COALESCE_CONFIG *coalesce_config = + &cmd->params.coalesce_config; + mlan_ds_coalesce_cfg *cfg = (mlan_ds_coalesce_cfg *) pdata_buf; + t_u16 cnt, idx, length; + struct coalesce_filt_field_param *param; + struct coalesce_receive_filt_rule *rule; + ENTER(); + + cmd->size = sizeof(HostCmd_DS_COALESCE_CONFIG) + S_DS_GEN; + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_COALESCE_CFG); + coalesce_config->action = wlan_cpu_to_le16(cmd_action); + coalesce_config->num_of_rules = wlan_cpu_to_le16(cfg->num_of_rules); + if (cmd_action == HostCmd_ACT_GEN_SET) { + rule = coalesce_config->rule; + for (cnt = 0; cnt < cfg->num_of_rules; cnt++) { + rule->header.type = + wlan_cpu_to_le16(TLV_TYPE_COALESCE_RULE); + rule->max_coalescing_delay = + wlan_cpu_to_le16(cfg->rule[cnt]. + max_coalescing_delay); + rule->pkt_type = cfg->rule[cnt].pkt_type; + rule->num_of_fields = cfg->rule[cnt].num_of_fields; + + length = 0; + + param = rule->params; + for (idx = 0; idx < cfg->rule[cnt].num_of_fields; idx++) { + param->operation = + cfg->rule[cnt].params[idx].operation; + param->operand_len = + cfg->rule[cnt].params[idx].operand_len; + param->offset = + wlan_cpu_to_le16(cfg->rule[cnt]. + params[idx].offset); + memcpy(pmpriv->adapter, + param->operand_byte_stream, + cfg->rule[cnt].params[idx]. + operand_byte_stream, param->operand_len); + + length += + sizeof(struct + coalesce_filt_field_param); + + param++; + } + + /* Total rule length is sizeof max_coalescing_delay(t_u16), + * num_of_fields(t_u8), pkt_type(t_u8) and total length of the all + * params + */ + rule->header.len = + wlan_cpu_to_le16(length + sizeof(t_u16) + + sizeof(t_u8) + sizeof(t_u8)); + + /* Add the rule length to the command size */ + cmd->size += + wlan_le16_to_cpu(rule->header.len) + + sizeof(MrvlIEtypesHeader_t); + + rule = (void *)((t_u8 *)rule->params + length); + } + + } + cmd->size = wlan_cpu_to_le16(cmd->size); + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/******************************************************** + Global Functions +********************************************************/ + +/** + * @brief This function prepares command for sensor temperature. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action The action: GET or SET + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_cmd_get_sensor_temp(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, IN t_u16 cmd_action) +{ + ENTER(); + + if (cmd_action != HostCmd_ACT_GEN_GET) { + PRINTM(MERROR, + "wlan_cmd_get_sensor_temp(): support GET only.\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + cmd->command = wlan_cpu_to_le16(HostCmd_DS_GET_SENSOR_TEMP); + cmd->size = wlan_cpu_to_le16(S_DS_GEN + 4); + + LEAVE(); + return MLAN_STATUS_SUCCESS; + +} + +/** + * @brief This function sends get nlist to FW + * + * @param pmpriv A pointer to mlan_private structure + * @param pcmd Hostcmd ID + * @param cmd_action Command action + * @return N/A + */ +mlan_status +wlan_cmd_802_11k_get_nlist(mlan_private *pmpriv, + HostCmd_DS_COMMAND *pcmd, t_u16 cmd_action) +{ + HostCmd_DS_802_11K_GET_NLIST *pget_nlist = &pcmd->params.get_nlist; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + pcmd->command = wlan_cpu_to_le16(HostCmd_CMD_802_11K_GET_NLIST); + pget_nlist->action = wlan_cpu_to_le16(cmd_action); /* only for get */ + pcmd->size = + wlan_cpu_to_le16(S_DS_GEN + + sizeof(HostCmd_DS_802_11K_GET_NLIST)); + + LEAVE(); + return ret; +} + +/** + * @brief This function sends get nlist. + * + * @param pmpriv A pointer to mlan_private structure + * @param pcmd Hostcmd ID + * @param cmd_action Command action + * @param pdata_buf A pointer to information buffer + * @return N/A + */ +mlan_status +wlan_cmd_offload_feature_ctrl(mlan_private *pmpriv, + HostCmd_DS_COMMAND *pcmd, + t_u16 cmd_action, void *pdata_buf) +{ + HostCmd_OFFLOAD_FEATURE_CTRL *pfctrl = &pcmd->params.fctrl; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + pcmd->command = wlan_cpu_to_le16(HostCmd_CMD_OFFLOAD_FEATURE_CONTROL); + memcpy(pmpriv->adapter, pfctrl, pdata_buf, + sizeof(HostCmd_OFFLOAD_FEATURE_CTRL)); + pcmd->size = + wlan_cpu_to_le16(S_DS_GEN + + sizeof(HostCmd_OFFLOAD_FEATURE_CTRL)); + + LEAVE(); + return ret; +} + +/** + * @brief This function sends get sta band channel command to firmware. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action Command action + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * @param pdata_buf A pointer to information buffer + * + * @return MLAN_STATUS_SUCCESS/ MLAN_STATUS_FAILURE + */ +static mlan_status +wlan_cmd_sta_config(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, + IN mlan_ioctl_req *pioctl_buf, IN t_void *pdata_buf) +{ + mlan_ds_bss *bss = MNULL; + HostCmd_DS_STA_CONFIGURE *sta_cfg_cmd = &cmd->params.sta_cfg; + MrvlIEtypes_channel_band_t *tlv_band_channel = MNULL; + mlan_status ret = MLAN_STATUS_FAILURE; + + ENTER(); + if (!pioctl_buf) + return ret; + + if (pioctl_buf->req_id == MLAN_IOCTL_BSS) { + bss = (mlan_ds_bss *)pioctl_buf->pbuf; + if ((bss->sub_command == MLAN_OID_BSS_CHAN_INFO) && + (cmd_action == HostCmd_ACT_GEN_GET)) { + cmd->command = + wlan_cpu_to_le16(HostCmd_CMD_STA_CONFIGURE); + cmd->size = + wlan_cpu_to_le16(S_DS_GEN + + sizeof + (HostCmd_DS_STA_CONFIGURE) + + sizeof(*tlv_band_channel)); + sta_cfg_cmd->action = wlan_cpu_to_le16(cmd_action); + tlv_band_channel = + (MrvlIEtypes_channel_band_t *)sta_cfg_cmd-> + tlv_buffer; + memset(pmpriv->adapter, tlv_band_channel, 0x00, + sizeof(*tlv_band_channel)); + tlv_band_channel->header.type = + wlan_cpu_to_le16(TLV_TYPE_CHANNELBANDLIST); + tlv_band_channel->header.len = + wlan_cpu_to_le16(sizeof + (MrvlIEtypes_channel_band_t) + - sizeof(MrvlIEtypesHeader_t)); + ret = MLAN_STATUS_SUCCESS; + } + } + + LEAVE(); + return ret; +} + +/** + * @brief This function prepare the config tlvs of roam offload. + * + * @param priv A pointer to mlan_private structure + * @param tlv_no TLV type + * @param roam Pointer to mlan_ds_misc_roam_offload structure + * @param trigger_condition Value of trigger_condition + * @param pos Pointer to the buffer of HostCmd_DS_ROAM_OFFLOAD + * + * @return Command size +*/ +static t_u16 +mlan_prepare_roam_offload_tlv(IN pmlan_private pmpriv, + IN t_u32 type, + IN mlan_ds_misc_roam_offload * roam, + IN t_u8 trigger_condition, IN t_u8 *pos) +{ + MrvlIEtypes_fw_roam_enable_t *enable_tlv = MNULL; + MrvlIEtypes_fw_roam_trigger_condition_t *trigger_condition_tlv = MNULL; + MrvlIEtypes_Bssid_t *bssid_tlv = MNULL; + MrvlIEtypes_SsIdParamSet_t *ssid_tlv = MNULL; + MrvlIEtypes_fw_roam_retry_count_t *retry_count_tlv = MNULL; + MrvlIEtypes_para_rssi_t *rssi_para_tlv = MNULL; + MrvlIEtypes_fw_roam_bgscan_setting_t *bgscan_set_tlv = MNULL; + MrvlIEtypes_roam_blacklist_t *blacklist_tlv = MNULL; + MrvlIEtypes_ees_param_set_t *ees_param_tlv = MNULL; + MrvlIEtypes_band_rssi_t *band_rssi_tlv = MNULL; + MrvlIEtypes_beacon_miss_threshold_t *bcn_miss_threshold_tlv = MNULL; + MrvlIEtypes_pre_beacon_miss_threshold_t *pre_bcn_miss_threshold_tlv = + MNULL; + MrvlIEtypes_RepeatCount_t *tlv_repeat = MNULL; + t_u8 zero_mac[MLAN_MAC_ADDR_LENGTH] = { 0 }, *begin; + int i = 0; + + ENTER(); + + begin = pos; + if (type & FW_ROAM_ENABLE) { + enable_tlv = (MrvlIEtypes_fw_roam_enable_t *) pos; + enable_tlv->header.type = wlan_cpu_to_le16(TLV_TYPE_ROAM); + enable_tlv->header.len = + wlan_cpu_to_le16(sizeof(MrvlIEtypes_fw_roam_enable_t) - + sizeof(MrvlIEtypesHeader_t)); + if (roam->enable <= ROAM_OFFLOAD_WITHOUT_APLIST) + enable_tlv->roam_enable = roam->enable; + else + enable_tlv->roam_enable = ROAM_OFFLOAD_WITHOUT_APLIST; + pos += sizeof(MrvlIEtypes_fw_roam_enable_t); + } + if (type & FW_ROAM_TRIGGER_COND) { + trigger_condition_tlv = + (MrvlIEtypes_fw_roam_trigger_condition_t *) pos; + trigger_condition_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_ROM_TRIGGER); + trigger_condition_tlv->header.len = + wlan_cpu_to_le16(sizeof + (trigger_condition_tlv-> + trigger_condition)); + trigger_condition_tlv->trigger_condition = + wlan_cpu_to_le16(trigger_condition); + pos += sizeof(trigger_condition_tlv->header) + + sizeof(trigger_condition_tlv->trigger_condition); + } + if (type & FW_ROAM_BSSID) { + bssid_tlv = (MrvlIEtypes_Bssid_t *)pos; + bssid_tlv->header.type = wlan_cpu_to_le16(TLV_TYPE_BSSID); + bssid_tlv->header.len = + wlan_cpu_to_le16(sizeof(bssid_tlv->bssid)); + if (0 != + memcmp(pmpriv->adapter, roam->bssid_reconnect, zero_mac, + sizeof(zero_mac))) + memcpy(pmpriv->adapter, bssid_tlv->bssid, + roam->bssid_reconnect, bssid_tlv->header.len); + else { + if (roam->config_mode == ROAM_OFFLOAD_SUSPEND_CFG) + memcpy(pmpriv->adapter, bssid_tlv->bssid, + pmpriv->curr_bss_params.bss_descriptor. + mac_address, bssid_tlv->header.len); + else if (roam->config_mode == ROAM_OFFLOAD_RESUME_CFG) + memcpy(pmpriv->adapter, bssid_tlv->bssid, + zero_mac, bssid_tlv->header.len); + } + pos += sizeof(bssid_tlv->header) + sizeof(bssid_tlv->bssid); + } + if (type & FW_ROAM_SSID) { + for (i = 0; i < roam->ssid_list.ssid_num; i++) { + ssid_tlv = (MrvlIEtypes_SsIdParamSet_t *)pos; + ssid_tlv->header.type = wlan_cpu_to_le16(TLV_TYPE_SSID); + memcpy(pmpriv->adapter, ssid_tlv->ssid, + roam->ssid_list.ssids[i].ssid, + roam->ssid_list.ssids[i].ssid_len); + pos += sizeof(ssid_tlv->header) + + wlan_strlen(ssid_tlv->ssid); + ssid_tlv->header.len = + wlan_cpu_to_le16(wlan_strlen(ssid_tlv->ssid)); + } + if (!roam->ssid_list.ssid_num) { + ssid_tlv = (MrvlIEtypes_SsIdParamSet_t *)pos; + ssid_tlv->header.type = wlan_cpu_to_le16(TLV_TYPE_SSID); + memcpy(pmpriv->adapter, ssid_tlv->ssid, + pmpriv->curr_bss_params.bss_descriptor.ssid.ssid, + pmpriv->curr_bss_params.bss_descriptor.ssid. + ssid_len); + ssid_tlv->header.len = + wlan_cpu_to_le16(wlan_strlen(ssid_tlv->ssid)); + pos += sizeof(ssid_tlv->header) + ssid_tlv->header.len; + } + } + if (type & FW_ROAM_RETRY_COUNT) { + retry_count_tlv = (MrvlIEtypes_fw_roam_retry_count_t *) pos; + retry_count_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_ROM_RETRY_COUNT); + retry_count_tlv->header.len = + wlan_cpu_to_le16(sizeof(retry_count_tlv->retry_count)); + if (roam->retry_count) + retry_count_tlv->retry_count = + wlan_cpu_to_le16(roam->retry_count); + else + retry_count_tlv->retry_count = + wlan_cpu_to_le16(RETRY_UNLIMITED_TIME); + pos += sizeof(retry_count_tlv->header) + + sizeof(retry_count_tlv->retry_count); + } + if (type & FW_ROAM_RSSI_PARA) { + rssi_para_tlv = (MrvlIEtypes_para_rssi_t *) pos; + rssi_para_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_ROM_PARA_RSSI); + rssi_para_tlv->header.len = + wlan_cpu_to_le16(sizeof(rssi_para_tlv->max_rssi) + + sizeof(rssi_para_tlv->min_rssi) + + sizeof(rssi_para_tlv->step_rssi)); + rssi_para_tlv->max_rssi = roam->para_rssi.max_rssi; + rssi_para_tlv->min_rssi = roam->para_rssi.min_rssi; + rssi_para_tlv->step_rssi = roam->para_rssi.step_rssi; + pos += sizeof(rssi_para_tlv->header) + + sizeof(rssi_para_tlv->max_rssi) + + sizeof(rssi_para_tlv->min_rssi) + + sizeof(rssi_para_tlv->step_rssi); + } + + if (type & FW_ROAM_BAND_RSSI) { + band_rssi_tlv = (MrvlIEtypes_band_rssi_t *) pos; + band_rssi_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_BAND_RSSI); + band_rssi_tlv->header.len = + wlan_cpu_to_le16(sizeof(MrvlIEtypes_band_rssi_t) - + sizeof(MrvlIEtypesHeader_t)); + band_rssi_tlv->band_rssi.band_preferred = + roam->band_rssi.band_preferred; + band_rssi_tlv->band_rssi.rssi_hysteresis = + roam->band_rssi.rssi_hysteresis; + pos += sizeof(MrvlIEtypes_band_rssi_t); + } + + if (type & FW_ROAM_BGSCAN_PARAM) { + bgscan_set_tlv = (MrvlIEtypes_fw_roam_bgscan_setting_t *) pos; + bgscan_set_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_ROM_BGSCAN); + bgscan_set_tlv->header.len = + wlan_cpu_to_le16(sizeof + (MrvlIEtypes_fw_roam_bgscan_setting_t) + - sizeof(MrvlIEtypesHeader_t)); + bgscan_set_tlv->bss_type = roam->bgscan_cfg.bss_type; + bgscan_set_tlv->channels_perscan = + roam->bgscan_cfg.channels_per_scan; + bgscan_set_tlv->scan_interval = + wlan_cpu_to_le32(roam->bgscan_cfg.scan_interval); + bgscan_set_tlv->report_condition = + wlan_cpu_to_le32(roam->bgscan_cfg.bg_rpt_condition); + pos += sizeof(MrvlIEtypes_fw_roam_bgscan_setting_t); + } + + if (type & FW_ROAM_EES_PARAM) { + ees_param_tlv = (MrvlIEtypes_ees_param_set_t *) pos; + ees_param_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_ENERGYEFFICIENTSCAN); + ees_param_tlv->header.len = + wlan_cpu_to_le16(sizeof(MrvlIEtypes_ees_param_set_t) - + sizeof(MrvlIEtypesHeader_t)); + ees_param_tlv->ees_cfg.ees_mode = + wlan_cpu_to_le16(roam->ees_cfg.ees_mode); + ees_param_tlv->ees_cfg.ees_rpt_condition = + wlan_cpu_to_le16(roam->ees_cfg.ees_rpt_condition); + ees_param_tlv->ees_cfg.high_scan_period = + wlan_cpu_to_le16(roam->ees_cfg.high_scan_period); + ees_param_tlv->ees_cfg.high_scan_count = + wlan_cpu_to_le16(roam->ees_cfg.high_scan_count); + ees_param_tlv->ees_cfg.mid_scan_period = + wlan_cpu_to_le16(roam->ees_cfg.mid_scan_period); + ees_param_tlv->ees_cfg.mid_scan_count = + wlan_cpu_to_le16(roam->ees_cfg.mid_scan_count); + ees_param_tlv->ees_cfg.low_scan_period = + wlan_cpu_to_le16(roam->ees_cfg.low_scan_period); + ees_param_tlv->ees_cfg.low_scan_count = + wlan_cpu_to_le16(roam->ees_cfg.low_scan_count); + pos += sizeof(MrvlIEtypes_ees_param_set_t); + } + + if (type & FW_ROAM_BCN_MISS_THRESHOLD) { + bcn_miss_threshold_tlv = + (MrvlIEtypes_beacon_miss_threshold_t *) pos; + bcn_miss_threshold_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_BCNMISS); + bcn_miss_threshold_tlv->header.len = + wlan_cpu_to_le16(sizeof + (MrvlIEtypes_beacon_miss_threshold_t) - + sizeof(MrvlIEtypesHeader_t)); + bcn_miss_threshold_tlv->bcn_miss_threshold = + roam->bcn_miss_threshold; + pos += sizeof(MrvlIEtypes_beacon_miss_threshold_t); + } + + if (type & FW_ROAM_PRE_BCN_MISS_THRESHOLD) { + pre_bcn_miss_threshold_tlv = + (MrvlIEtypes_pre_beacon_miss_threshold_t *) pos; + pre_bcn_miss_threshold_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_PRE_BCNMISS); + pre_bcn_miss_threshold_tlv->header.len = + wlan_cpu_to_le16(sizeof + (MrvlIEtypes_pre_beacon_miss_threshold_t) + - sizeof(MrvlIEtypesHeader_t)); + pre_bcn_miss_threshold_tlv->pre_bcn_miss_threshold = + roam->pre_bcn_miss_threshold; + pos += sizeof(MrvlIEtypes_pre_beacon_miss_threshold_t); + } + + if (type & FW_ROAM_BLACKLIST) { + blacklist_tlv = (MrvlIEtypes_roam_blacklist_t *) pos; + blacklist_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_BLACKLIST_BSSID); + blacklist_tlv->header.len = + roam->black_list.ap_num * MLAN_MAC_ADDR_LENGTH + + sizeof(roam->black_list.ap_num); + memcpy(pmpriv->adapter, (t_u8 *)&blacklist_tlv->blacklist, + (t_u8 *)&roam->black_list, blacklist_tlv->header.len); + pos += sizeof(MrvlIEtypesHeader_t) + blacklist_tlv->header.len; + blacklist_tlv->header.len = + wlan_cpu_to_le16(blacklist_tlv->header.len); + } + + if (type & FW_ROAM_REPEAT_CNT) { + tlv_repeat = (MrvlIEtypes_RepeatCount_t *)pos; + tlv_repeat->header.type = + wlan_cpu_to_le16(TLV_TYPE_REPEAT_COUNT); + tlv_repeat->header.len = + wlan_cpu_to_le16(sizeof(MrvlIEtypes_RepeatCount_t) - + sizeof(MrvlIEtypesHeader_t)); + tlv_repeat->repeat_count = wlan_cpu_to_le16(roam->repeat_count); + pos += sizeof(MrvlIEtypes_RepeatCount_t); + } + + LEAVE(); + return (pos - begin); +} + +/** + * @brief This function sends enable/disable roam offload command to firmware. + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action Command action + * @param pdata_buf A pointer to information buffer + * + * @return MLAN_STATUS_SUCCESS/ MLAN_STATUS_FAILURE + */ +static mlan_status +wlan_cmd_roam_offload(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, IN t_void *pdata_buf) +{ + HostCmd_DS_ROAM_OFFLOAD *roam_cmd = &cmd->params.roam_offload; + MrvlIEtypes_roam_aplist_t *aplist = MNULL; + t_u8 *pos = (t_u8 *)roam_cmd + sizeof(roam_cmd->action); + mlan_ds_misc_roam_offload *roam = MNULL; + t_u8 zero_mac[MLAN_MAC_ADDR_LENGTH] = { 0 }; + t_u32 type = 0; + t_u8 trigger_condition = 0; + + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_ROAM_OFFLOAD); + cmd->size = S_DS_GEN + sizeof(HostCmd_DS_ROAM_OFFLOAD); + roam_cmd->action = wlan_cpu_to_le16(cmd_action); + + roam = (mlan_ds_misc_roam_offload *) pdata_buf; + + if (roam->config_mode) { + switch (roam->config_mode) { + case ROAM_OFFLOAD_ENABLE: + type |= FW_ROAM_ENABLE; + if (roam->enable && roam->enable != AUTO_RECONNECT) { + type |= FW_ROAM_TRIGGER_COND; + trigger_condition |= + RSSI_LOW_TRIGGER | + PRE_BEACON_LOST_TRIGGER; + } + break; + case ROAM_OFFLOAD_SUSPEND_CFG: + type |= FW_ROAM_TRIGGER_COND | FW_ROAM_RETRY_COUNT; + if (roam->enable == AUTO_RECONNECT) { + type |= FW_ROAM_BSSID | FW_ROAM_SSID; + trigger_condition = + LINK_LOST_TRIGGER | + DEAUTH_WITH_EXT_AP_TRIGGER; + } else + trigger_condition = + LINK_LOST_TRIGGER | + DEAUTH_WITH_EXT_AP_TRIGGER | + RSSI_LOW_TRIGGER | + PRE_BEACON_LOST_TRIGGER; + + if (roam->enable == ROAM_OFFLOAD_WITH_BSSID) + type |= FW_ROAM_BSSID; + if (roam->enable == ROAM_OFFLOAD_WITH_SSID) + type |= FW_ROAM_SSID; + break; + case ROAM_OFFLOAD_RESUME_CFG: + type |= FW_ROAM_TRIGGER_COND; + if (roam->enable == AUTO_RECONNECT) + trigger_condition = NO_TRIGGER; + else + trigger_condition = + RSSI_LOW_TRIGGER | + PRE_BEACON_LOST_TRIGGER; + if (roam->enable == ROAM_OFFLOAD_WITH_BSSID || + roam->enable == AUTO_RECONNECT) + type |= FW_ROAM_BSSID; + break; + case ROAM_OFFLOAD_PARAM_CFG: + if (roam->enable && roam->enable != AUTO_RECONNECT) { + if (roam->retry_count != 0) + type |= FW_ROAM_RETRY_COUNT; + if (roam->ssid_list.ssid_num) + type |= FW_ROAM_SSID; + if (roam->para_rssi.set_flag) + type |= FW_ROAM_RSSI_PARA; + if (0 != + memcmp(pmpriv->adapter, + roam->bssid_reconnect, zero_mac, + sizeof(zero_mac))) + type |= FW_ROAM_BSSID; + if (roam->band_rssi_flag) + type |= FW_ROAM_BAND_RSSI; + if (roam->bgscan_set_flag) + type |= FW_ROAM_BGSCAN_PARAM; + if (roam->ees_param_set_flag) + type |= FW_ROAM_EES_PARAM; + if (roam->bcn_miss_threshold) + type |= FW_ROAM_BCN_MISS_THRESHOLD; + if (roam->pre_bcn_miss_threshold) + type |= FW_ROAM_PRE_BCN_MISS_THRESHOLD; + if (roam->black_list.ap_num) + type |= FW_ROAM_BLACKLIST; + if (roam->trigger_condition != 0xff) { + type |= FW_ROAM_TRIGGER_COND; + trigger_condition = + roam->trigger_condition; + } + if (roam->repeat_count) + type |= FW_ROAM_REPEAT_CNT; + } + break; + } + cmd->size += + mlan_prepare_roam_offload_tlv(pmpriv, type, roam, + trigger_condition, pos); + } + if (roam->aplist.ap_num) { + aplist = (MrvlIEtypes_roam_aplist_t *) pos; + aplist->header.type = wlan_cpu_to_le16(TLV_TYPE_APLIST); + aplist->header.len = roam->aplist.ap_num * MLAN_MAC_ADDR_LENGTH; + memcpy(pmpriv->adapter, aplist->ap_mac, roam->aplist.ap_mac, + roam->aplist.ap_num * MLAN_MAC_ADDR_LENGTH); + pos += sizeof(aplist->header) + aplist->header.len; + cmd->size += sizeof(aplist->header) + aplist->header.len; + aplist->header.len = wlan_cpu_to_le16(aplist->header.len); + } + cmd->size = wlan_cpu_to_le16(cmd->size); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function check if the command is supported by firmware + * + * @param priv A pointer to mlan_private structure + * @param cmd_no Command number + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_is_cmd_allowed(mlan_private *priv, IN t_u16 cmd_no) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + ENTER(); + if (!IS_FW_SUPPORT_ADHOC(priv->adapter)) { + switch (cmd_no) { + case HostCmd_ACT_MAC_ADHOC_G_PROTECTION_ON: + case HostCmd_CMD_802_11_IBSS_COALESCING_STATUS: + case HostCmd_CMD_802_11_AD_HOC_START: + case HostCmd_CMD_802_11_AD_HOC_JOIN: + case HostCmd_CMD_802_11_AD_HOC_STOP: + ret = MLAN_STATUS_FAILURE; + break; + default: + break; + } + } + LEAVE(); + return ret; +} + +/** + * @brief This function prepare the command before sending to firmware. + * + * @param priv A pointer to mlan_private structure + * @param cmd_no Command number + * @param cmd_action Command action: GET or SET + * @param cmd_oid Cmd oid: treated as sub command + * @param pioctl_buf A pointer to MLAN IOCTL Request buffer + * @param pdata_buf A pointer to information buffer + * @param pcmd_buf A pointer to cmd buf + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_ops_sta_prepare_cmd(IN t_void *priv, + IN t_u16 cmd_no, + IN t_u16 cmd_action, + IN t_u32 cmd_oid, + IN t_void *pioctl_buf, + IN t_void *pdata_buf, IN t_void *pcmd_buf) +{ + HostCmd_DS_COMMAND *cmd_ptr = (HostCmd_DS_COMMAND *)pcmd_buf; + mlan_private *pmpriv = (mlan_private *)priv; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (wlan_is_cmd_allowed(pmpriv, cmd_no)) { + PRINTM(MERROR, "FW don't support the command 0x%x\n", cmd_no); + return MLAN_STATUS_FAILURE; + } + /* Prepare command */ + switch (cmd_no) { + case HostCmd_CMD_GET_HW_SPEC: + ret = wlan_cmd_get_hw_spec(pmpriv, cmd_ptr); + break; + case HostCmd_CMD_SDIO_SP_RX_AGGR_CFG: + ret = wlan_cmd_sdio_rx_aggr_cfg(cmd_ptr, cmd_action, pdata_buf); + break; + case HostCmd_CMD_CFG_DATA: + ret = wlan_cmd_cfg_data(pmpriv, cmd_ptr, cmd_action, cmd_oid, + pdata_buf); + break; + case HostCmd_CMD_MAC_CONTROL: + ret = wlan_cmd_mac_control(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_802_11_MAC_ADDRESS: + ret = wlan_cmd_802_11_mac_address(pmpriv, cmd_ptr, cmd_action); + break; + case HostCmd_CMD_MAC_MULTICAST_ADR: + ret = wlan_cmd_mac_multicast_adr(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_TX_RATE_CFG: + ret = wlan_cmd_tx_rate_cfg(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_802_11_RF_ANTENNA: + ret = wlan_cmd_802_11_rf_antenna(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_CW_MODE_CTRL: + ret = wlan_cmd_cw_mode_ctrl(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_TXPWR_CFG: + ret = wlan_cmd_tx_power_cfg(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_802_11_RF_TX_POWER: + ret = wlan_cmd_802_11_rf_tx_power(pmpriv, cmd_ptr, + cmd_action, pdata_buf); + break; + case HostCmd_CMD_802_11_PS_MODE_ENH: + ret = wlan_cmd_enh_power_mode(pmpriv, cmd_ptr, cmd_action, + (t_u16)cmd_oid, pdata_buf); + break; + case HostCmd_CMD_802_11_HS_CFG_ENH: + ret = wlan_cmd_802_11_hs_cfg(pmpriv, cmd_ptr, cmd_action, + (hs_config_param *)pdata_buf); + break; + case HostCmd_CMD_802_11_FW_WAKE_METHOD: + ret = wlan_cmd_802_11_fw_wakeup_method(pmpriv, cmd_ptr, + cmd_action, pdata_buf); + break; + case HostCmd_CMD_802_11_ROBUSTCOEX: + ret = wlan_cmd_robustcoex(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HOST_CMD_PMIC_CONFIGURE: + cmd_ptr->command = wlan_cpu_to_le16(cmd_no); + cmd_ptr->size = wlan_cpu_to_le16(S_DS_GEN); + break; + case HostCmd_CMD_802_11_SLEEP_PERIOD: + ret = wlan_cmd_802_11_sleep_period(pmpriv, cmd_ptr, + cmd_action, + (t_u16 *)pdata_buf); + break; + case HostCmd_CMD_802_11_SLEEP_PARAMS: + ret = wlan_cmd_802_11_sleep_params(pmpriv, cmd_ptr, + cmd_action, + (t_u16 *)pdata_buf); + break; + case HostCmd_CMD_802_11_SCAN: + ret = wlan_cmd_802_11_scan(pmpriv, cmd_ptr, pdata_buf); + break; + case HostCmd_CMD_802_11_BG_SCAN_CONFIG: + ret = wlan_cmd_bgscan_config(pmpriv, cmd_ptr, pdata_buf); + break; + case HostCmd_CMD_802_11_BG_SCAN_QUERY: + ret = wlan_cmd_802_11_bg_scan_query(pmpriv, cmd_ptr, pdata_buf); + break; + case HostCmd_CMD_802_11_ASSOCIATE: + ret = wlan_cmd_802_11_associate(pmpriv, cmd_ptr, pdata_buf); + break; + case HostCmd_CMD_802_11_DEAUTHENTICATE: + case HostCmd_CMD_802_11_DISASSOCIATE: + ret = wlan_cmd_802_11_deauthenticate(pmpriv, cmd_no, cmd_ptr, + pdata_buf); + break; + case HostCmd_CMD_802_11_AD_HOC_START: + ret = wlan_cmd_802_11_ad_hoc_start(pmpriv, cmd_ptr, pdata_buf); + break; + case HostCmd_CMD_802_11_AD_HOC_JOIN: + ret = wlan_cmd_802_11_ad_hoc_join(pmpriv, cmd_ptr, pdata_buf); + break; + case HostCmd_CMD_802_11_AD_HOC_STOP: + ret = wlan_cmd_802_11_ad_hoc_stop(pmpriv, cmd_ptr); + break; + case HostCmd_CMD_802_11_GET_LOG: + ret = wlan_cmd_802_11_get_log(pmpriv, cmd_ptr); + break; + case HostCmd_CMD_RSSI_INFO: + ret = wlan_cmd_802_11_rssi_info(pmpriv, cmd_ptr, cmd_action); + break; + case HostCmd_CMD_RSSI_INFO_EXT: + ret = wlan_cmd_802_11_rssi_info_ext(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_802_11_SNMP_MIB: + ret = wlan_cmd_802_11_snmp_mib(pmpriv, cmd_ptr, cmd_action, + cmd_oid, pdata_buf); + break; + case HostCmd_CMD_802_11_RADIO_CONTROL: + ret = wlan_cmd_802_11_radio_control(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_802_11_TX_RATE_QUERY: + cmd_ptr->command = + wlan_cpu_to_le16(HostCmd_CMD_802_11_TX_RATE_QUERY); + cmd_ptr->size = + wlan_cpu_to_le16(sizeof(HostCmd_TX_RATE_QUERY) + + S_DS_GEN); + pmpriv->tx_rate = 0; + ret = MLAN_STATUS_SUCCESS; + break; + case HostCmd_CMD_VERSION_EXT: + cmd_ptr->command = wlan_cpu_to_le16(cmd_no); + cmd_ptr->params.verext.version_str_sel = + (t_u8)(*((t_u32 *)pdata_buf)); + cmd_ptr->size = + wlan_cpu_to_le16(sizeof(HostCmd_DS_VERSION_EXT) + + S_DS_GEN); + ret = MLAN_STATUS_SUCCESS; + break; + case HostCmd_CMD_RX_MGMT_IND: + cmd_ptr->command = wlan_cpu_to_le16(cmd_no); + cmd_ptr->params.rx_mgmt_ind.action = + wlan_cpu_to_le16(cmd_action); + cmd_ptr->params.rx_mgmt_ind.mgmt_subtype_mask = + wlan_cpu_to_le32((t_u32)(*((t_u32 *)pdata_buf))); + cmd_ptr->size = + wlan_cpu_to_le16(sizeof(HostCmd_DS_RX_MGMT_IND) + + S_DS_GEN); + break; + case HostCmd_CMD_802_11_RF_CHANNEL: + ret = wlan_cmd_802_11_rf_channel(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_FUNC_INIT: + if (pmpriv->adapter->hw_status == WlanHardwareStatusReset) + pmpriv->adapter->hw_status = + WlanHardwareStatusInitializing; + cmd_ptr->command = wlan_cpu_to_le16(cmd_no); + cmd_ptr->size = wlan_cpu_to_le16(S_DS_GEN); + break; + case HostCmd_CMD_FUNC_SHUTDOWN: + pmpriv->adapter->hw_status = WlanHardwareStatusReset; + cmd_ptr->command = wlan_cpu_to_le16(cmd_no); + cmd_ptr->size = wlan_cpu_to_le16(S_DS_GEN); + break; + case HostCmd_CMD_SOFT_RESET: + cmd_ptr->command = wlan_cpu_to_le16(cmd_no); + cmd_ptr->size = wlan_cpu_to_le16(S_DS_GEN); + break; + case HostCmd_CMD_11N_ADDBA_REQ: + ret = wlan_cmd_11n_addba_req(pmpriv, cmd_ptr, pdata_buf); + break; + case HostCmd_CMD_11N_DELBA: + ret = wlan_cmd_11n_delba(pmpriv, cmd_ptr, pdata_buf); + break; + case HostCmd_CMD_11N_ADDBA_RSP: + ret = wlan_cmd_11n_addba_rspgen(pmpriv, cmd_ptr, pdata_buf); + break; + case HostCmd_CMD_802_11_KEY_MATERIAL: + ret = wlan_cmd_802_11_key_material(pmpriv, cmd_ptr, cmd_action, + cmd_oid, pdata_buf); + break; + + case HostCmd_CMD_GTK_REKEY_OFFLOAD_CFG: + ret = wlan_cmd_gtk_rekey_offload(pmpriv, cmd_ptr, cmd_action, + cmd_oid, pdata_buf); + break; + + case HostCmd_CMD_SUPPLICANT_PMK: + ret = wlan_cmd_802_11_supplicant_pmk(pmpriv, cmd_ptr, + cmd_action, pdata_buf); + break; + case HostCmd_CMD_802_11_EAPOL_PKT: + ret = wlan_cmd_eapol_pkt(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_SUPPLICANT_PROFILE: + ret = wlan_cmd_802_11_supplicant_profile(pmpriv, cmd_ptr, + cmd_action, pdata_buf); + break; + + case HostCmd_CMD_802_11D_DOMAIN_INFO: + ret = wlan_cmd_802_11d_domain_info(pmpriv, cmd_ptr, cmd_action); + break; + case HostCmd_CMD_802_11K_GET_NLIST: + ret = wlan_cmd_802_11k_get_nlist(pmpriv, cmd_ptr, cmd_action); + break; + case HostCmd_CMD_OFFLOAD_FEATURE_CONTROL: + ret = wlan_cmd_offload_feature_ctrl(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_802_11_TPC_ADAPT_REQ: + case HostCmd_CMD_802_11_TPC_INFO: + case HostCmd_CMD_802_11_CHAN_SW_ANN: + case HostCmd_CMD_CHAN_REPORT_REQUEST: + ret = wlan_11h_cmd_process(pmpriv, cmd_ptr, pdata_buf); + break; + case HostCmd_CMD_RECONFIGURE_TX_BUFF: + ret = wlan_cmd_recfg_tx_buf(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_AMSDU_AGGR_CTRL: + ret = wlan_cmd_amsdu_aggr_ctrl(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_11N_CFG: + ret = wlan_cmd_11n_cfg(pmpriv, cmd_ptr, cmd_action, pdata_buf); + break; + case HostCmd_CMD_TX_BF_CFG: + ret = wlan_cmd_tx_bf_cfg(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_WMM_GET_STATUS: + PRINTM(MINFO, "WMM: WMM_GET_STATUS cmd sent\n"); + cmd_ptr->command = wlan_cpu_to_le16(HostCmd_CMD_WMM_GET_STATUS); + cmd_ptr->size = + wlan_cpu_to_le16(sizeof(HostCmd_DS_WMM_GET_STATUS) + + S_DS_GEN); + ret = MLAN_STATUS_SUCCESS; + break; + case HostCmd_CMD_WMM_ADDTS_REQ: + ret = wlan_cmd_wmm_addts_req(pmpriv, cmd_ptr, pdata_buf); + break; + case HostCmd_CMD_WMM_DELTS_REQ: + ret = wlan_cmd_wmm_delts_req(pmpriv, cmd_ptr, pdata_buf); + break; + case HostCmd_CMD_WMM_QUEUE_CONFIG: + ret = wlan_cmd_wmm_queue_config(pmpriv, cmd_ptr, pdata_buf); + break; + case HostCmd_CMD_WMM_QUEUE_STATS: + ret = wlan_cmd_wmm_queue_stats(pmpriv, cmd_ptr, pdata_buf); + break; + case HostCmd_CMD_WMM_TS_STATUS: + ret = wlan_cmd_wmm_ts_status(pmpriv, cmd_ptr, pdata_buf); + break; + case HostCmd_CMD_WMM_PARAM_CONFIG: + ret = wlan_cmd_wmm_param_config(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_802_11_IBSS_COALESCING_STATUS: + ret = wlan_cmd_ibss_coalescing_status(pmpriv, cmd_ptr, + cmd_action, pdata_buf); + break; + case HostCmd_CMD_MGMT_IE_LIST: + ret = wlan_cmd_mgmt_ie_list(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_TDLS_CONFIG: + ret = wlan_cmd_tdls_config(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_TDLS_OPERATION: + ret = wlan_cmd_tdls_oper(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_802_11_SCAN_EXT: + ret = wlan_cmd_802_11_scan_ext(pmpriv, cmd_ptr, pdata_buf); + break; + case HostCmd_CMD_ECL_SYSTEM_CLOCK_CONFIG: + ret = wlan_cmd_sysclock_cfg(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_MAC_REG_ACCESS: + case HostCmd_CMD_BBP_REG_ACCESS: + case HostCmd_CMD_RF_REG_ACCESS: + case HostCmd_CMD_CAU_REG_ACCESS: + case HostCmd_CMD_TARGET_ACCESS: + case HostCmd_CMD_802_11_EEPROM_ACCESS: + ret = wlan_cmd_reg_access(cmd_ptr, cmd_action, pdata_buf); + break; + case HostCmd_CMD_MEM_ACCESS: + ret = wlan_cmd_mem_access(cmd_ptr, cmd_action, pdata_buf); + break; + case HostCmd_CMD_INACTIVITY_TIMEOUT_EXT: + ret = wlan_cmd_inactivity_timeout(cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_GET_TSF: + ret = wlan_cmd_get_tsf(pmpriv, cmd_ptr, cmd_action); + break; + case HostCmd_CMD_SDIO_GPIO_INT_CONFIG: + ret = wlan_cmd_sdio_gpio_int(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_SET_BSS_MODE: + cmd_ptr->command = wlan_cpu_to_le16(cmd_no); +#ifdef WIFI_DIRECT_SUPPORT + if (pdata_buf) { + cmd_ptr->params.bss_mode.con_type = *(t_u8 *)pdata_buf; + } else +#endif + if (pmpriv->bss_mode == MLAN_BSS_MODE_IBSS) + cmd_ptr->params.bss_mode.con_type = + CONNECTION_TYPE_ADHOC; + else if (pmpriv->bss_mode == MLAN_BSS_MODE_INFRA) + cmd_ptr->params.bss_mode.con_type = + CONNECTION_TYPE_INFRA; + cmd_ptr->size = + wlan_cpu_to_le16(sizeof(HostCmd_DS_SET_BSS_MODE) + + S_DS_GEN); + ret = MLAN_STATUS_SUCCESS; + break; + case HostCmd_CMD_802_11_NET_MONITOR: + ret = wlan_cmd_net_monitor(cmd_ptr, cmd_action, pdata_buf); + break; + case HostCmd_CMD_MEASUREMENT_REQUEST: + case HostCmd_CMD_MEASUREMENT_REPORT: + ret = wlan_meas_cmd_process(pmpriv, cmd_ptr, pdata_buf); + break; +#if defined(SYSKT_MULTI) && defined(OOB_WAKEUP) || defined(SUSPEND_SDIO_PULL_DOWN) + case HostCmd_CMD_SDIO_PULL_CTRL: + ret = wlan_cmd_sdio_pull_ctl(pmpriv, cmd_ptr, cmd_action); + break; +#endif + case HostCmd_CMD_802_11_REMAIN_ON_CHANNEL: + ret = wlan_cmd_remain_on_channel(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; +#ifdef WIFI_DIRECT_SUPPORT + case HOST_CMD_WIFI_DIRECT_MODE_CONFIG: + ret = wlan_cmd_wifi_direct_mode(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; +#endif + case HostCmd_CMD_802_11_SUBSCRIBE_EVENT: + ret = wlan_cmd_subscribe_event(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_OTP_READ_USER_DATA: + ret = wlan_cmd_otp_user_data(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_HS_WAKEUP_REASON: + ret = wlan_cmd_hs_wakeup_reason(pmpriv, cmd_ptr, pdata_buf); + break; + case HostCmd_CMD_REJECT_ADDBA_REQ: + ret = wlan_cmd_reject_addba_req(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; +#ifdef RX_PACKET_COALESCE + case HostCmd_CMD_RX_PKT_COALESCE_CFG: + ret = wlan_cmd_rx_pkt_coalesce_cfg(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; +#endif + case HostCmd_CMD_MULTI_CHAN_CONFIG: + ret = wlan_cmd_multi_chan_cfg(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_MULTI_CHAN_POLICY: + ret = wlan_cmd_multi_chan_policy(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_DRCS_CONFIG: + ret = wlan_cmd_drcs_cfg(pmpriv, cmd_ptr, cmd_action, pdata_buf); + break; + case HostCmd_DFS_REPEATER_MODE: + ret = wlan_cmd_dfs_repeater_cfg(pmpriv, cmd_ptr, + cmd_action, pdata_buf); + break; + case HostCmd_CMD_COALESCE_CFG: + ret = wlan_cmd_coalesce_config(pmpriv, cmd_ptr, + cmd_action, pdata_buf); + break; + case HostCmd_DS_GET_SENSOR_TEMP: + ret = wlan_cmd_get_sensor_temp(pmpriv, cmd_ptr, cmd_action); + break; + case HostCmd_CMD_STA_CONFIGURE: + ret = wlan_cmd_sta_config(pmpriv, cmd_ptr, cmd_action, + pioctl_buf, pdata_buf); + break; + + case HostCmd_CMD_INDEPENDENT_RESET_CFG: + ret = wlan_cmd_ind_rst_cfg(cmd_ptr, cmd_action, pdata_buf); + break; + + case HostCmd_CMD_ROAM_OFFLOAD: + ret = wlan_cmd_roam_offload(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + + case HostCmd_CMD_802_11_PS_INACTIVITY_TIMEOUT: + ret = wlan_cmd_ps_inactivity_timeout(pmpriv, cmd_ptr, + cmd_action, pdata_buf); + break; + case HostCmd_CMD_HOST_CLOCK_CFG: + ret = wlan_cmd_host_clock_cfg(cmd_ptr, cmd_action, pdata_buf); + break; + case HostCmd_CMD_CHAN_REGION_CFG: + cmd_ptr->command = wlan_cpu_to_le16(cmd_no); + cmd_ptr->size = + wlan_cpu_to_le16(sizeof(HostCmd_DS_CHAN_REGION_CFG) + + S_DS_GEN); + cmd_ptr->params.reg_cfg.action = wlan_cpu_to_le16(cmd_action); + break; + case HostCmd_CMD_BOOT_SLEEP: + ret = wlan_cmd_boot_sleep(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_FW_DUMP_EVENT: + ret = wlan_cmd_fw_dump_event(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + default: + PRINTM(MERROR, "PREP_CMD: unknown command- %#x\n", cmd_no); + ret = MLAN_STATUS_FAILURE; + break; + } + + LEAVE(); + return ret; +} + +/** + * @brief This function issues commands to initialize firmware + * + * @param priv A pointer to mlan_private structure + * @param first_bss flag for first BSS + * + * @return MLAN_STATUS_PENDING or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_ops_sta_init_cmd(IN t_void *priv, IN t_u8 first_bss) +{ + pmlan_private pmpriv = (pmlan_private)priv; + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u16 enable = MTRUE; + mlan_ds_11n_amsdu_aggr_ctrl amsdu_aggr_ctrl; + + ENTER(); + + if (!pmpriv) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + if (first_bss == MTRUE) { + ret = wlan_adapter_init_cmd(pmpriv->adapter); + if (ret == MLAN_STATUS_FAILURE) + goto done; + } + + /* get tx rate */ + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_TX_RATE_CFG, + HostCmd_ACT_GEN_GET, 0, MNULL, MNULL); + if (ret) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + pmpriv->data_rate = 0; + + /* get tx power */ + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_802_11_RF_TX_POWER, + HostCmd_ACT_GEN_GET, 0, MNULL, MNULL); + if (ret) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + if (IS_FW_SUPPORT_ADHOC(pmpriv->adapter)) { + /* set ibss coalescing_status */ + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_802_11_IBSS_COALESCING_STATUS, + HostCmd_ACT_GEN_SET, 0, MNULL, &enable); + if (ret) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + } + + memset(pmpriv->adapter, &amsdu_aggr_ctrl, 0, sizeof(amsdu_aggr_ctrl)); + amsdu_aggr_ctrl.enable = MLAN_ACT_ENABLE; + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_AMSDU_AGGR_CTRL, + HostCmd_ACT_GEN_SET, 0, MNULL, + (t_void *)&amsdu_aggr_ctrl); + if (ret) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + /* MAC Control must be the last command in init_fw */ + /* set MAC Control */ + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_MAC_CONTROL, + HostCmd_ACT_GEN_SET, 0, MNULL, + &pmpriv->curr_pkt_filter); + if (ret) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + /** set last_init_cmd */ + pmpriv->adapter->last_init_cmd = HostCmd_CMD_MAC_CONTROL; + + if (first_bss == MFALSE) { + /* Get MAC address */ + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_802_11_MAC_ADDRESS, + HostCmd_ACT_GEN_GET, 0, MNULL, MNULL); + if (ret) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + pmpriv->adapter->last_init_cmd = HostCmd_CMD_802_11_MAC_ADDRESS; + } + + ret = MLAN_STATUS_PENDING; +done: + LEAVE(); + return ret; +} diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_sta_cmdresp.c b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_sta_cmdresp.c new file mode 100644 index 000000000000..f969d5c7380f --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_sta_cmdresp.c @@ -0,0 +1,2843 @@ +/** @file mlan_sta_cmdresp.c + * + * @brief This file contains the handling of command + * responses generated by firmware. + * + * Copyright (C) 2008-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 10/21/2008: initial version +******************************************************/ + +#include "mlan.h" +#include "mlan_join.h" +#include "mlan_util.h" +#include "mlan_fw.h" +#include "mlan_main.h" +#include "mlan_wmm.h" +#include "mlan_11n.h" +#include "mlan_11h.h" +#include "mlan_sdio.h" +#include "mlan_meas.h" + +/******************************************************** + Local Variables +********************************************************/ + +/******************************************************** + Global Variables +********************************************************/ + +/******************************************************** + Local Functions +********************************************************/ +/** + * @brief This function handles the command response error for TDLS operation + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return N/A + */ +static void +wlan_process_cmdreps_error_tdls_operation(mlan_private *pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + HostCmd_DS_TDLS_OPER *ptdls_oper_data = &(resp->params.tdls_oper_data); + sta_node *sta_ptr = MNULL; + t_u16 reason; + + ENTER(); + ptdls_oper_data->tdls_action = + wlan_le16_to_cpu(ptdls_oper_data->tdls_action); + reason = wlan_le16_to_cpu(ptdls_oper_data->reason); + sta_ptr = wlan_get_station_entry(pmpriv, ptdls_oper_data->peer_mac); + switch (ptdls_oper_data->tdls_action) { + case TDLS_CREATE: + /* TDLS create command error */ + if (reason != TDLS_LINK_EXISTS && sta_ptr) { + PRINTM(MERROR, + "TDLS CREATE operation: command error, reason %d\n", + reason); + sta_ptr->status = TDLS_SETUP_FAILURE; + } + if (reason == TDLS_LINK_EXISTS && pioctl_buf) + pioctl_buf->status_code = MLAN_STATUS_SUCCESS; + break; + case TDLS_CONFIG: + /* TDLS config command error */ + PRINTM(MERROR, + "TDLS CONFIG operation: command error, reason %d\n", + reason); + if (sta_ptr) + sta_ptr->status = TDLS_SETUP_FAILURE; + break; + case TDLS_DELETE: + /* TDLS delete command error */ + wlan_restore_tdls_packets(pmpriv, ptdls_oper_data->peer_mac, + TDLS_TEAR_DOWN); + if (sta_ptr) { + /**tdls cs stop*/ + if (ISSUPP_EXTCAP_TDLS_CHAN_SWITCH + (sta_ptr->ExtCap.ext_cap)) + wlan_tdls_config(pmpriv, MFALSE); + if (sta_ptr->is_11n_enabled) { + wlan_cleanup_reorder_tbl(pmpriv, + ptdls_oper_data-> + peer_mac); + wlan_11n_cleanup_txbastream_tbl(pmpriv, + ptdls_oper_data-> + peer_mac); + } + if (sta_ptr->status >= TDLS_SETUP_INPROGRESS) + wlan_delete_station_entry(pmpriv, + ptdls_oper_data-> + peer_mac); + } + if (MTRUE == wlan_is_station_list_empty(pmpriv)) + pmadapter->tdls_status = TDLS_NOT_SETUP; + else + pmadapter->tdls_status = TDLS_IN_BASE_CHANNEL; + + if (reason == TDLS_LINK_NONEXISTENT) { + if (pioctl_buf) + pioctl_buf->status_code = MLAN_STATUS_SUCCESS; + } else { + PRINTM(MERROR, + "TDLS DELETE operation: command error, reason %d\n", + reason); + } + break; + } + LEAVE(); + return; +} + +/** + * @brief This function handles the command response error + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return N/A + */ +static mlan_status +wlan_process_cmdresp_error(mlan_private *pmpriv, HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + pmlan_ioctl_req pscan_ioctl_req = MNULL; + mlan_callbacks *pcb = MNULL; + tdls_all_config *tdls_all_cfg = MNULL; + HostCmd_DS_TDLS_CONFIG *ptdls_config_data = + &(resp->params.tdls_config_data); + + mlan_status ret = MLAN_STATUS_FAILURE; + + ENTER(); + if (resp->command != HostCmd_CMD_WMM_PARAM_CONFIG + || resp->command != HostCmd_CMD_CHAN_REGION_CFG) + PRINTM(MERROR, "CMD_RESP: cmd %#x error, result=%#x\n", + resp->command, resp->result); + if (pioctl_buf) + pioctl_buf->status_code = MLAN_ERROR_FW_CMDRESP; + + switch (resp->command) { + case HostCmd_CMD_802_11_PS_MODE_ENH: + { + HostCmd_DS_802_11_PS_MODE_ENH *pm = + &resp->params.psmode_enh; + PRINTM(MERROR, + "PS_MODE_ENH command failed: result=0x%x action=0x%X\n", + resp->result, wlan_le16_to_cpu(pm->action)); + /* + * We do not re-try enter-ps command in ad-hoc mode. + */ + if (wlan_le16_to_cpu(pm->action) == EN_AUTO_PS && + (wlan_le16_to_cpu(pm->params.auto_ps.ps_bitmap) & + BITMAP_STA_PS) && + pmpriv->bss_mode == MLAN_BSS_MODE_IBSS) + pmadapter->ps_mode = Wlan802_11PowerModeCAM; + } + break; + case HostCmd_CMD_802_11_SCAN_EXT: + case HostCmd_CMD_802_11_SCAN: + /* Cancel all pending scan command */ + wlan_flush_scan_queue(pmadapter); + + pcb = (pmlan_callbacks)&pmadapter->callbacks; + + wlan_request_cmd_lock(pmadapter); + pmadapter->scan_processing = MFALSE; + pscan_ioctl_req = pmadapter->pscan_ioctl_req; + pmadapter->pscan_ioctl_req = MNULL; + /* Need to indicate IOCTL complete */ + if (pscan_ioctl_req) { + pscan_ioctl_req->status_code = MLAN_ERROR_CMD_SCAN_FAIL; + /* Indicate ioctl complete */ + pcb->moal_ioctl_complete(pmadapter->pmoal_handle, + (pmlan_ioctl_req) + pscan_ioctl_req, + MLAN_STATUS_FAILURE); + } + wlan_release_cmd_lock(pmadapter); + wlan_recv_event(pmpriv, MLAN_EVENT_ID_DRV_SCAN_REPORT, MNULL); + break; + + case HostCmd_CMD_MAC_CONTROL: + break; + + case HostCmd_CMD_TDLS_CONFIG: + ptdls_config_data->tdls_info.tdls_action = + wlan_le16_to_cpu(ptdls_config_data->tdls_info. + tdls_action); + switch (ptdls_config_data->tdls_info.tdls_action) { + case WLAN_TDLS_SETUP_REQ: + /* TDLS link setup error ;display error in logs */ + tdls_all_cfg = + (tdls_all_config *)ptdls_config_data->tdls_info. + tdls_data; + PRINTM(MERROR, "TDLS Setup Failed, error %d\n", + wlan_le16_to_cpu(tdls_all_cfg->u.tdls_cmd_resp. + reason_code)); + break; + case WLAN_TDLS_INIT_CHAN_SWITCH: + tdls_all_cfg = + (tdls_all_config *)ptdls_config_data->tdls_info. + tdls_data; + PRINTM(MERROR, + "TDLS init channel switch failed," MACSTR + ": reason=%d\n", + MAC2STR(tdls_all_cfg->u.tdls_cmd_resp. + peer_mac_addr), + wlan_le16_to_cpu(tdls_all_cfg->u.tdls_cmd_resp. + reason_code)); + break; + } + break; + case HostCmd_CMD_TDLS_OPERATION: + wlan_process_cmdreps_error_tdls_operation(pmpriv, resp, + pioctl_buf); + break; + case HostCmd_CMD_802_11_ASSOCIATE: + wlan_reset_connect_state(pmpriv, MTRUE); + break; + case HostCmd_CMD_SDIO_SP_RX_AGGR_CFG: + pmadapter->sdio_rx_aggr_enable = MFALSE; + PRINTM(MMSG, "FW don't support SDIO single port rx aggr\n"); + break; + + case HostCmd_CMD_MGMT_IE_LIST: + { + HostCmd_DS_MGMT_IE_LIST_CFG *pmgmt_ie_list = + &(resp->params.mgmt_ie_list); + t_u16 resp_len = 0, travel_len = 0, index; + mlan_ds_misc_custom_ie *cust_ie = MNULL; + custom_ie *cptr; + + if (wlan_le16_to_cpu(pmgmt_ie_list->action) == + HostCmd_ACT_GEN_GET) + break; + + cust_ie = + (mlan_ds_misc_custom_ie *)&pmgmt_ie_list-> + ds_mgmt_ie; + if (cust_ie) { + cust_ie->type = wlan_le16_to_cpu(cust_ie->type); + resp_len = cust_ie->len = + wlan_le16_to_cpu(cust_ie->len); + travel_len = 0; + /* conversion for index, mask, len */ + if (resp_len == sizeof(t_u16)) + cust_ie->ie_data_list[0].ie_index = + wlan_cpu_to_le16(cust_ie-> + ie_data_list + [0].ie_index); + + while (resp_len > sizeof(t_u16)) { + cptr = (custom_ie + *)(((t_u8 *)cust_ie-> + ie_data_list) + travel_len); + index = cptr->ie_index = + wlan_le16_to_cpu(cptr-> + ie_index); + cptr->mgmt_subtype_mask = + wlan_le16_to_cpu(cptr-> + mgmt_subtype_mask); + cptr->ie_length = + wlan_le16_to_cpu(cptr-> + ie_length); + travel_len += + cptr->ie_length + + sizeof(custom_ie) - MAX_IE_SIZE; + resp_len -= + cptr->ie_length + + sizeof(custom_ie) - MAX_IE_SIZE; + if ((pmpriv->mgmt_ie[index]. + mgmt_subtype_mask == + cptr->mgmt_subtype_mask) && + (pmpriv->mgmt_ie[index].ie_length == + cptr->ie_length) && + !memcmp(pmpriv->adapter, + pmpriv->mgmt_ie[index]. + ie_buffer, cptr->ie_buffer, + cptr->ie_length)) { + PRINTM(MERROR, + "set custom ie fail, remove ie index :%d\n", + index); + memset(pmadapter, + &pmpriv->mgmt_ie[index], + 0, sizeof(custom_ie)); + } + } + } + } + break; + case HostCmd_CMD_ROAM_OFFLOAD: + wlan_clear_fw_roaming_pmk(pmpriv); + pmpriv->adapter->fw_roaming = MFALSE; + PRINTM(MERROR, "FW do not support roaming!\n"); + break; + case HostCmd_CMD_CHAN_REGION_CFG: + ret = MLAN_STATUS_SUCCESS; + PRINTM(MCMND, "FW don't support chan region cfg command!\n"); + break; + default: + break; + } + /* + * Handling errors here + */ + wlan_request_cmd_lock(pmadapter); + wlan_insert_cmd_to_free_q(pmadapter, pmadapter->curr_cmd); + pmadapter->curr_cmd = MNULL; + wlan_release_cmd_lock(pmadapter); + + LEAVE(); + return ret; +} + +/** + * @brief This function handles the command response of rssi_info_ext + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_ret_802_11_rssi_info_ext(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf) +{ + HostCmd_DS_802_11_RSSI_INFO_EXT *prssi_info_rsp = + &resp->params.rssi_info_ext; + mlan_ds_get_signal *signal = MNULL; + mlan_ds_get_info *info = MNULL; + MrvlIEtypes_RSSI_EXT_t *signal_info_tlv = MNULL; + t_u16 tlv_left_len = 0, tlv_num = 0; + t_u16 tlv_id, tlv_len; + + ENTER(); + + /* Need to indicate IOCTL complete */ + if (pioctl_buf != MNULL) { + info = (mlan_ds_get_info *)pioctl_buf->pbuf; + signal_info_tlv = + (MrvlIEtypes_RSSI_EXT_t *) ((t_u8 *)prssi_info_rsp + + sizeof + (HostCmd_DS_802_11_RSSI_INFO_EXT)); + tlv_left_len = + resp->size - (sizeof(HostCmd_DS_802_11_RSSI_INFO_EXT) + + S_DS_GEN); + + while (tlv_left_len >= sizeof(MrvlIEtypes_RSSI_EXT_t)) { + tlv_id = wlan_le16_to_cpu(signal_info_tlv->header.type); + tlv_len = wlan_le16_to_cpu(signal_info_tlv->header.len); + if ((tlv_id != TLV_TYPE_RSSI_INFO) || + (tlv_len != + sizeof(MrvlIEtypes_RSSI_EXT_t) - + sizeof(MrvlIEtypesHeader_t))) { + PRINTM(MERROR, + "Invalid RSSI INFO TLV, type=%d, len=%d\n", + tlv_id, tlv_len); + break; + } + + signal = (mlan_ds_get_signal *)&info->param. + signal_ext[tlv_num]; + /* PATH ID */ + signal->selector = + wlan_le16_to_cpu(signal_info_tlv->path_id); + + /* RSSI */ + signal->bcn_rssi_last = + wlan_le16_to_cpu(signal_info_tlv-> + bcn_rssi_last); + signal->bcn_rssi_avg = + wlan_le16_to_cpu(signal_info_tlv->bcn_rssi_avg); + signal->data_rssi_last = + wlan_le16_to_cpu(signal_info_tlv-> + data_rssi_last); + signal->data_rssi_avg = + wlan_le16_to_cpu(signal_info_tlv-> + data_rssi_avg); + + /* SNR */ + signal->bcn_snr_last = + CAL_SNR(wlan_le16_to_cpu + (signal_info_tlv->bcn_rssi_last), + wlan_le16_to_cpu(signal_info_tlv-> + bcn_nf_last)); + signal->bcn_snr_avg = + CAL_SNR(wlan_le16_to_cpu + (signal_info_tlv->bcn_rssi_avg), + wlan_le16_to_cpu(signal_info_tlv-> + bcn_nf_avg)); + signal->data_snr_last = + CAL_SNR(wlan_le16_to_cpu + (signal_info_tlv->data_rssi_last), + wlan_le16_to_cpu(signal_info_tlv-> + data_nf_last)); + signal->data_snr_avg = + CAL_SNR(wlan_le16_to_cpu + (signal_info_tlv->data_rssi_avg), + wlan_le16_to_cpu(signal_info_tlv-> + data_nf_avg)); + + /* NF */ + signal->bcn_nf_last = + wlan_le16_to_cpu(signal_info_tlv->bcn_nf_last); + signal->bcn_nf_avg = + wlan_le16_to_cpu(signal_info_tlv->bcn_nf_avg); + signal->data_nf_last = + wlan_le16_to_cpu(signal_info_tlv->data_nf_last); + signal->data_nf_avg = + wlan_le16_to_cpu(signal_info_tlv->data_nf_avg); + + tlv_left_len -= sizeof(MrvlIEtypes_RSSI_EXT_t); + signal_info_tlv++; + tlv_num++; + if (tlv_num > MAX_PATH_NUM) + break; + } + + pioctl_buf->data_read_written = + tlv_num * sizeof(mlan_ds_get_signal) + sizeof(t_u32); + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of RSSI info + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_ret_802_11_rssi_info(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf) +{ + HostCmd_DS_802_11_RSSI_INFO_RSP *prssi_info_rsp = + &resp->params.rssi_info_rsp; + mlan_ds_get_info *pget_info = MNULL; + BSSDescriptor_t *pbss_desc; + t_s32 tbl_idx = 0; + + ENTER(); + + pmpriv->data_rssi_last = + wlan_le16_to_cpu(prssi_info_rsp->data_rssi_last); + pmpriv->data_nf_last = wlan_le16_to_cpu(prssi_info_rsp->data_nf_last); + + pmpriv->data_rssi_avg = wlan_le16_to_cpu(prssi_info_rsp->data_rssi_avg); + pmpriv->data_nf_avg = wlan_le16_to_cpu(prssi_info_rsp->data_nf_avg); + + pmpriv->bcn_rssi_last = wlan_le16_to_cpu(prssi_info_rsp->bcn_rssi_last); + pmpriv->bcn_nf_last = wlan_le16_to_cpu(prssi_info_rsp->bcn_nf_last); + + pmpriv->bcn_rssi_avg = wlan_le16_to_cpu(prssi_info_rsp->bcn_rssi_avg); + pmpriv->bcn_nf_avg = wlan_le16_to_cpu(prssi_info_rsp->bcn_nf_avg); + + /* Get current BSS info */ + pbss_desc = &pmpriv->curr_bss_params.bss_descriptor; + pbss_desc->rssi = -pmpriv->bcn_rssi_avg; + tbl_idx = + wlan_find_ssid_in_list(pmpriv, &pbss_desc->ssid, + pbss_desc->mac_address, + pmpriv->bss_mode); + if (tbl_idx >= 0) { + pbss_desc = &pmpriv->adapter->pscan_table[tbl_idx]; + pbss_desc->rssi = -pmpriv->bcn_rssi_avg; + } + + /* Need to indicate IOCTL complete */ + if (pioctl_buf != MNULL) { + pget_info = (mlan_ds_get_info *)pioctl_buf->pbuf; + + memset(pmpriv->adapter, &pget_info->param.signal, 0, + sizeof(mlan_ds_get_signal)); + + pget_info->param.signal.selector = ALL_RSSI_INFO_MASK; + + /* RSSI */ + pget_info->param.signal.bcn_rssi_last = pmpriv->bcn_rssi_last; + pget_info->param.signal.bcn_rssi_avg = pmpriv->bcn_rssi_avg; + pget_info->param.signal.data_rssi_last = pmpriv->data_rssi_last; + pget_info->param.signal.data_rssi_avg = pmpriv->data_rssi_avg; + + /* SNR */ + pget_info->param.signal.bcn_snr_last = + CAL_SNR(pmpriv->bcn_rssi_last, pmpriv->bcn_nf_last); + pget_info->param.signal.bcn_snr_avg = + CAL_SNR(pmpriv->bcn_rssi_avg, pmpriv->bcn_nf_avg); + pget_info->param.signal.data_snr_last = + CAL_SNR(pmpriv->data_rssi_last, pmpriv->data_nf_last); + pget_info->param.signal.data_snr_avg = + CAL_SNR(pmpriv->data_rssi_avg, pmpriv->data_nf_avg); + + /* NF */ + pget_info->param.signal.bcn_nf_last = pmpriv->bcn_nf_last; + pget_info->param.signal.bcn_nf_avg = pmpriv->bcn_nf_avg; + pget_info->param.signal.data_nf_last = pmpriv->data_nf_last; + pget_info->param.signal.data_nf_avg = pmpriv->data_nf_avg; + + pioctl_buf->data_read_written = sizeof(mlan_ds_get_info); + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of snmp_mib + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_ret_802_11_snmp_mib(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf) +{ + HostCmd_DS_802_11_SNMP_MIB *psmib = &resp->params.smib; + t_u16 oid = wlan_le16_to_cpu(psmib->oid); + t_u16 query_type = wlan_le16_to_cpu(psmib->query_type); + t_u32 ul_temp; + mlan_ds_snmp_mib *mib = MNULL; + + ENTER(); + + if (pioctl_buf) + mib = (mlan_ds_snmp_mib *)pioctl_buf->pbuf; + + PRINTM(MINFO, "SNMP_RESP: value of the oid = 0x%x, query_type=0x%x\n", + oid, query_type); + PRINTM(MINFO, "SNMP_RESP: Buf size = 0x%x\n", + wlan_le16_to_cpu(psmib->buf_size)); + if (query_type == HostCmd_ACT_GEN_GET) { + switch (oid) { + case DtimPeriod_i: + ul_temp = psmib->value[0]; + PRINTM(MINFO, "SNMP_RESP: DTIM Period =%u\n", ul_temp); + if (mib) + mib->param.dtim_period = ul_temp; + break; + case FragThresh_i: + ul_temp = wlan_le16_to_cpu(*((t_u16 *)(psmib->value))); + PRINTM(MINFO, "SNMP_RESP: FragThsd =%u\n", ul_temp); + if (mib) + mib->param.frag_threshold = ul_temp; + break; + + case RtsThresh_i: + ul_temp = wlan_le16_to_cpu(*((t_u16 *)(psmib->value))); + PRINTM(MINFO, "SNMP_RESP: RTSThsd =%u\n", ul_temp); + if (mib) + mib->param.rts_threshold = ul_temp; + break; + + case ShortRetryLim_i: + ul_temp = wlan_le16_to_cpu(*((t_u16 *)(psmib->value))); + PRINTM(MINFO, "SNMP_RESP: TxRetryCount=%u\n", ul_temp); + if (mib) + mib->param.retry_count = ul_temp; + break; + case WwsMode_i: + ul_temp = wlan_le16_to_cpu(*((t_u16 *)(psmib->value))); + PRINTM(MINFO, "SNMP_RESP: WWSCfg =%u\n", ul_temp); + if (pioctl_buf) + ((mlan_ds_misc_cfg *)pioctl_buf->pbuf)->param. + wws_cfg = ul_temp; + break; + case Thermal_i: + ul_temp = wlan_le32_to_cpu(*((t_u32 *)(psmib->value))); + PRINTM(MINFO, "SNMP_RESP: Thermal =%u\n", ul_temp); + if (pioctl_buf) + ((mlan_ds_misc_cfg *)pioctl_buf->pbuf)->param. + thermal = ul_temp; + break; + case NullPktPeriod_i: + ul_temp = psmib->value[0]; + PRINTM(MINFO, "SNMP_RESP: Auto NULL Pkt Period =%u\n", + ul_temp); + break; + default: + break; + } + } else { /* (query_type == HostCmd_ACT_GEN_SET) */ + /* Update state for 11d */ + if (oid == Dot11D_i) { + ul_temp = wlan_le16_to_cpu(*((t_u16 *)(psmib->value))); + /* Set 11d state to private */ + pmpriv->state_11d.enable_11d = ul_temp; + /* Set user enable flag if called from ioctl */ + if (pioctl_buf) + pmpriv->state_11d.user_enable_11d = ul_temp; + } + /* Update state for 11h */ + if (oid == Dot11H_i) { + ul_temp = wlan_le16_to_cpu(*((t_u16 *)(psmib->value))); + /* Set 11h state to priv */ + pmpriv->intf_state_11h.is_11h_active = + (ul_temp & ENABLE_11H_MASK); + /* Set radar_det state to adapter */ + pmpriv->adapter->state_11h.is_master_radar_det_active + = + (ul_temp & MASTER_RADAR_DET_MASK) ? MTRUE : + MFALSE; + pmpriv->adapter->state_11h.is_slave_radar_det_active = + (ul_temp & SLAVE_RADAR_DET_MASK) ? MTRUE : + MFALSE; + } + } + + if (pioctl_buf) { + /* Indicate ioctl complete */ + pioctl_buf->data_read_written = sizeof(mlan_ds_snmp_mib); + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of get_log + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_ret_get_log(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, IN mlan_ioctl_req *pioctl_buf) +{ + HostCmd_DS_802_11_GET_LOG *pget_log = + (HostCmd_DS_802_11_GET_LOG *)&resp->params.get_log; + mlan_ds_get_info *pget_info = MNULL; + int i = 0; + + ENTER(); + + if (pioctl_buf) { + pget_info = (mlan_ds_get_info *)pioctl_buf->pbuf; + pget_info->param.stats.mcast_tx_frame = + wlan_le32_to_cpu(pget_log->mcast_tx_frame); + pget_info->param.stats.failed = + wlan_le32_to_cpu(pget_log->failed); + pget_info->param.stats.retry = + wlan_le32_to_cpu(pget_log->retry); + pget_info->param.stats.multi_retry = + wlan_le32_to_cpu(pget_log->multiretry); + pget_info->param.stats.frame_dup = + wlan_le32_to_cpu(pget_log->frame_dup); + pget_info->param.stats.rts_success = + wlan_le32_to_cpu(pget_log->rts_success); + pget_info->param.stats.rts_failure = + wlan_le32_to_cpu(pget_log->rts_failure); + pget_info->param.stats.ack_failure = + wlan_le32_to_cpu(pget_log->ack_failure); + pget_info->param.stats.rx_frag = + wlan_le32_to_cpu(pget_log->rx_frag); + pget_info->param.stats.mcast_rx_frame = + wlan_le32_to_cpu(pget_log->mcast_rx_frame); + pget_info->param.stats.fcs_error = + wlan_le32_to_cpu(pget_log->fcs_error); + pget_info->param.stats.tx_frame = + wlan_le32_to_cpu(pget_log->tx_frame); + pget_info->param.stats.wep_icv_error[0] = + wlan_le32_to_cpu(pget_log->wep_icv_err_cnt[0]); + pget_info->param.stats.wep_icv_error[1] = + wlan_le32_to_cpu(pget_log->wep_icv_err_cnt[1]); + pget_info->param.stats.wep_icv_error[2] = + wlan_le32_to_cpu(pget_log->wep_icv_err_cnt[2]); + pget_info->param.stats.wep_icv_error[3] = + wlan_le32_to_cpu(pget_log->wep_icv_err_cnt[3]); + pget_info->param.stats.bcn_rcv_cnt = + wlan_le32_to_cpu(pget_log->bcn_rcv_cnt); + pget_info->param.stats.bcn_miss_cnt = + wlan_le32_to_cpu(pget_log->bcn_miss_cnt); + pget_info->param.stats.amsdu_rx_cnt = pmpriv->amsdu_rx_cnt; + pget_info->param.stats.msdu_in_rx_amsdu_cnt = + pmpriv->msdu_in_rx_amsdu_cnt; + pget_info->param.stats.amsdu_tx_cnt = pmpriv->amsdu_tx_cnt; + pget_info->param.stats.msdu_in_tx_amsdu_cnt = + pmpriv->msdu_in_tx_amsdu_cnt; + if (pmpriv->adapter->getlog_enable) { + pget_info->param.stats.tx_frag_cnt = + wlan_le32_to_cpu(pget_log->tx_frag_cnt); + for (i = 0; i < 8; i++) { + pget_info->param.stats.qos_tx_frag_cnt[i] = + wlan_le32_to_cpu(pget_log-> + qos_tx_frag_cnt[i]); + pget_info->param.stats.qos_failed_cnt[i] = + wlan_le32_to_cpu(pget_log-> + qos_failed_cnt[i]); + pget_info->param.stats.qos_retry_cnt[i] = + wlan_le32_to_cpu(pget_log-> + qos_retry_cnt[i]); + pget_info->param.stats.qos_multi_retry_cnt[i] = + wlan_le32_to_cpu(pget_log-> + qos_multi_retry_cnt + [i]); + pget_info->param.stats.qos_frm_dup_cnt[i] = + wlan_le32_to_cpu(pget_log-> + qos_frm_dup_cnt[i]); + pget_info->param.stats.qos_rts_suc_cnt[i] = + wlan_le32_to_cpu(pget_log-> + qos_rts_suc_cnt[i]); + pget_info->param.stats.qos_rts_failure_cnt[i] = + wlan_le32_to_cpu(pget_log-> + qos_rts_failure_cnt + [i]); + pget_info->param.stats.qos_ack_failure_cnt[i] = + wlan_le32_to_cpu(pget_log-> + qos_ack_failure_cnt + [i]); + pget_info->param.stats.qos_rx_frag_cnt[i] = + wlan_le32_to_cpu(pget_log-> + qos_rx_frag_cnt[i]); + pget_info->param.stats.qos_tx_frm_cnt[i] = + wlan_le32_to_cpu(pget_log-> + qos_tx_frm_cnt[i]); + pget_info->param.stats. + qos_discarded_frm_cnt[i] = + wlan_le32_to_cpu(pget_log-> + qos_discarded_frm_cnt + [i]); + pget_info->param.stats.qos_mpdus_rx_cnt[i] = + wlan_le32_to_cpu(pget_log-> + qos_mpdus_rx_cnt[i]); + pget_info->param.stats.qos_retries_rx_cnt[i] = + wlan_le32_to_cpu(pget_log-> + qos_retries_rx_cnt[i]); + } + pget_info->param.stats.cmacicv_errors = + wlan_le32_to_cpu(pget_log->cmacicv_errors); + pget_info->param.stats.cmac_replays = + wlan_le32_to_cpu(pget_log->cmac_replays); + pget_info->param.stats.mgmt_ccmp_replays = + wlan_le32_to_cpu(pget_log->mgmt_ccmp_replays); + pget_info->param.stats.tkipicv_errors = + wlan_le32_to_cpu(pget_log->tkipicv_errors); + pget_info->param.stats.tkip_replays = + wlan_le32_to_cpu(pget_log->tkip_replays); + pget_info->param.stats.ccmp_decrypt_errors = + wlan_le32_to_cpu(pget_log->ccmp_decrypt_errors); + pget_info->param.stats.ccmp_replays = + wlan_le32_to_cpu(pget_log->ccmp_replays); + pget_info->param.stats.tx_amsdu_cnt = + wlan_le32_to_cpu(pget_log->tx_amsdu_cnt); + pget_info->param.stats.failed_amsdu_cnt = + wlan_le32_to_cpu(pget_log->failed_amsdu_cnt); + pget_info->param.stats.retry_amsdu_cnt = + wlan_le32_to_cpu(pget_log->retry_amsdu_cnt); + pget_info->param.stats.multi_retry_amsdu_cnt = + wlan_le32_to_cpu(pget_log-> + multi_retry_amsdu_cnt); + pget_info->param.stats.tx_octets_in_amsdu_cnt = + wlan_le64_to_cpu(pget_log-> + tx_octets_in_amsdu_cnt); + pget_info->param.stats.amsdu_ack_failure_cnt = + wlan_le32_to_cpu(pget_log-> + amsdu_ack_failure_cnt); + pget_info->param.stats.rx_amsdu_cnt = + wlan_le32_to_cpu(pget_log->rx_amsdu_cnt); + pget_info->param.stats.rx_octets_in_amsdu_cnt = + wlan_le64_to_cpu(pget_log-> + rx_octets_in_amsdu_cnt); + pget_info->param.stats.tx_ampdu_cnt = + wlan_le32_to_cpu(pget_log->tx_ampdu_cnt); + pget_info->param.stats.tx_mpdus_in_ampdu_cnt = + wlan_le32_to_cpu(pget_log-> + tx_mpdus_in_ampdu_cnt); + pget_info->param.stats.tx_octets_in_ampdu_cnt = + wlan_le64_to_cpu(pget_log-> + tx_octets_in_ampdu_cnt); + pget_info->param.stats.ampdu_rx_cnt = + wlan_le32_to_cpu(pget_log->ampdu_rx_cnt); + pget_info->param.stats.mpdu_in_rx_ampdu_cnt = + wlan_le32_to_cpu(pget_log-> + mpdu_in_rx_ampdu_cnt); + pget_info->param.stats.rx_octets_in_ampdu_cnt = + wlan_le64_to_cpu(pget_log-> + rx_octets_in_ampdu_cnt); + pget_info->param.stats.ampdu_delimiter_crc_error_cnt = + wlan_le32_to_cpu(pget_log-> + ampdu_delimiter_crc_error_cnt); + + /* Indicate ioctl complete */ + pioctl_buf->data_read_written = + sizeof(mlan_ds_get_info); + } else + pioctl_buf->data_read_written = + sizeof(mlan_ds_get_stats_org) + + sizeof(pget_info->sub_command); + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Get power level and rate index + * + * @param pmpriv A pointer to mlan_private structure + * @param pdata_buf Pointer to the data buffer + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status +wlan_get_power_level(pmlan_private pmpriv, void *pdata_buf) +{ + t_u16 length = 0; + t_s8 max_power = -1, min_power = -1; + MrvlTypes_Power_Group_t *ppg_tlv = MNULL; + Power_Group_t *pg = MNULL; + + ENTER(); + + if (pdata_buf) { + ppg_tlv = + (MrvlTypes_Power_Group_t *)((t_u8 *)pdata_buf + + sizeof + (HostCmd_DS_TXPWR_CFG)); + pg = (Power_Group_t *)((t_u8 *)ppg_tlv + + sizeof(MrvlTypes_Power_Group_t)); + length = ppg_tlv->length; + if (length > 0) { + max_power = pg->power_max; + min_power = pg->power_min; + length -= sizeof(Power_Group_t); + } + while (length) { + pg++; + if (max_power < pg->power_max) + max_power = pg->power_max; + if (min_power > pg->power_min) + min_power = pg->power_min; + length -= sizeof(Power_Group_t); + } + if (ppg_tlv->length > 0) { + pmpriv->min_tx_power_level = min_power; + pmpriv->max_tx_power_level = max_power; + } + } else { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of tx_power_cfg + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_ret_tx_power_cfg(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf) +{ + HostCmd_DS_TXPWR_CFG *ptxp_cfg = &resp->params.txp_cfg; + MrvlTypes_Power_Group_t *ppg_tlv = MNULL; + Power_Group_t *pg = MNULL; + t_u16 action = wlan_le16_to_cpu(ptxp_cfg->action); + mlan_ds_power_cfg *power = MNULL; + mlan_power_group *pwr_grp = MNULL; + t_u8 i = 0; + + ENTER(); + + ppg_tlv = (MrvlTypes_Power_Group_t *)((t_u8 *)ptxp_cfg + + sizeof(HostCmd_DS_TXPWR_CFG)); + pg = (Power_Group_t *)((t_u8 *)ppg_tlv + + sizeof(MrvlTypes_Power_Group_t)); + + switch (action) { + case HostCmd_ACT_GEN_GET: + ppg_tlv->length = wlan_le16_to_cpu(ppg_tlv->length); + if (pmpriv->adapter->hw_status == + WlanHardwareStatusInitializing) + wlan_get_power_level(pmpriv, ptxp_cfg); + pmpriv->tx_power_level = (t_s16)pg->power_min; + break; + + case HostCmd_ACT_GEN_SET: + if (wlan_le32_to_cpu(ptxp_cfg->mode)) { + if (pg->power_max == pg->power_min) + pmpriv->tx_power_level = (t_s16)pg->power_min; + } + break; + + default: + PRINTM(MERROR, "CMD_RESP: unknown command action %d\n", action); + LEAVE(); + return MLAN_STATUS_SUCCESS; + } + + PRINTM(MINFO, "Current TxPower Level = %d,Max Power=%d, Min Power=%d\n", + pmpriv->tx_power_level, + pmpriv->max_tx_power_level, pmpriv->min_tx_power_level); + + if (pioctl_buf) { + power = (mlan_ds_power_cfg *)pioctl_buf->pbuf; + if (action == HostCmd_ACT_GEN_GET) { + if (power->sub_command == MLAN_OID_POWER_CFG) { + pioctl_buf->data_read_written + = + sizeof(mlan_power_cfg_t) + + MLAN_SUB_COMMAND_SIZE; + power->param.power_cfg.power_level = + pmpriv->tx_power_level; + if (wlan_le32_to_cpu(ptxp_cfg->mode)) + power->param.power_cfg.is_power_auto = + 0; + else + power->param.power_cfg.is_power_auto = + 1; + } else { + power->param.power_ext.num_pwr_grp = 0; + i = 0; + while ((ppg_tlv->length) && + (i < MAX_POWER_GROUP)) { + pwr_grp = + (mlan_power_group *)&power-> + param.power_ext.power_group[i]; + pwr_grp->first_rate_ind = 0; + pwr_grp->last_rate_ind = 0; + if (pg->modulation_class == + MOD_CLASS_HR_DSSS) { + pwr_grp->rate_format = + MLAN_RATE_FORMAT_LG; + pwr_grp->first_rate_ind = + pg->first_rate_code; + pwr_grp->last_rate_ind = + pg->last_rate_code; + } else if (pg->modulation_class == + MOD_CLASS_OFDM) { + pwr_grp->rate_format = + MLAN_RATE_FORMAT_LG; + pwr_grp->first_rate_ind = + MLAN_RATE_INDEX_OFDM0 + + pg->first_rate_code; + pwr_grp->last_rate_ind = + MLAN_RATE_INDEX_OFDM0 + + pg->last_rate_code; + } else if (pg->modulation_class == + MOD_CLASS_HT) { + pwr_grp->rate_format = + MLAN_RATE_FORMAT_HT; + pwr_grp->first_rate_ind = + pg->first_rate_code; + pwr_grp->last_rate_ind = + pg->last_rate_code; + } + pwr_grp->bandwidth = pg->ht_bandwidth; + pwr_grp->power_min = pg->power_min; + pwr_grp->power_max = pg->power_max; + pwr_grp->power_step = pg->power_step; + ppg_tlv->length -= + sizeof(Power_Group_t); + pg++; + i++; + } + power->param.power_ext.num_pwr_grp = i; + pioctl_buf->data_read_written + = + sizeof(mlan_power_cfg_ext) + + MLAN_SUB_COMMAND_SIZE; + } + } + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of rf_tx_power + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_ret_802_11_rf_tx_power(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf) +{ + HostCmd_DS_802_11_RF_TX_POWER *rtp = &resp->params.txp; + t_u16 action = wlan_le16_to_cpu(rtp->action); + mlan_ds_power_cfg *power = MNULL; + + ENTER(); + + pmpriv->tx_power_level = wlan_le16_to_cpu(rtp->current_level); + + if (action == HostCmd_ACT_GEN_GET) { + pmpriv->max_tx_power_level = rtp->max_power; + pmpriv->min_tx_power_level = rtp->min_power; + if (pioctl_buf) { + power = (mlan_ds_power_cfg *)pioctl_buf->pbuf; + if (power->sub_command == MLAN_OID_POWER_CFG) { + pioctl_buf->data_read_written + = + sizeof(mlan_power_cfg_t) + + MLAN_SUB_COMMAND_SIZE; + power->param.power_cfg.power_level = + pmpriv->tx_power_level; + } + } + } + + PRINTM(MINFO, "Current TxPower Level = %d,Max Power=%d, Min Power=%d\n", + pmpriv->tx_power_level, + pmpriv->max_tx_power_level, pmpriv->min_tx_power_level); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of sleep_period + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_ret_802_11_sleep_period(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf) +{ + HostCmd_DS_802_11_SLEEP_PERIOD *pcmd_sleep_pd = &resp->params.sleep_pd; + mlan_ds_pm_cfg *pm_cfg = MNULL; + t_u16 sleep_pd = 0; + + ENTER(); + + sleep_pd = wlan_le16_to_cpu(pcmd_sleep_pd->sleep_pd); + if (pioctl_buf) { + pm_cfg = (mlan_ds_pm_cfg *)pioctl_buf->pbuf; + pm_cfg->param.sleep_period = (t_u32)sleep_pd; + pioctl_buf->data_read_written = + sizeof(pm_cfg->param.sleep_period) + + MLAN_SUB_COMMAND_SIZE; + } + pmpriv->adapter->sleep_period.period = sleep_pd; + pmpriv->adapter->saved_sleep_period.period = sleep_pd; + + pmpriv->adapter->pps_uapsd_mode = MFALSE; + if ((pmpriv->adapter->sleep_period.period != 0) && + (pmpriv->adapter->sleep_period.period != + SLEEP_PERIOD_RESERVED_FF)) { + pmpriv->adapter->gen_null_pkt = MTRUE; + } else { + pmpriv->adapter->delay_null_pkt = MFALSE; + pmpriv->adapter->gen_null_pkt = MFALSE; + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of sleep_params + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_ret_802_11_sleep_params(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf) +{ + HostCmd_DS_802_11_SLEEP_PARAMS *presp_sp = &resp->params.sleep_param; + mlan_ds_pm_cfg *pm_cfg = MNULL; + mlan_ds_sleep_params *psp = MNULL; + sleep_params_t *psleep_params = &pmpriv->adapter->sleep_params; + + ENTER(); + + psleep_params->sp_reserved = wlan_le16_to_cpu(presp_sp->reserved); + psleep_params->sp_error = wlan_le16_to_cpu(presp_sp->error); + psleep_params->sp_offset = wlan_le16_to_cpu(presp_sp->offset); + psleep_params->sp_stable_time = wlan_le16_to_cpu(presp_sp->stable_time); + psleep_params->sp_cal_control = presp_sp->cal_control; + psleep_params->sp_ext_sleep_clk = presp_sp->external_sleep_clk; + + if (pioctl_buf) { + pm_cfg = (mlan_ds_pm_cfg *)pioctl_buf->pbuf; + psp = (mlan_ds_sleep_params *)&pm_cfg->param.sleep_params; + + psp->error = (t_u32)psleep_params->sp_error; + psp->offset = (t_u32)psleep_params->sp_offset; + psp->stable_time = (t_u32)psleep_params->sp_stable_time; + psp->cal_control = (t_u32)psleep_params->sp_cal_control; + psp->ext_sleep_clk = (t_u32)psleep_params->sp_ext_sleep_clk; + psp->reserved = (t_u32)psleep_params->sp_reserved; + + pioctl_buf->data_read_written = + sizeof(pm_cfg->param.sleep_params) + + MLAN_SUB_COMMAND_SIZE; + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of mac_address + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_ret_802_11_mac_address(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf) +{ + HostCmd_DS_802_11_MAC_ADDRESS *pmac_addr = &resp->params.mac_addr; + mlan_ds_bss *bss = MNULL; + + ENTER(); + + memcpy(pmpriv->adapter, pmpriv->curr_addr, pmac_addr->mac_addr, + MLAN_MAC_ADDR_LENGTH); + + PRINTM(MINFO, "MAC address: " MACSTR "\n", MAC2STR(pmpriv->curr_addr)); + if (pioctl_buf) { + bss = (mlan_ds_bss *)pioctl_buf->pbuf; + memcpy(pmpriv->adapter, &bss->param.mac_addr, pmpriv->curr_addr, + MLAN_MAC_ADDR_LENGTH); + pioctl_buf->data_read_written = + MLAN_MAC_ADDR_LENGTH + MLAN_SUB_COMMAND_SIZE; + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of multicast_address + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_ret_mac_multicast_adr(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf) +{ + ENTER(); + if (pioctl_buf) { + pioctl_buf->data_read_written = + sizeof(mlan_multicast_list) + MLAN_SUB_COMMAND_SIZE; + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of deauthenticate + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_ret_802_11_deauthenticate(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + ENTER(); + + pmadapter->dbg.num_cmd_deauth++; + + if (!memcmp(pmadapter, resp->params.deauth.mac_addr, + &pmpriv->curr_bss_params.bss_descriptor.mac_address, + sizeof(resp->params.deauth.mac_addr))) { + wlan_reset_connect_state(pmpriv, MTRUE); + + } + if (pmpriv->adapter->state_rdh.stage == RDH_STOP_INTFS) + wlan_11h_radar_detected_callback((t_void *)pmpriv); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of ad_hoc_stop + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_ret_802_11_ad_hoc_stop(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf) +{ + ENTER(); + + wlan_reset_connect_state(pmpriv, MTRUE); + if (pmpriv->adapter->state_rdh.stage == RDH_STOP_INTFS) + wlan_11h_radar_detected_callback((t_void *)pmpriv); + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of key_material + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_ret_802_11_key_material(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf) +{ + HostCmd_DS_802_11_KEY_MATERIAL *pkey = &resp->params.key_material; + mlan_ds_sec_cfg *sec = MNULL; + t_u8 zero_kek[MLAN_KEK_LEN] = { 0 }; + + ENTER(); + + if (wlan_le16_to_cpu(pkey->action) == HostCmd_ACT_GEN_SET) { + if ((wlan_le16_to_cpu(pkey->key_param_set.key_info) & + KEY_INFO_TKIP_MCAST)) { + PRINTM(MINFO, "key: GTK is set\n"); + pmpriv->wpa_is_gtk_set = MTRUE; + if (pmpriv->port_ctrl_mode == MTRUE) { + /* GTK is set, open the port */ + PRINTM(MINFO, + "GTK_SET: Open port for WPA/WPA2 h-supp mode\n"); + pmpriv->port_open = MTRUE; + } + if (0 != + memcmp(pmpriv->adapter, pmpriv->gtk_rekey.kek, + zero_kek, sizeof(zero_kek))) { + wlan_prepare_cmd(pmpriv, + HostCmd_CMD_GTK_REKEY_OFFLOAD_CFG, + HostCmd_ACT_GEN_SET, 0, MNULL, + &pmpriv->gtk_rekey); + memset(pmpriv->adapter, &pmpriv->gtk_rekey, 0, + sizeof(mlan_ds_misc_gtk_rekey_data)); + } + pmpriv->adapter->scan_block = MFALSE; + } + } else { + if (pioctl_buf && + (wlan_le16_to_cpu(pkey->key_param_set.type) == + TLV_TYPE_KEY_PARAM_V2)) { + sec = (mlan_ds_sec_cfg *)pioctl_buf->pbuf; + memcpy(pmpriv->adapter, sec->param.encrypt_key.mac_addr, + pkey->key_param_set.mac_addr, + MLAN_MAC_ADDR_LENGTH); + sec->param.encrypt_key.key_index = + pkey->key_param_set.key_idx; + PRINTM(MIOCTL, + "key_type=%d, key_index=%d, key_info=0x%x " + MACSTR "\n", pkey->key_param_set.key_type, + pkey->key_param_set.key_idx, + wlan_le16_to_cpu(pkey->key_param_set.key_info), + MAC2STR(sec->param.encrypt_key.mac_addr)); + switch (pkey->key_param_set.key_type) { + case KEY_TYPE_ID_WAPI: + sec->param.encrypt_key.is_wapi_key = MTRUE; + sec->param.encrypt_key.key_len = + wlan_le16_to_cpu(pkey->key_param_set. + key_params.wapi. + key_len); + memcpy(pmpriv->adapter, + sec->param.encrypt_key.key_material, + pkey->key_param_set.key_params.wapi.key, + sec->param.encrypt_key.key_len); + memcpy(pmpriv->adapter, + sec->param.encrypt_key.pn, + pkey->key_param_set.key_params.wapi.pn, + PN_SIZE); + break; + case KEY_TYPE_ID_TKIP: + sec->param.encrypt_key.key_len = + wlan_le16_to_cpu(pkey->key_param_set. + key_params.tkip. + key_len); + memcpy(pmpriv->adapter, + sec->param.encrypt_key.key_material, + pkey->key_param_set.key_params.tkip.key, + sec->param.encrypt_key.key_len); + memcpy(pmpriv->adapter, + sec->param.encrypt_key.pn, + pkey->key_param_set.key_params.tkip.pn, + WPA_PN_SIZE); + break; + case KEY_TYPE_ID_AES: + sec->param.encrypt_key.key_len = + wlan_le16_to_cpu(pkey->key_param_set. + key_params.aes. + key_len); + memcpy(pmpriv->adapter, + sec->param.encrypt_key.key_material, + pkey->key_param_set.key_params.aes.key, + sec->param.encrypt_key.key_len); + memcpy(pmpriv->adapter, + sec->param.encrypt_key.pn, + pkey->key_param_set.key_params.aes.pn, + WPA_PN_SIZE); + break; + case KEY_TYPE_ID_AES_CMAC: + sec->param.encrypt_key.key_len = + wlan_le16_to_cpu(pkey->key_param_set. + key_params.cmac_aes. + key_len); + memcpy(pmpriv->adapter, + sec->param.encrypt_key.key_material, + pkey->key_param_set.key_params.cmac_aes. + key, sec->param.encrypt_key.key_len); + memcpy(pmpriv->adapter, + sec->param.encrypt_key.pn, + pkey->key_param_set.key_params.cmac_aes. + ipn, IGTK_PN_SIZE); + break; + case KEY_TYPE_ID_WEP: + sec->param.encrypt_key.key_len = + wlan_le16_to_cpu(pkey->key_param_set. + key_params.wep. + key_len); + memcpy(pmpriv->adapter, + sec->param.encrypt_key.key_material, + pkey->key_param_set.key_params.wep.key, + sec->param.encrypt_key.key_len); + break; + } + } + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Handle the supplicant profile response + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_ret_802_11_supplicant_profile(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf) +{ + HostCmd_DS_802_11_SUPPLICANT_PROFILE *psup_profile = + &resp->params.esupplicant_profile; + MrvlIEtypesHeader_t *head; + MrvlIEtypes_EncrProto_t *encr_proto_tlv = MNULL; + MrvlIEtypes_Cipher_t *pcipher_tlv = MNULL; + mlan_ds_sec_cfg *sec = MNULL; + t_u8 *tlv; + int len; + + ENTER(); + + len = resp->size - S_DS_GEN - sizeof(t_u16); + tlv = psup_profile->tlv_buf; + if (pioctl_buf) { + sec = (mlan_ds_sec_cfg *)pioctl_buf->pbuf; + while (len > 0) { + head = (MrvlIEtypesHeader_t *)tlv; + head->type = wlan_le16_to_cpu(head->type); + head->len = wlan_le16_to_cpu(head->len); + switch (head->type) { + case TLV_TYPE_ENCRYPTION_PROTO: + encr_proto_tlv = + (MrvlIEtypes_EncrProto_t *)head; + sec->param.esupp_mode.rsn_mode = + wlan_le16_to_cpu(encr_proto_tlv-> + rsn_mode); + PRINTM(MINFO, "rsn_mode=0x%x\n", + sec->param.esupp_mode.rsn_mode); + break; + case TLV_TYPE_CIPHER: + pcipher_tlv = (MrvlIEtypes_Cipher_t *)head; + sec->param.esupp_mode.act_paircipher = + pcipher_tlv->pair_cipher; + sec->param.esupp_mode.act_groupcipher = + pcipher_tlv->group_cipher; + PRINTM(MINFO, + "paircipher=0x%x, groupcipher=0x%x\n", + sec->param.esupp_mode.act_paircipher, + sec->param.esupp_mode.act_groupcipher); + break; + } + len -= (head->len - sizeof(MrvlIEtypesHeader_t)); + tlv = tlv + (head->len + sizeof(MrvlIEtypesHeader_t)); + } + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of rf_channel + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_ret_802_11_rf_channel(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf) +{ + HostCmd_DS_802_11_RF_CHANNEL *prf_channel = &resp->params.rf_channel; + t_u16 new_channel = wlan_le16_to_cpu(prf_channel->current_channel); + mlan_ds_bss *bss = MNULL; + ENTER(); + if (pmpriv->curr_bss_params.bss_descriptor.channel != new_channel) { + PRINTM(MINFO, "Channel Switch: %d to %d\n", + pmpriv->curr_bss_params.bss_descriptor.channel, + new_channel); + /* Update the channel again */ + pmpriv->curr_bss_params.bss_descriptor.channel = new_channel; + } + if (pioctl_buf) { + bss = (mlan_ds_bss *)pioctl_buf->pbuf; + bss->param.bss_chan.channel = new_channel; + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Handle the ibss_coalescing_status resp + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_ret_ibss_coalescing_status(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp) +{ + HostCmd_DS_802_11_IBSS_STATUS *pibss_coal_resp = + &(resp->params.ibss_coalescing); + t_u8 zero_mac[MLAN_MAC_ADDR_LENGTH] = { 0, 0, 0, 0, 0, 0 }; + + ENTER(); + + if (wlan_le16_to_cpu(pibss_coal_resp->action) == HostCmd_ACT_GEN_SET) { + LEAVE(); + return MLAN_STATUS_SUCCESS; + } + + PRINTM(MINFO, "New BSSID " MACSTR "\n", + MAC2STR(pibss_coal_resp->bssid)); + + /* If rsp has MNULL BSSID, Just return..... No Action */ + if (!memcmp + (pmpriv->adapter, pibss_coal_resp->bssid, zero_mac, + MLAN_MAC_ADDR_LENGTH)) { + PRINTM(MMSG, "New BSSID is MNULL\n"); + LEAVE(); + return MLAN_STATUS_SUCCESS; + } + + /* If BSSID is diff, modify current BSS parameters */ + if (memcmp + (pmpriv->adapter, + pmpriv->curr_bss_params.bss_descriptor.mac_address, + pibss_coal_resp->bssid, MLAN_MAC_ADDR_LENGTH)) { + /* BSSID */ + memcpy(pmpriv->adapter, + pmpriv->curr_bss_params.bss_descriptor.mac_address, + pibss_coal_resp->bssid, MLAN_MAC_ADDR_LENGTH); + + /* Beacon Interval and ATIM window */ + pmpriv->curr_bss_params.bss_descriptor.beacon_period + = wlan_le16_to_cpu(pibss_coal_resp->beacon_interval); + pmpriv->curr_bss_params.bss_descriptor.atim_window + = wlan_le16_to_cpu(pibss_coal_resp->atim_window); + + /* ERP Information */ + pmpriv->curr_bss_params.bss_descriptor.erp_flags + = + (t_u8)wlan_le16_to_cpu(pibss_coal_resp-> + use_g_rate_protect); + + pmpriv->adhoc_state = ADHOC_COALESCED; + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of MGMT_IE_LIST + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_ret_mgmt_ie_list(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + OUT mlan_ioctl_req *pioctl_buf) +{ + t_u16 resp_len = 0, travel_len = 0; + int i = 0; + mlan_ds_misc_cfg *misc = MNULL; + mlan_ds_misc_custom_ie *cust_ie = MNULL; + custom_ie *cptr; + tlvbuf_max_mgmt_ie *max_mgmt_ie = MNULL; + HostCmd_DS_MGMT_IE_LIST_CFG *pmgmt_ie_list = + &(resp->params.mgmt_ie_list); + + ENTER(); + + if (wlan_le16_to_cpu(pmgmt_ie_list->action) == HostCmd_ACT_GEN_SET) { + if ((pmpriv->adapter->state_rdh.stage == RDH_SET_CUSTOM_IE) || + (pmpriv->adapter->state_rdh.stage == RDH_REM_CUSTOM_IE)) + if (!pmpriv->adapter->ecsa_enable) + wlan_11h_radar_detected_callback((t_void *) + pmpriv); + LEAVE(); + return MLAN_STATUS_SUCCESS; + } + + misc = (mlan_ds_misc_cfg *)pioctl_buf->pbuf; + cust_ie = (mlan_ds_misc_custom_ie *)&pmgmt_ie_list->ds_mgmt_ie; + if (cust_ie) { + cust_ie->type = wlan_le16_to_cpu(cust_ie->type); + resp_len = cust_ie->len = wlan_le16_to_cpu(cust_ie->len); + travel_len = 0; + /* conversion for index, mask, len */ + if (resp_len == sizeof(t_u16)) + cust_ie->ie_data_list[0].ie_index = + wlan_cpu_to_le16(cust_ie->ie_data_list[0]. + ie_index); + + while (resp_len > sizeof(t_u16)) { + cptr = (custom_ie *)(((t_u8 *)cust_ie->ie_data_list) + + travel_len); + cptr->ie_index = wlan_le16_to_cpu(cptr->ie_index); + cptr->mgmt_subtype_mask = + wlan_le16_to_cpu(cptr->mgmt_subtype_mask); + cptr->ie_length = wlan_le16_to_cpu(cptr->ie_length); + travel_len += + cptr->ie_length + sizeof(custom_ie) - + MAX_IE_SIZE; + resp_len -= + cptr->ie_length + sizeof(custom_ie) - + MAX_IE_SIZE; + } + memcpy(pmpriv->adapter, &misc->param.cust_ie, cust_ie, + (cust_ie->len + sizeof(MrvlIEtypesHeader_t))); + max_mgmt_ie = + (tlvbuf_max_mgmt_ie *)((t_u8 *)cust_ie + cust_ie->len + + sizeof(MrvlIEtypesHeader_t)); + if (max_mgmt_ie) { + max_mgmt_ie->type = wlan_le16_to_cpu(max_mgmt_ie->type); + if (max_mgmt_ie->type == TLV_TYPE_MAX_MGMT_IE) { + max_mgmt_ie->len = + wlan_le16_to_cpu(max_mgmt_ie->len); + max_mgmt_ie->count = + wlan_le16_to_cpu(max_mgmt_ie->count); + for (i = 0; i < max_mgmt_ie->count; i++) { + max_mgmt_ie->info[i].buf_size = + wlan_le16_to_cpu(max_mgmt_ie-> + info[i]. + buf_size); + max_mgmt_ie->info[i].buf_count = + wlan_le16_to_cpu(max_mgmt_ie-> + info[i]. + buf_count); + } + /* Append max_mgmt_ie TLV after custom_ie */ + memcpy(pmpriv->adapter, + (t_u8 *)&misc->param.cust_ie + + (cust_ie->len + + sizeof(MrvlIEtypesHeader_t)), + max_mgmt_ie, + max_mgmt_ie->len + + sizeof(MrvlIEtypesHeader_t)); + } + } + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function enable/disable tdls powermode + * + * @param pmpriv A pointer to mlan_private structure + * @param powermode 1--enable, 0--disable + * + * @return N/A + */ +static void +wlan_set_tdls_powermode(IN pmlan_private pmpriv, t_u8 powermode) +{ + ENTER(); + + if (powermode) { + pmpriv->wmm_qosinfo = DEFAULT_TDLS_WMM_QOS_INFO; + if (!pmpriv->adapter->sleep_period.period) + pmpriv->adapter->sleep_period.period = + DEFAULT_TDLS_SLEEP_PERIOD; + } else { + pmpriv->wmm_qosinfo = pmpriv->saved_wmm_qosinfo; + pmpriv->adapter->sleep_period.period = + pmpriv->adapter->saved_sleep_period.period; + } + pmpriv->adapter->pps_uapsd_mode = MFALSE; + if ((pmpriv->adapter->sleep_period.period != 0) && + (pmpriv->adapter->sleep_period.period != + SLEEP_PERIOD_RESERVED_FF)) { + pmpriv->adapter->gen_null_pkt = MTRUE; + } else { + pmpriv->adapter->delay_null_pkt = MFALSE; + pmpriv->adapter->gen_null_pkt = MFALSE; + } + LEAVE(); + return; +} + +/** + * @brief This function handles the command response of TDLS_CONFIG + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status +wlan_ret_tdls_config(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + OUT mlan_ioctl_req *pioctl_buf) +{ + t_u8 i = 0; + t_u16 link_length = 0, final_data_rate = 0; + mlan_ds_misc_cfg *misc = MNULL; + tdls_all_config *tdls_all_cfg = MNULL; + sta_node *sta_ptr = MNULL; + HostCmd_DS_TDLS_CONFIG *ptdls_config_data = + &(resp->params.tdls_config_data); + pmlan_adapter pmadapter = pmpriv->adapter; + tdls_each_link_status *link_ptr = MNULL; + + ENTER(); + + ptdls_config_data->tdls_info.tdls_action = + wlan_le16_to_cpu(ptdls_config_data->tdls_info.tdls_action); + switch (ptdls_config_data->tdls_info.tdls_action) { + case WLAN_TDLS_CONFIG: + misc = (mlan_ds_misc_cfg *)pioctl_buf->pbuf; + tdls_all_cfg = + (tdls_all_config *)ptdls_config_data->tdls_info. + tdls_data; + if (pmpriv->host_tdls_cs_support || + pmpriv->host_tdls_uapsd_support) + break; + if (wlan_le16_to_cpu(tdls_all_cfg->u.tdls_config.enable) == 0) { + PRINTM(MINFO, "TDLS disable successful.\n"); + wlan_delete_station_list(pmpriv); + pmadapter->tdls_status = TDLS_NOT_SETUP; + if (pmpriv->saved_wmm_qosinfo) + pmpriv->wmm_qosinfo = pmpriv->saved_wmm_qosinfo; + if (pmadapter->saved_sleep_period.period) + pmadapter->sleep_period.period = + pmadapter->saved_sleep_period.period; + } + break; + + case WLAN_TDLS_SET_INFO: + break; + + case WLAN_TDLS_DISCOVERY_REQ: + misc = (mlan_ds_misc_cfg *)pioctl_buf->pbuf; + tdls_all_cfg = + (tdls_all_config *)ptdls_config_data->tdls_info. + tdls_data; + tdls_all_cfg->u.tdls_discovery_resp.payload_len = + wlan_le16_to_cpu(tdls_all_cfg->u.tdls_discovery_resp. + payload_len); + tdls_all_cfg->u.tdls_discovery_resp.cap_info = + wlan_le16_to_cpu(tdls_all_cfg->u.tdls_discovery_resp. + cap_info); + memcpy(pmpriv->adapter, &misc->param.tdls_config, + &ptdls_config_data->tdls_info, + MIN(sizeof(mlan_ds_misc_tdls_config), + (resp->size - S_DS_GEN))); + PRINTM(MCMND, "TDLS_DISCOVERY_REQ: " MACSTR "\n", + MAC2STR(tdls_all_cfg->u.tdls_discovery_resp. + peer_mac_addr)); + break; + + case WLAN_TDLS_SETUP_REQ: + /* + * TDLS link being setup, block all data for this Peer + */ + tdls_all_cfg = + (tdls_all_config *)ptdls_config_data->tdls_info. + tdls_data; + PRINTM(MCMND, "TDLS_SETUP_REQ: " MACSTR "\n", + MAC2STR(tdls_all_cfg->u.tdls_setup.peer_mac_addr)); + sta_ptr = + wlan_get_station_entry(pmpriv, + tdls_all_cfg->u.tdls_setup. + peer_mac_addr); + if (!sta_ptr) { + sta_ptr = + wlan_add_station_entry(pmpriv, + tdls_all_cfg->u. + tdls_setup. + peer_mac_addr); + if (sta_ptr) { + sta_ptr->status = TDLS_SETUP_INPROGRESS; + wlan_hold_tdls_packets(pmpriv, + tdls_all_cfg->u. + tdls_setup. + peer_mac_addr); + } + } + break; + + case WLAN_TDLS_TEAR_DOWN_REQ: + /* + * TDLS link torn down, open data ports if blocked + */ + tdls_all_cfg = + (tdls_all_config *)ptdls_config_data->tdls_info. + tdls_data; + wlan_restore_tdls_packets(pmpriv, + tdls_all_cfg->u.tdls_tear_down. + peer_mac_addr, TDLS_TEAR_DOWN); + PRINTM(MCMND, "TDLS_TEARDOWN_REQ: " MACSTR "\n", + MAC2STR(tdls_all_cfg->u.tdls_tear_down.peer_mac_addr)); + sta_ptr = + wlan_get_station_entry(pmpriv, + tdls_all_cfg->u.tdls_tear_down. + peer_mac_addr); + if (sta_ptr) { + + if (sta_ptr->is_11n_enabled) { + wlan_cleanup_reorder_tbl(pmpriv, + tdls_all_cfg->u. + tdls_tear_down. + peer_mac_addr); + wlan_11n_cleanup_txbastream_tbl(pmpriv, + tdls_all_cfg->u. + tdls_tear_down. + peer_mac_addr); + } + wlan_delete_station_entry(pmpriv, + tdls_all_cfg->u. + tdls_tear_down.peer_mac_addr); + if (MTRUE == wlan_is_station_list_empty(pmpriv)) + pmadapter->tdls_status = TDLS_NOT_SETUP; + else + pmadapter->tdls_status = TDLS_IN_BASE_CHANNEL; + } + break; + case WLAN_TDLS_INIT_CHAN_SWITCH: + tdls_all_cfg = + (tdls_all_config *)ptdls_config_data->tdls_info. + tdls_data; + PRINTM(MCMND, + "TDLS_INIT_CHANNEL_SWITCH: " MACSTR + " chan=%d periodicity=%d\n", + MAC2STR(tdls_all_cfg->u.tdls_chan_switch.peer_mac_addr), + (int)tdls_all_cfg->u.tdls_chan_switch.primary_channel, + (int)tdls_all_cfg->u.tdls_chan_switch.periodicity); + break; + + case WLAN_TDLS_LINK_STATUS: + misc = (mlan_ds_misc_cfg *)pioctl_buf->pbuf; + tdls_all_cfg = + (tdls_all_config *)ptdls_config_data->tdls_info. + tdls_data; + tdls_all_cfg->u.tdls_link_status_resp.payload_len = + wlan_le16_to_cpu(tdls_all_cfg->u.tdls_link_status_resp. + payload_len); + link_ptr = tdls_all_cfg->u.tdls_link_status_resp.link_stats; + for (i = 0; + i < tdls_all_cfg->u.tdls_link_status_resp.active_links; + i++) { + link_ptr->active_channel = + wlan_le32_to_cpu(link_ptr->active_channel); + link_ptr->data_rssi_last = + wlan_le16_to_cpu(link_ptr->data_rssi_last); + link_ptr->data_nf_last = + wlan_le16_to_cpu(link_ptr->data_nf_last); + link_ptr->data_rssi_avg = + wlan_le16_to_cpu(link_ptr->data_rssi_avg); + link_ptr->data_nf_avg = + wlan_le16_to_cpu(link_ptr->data_nf_avg); + link_length = sizeof(tdls_each_link_status); + /* adjust as per open or secure network */ + if (link_ptr->link_flags & 0x02) { + link_ptr->key_lifetime = + wlan_le32_to_cpu(link_ptr-> + key_lifetime); + link_length += link_ptr->key_length; + } else { + link_length -= + sizeof(link_ptr->security_method) + + sizeof(link_ptr->key_lifetime) + + sizeof(link_ptr->key_length); + } + final_data_rate = + (t_u16)wlan_index_to_data_rate(pmadapter, + link_ptr->u. + rate_info. + tx_data_rate, + link_ptr->u. + rate_info. + tx_rate_htinfo); + link_ptr->u.final_data_rate = final_data_rate / 2; + + link_ptr = + (tdls_each_link_status *)(((t_u8 *)link_ptr) + + link_length); + } + memcpy(pmpriv->adapter, &misc->param.tdls_config, + &ptdls_config_data->tdls_info, + MIN(sizeof(mlan_ds_misc_tdls_config), + (resp->size - S_DS_GEN))); + break; + case WLAN_TDLS_POWER_MODE: + misc = (mlan_ds_misc_cfg *)pioctl_buf->pbuf; + tdls_all_cfg = + (tdls_all_config *)ptdls_config_data->tdls_info. + tdls_data; + tdls_all_cfg->u.tdls_power_mode.power_mode = + wlan_le16_to_cpu(tdls_all_cfg->u.tdls_power_mode. + power_mode); + wlan_set_tdls_powermode(pmpriv, + (t_u8)tdls_all_cfg->u.tdls_power_mode. + power_mode); + break; + case WLAN_TDLS_STOP_CHAN_SWITCH: + tdls_all_cfg = + (tdls_all_config *)ptdls_config_data->tdls_info. + tdls_data; + PRINTM(MCMND, "TDLS_STOP_CHANNEL_SWITCH: " MACSTR "\n", + MAC2STR(tdls_all_cfg->u.tdls_stop_chan_switch. + peer_mac_addr)); + break; + case WLAN_TDLS_CS_PARAMS: + case WLAN_TDLS_CS_DISABLE: + case WLAN_TDLS_DEBUG_STOP_RX: + case WLAN_TDLS_DEBUG_ALLOW_WEAK_SECURITY: + case WLAN_TDLS_DEBUG_SETUP_SAME_LINK: + case WLAN_TDLS_DEBUG_FAIL_SETUP_CONFIRM: + case WLAN_TDLS_DEBUG_WRONG_BSS: + case WLAN_TDLS_DEBUG_SETUP_PROHIBITED: + case WLAN_TDLS_DEBUG_HIGHER_LOWER_MAC: + case WLAN_TDLS_DEBUG_IGNORE_KEY_EXPIRY: + case WLAN_TDLS_DEBUG_CS_RET_IM: + break; + default: + if (pioctl_buf) + pioctl_buf->status_code = MLAN_ERROR_CMD_RESP_FAIL; + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of TDLS_OPERATION + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status +wlan_ret_tdls_oper(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, OUT mlan_ioctl_req *pioctl_buf) +{ + HostCmd_DS_TDLS_OPER *ptdls_oper = &(resp->params.tdls_oper_data); + sta_node *sta_ptr = MNULL; + t_u16 reason = 0; + pmlan_adapter pmadapter = pmpriv->adapter; + + ENTER(); + + ptdls_oper->tdls_action = wlan_le16_to_cpu(ptdls_oper->tdls_action); + + sta_ptr = wlan_get_station_entry(pmpriv, ptdls_oper->peer_mac); + reason = wlan_le16_to_cpu(ptdls_oper->reason); + switch (ptdls_oper->tdls_action) { + case TDLS_CREATE: + if (reason) { + PRINTM(MMSG, + "TDLS: create link " MACSTR " fail, reason=%d\n", + MAC2STR(ptdls_oper->peer_mac), reason); + if (reason != TDLS_LINK_EXISTS && sta_ptr) + sta_ptr->status = TDLS_SETUP_FAILURE; + } else { + PRINTM(MMSG, "TDLS: create link " MACSTR " success\n", + MAC2STR(ptdls_oper->peer_mac), reason); + } + break; + case TDLS_CONFIG: + if (reason) { + PRINTM(MMSG, + "TDLS: Config link " MACSTR " fail, reason=%d\n", + MAC2STR(ptdls_oper->peer_mac), reason); + if (sta_ptr) + sta_ptr->status = TDLS_SETUP_FAILURE; + } else { + PRINTM(MMSG, "TDLS: Config link " MACSTR " success\n", + MAC2STR(ptdls_oper->peer_mac)); + } + break; + case TDLS_DELETE: + wlan_restore_tdls_packets(pmpriv, ptdls_oper->peer_mac, + TDLS_TEAR_DOWN); + if (sta_ptr) { + /**tdls cs stop*/ + if (ISSUPP_EXTCAP_TDLS_CHAN_SWITCH + (sta_ptr->ExtCap.ext_cap)) + wlan_tdls_config(pmpriv, MFALSE); + if (sta_ptr->is_11n_enabled) { + wlan_cleanup_reorder_tbl(pmpriv, + ptdls_oper->peer_mac); + wlan_11n_cleanup_txbastream_tbl(pmpriv, + ptdls_oper-> + peer_mac); + } + if (sta_ptr->status >= TDLS_SETUP_INPROGRESS) + wlan_delete_station_entry(pmpriv, + ptdls_oper->peer_mac); + } + if (MTRUE == wlan_is_station_list_empty(pmpriv)) + pmadapter->tdls_status = TDLS_NOT_SETUP; + else + pmadapter->tdls_status = TDLS_IN_BASE_CHANNEL; + + if (reason) + PRINTM(MMSG, + "TDLS: Delete link " MACSTR " fail, reason=%d\n", + MAC2STR(ptdls_oper->peer_mac), reason); + else + PRINTM(MMSG, "TDLS: Delete link " MACSTR " success\n", + MAC2STR(ptdls_oper->peer_mac)); + break; + default: + break; + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of sysclock + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_ret_sysclock_cfg(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf) +{ + mlan_ds_misc_cfg *mis_ccfg = MNULL; + HostCmd_DS_ECL_SYSTEM_CLOCK_CONFIG *clk_cfg = + &resp->params.sys_clock_cfg; + int i = 0; + + ENTER(); + + if (pioctl_buf) { + mis_ccfg = (mlan_ds_misc_cfg *)pioctl_buf->pbuf; + mis_ccfg->param.sys_clock.cur_sys_clk = + wlan_le16_to_cpu(clk_cfg->cur_sys_clk); + mis_ccfg->param.sys_clock.sys_clk_type = + wlan_le16_to_cpu(clk_cfg->sys_clk_type); + mis_ccfg->param.sys_clock.sys_clk_num = + wlan_le16_to_cpu(clk_cfg->sys_clk_len) / sizeof(t_u16); + for (i = 0; i < mis_ccfg->param.sys_clock.sys_clk_num; i++) + mis_ccfg->param.sys_clock.sys_clk[i] = + wlan_le16_to_cpu(clk_cfg->sys_clk[i]); + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of inactivity timeout + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to command buffer + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_ret_inactivity_timeout(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf) +{ + mlan_ds_pm_cfg *pmcfg = MNULL; + mlan_ds_inactivity_to *inac_to = MNULL; + HostCmd_DS_INACTIVITY_TIMEOUT_EXT *cmd_inac_to = + (HostCmd_DS_INACTIVITY_TIMEOUT_EXT *)&resp->params. + inactivity_to; + + ENTER(); + + if (pioctl_buf) { + pmcfg = (mlan_ds_pm_cfg *)pioctl_buf->pbuf; + inac_to = &pmcfg->param.inactivity_to; + inac_to->timeout_unit = + wlan_le16_to_cpu(cmd_inac_to->timeout_unit); + inac_to->unicast_timeout = + wlan_le16_to_cpu(cmd_inac_to->unicast_timeout); + inac_to->mcast_timeout = + wlan_le16_to_cpu(cmd_inac_to->mcast_timeout); + inac_to->ps_entry_timeout = + wlan_le16_to_cpu(cmd_inac_to->ps_entry_timeout); + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of + * network monitor + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to command buffer + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_ret_net_monitor(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, IN mlan_ioctl_req *pioctl_buf) +{ + mlan_ds_misc_cfg *pmisc = MNULL; + mlan_ds_misc_net_monitor *net_mon = MNULL; + HostCmd_DS_802_11_NET_MONITOR *cmd_net_mon = + (HostCmd_DS_802_11_NET_MONITOR *)&resp->params.net_mon; + ChanBandParamSet_t *pchan_band = MNULL; + t_u8 band_info = 0; + + ENTER(); + + if (pioctl_buf && (pioctl_buf->action == MLAN_ACT_GET)) { + pmisc = (mlan_ds_misc_cfg *)pioctl_buf->pbuf; + net_mon = &pmisc->param.net_mon; + net_mon->enable_net_mon = + wlan_le16_to_cpu(cmd_net_mon->enable_net_mon); + net_mon->filter_flag = + wlan_le16_to_cpu(cmd_net_mon->filter_flag); + pchan_band = &cmd_net_mon->monitor_chan.chan_band_param[0]; + /* Band information in the TLV is bits[1:0] */ + band_info = pchan_band->bandcfg.chanBand; + net_mon->channel = pchan_band->chan_number; + if (band_info == BAND_2GHZ) + net_mon->band |= (BAND_B | BAND_G); + if (band_info == BAND_5GHZ) + net_mon->band |= BAND_A; + net_mon->chan_bandwidth = + GET_SECONDARYCHAN(pchan_band->bandcfg.chan2Offset); + if (band_info == BAND_2GHZ) + net_mon->band |= BAND_GN; + if (band_info == BAND_5GHZ) + net_mon->band |= BAND_AN; + } + pmpriv->adapter->enable_net_mon = + wlan_le16_to_cpu(cmd_net_mon->enable_net_mon); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of + * subscribe event + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to command buffer + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_ret_subscribe_event(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf) +{ + + HostCmd_DS_SUBSCRIBE_EVENT *evt = + (HostCmd_DS_SUBSCRIBE_EVENT *)&resp->params.subscribe_event; + mlan_ds_subscribe_evt *sub_evt = MNULL; + mlan_ds_misc_cfg *misc = MNULL; + + ENTER(); + if (pioctl_buf && (pioctl_buf->action == MLAN_ACT_GET)) { + misc = (mlan_ds_misc_cfg *)pioctl_buf->pbuf; + sub_evt = &misc->param.subscribe_event; + sub_evt->evt_bitmap = wlan_le16_to_cpu(evt->event_bitmap); + pioctl_buf->data_read_written = sizeof(mlan_ds_misc_cfg); + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of + * OTP user data + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to command buffer + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_ret_otp_user_data(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf) +{ + + HostCmd_DS_OTP_USER_DATA *cmd_user_data = + (HostCmd_DS_OTP_USER_DATA *)&resp->params.otp_user_data; + mlan_ds_misc_otp_user_data *user_data = MNULL; + + ENTER(); + if (pioctl_buf && (pioctl_buf->action == MLAN_ACT_GET)) { + user_data = (mlan_ds_misc_otp_user_data *)pioctl_buf->pbuf; + user_data->user_data_length = MIN(MAX_OTP_USER_DATA_LEN, + wlan_le16_to_cpu + (cmd_user_data-> + user_data_length)); + memcpy(pmpriv->adapter, user_data->user_data, + cmd_user_data->user_data, user_data->user_data_length); + pioctl_buf->data_read_written = + sizeof(mlan_ds_misc_otp_user_data) + + user_data->user_data_length; + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of + * DFS Repeater mode configuration + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to command buffer + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_ret_dfs_repeater_cfg(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf) +{ + HostCmd_DS_DFS_REPEATER_MODE *cmd_dfs_repeater = + &resp->params.dfs_repeater; + mlan_ds_misc_cfg *misc = MNULL; + mlan_ds_misc_dfs_repeater *dfs_cfg = MNULL; + + ENTER(); + + if (pioctl_buf && (pioctl_buf->action == MLAN_ACT_GET)) { + misc = (mlan_ds_misc_cfg *)pioctl_buf->pbuf; + dfs_cfg = + (mlan_ds_misc_dfs_repeater *)&misc->param.dfs_repeater; + dfs_cfg->mode = wlan_le16_to_cpu(cmd_dfs_repeater->mode); + } + if (pioctl_buf && (pioctl_buf->action == MLAN_ACT_SET)) { + if (wlan_le16_to_cpu(cmd_dfs_repeater->mode) == 1) { + /* Set dfs_repeater mode to true/enabled + * for futher references. + */ + pmpriv->adapter->dfs_repeater = MTRUE; + } else { + pmpriv->adapter->dfs_repeater = MFALSE; + } + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of coalesce config + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_ret_coalesce_config(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf) +{ + ENTER(); + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +mlan_status +wlan_ret_get_sensor_temp(IN pmlan_private pmpriv, + const IN HostCmd_DS_COMMAND *resp, + OUT mlan_ioctl_req *pioctl_buf) +{ + mlan_ds_misc_cfg *pcfg = MNULL; + const HostCmd_DS_SENSOR_TEMP *pSensorT = &resp->params.temp_sensor; + + ENTER(); + + if (pioctl_buf) { + pcfg = (mlan_ds_misc_cfg *)pioctl_buf->pbuf; + pcfg->param.sensor_temp.temperature = + wlan_le32_to_cpu(pSensorT->temperature); + PRINTM(MCMND, "get SOC temperature %u C \n", + pSensorT->temperature); + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of offload feature + * + * @param priv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_ret_offload_feature_ctrl(mlan_private *priv, HostCmd_DS_COMMAND *resp) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + HostCmd_OFFLOAD_FEATURE_CTRL *fctrl = &resp->params.fctrl; + ENTER(); + + PRINTM(MINFO, "offload feature ctrl set successful \n"); + if (fctrl->featureSelect == 0) { + PRINTM(MCMND, "11k: Neighbor Report %s \n", + fctrl->control.std. + dot11k_nbor_support ? "enabled" : "disabled"); + PRINTM(MCMND, "11k: Traffic Stream Measurement %s \n", + fctrl->control.std.dot11k_tsm ? "enabled" : "disabled"); + PRINTM(MCMND, "11k: Link Measurement %s \n", + fctrl->control.std.dot11k_lm ? "enabled" : "disabled"); + PRINTM(MCMND, "11k: Beacon Report %s \n", + fctrl->control.std.dot11k_rm ? "enabled" : "disabled"); + PRINTM(MCMND, "11v: BSS Transition %s \n", + fctrl->control.std. + dot11v_bss_trans ? "enabled" : "disabled"); + + priv->enable_11k = fctrl->control.std.dot11k_nbor_support + | fctrl->control.std.dot11k_tsm + | fctrl->control.std.dot11k_lm + | fctrl->control.std.dot11k_rm; + if (priv->enable_11k) + SET_EXTCAP_BSS_TRANSITION(priv->ext_cap); + else + RESET_EXTCAP_BSS_TRANSITION(priv->ext_cap); + PRINTM(MMSG, "11K %s \n", + priv->enable_11k ? "enable" : "disable"); + } + + LEAVE(); + return ret; +} + +/** + * @brief This function handles the command response of sta get band and channel + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_ret_sta_config(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, IN mlan_ioctl_req *pioctl_buf) +{ + HostCmd_DS_STA_CONFIGURE *cmdrsp_sta_cfg = + (HostCmd_DS_STA_CONFIGURE *) & resp->params.sta_cfg; + mlan_ds_bss *bss = MNULL; + MrvlIEtypes_channel_band_t *tlv_band_channel = MNULL; + + ENTER(); + if (pioctl_buf) { + if (pioctl_buf->req_id == MLAN_IOCTL_BSS) { + bss = (mlan_ds_bss *)pioctl_buf->pbuf; + if (bss->sub_command == MLAN_OID_BSS_CHAN_INFO) { + tlv_band_channel = + (MrvlIEtypes_channel_band_t *) + cmdrsp_sta_cfg->tlv_buffer; + bss->param.sta_channel.bandcfg = + tlv_band_channel->bandcfg; + bss->param.sta_channel.channel = + tlv_band_channel->channel; + bss->param.sta_channel.is_11n_enabled = + IS_11N_ENABLED(pmpriv); + PRINTM(MCMND, + "Get STA channel, band=0x%x, channel=%d, is_11n_enabled=%d center_chan=%d\n", + bss->param.sta_channel.bandcfg.chanBand, + bss->param.sta_channel.channel, + bss->param.sta_channel.is_11n_enabled, + bss->param.sta_channel.center_chan); + + pioctl_buf->data_read_written = + sizeof(mlan_ds_bss); + } + } + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function clears PMK in fw for fw roaming + * + * @param pmpriv A pointer to mlan_private structure + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_clear_fw_roaming_pmk(IN pmlan_private pmpriv) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_SUPPLICANT_PMK, + HostCmd_ACT_GEN_REMOVE, 0, MNULL, MNULL); + + if (ret == MLAN_STATUS_SUCCESS) { + ret = MLAN_STATUS_FAILURE; + } + + LEAVE(); + return ret; +} + +/** + * @brief This function handles the command response of enable/disable roaming offload to fw + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_ret_roam_offload(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf) +{ + HostCmd_DS_ROAM_OFFLOAD *cmdrsp_roam_offload = + (HostCmd_DS_ROAM_OFFLOAD *) & resp->params.roam_offload; + mlan_status status = MLAN_STATUS_SUCCESS; + MrvlIEtypesHeader_t *header = MNULL; + MrvlIEtypes_fw_roam_enable_t *roam_offload_enable = MNULL; + + ENTER(); + + if (resp->result == HostCmd_RESULT_OK) { + header = (MrvlIEtypesHeader_t *)cmdrsp_roam_offload->tlv; + header->type = wlan_le16_to_cpu(header->type); + if (header->type == TLV_TYPE_ROAM) { + roam_offload_enable = + (MrvlIEtypes_fw_roam_enable_t *) + cmdrsp_roam_offload->tlv; + if ((t_u8)roam_offload_enable->roam_enable) + pmpriv->adapter->fw_roaming = MTRUE; + else { + pmpriv->adapter->fw_roaming = MFALSE; + wlan_clear_fw_roaming_pmk(pmpriv); + } + } + } + + LEAVE(); + return status; +} + +/******************************************************** + Global Functions +********************************************************/ + +/** + * @brief This function handles the station command response + * + * @param priv A pointer to mlan_private structure + * @param cmdresp_no cmd no + * @param pcmd_buf cmdresp buf + * @param pioctl A pointer to ioctl buf + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_ops_sta_process_cmdresp(IN t_void *priv, + IN t_u16 cmdresp_no, + IN t_void *pcmd_buf, IN t_void *pioctl) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = (mlan_private *)priv; + HostCmd_DS_COMMAND *resp = (HostCmd_DS_COMMAND *)pcmd_buf; + mlan_ioctl_req *pioctl_buf = (mlan_ioctl_req *)pioctl; + + mlan_adapter *pmadapter = pmpriv->adapter; + int ctr; + + ENTER(); + + /* If the command is not successful, cleanup and return failure */ + if ((resp->result != HostCmd_RESULT_OK) + ) { + ret = wlan_process_cmdresp_error(pmpriv, resp, pioctl_buf); + LEAVE(); + return ret; + } + /* Command successful, handle response */ + switch (cmdresp_no) { + case HostCmd_CMD_GET_HW_SPEC: + ret = wlan_ret_get_hw_spec(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_SDIO_SP_RX_AGGR_CFG: + ret = wlan_ret_sdio_rx_aggr_cfg(pmpriv, resp); + break; + case HostCmd_CMD_CFG_DATA: + ret = wlan_ret_cfg_data(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_MAC_CONTROL: + ret = wlan_ret_mac_control(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_802_11_MAC_ADDRESS: + ret = wlan_ret_802_11_mac_address(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_MAC_MULTICAST_ADR: + ret = wlan_ret_mac_multicast_adr(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_TX_RATE_CFG: + ret = wlan_ret_tx_rate_cfg(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_802_11_SCAN: + ret = wlan_ret_802_11_scan(pmpriv, resp, pioctl_buf); + pioctl_buf = MNULL; + pmadapter->curr_cmd->pioctl_buf = MNULL; + break; + case HostCmd_CMD_802_11_SCAN_EXT: + ret = wlan_ret_802_11_scan_ext(pmpriv, resp, pioctl_buf); + pioctl_buf = MNULL; + pmadapter->curr_cmd->pioctl_buf = MNULL; + break; + case HostCmd_CMD_802_11_BG_SCAN_CONFIG: + ret = wlan_ret_bgscan_config(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_802_11_BG_SCAN_QUERY: + ret = wlan_ret_802_11_bgscan_query(pmpriv, resp, pioctl_buf); + wlan_recv_event(pmpriv, MLAN_EVENT_ID_DRV_BGSCAN_RESULT, MNULL); + PRINTM(MINFO, "CMD_RESP: BG_SCAN result is ready!\n"); + break; + case HostCmd_CMD_TXPWR_CFG: + ret = wlan_ret_tx_power_cfg(pmpriv, resp, pioctl_buf); + break; + + case HostCmd_CMD_802_11_RF_TX_POWER: + ret = wlan_ret_802_11_rf_tx_power(pmpriv, resp, pioctl_buf); + break; + + case HostCmd_CMD_802_11_PS_MODE_ENH: + ret = wlan_ret_enh_power_mode(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_802_11_HS_CFG_ENH: + ret = wlan_ret_802_11_hs_cfg(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_802_11_SLEEP_PERIOD: + ret = wlan_ret_802_11_sleep_period(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_802_11_SLEEP_PARAMS: + ret = wlan_ret_802_11_sleep_params(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_802_11_FW_WAKE_METHOD: + ret = wlan_ret_fw_wakeup_method(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_802_11_ROBUSTCOEX: + break; + case HostCmd_CMD_802_11_ASSOCIATE: + ret = wlan_ret_802_11_associate(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_802_11_DEAUTHENTICATE: + case HostCmd_CMD_802_11_DISASSOCIATE: + ret = wlan_ret_802_11_deauthenticate(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_802_11_AD_HOC_START: + case HostCmd_CMD_802_11_AD_HOC_JOIN: + ret = wlan_ret_802_11_ad_hoc(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_802_11_AD_HOC_STOP: + ret = wlan_ret_802_11_ad_hoc_stop(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_802_11_GET_LOG: + ret = wlan_ret_get_log(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_RSSI_INFO_EXT: + ret = wlan_ret_802_11_rssi_info_ext(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_RSSI_INFO: + ret = wlan_ret_802_11_rssi_info(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_802_11_SNMP_MIB: + ret = wlan_ret_802_11_snmp_mib(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_802_11_RADIO_CONTROL: + ret = wlan_ret_802_11_radio_control(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_802_11_TX_RATE_QUERY: + ret = wlan_ret_802_11_tx_rate_query(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_802_11_RF_CHANNEL: + ret = wlan_ret_802_11_rf_channel(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_802_11_RF_ANTENNA: + ret = wlan_ret_802_11_rf_antenna(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_CW_MODE_CTRL: + ret = wlan_ret_cw_mode_ctrl(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_VERSION_EXT: + ret = wlan_ret_ver_ext(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_RX_MGMT_IND: + ret = wlan_ret_rx_mgmt_ind(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_FUNC_INIT: + case HostCmd_CMD_FUNC_SHUTDOWN: + break; + case HostCmd_CMD_802_11_KEY_MATERIAL: + ret = wlan_ret_802_11_key_material(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_GTK_REKEY_OFFLOAD_CFG: + break; + case HostCmd_CMD_SUPPLICANT_PMK: + ret = wlan_ret_802_11_supplicant_pmk(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_SUPPLICANT_PROFILE: + ret = wlan_ret_802_11_supplicant_profile(pmpriv, resp, + pioctl_buf); + break; + case HostCmd_CMD_802_11_EAPOL_PKT: + break; + case HostCmd_CMD_802_11D_DOMAIN_INFO: + ret = wlan_ret_802_11d_domain_info(pmpriv, resp); + break; + case HostCmd_CMD_802_11K_GET_NLIST: + break; + case HostCmd_CMD_OFFLOAD_FEATURE_CONTROL: + ret = wlan_ret_offload_feature_ctrl(pmpriv, resp); + break; + case HostCmd_CMD_802_11_TPC_ADAPT_REQ: + case HostCmd_CMD_802_11_TPC_INFO: + case HostCmd_CMD_802_11_CHAN_SW_ANN: + case HostCmd_CMD_CHAN_REPORT_REQUEST: + ret = wlan_11h_cmdresp_process(pmpriv, resp); + break; + case HostCmd_CMD_11N_ADDBA_REQ: + ret = wlan_ret_11n_addba_req(pmpriv, resp); + break; + case HostCmd_CMD_11N_DELBA: + ret = wlan_ret_11n_delba(pmpriv, resp); + break; + case HostCmd_CMD_11N_ADDBA_RSP: + ret = wlan_ret_11n_addba_resp(pmpriv, resp); + break; + case HostCmd_CMD_RECONFIGURE_TX_BUFF: + wlan_set_tx_pause_flag(pmpriv, MFALSE); + + pmadapter->tx_buf_size = + (t_u16)wlan_le16_to_cpu(resp->params.tx_buf.buff_size); + pmadapter->tx_buf_size = + (pmadapter->tx_buf_size / MLAN_SDIO_BLOCK_SIZE) * + MLAN_SDIO_BLOCK_SIZE; + pmadapter->curr_tx_buf_size = pmadapter->tx_buf_size; + pmadapter->mp_end_port = + wlan_le16_to_cpu(resp->params.tx_buf.mp_end_port); + pmadapter->mp_data_port_mask = DATA_PORT_MASK; + + for (ctr = 1; ctr <= MAX_PORT - pmadapter->mp_end_port; ctr++) { + pmadapter->mp_data_port_mask &= + ~(1 << (MAX_PORT - ctr)); + } + + pmadapter->curr_wr_port = 0; + pmadapter->mpa_tx.pkt_aggr_limit = + MIN(SDIO_MP_AGGR_DEF_PKT_LIMIT, + (pmadapter->mp_end_port >> 1)); + PRINTM(MCMND, "end port %d, data port mask %x\n", + wlan_le16_to_cpu(resp->params.tx_buf.mp_end_port), + pmadapter->mp_data_port_mask); + PRINTM(MCMND, "max_tx_buf_size=%d, tx_buf_size=%d\n", + pmadapter->max_tx_buf_size, pmadapter->tx_buf_size); + break; + case HostCmd_CMD_AMSDU_AGGR_CTRL: + ret = wlan_ret_amsdu_aggr_ctrl(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_WMM_GET_STATUS: + ret = wlan_ret_wmm_get_status(pmpriv, + resp->params.get_wmm_status. + queue_status_tlv, + resp->size - S_DS_GEN); + break; + case HostCmd_CMD_WMM_ADDTS_REQ: + ret = wlan_ret_wmm_addts_req(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_WMM_DELTS_REQ: + ret = wlan_ret_wmm_delts_req(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_WMM_QUEUE_CONFIG: + ret = wlan_ret_wmm_queue_config(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_WMM_QUEUE_STATS: + ret = wlan_ret_wmm_queue_stats(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_WMM_TS_STATUS: + ret = wlan_ret_wmm_ts_status(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_WMM_PARAM_CONFIG: + ret = wlan_ret_wmm_param_config(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_802_11_IBSS_COALESCING_STATUS: + ret = wlan_ret_ibss_coalescing_status(pmpriv, resp); + break; + case HostCmd_CMD_MGMT_IE_LIST: + ret = wlan_ret_mgmt_ie_list(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_TDLS_CONFIG: + ret = wlan_ret_tdls_config(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_TDLS_OPERATION: + ret = wlan_ret_tdls_oper(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_11N_CFG: + ret = wlan_ret_11n_cfg(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_TX_BF_CFG: + ret = wlan_ret_tx_bf_cfg(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_ECL_SYSTEM_CLOCK_CONFIG: + ret = wlan_ret_sysclock_cfg(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_MAC_REG_ACCESS: + case HostCmd_CMD_BBP_REG_ACCESS: + case HostCmd_CMD_RF_REG_ACCESS: + case HostCmd_CMD_CAU_REG_ACCESS: + case HostCmd_CMD_TARGET_ACCESS: + case HostCmd_CMD_802_11_EEPROM_ACCESS: + ret = wlan_ret_reg_access(pmpriv->adapter, cmdresp_no, resp, + pioctl_buf); + break; + case HostCmd_CMD_MEM_ACCESS: + ret = wlan_ret_mem_access(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_INACTIVITY_TIMEOUT_EXT: + ret = wlan_ret_inactivity_timeout(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_SDIO_GPIO_INT_CONFIG: + break; + case HostCmd_CMD_SET_BSS_MODE: + break; + case HostCmd_CMD_MEASUREMENT_REQUEST: + case HostCmd_CMD_MEASUREMENT_REPORT: + ret = wlan_meas_cmdresp_process(pmpriv, resp); + break; + case HostCmd_CMD_802_11_NET_MONITOR: + ret = wlan_ret_net_monitor(pmpriv, resp, pioctl_buf); + break; +#if defined(SYSKT_MULTI) && defined(OOB_WAKEUP) || defined(SUSPEND_SDIO_PULL_DOWN) + case HostCmd_CMD_SDIO_PULL_CTRL: + break; +#endif + case HostCmd_CMD_802_11_REMAIN_ON_CHANNEL: + ret = wlan_ret_remain_on_channel(pmpriv, resp, pioctl_buf); + break; +#ifdef WIFI_DIRECT_SUPPORT + case HOST_CMD_WIFI_DIRECT_MODE_CONFIG: + ret = wlan_ret_wifi_direct_mode(pmpriv, resp, pioctl_buf); + break; +#endif + case HostCmd_CMD_802_11_SUBSCRIBE_EVENT: + ret = wlan_ret_subscribe_event(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_OTP_READ_USER_DATA: + ret = wlan_ret_otp_user_data(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_HS_WAKEUP_REASON: + ret = wlan_ret_hs_wakeup_reason(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_REJECT_ADDBA_REQ: + ret = wlan_ret_reject_addba_req(pmpriv, resp, pioctl_buf); + break; +#ifdef RX_PACKET_COALESCE + case HostCmd_CMD_RX_PKT_COALESCE_CFG: + ret = wlan_ret_rx_pkt_coalesce_cfg(pmpriv, resp, pioctl_buf); + break; +#endif + case HostCmd_CMD_MULTI_CHAN_CONFIG: + ret = wlan_ret_multi_chan_cfg(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_MULTI_CHAN_POLICY: + ret = wlan_ret_multi_chan_policy(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_DRCS_CONFIG: + ret = wlan_ret_drcs_cfg(pmpriv, resp, pioctl_buf); + break; + case HostCmd_DFS_REPEATER_MODE: + ret = wlan_ret_dfs_repeater_cfg(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_COALESCE_CFG: + ret = wlan_ret_coalesce_config(pmpriv, resp, pioctl_buf); + break; + case HostCmd_DS_GET_SENSOR_TEMP: + ret = wlan_ret_get_sensor_temp(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_STA_CONFIGURE: + ret = wlan_ret_sta_config(pmpriv, resp, pioctl_buf); + break; + case HOST_CMD_PMIC_CONFIGURE: + break; + case HostCmd_CMD_INDEPENDENT_RESET_CFG: + ret = wlan_ret_ind_rst_cfg(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_802_11_PS_INACTIVITY_TIMEOUT: + break; + case HostCmd_CMD_ROAM_OFFLOAD: + ret = wlan_ret_roam_offload(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_GET_TSF: + ret = wlan_ret_get_tsf(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_HOST_CLOCK_CFG: + ret = wlan_ret_host_clock_cfg(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_CHAN_REGION_CFG: + ret = wlan_ret_chan_region_cfg(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_BOOT_SLEEP: + ret = wlan_ret_boot_sleep(pmpriv, resp, pioctl_buf); + break; + default: + PRINTM(MERROR, "CMD_RESP: Unknown command response %#x\n", + resp->command); + break; + } + + LEAVE(); + return ret; +} diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_sta_event.c b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_sta_event.c new file mode 100644 index 000000000000..482120c56f86 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_sta_event.c @@ -0,0 +1,1411 @@ +/** @file mlan_sta_event.c + * + * @brief This file contains MLAN event handling. + * + * Copyright (C) 2008-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/******************************************************** +Change log: + 10/13/2008: initial version +********************************************************/ + +#include "mlan.h" +#include "mlan_join.h" +#include "mlan_util.h" +#include "mlan_fw.h" +#include "mlan_main.h" +#include "mlan_wmm.h" +#include "mlan_11n.h" +#include "mlan_11h.h" +#ifdef DRV_EMBEDDED_SUPPLICANT +#include "authenticator_api.h" +#endif + +/******************************************************** + Global Variables +********************************************************/ + +/******************************************************** + Local Functions +********************************************************/ + +/** + * @brief This function handles link lost, deauth and + * disassoc events. + * + * @param pmpriv A pointer to mlan_private structure + * @return N/A + */ +static t_void +wlan_handle_disconnect_event(pmlan_private pmpriv) +{ + ENTER(); + + if (pmpriv->media_connected == MTRUE) + wlan_reset_connect_state(pmpriv, MTRUE); + + LEAVE(); +} + +/** + * @brief This function will parse the TDLS event for further wlan action + * + * @param priv A pointer to mlan_private + * @param pevent A pointer to event buf + * + * @return N/A + */ +static void +wlan_parse_tdls_event(pmlan_private priv, pmlan_buffer pevent) +{ + Event_tdls_generic *tdls_event = (Event_tdls_generic *) + (pevent->pbuf + pevent->data_offset + sizeof(mlan_event_id)); + sta_node *sta_ptr = MNULL; + pmlan_adapter pmadapter = priv->adapter; + t_u8 i = 0; + IEEEtypes_HTCap_t *pht_cap = MNULL; + t_u16 ie_len = 0; + mlan_ds_misc_tdls_oper tdls_oper; + t_u8 event_buf[100]; + mlan_event *ptdls_event = (mlan_event *)event_buf; + tdls_tear_down_event *tdls_evt = + (tdls_tear_down_event *)ptdls_event->event_buf; + ENTER(); + + /* reason code is not mandatory, hence less by sizeof(t_u16) */ + if (pevent->data_len < (sizeof(Event_tdls_generic) - + sizeof(t_u16) - sizeof(mlan_event_id))) { + PRINTM(MERROR, "Invalid length %d for TDLS event\n", + pevent->data_len); + LEAVE(); + return; + } + sta_ptr = wlan_get_station_entry(priv, tdls_event->peer_mac_addr); + PRINTM(MEVENT, "TDLS_EVENT: %d " MACSTR "\n", + wlan_le16_to_cpu(tdls_event->event_type), + MAC2STR(tdls_event->peer_mac_addr)); + switch (wlan_le16_to_cpu(tdls_event->event_type)) { + case TDLS_EVENT_TYPE_SETUP_REQ: + if (sta_ptr == MNULL) { + sta_ptr = + wlan_add_station_entry(priv, + tdls_event-> + peer_mac_addr); + if (sta_ptr) { + sta_ptr->status = TDLS_SETUP_INPROGRESS; + wlan_hold_tdls_packets(priv, + tdls_event-> + peer_mac_addr); + } + } + break; + + case TDLS_EVENT_TYPE_LINK_ESTABLISHED: + if (sta_ptr) { + sta_ptr->status = TDLS_SETUP_COMPLETE; + /* parse the TLV for station's capability */ + ie_len = wlan_le16_to_cpu(tdls_event->u.ie_data. + ie_length); + if (ie_len) { + pht_cap = + (IEEEtypes_HTCap_t *) + wlan_get_specific_ie(priv, + tdls_event->u. + ie_data.ie_ptr, + ie_len, + HT_CAPABILITY); + if (pht_cap) { + sta_ptr->is_11n_enabled = MTRUE; + if (GETHT_MAXAMSDU + (pht_cap->ht_cap.ht_cap_info)) + sta_ptr->max_amsdu = + MLAN_TX_DATA_BUF_SIZE_8K; + else + sta_ptr->max_amsdu = + MLAN_TX_DATA_BUF_SIZE_4K; + } + } + for (i = 0; i < MAX_NUM_TID; i++) { + if (sta_ptr->is_11n_enabled) + sta_ptr->ampdu_sta[i] = + priv->aggr_prio_tbl[i]. + ampdu_user; + else + sta_ptr->ampdu_sta[i] = + BA_STREAM_NOT_ALLOWED; + } + memset(priv->adapter, sta_ptr->rx_seq, 0xff, + sizeof(sta_ptr->rx_seq)); + wlan_restore_tdls_packets(priv, + tdls_event->peer_mac_addr, + TDLS_SETUP_COMPLETE); + pmadapter->tdls_status = TDLS_IN_BASE_CHANNEL; + } + break; + + case TDLS_EVENT_TYPE_SETUP_FAILURE: + wlan_restore_tdls_packets(priv, tdls_event->peer_mac_addr, + TDLS_SETUP_FAILURE); + if (sta_ptr) + wlan_delete_station_entry(priv, + tdls_event->peer_mac_addr); + if (MTRUE == wlan_is_station_list_empty(priv)) + pmadapter->tdls_status = TDLS_NOT_SETUP; + else + pmadapter->tdls_status = TDLS_IN_BASE_CHANNEL; + break; + case TDLS_EVENT_TYPE_LINK_TORN_DOWN: + if (sta_ptr) { + if (sta_ptr->external_tdls) { + PRINTM(MMSG, + "Receive TDLS TEAR DOWN event, Disable TDLS LINK\n"); + pmadapter->tdls_status = TDLS_TEAR_DOWN; + memset(pmadapter, &tdls_oper, 0, + sizeof(tdls_oper)); + tdls_oper.tdls_action = WLAN_TDLS_DISABLE_LINK; + memcpy(priv->adapter, tdls_oper.peer_mac, + tdls_event->peer_mac_addr, + MLAN_MAC_ADDR_LENGTH); + /* Send command to firmware to delete tdls link */ + wlan_prepare_cmd(priv, + HostCmd_CMD_TDLS_OPERATION, + HostCmd_ACT_GEN_SET, + 0, + (t_void *)MNULL, &tdls_oper); + ptdls_event->bss_index = priv->bss_index; + ptdls_event->event_id = + MLAN_EVENT_ID_DRV_TDLS_TEARDOWN_REQ; + ptdls_event->event_len = + sizeof(tdls_tear_down_event); + memcpy(priv->adapter, + (t_u8 *)tdls_evt->peer_mac_addr, + tdls_event->peer_mac_addr, + MLAN_MAC_ADDR_LENGTH); + tdls_evt->reason_code = + wlan_le16_to_cpu(tdls_event->u. + reason_code); + wlan_recv_event(priv, + MLAN_EVENT_ID_DRV_TDLS_TEARDOWN_REQ, + ptdls_event); + /* Signal MOAL to trigger mlan_main_process */ + wlan_recv_event(priv, + MLAN_EVENT_ID_DRV_DEFER_HANDLING, + MNULL); + LEAVE(); + return; + } + wlan_restore_tdls_packets(priv, + tdls_event->peer_mac_addr, + TDLS_TEAR_DOWN); + if (sta_ptr->is_11n_enabled) { + wlan_cleanup_reorder_tbl(priv, + tdls_event-> + peer_mac_addr); + wlan_11n_cleanup_txbastream_tbl(priv, + tdls_event-> + peer_mac_addr); + } + wlan_delete_station_entry(priv, + tdls_event->peer_mac_addr); + if (MTRUE == wlan_is_station_list_empty(priv)) + pmadapter->tdls_status = TDLS_NOT_SETUP; + else + pmadapter->tdls_status = TDLS_IN_BASE_CHANNEL; + } + break; + case TDLS_EVENT_TYPE_CHAN_SWITCH_RESULT: + PRINTM(MEVENT, + "TDLS_CHAN_SWITCH_RESULT: status=0x%x, reason=0x%x current_channel=%d\n", + tdls_event->u.switch_result.status, + tdls_event->u.switch_result.reason, + (int)tdls_event->u.switch_result.current_channel); + if (tdls_event->u.switch_result.status == MLAN_STATUS_SUCCESS) { + if (tdls_event->u.switch_result.current_channel == + TDLS_BASE_CHANNEL) { + /* enable traffic to AP */ + if (pmadapter->tdls_status != + TDLS_IN_BASE_CHANNEL) { + wlan_update_non_tdls_ralist(priv, + tdls_event-> + peer_mac_addr, + MFALSE); + pmadapter->tdls_status = + TDLS_IN_BASE_CHANNEL; + } + } else if (tdls_event->u.switch_result. + current_channel == TDLS_OFF_CHANNEL) { + /* pause traffic to AP */ + if (pmadapter->tdls_status != + TDLS_IN_OFF_CHANNEL) { + wlan_update_non_tdls_ralist(priv, + tdls_event-> + peer_mac_addr, + MTRUE); + pmadapter->tdls_status = + TDLS_IN_OFF_CHANNEL; + } + } + } else { + if (tdls_event->u.switch_result.current_channel == + TDLS_BASE_CHANNEL) + pmadapter->tdls_status = TDLS_IN_BASE_CHANNEL; + else if (tdls_event->u.switch_result.current_channel == + TDLS_OFF_CHANNEL) + pmadapter->tdls_status = TDLS_IN_OFF_CHANNEL; + } + break; + case TDLS_EVENT_TYPE_START_CHAN_SWITCH: + PRINTM(MEVENT, "TDLS start channel switch....\n"); + pmadapter->tdls_status = TDLS_SWITCHING_CHANNEL; + break; + case TDLS_EVENT_TYPE_CHAN_SWITCH_STOPPED: + PRINTM(MEVENT, "TDLS channel switch stopped, reason=%d\n", + tdls_event->u.cs_stop_reason); + break; + case TDLS_EVENT_TYPE_DEBUG: + case TDLS_EVENT_TYPE_PACKET: + break; + default: + PRINTM(MERROR, "unknown event type %d\n", + wlan_le16_to_cpu(tdls_event->event_type)); + break; + } + LEAVE(); +} + +/** + * @brief This function send the tdls teardown request event. + * + * @param priv A pointer to mlan_private + * + * @return N/A + */ +void +wlan_send_tdls_tear_down_request(pmlan_private priv) +{ + t_u8 event_buf[100]; + mlan_event *ptdls_event = (mlan_event *)event_buf; + tdls_tear_down_event *tdls_evt = + (tdls_tear_down_event *)ptdls_event->event_buf; + sta_node *sta_ptr = MNULL; + + ENTER(); + + sta_ptr = (sta_node *)util_peek_list(priv->adapter->pmoal_handle, + &priv->sta_list, + priv->adapter->callbacks. + moal_spin_lock, + priv->adapter->callbacks. + moal_spin_unlock); + if (!sta_ptr) { + LEAVE(); + return; + } + while (sta_ptr != (sta_node *)&priv->sta_list) { + if (sta_ptr->external_tdls) { + ptdls_event->bss_index = priv->bss_index; + ptdls_event->event_id = + MLAN_EVENT_ID_DRV_TDLS_TEARDOWN_REQ; + ptdls_event->event_len = sizeof(tdls_tear_down_event); + memcpy(priv->adapter, (t_u8 *)tdls_evt->peer_mac_addr, + sta_ptr->mac_addr, MLAN_MAC_ADDR_LENGTH); + tdls_evt->reason_code = + MLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED; + wlan_recv_event(priv, + MLAN_EVENT_ID_DRV_TDLS_TEARDOWN_REQ, + ptdls_event); + } + sta_ptr = sta_ptr->pnext; + } + LEAVE(); + return; +} + +/** + * @brief This function will handle the generic NAN event for further wlan action + * based on the Event subtypes + * + * @param pmpriv A pointer to mlan_private + * @param evt_buf A pointer to mlan_event + * @param pmbuf A pointer to mlan buffer + * + * @return N/A + */ +static void +wlan_process_nan_event(pmlan_private pmpriv, pmlan_buffer pmbuf) +{ + t_u8 *evt_buf = MNULL; + mlan_event *pevent; + mlan_status ret = MLAN_STATUS_SUCCESS; + event_nan_generic *nan_event = (event_nan_generic *) + (pmbuf->pbuf + pmbuf->data_offset + sizeof(mlan_event_id)); + pmlan_adapter pmadapter = pmpriv->adapter; + pmlan_callbacks pcb = &pmadapter->callbacks; + + ENTER(); + + ret = pcb->moal_malloc(pmadapter->pmoal_handle, MAX_EVENT_SIZE, + MLAN_MEM_DEF, &evt_buf); + if (ret != MLAN_STATUS_SUCCESS || !evt_buf) { + LEAVE(); + return; + } + + pevent = (pmlan_event)evt_buf; + + pevent->bss_index = pmpriv->bss_index; + if (wlan_le16_to_cpu(nan_event->event_sub_type) == + NAN_EVT_SUBTYPE_SD_EVENT || + wlan_le16_to_cpu(nan_event->event_sub_type) == + NAN_EVT_SUBTYPE_SDF_TX_DONE) { + pevent->event_id = MLAN_EVENT_ID_DRV_PASSTHRU; + pevent->event_len = pmbuf->data_len; + memcpy(pmadapter, (t_u8 *)pevent->event_buf, + pmbuf->pbuf + pmbuf->data_offset, pevent->event_len); + wlan_recv_event(pmpriv, pevent->event_id, pevent); + pcb->moal_mfree(pmadapter->pmoal_handle, evt_buf); + } else { + t_u8 test_mac[MLAN_MAC_ADDR_LENGTH] + = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 }; + pevent->event_id = MLAN_EVENT_ID_DRV_CONNECTED; + pevent->event_len = MLAN_MAC_ADDR_LENGTH; + memcpy(pmpriv->adapter, (t_u8 *)pevent->event_buf, + test_mac, MLAN_MAC_ADDR_LENGTH); + wlan_ralist_add(pmpriv, test_mac); + memcpy(pmpriv->adapter, + pmpriv->curr_bss_params.bss_descriptor.mac_address, + test_mac, MLAN_MAC_ADDR_LENGTH); + wlan_recv_event(pmpriv, MLAN_EVENT_ID_DRV_CONNECTED, pevent); + if (pmpriv->port_ctrl_mode == MTRUE) + pmpriv->port_open = MTRUE; + pmpriv->media_connected = MTRUE; + PRINTM_NETINTF(MEVENT, pmpriv); + PRINTM(MEVENT, "nan interface - opened\n"); + pcb->moal_mfree(pmadapter->pmoal_handle, evt_buf); + } + + LEAVE(); + return; +} + +/******************************************************** + Global Functions +********************************************************/ +/** + * @brief This function handles disconnect event, reports disconnect + * to upper layer, cleans tx/rx packets, + * resets link state etc. + * + * @param priv A pointer to mlan_private structure + * @param drv_disconnect Flag indicating the driver should disconnect + * and flush pending packets. + * + * @return N/A + */ +t_void +wlan_reset_connect_state(pmlan_private priv, t_u8 drv_disconnect) +{ + mlan_adapter *pmadapter = priv->adapter; + mlan_status ret = MLAN_STATUS_SUCCESS; + state_11d_t enable; + + ENTER(); + + PRINTM(MINFO, "Handles disconnect event.\n"); + + /* If DFS repeater mode is enabled and station interface disconnects + * then make sure that all uAPs are stopped. + */ + if (pmadapter->dfs_repeater) + wlan_dfs_rep_disconnect(pmadapter); + + if (drv_disconnect) { + priv->media_connected = MFALSE; + pmadapter->state_rdh.tx_block = MFALSE; + wlan_11h_check_update_radar_det_state(priv); + } + + if (priv->port_ctrl_mode == MTRUE) { + /* Close the port on Disconnect */ + PRINTM(MINFO, "DISC: port_status = CLOSED\n"); + priv->port_open = MFALSE; + } + memset(pmadapter, &priv->gtk_rekey, 0, + sizeof(mlan_ds_misc_gtk_rekey_data)); + priv->tx_pause = MFALSE; + pmadapter->scan_block = MFALSE; + + /* Reset SNR/NF/RSSI values */ + priv->data_rssi_last = 0; + priv->data_nf_last = 0; + priv->data_rssi_avg = 0; + priv->data_nf_avg = 0; + priv->bcn_rssi_last = 0; + priv->bcn_nf_last = 0; + priv->bcn_rssi_avg = 0; + priv->bcn_nf_avg = 0; + priv->rxpd_rate = 0; + priv->rxpd_rate_info = 0; + priv->max_amsdu = 0; + wlan_coex_ampdu_rxwinsize(pmadapter); + + priv->sec_info.ewpa_enabled = MFALSE; + priv->sec_info.wpa_enabled = MFALSE; + priv->sec_info.wpa2_enabled = MFALSE; + priv->wpa_ie_len = 0; +#ifdef DRV_EMBEDDED_SUPPLICANT + supplicantStopSessionTimer(priv->psapriv); + supplicantClrEncryptKey(priv->psapriv); + supplicantDisable(priv->psapriv); +#endif + + priv->sec_info.wapi_enabled = MFALSE; + priv->wapi_ie_len = 0; + priv->sec_info.wapi_key_on = MFALSE; + + priv->wps.session_enable = MFALSE; + memset(priv->adapter, (t_u8 *)&priv->wps.wps_ie, 0x00, + sizeof(priv->wps.wps_ie)); + + priv->sec_info.encryption_mode = MLAN_ENCRYPTION_MODE_NONE; + + /*Enable auto data rate */ + priv->is_data_rate_auto = MTRUE; + priv->data_rate = 0; + + if (priv->bss_mode == MLAN_BSS_MODE_IBSS) { + priv->adhoc_state = ADHOC_IDLE; + priv->adhoc_is_link_sensed = MFALSE; + priv->intf_state_11h.adhoc_auto_sel_chan = MTRUE; + } + + if (drv_disconnect) { + /* Free Tx and Rx packets, report disconnect to upper layer */ + wlan_clean_txrx(priv); + + /* Need to erase the current SSID and BSSID info */ + memset(pmadapter, + &priv->curr_bss_params, 0x00, + sizeof(priv->curr_bss_params)); + priv->assoc_rsp_size = 0; + } + wlan_send_tdls_tear_down_request(priv); + wlan_delete_station_list(priv); + pmadapter->tdls_status = TDLS_NOT_SETUP; + priv->wmm_qosinfo = priv->saved_wmm_qosinfo; + pmadapter->sleep_period.period = pmadapter->saved_sleep_period.period; + pmadapter->tx_lock_flag = MFALSE; + pmadapter->pps_uapsd_mode = MFALSE; + pmadapter->delay_null_pkt = MFALSE; + if (priv->bss_type == MLAN_BSS_TYPE_STA) + pmadapter->hs_wake_interval = 0; + + if ((wlan_11d_is_enabled(priv)) && + (priv->state_11d.user_enable_11d == DISABLE_11D)) { + + priv->state_11d.enable_11d = DISABLE_11D; + enable = DISABLE_11D; + + /* Send cmd to FW to enable/disable 11D function */ + ret = wlan_prepare_cmd(priv, + HostCmd_CMD_802_11_SNMP_MIB, + HostCmd_ACT_GEN_SET, + Dot11D_i, MNULL, &enable); + if (ret) + PRINTM(MERROR, "11D: Failed to enable 11D\n"); + } + if (pmadapter->num_cmd_timeout && pmadapter->curr_cmd && + (pmadapter->cmd_timer_is_set == MFALSE)) { + LEAVE(); + return; + } + + wlan_recv_event(priv, MLAN_EVENT_ID_FW_DISCONNECTED, MNULL); + + LEAVE(); +} + +/** + * @brief This function sends the OBSS scan parameters to the application + * + * @param pmpriv A pointer to mlan_private structure + * + * @return N/A + */ +t_void +wlan_2040_coex_event(pmlan_private pmpriv) +{ + t_u8 event_buf[100]; + mlan_event *pevent = (mlan_event *)event_buf; + t_u8 ele_len; + + ENTER(); + + if (pmpriv->curr_bss_params.bss_descriptor.poverlap_bss_scan_param && + pmpriv->curr_bss_params.bss_descriptor.poverlap_bss_scan_param-> + ieee_hdr.element_id == OVERLAPBSSSCANPARAM) { + ele_len = + pmpriv->curr_bss_params.bss_descriptor. + poverlap_bss_scan_param->ieee_hdr.len; + pevent->bss_index = pmpriv->bss_index; + pevent->event_id = MLAN_EVENT_ID_DRV_OBSS_SCAN_PARAM; + /* Copy OBSS scan parameters */ + memcpy(pmpriv->adapter, (t_u8 *)pevent->event_buf, + (t_u8 *)&pmpriv->curr_bss_params.bss_descriptor. + poverlap_bss_scan_param->obss_scan_param, ele_len); + pevent->event_len = ele_len; + wlan_recv_event(pmpriv, MLAN_EVENT_ID_DRV_OBSS_SCAN_PARAM, + pevent); + } + + LEAVE(); +} + +/** + * @brief This function will process tx pause event + * + * @param priv A pointer to mlan_private + * @param pevent A pointer to event buf + * + * @return N/A + */ +static void +wlan_process_sta_tx_pause_event(pmlan_private priv, pmlan_buffer pevent) +{ + t_u16 tlv_type, tlv_len; + int tlv_buf_left = pevent->data_len - sizeof(t_u32); + MrvlIEtypesHeader_t *tlv = + (MrvlIEtypesHeader_t *)(pevent->pbuf + pevent->data_offset + + sizeof(t_u32)); + MrvlIEtypes_tx_pause_t *tx_pause_tlv; + sta_node *sta_ptr = MNULL; + tdlsStatus_e status; + t_u8 *bssid = MNULL; + ENTER(); + if (priv->media_connected) + bssid = priv->curr_bss_params.bss_descriptor.mac_address; + while (tlv_buf_left >= (int)sizeof(MrvlIEtypesHeader_t)) { + tlv_type = wlan_le16_to_cpu(tlv->type); + tlv_len = wlan_le16_to_cpu(tlv->len); + if ((sizeof(MrvlIEtypesHeader_t) + tlv_len) > + (unsigned int)tlv_buf_left) { + PRINTM(MERROR, "wrong tlv: tlvLen=%d, tlvBufLeft=%d\n", + tlv_len, tlv_buf_left); + break; + } + if (tlv_type == TLV_TYPE_TX_PAUSE) { + tx_pause_tlv = (MrvlIEtypes_tx_pause_t *)tlv; + PRINTM(MCMND, "TxPause: " MACSTR " pause=%d, pkts=%d\n", + MAC2STR(tx_pause_tlv->peermac), + tx_pause_tlv->tx_pause, tx_pause_tlv->pkt_cnt); + + if (bssid && + !memcmp(priv->adapter, bssid, tx_pause_tlv->peermac, + MLAN_MAC_ADDR_LENGTH)) { + if (tx_pause_tlv->tx_pause) + priv->tx_pause = MTRUE; + else + priv->tx_pause = MFALSE; + } + + else { + status = wlan_get_tdls_link_status(priv, + tx_pause_tlv-> + peermac); + if (MTRUE == wlan_is_tdls_link_setup(status)) { + sta_ptr = + wlan_get_station_entry(priv, + tx_pause_tlv-> + peermac); + if (sta_ptr) { + if (sta_ptr->tx_pause != + tx_pause_tlv->tx_pause) { + sta_ptr->tx_pause = + tx_pause_tlv-> + tx_pause; + wlan_update_ralist_tx_pause + (priv, + tx_pause_tlv-> + peermac, + tx_pause_tlv-> + tx_pause); + } + } + } + } + } + tlv_buf_left -= (sizeof(MrvlIEtypesHeader_t) + tlv_len); + tlv = (MrvlIEtypesHeader_t *)((t_u8 *)tlv + tlv_len + + sizeof(MrvlIEtypesHeader_t)); + } + + LEAVE(); + return; +} + +/** + * @brief This function handles events generated by firmware + * + * @param priv A pointer to mlan_private structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_ops_sta_process_event(IN t_void *priv) +{ + pmlan_private pmpriv = (pmlan_private)priv; + pmlan_adapter pmadapter = pmpriv->adapter; + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u32 eventcause = pmadapter->event_cause; + t_u8 event_buf[100]; + t_u8 *evt_buf = MNULL; + pmlan_buffer pmbuf = pmadapter->pmlan_buffer_event; + t_u16 reason_code; + t_u8 sta_addr[MLAN_MAC_ADDR_LENGTH]; + sta_node *sta_ptr = MNULL; + t_u8 i = 0; + pmlan_callbacks pcb = &pmadapter->callbacks; + mlan_event *pevent = (mlan_event *)event_buf; + + ENTER(); + + if (!pmbuf) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + /* Event length check */ + if ((pmbuf->data_len - sizeof(eventcause)) > MAX_EVENT_SIZE) { + pmbuf->status_code = MLAN_ERROR_PKT_SIZE_INVALID; + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + if (eventcause != EVENT_PS_SLEEP && eventcause != EVENT_PS_AWAKE && + pmbuf->data_len > sizeof(eventcause)) + DBG_HEXDUMP(MEVT_D, "EVENT", pmbuf->pbuf + pmbuf->data_offset, + pmbuf->data_len); + + switch (eventcause) { + case EVENT_DUMMY_HOST_WAKEUP_SIGNAL: + PRINTM(MERROR, + "Invalid EVENT: DUMMY_HOST_WAKEUP_SIGNAL, ignoring it\n"); + break; + case EVENT_LINK_SENSED: + PRINTM(MEVENT, "EVENT: LINK_SENSED\n"); + pmpriv->adhoc_is_link_sensed = MTRUE; + wlan_recv_event(pmpriv, MLAN_EVENT_ID_FW_ADHOC_LINK_SENSED, + MNULL); + break; + + case EVENT_DEAUTHENTICATED: + if (pmpriv->wps.session_enable) { + PRINTM(MMSG, + "wlan: Recevie deauth event in wps session\n"); + break; + } + reason_code = + wlan_le16_to_cpu(*(t_u16 *) + (pmbuf->pbuf + pmbuf->data_offset + + sizeof(eventcause))); + PRINTM(MMSG, "wlan: EVENT: Deauthenticated (reason 0x%x)\n", + reason_code); + pmadapter->dbg.num_event_deauth++; + wlan_handle_disconnect_event(pmpriv); + + break; + + case EVENT_DISASSOCIATED: + if (pmpriv->wps.session_enable) { + PRINTM(MMSG, + "wlan: Recevie disassociate event in wps session\n"); + break; + } + reason_code = + wlan_le16_to_cpu(*(t_u16 *) + (pmbuf->pbuf + pmbuf->data_offset + + sizeof(eventcause))); + PRINTM(MMSG, "wlan: EVENT: Disassociated (reason 0x%x)\n", + reason_code); + pmadapter->dbg.num_event_disassoc++; + wlan_handle_disconnect_event(pmpriv); + break; + + case EVENT_LINK_LOST: + reason_code = + wlan_le16_to_cpu(*(t_u16 *) + (pmbuf->pbuf + pmbuf->data_offset + + sizeof(eventcause))); + PRINTM(MMSG, "wlan: EVENT: Link lost (reason 0x%x)\n", + reason_code); + pmadapter->dbg.num_event_link_lost++; + wlan_handle_disconnect_event(pmpriv); + break; + + case EVENT_PS_SLEEP: + PRINTM(MINFO, "EVENT: SLEEP\n"); + PRINTM(MEVENT, "_"); + + /* Handle unexpected PS SLEEP event */ + if (pmadapter->ps_state == PS_STATE_SLEEP_CFM) + break; + pmadapter->ps_state = PS_STATE_PRE_SLEEP; + + wlan_check_ps_cond(pmadapter); + break; + + case EVENT_PS_AWAKE: + PRINTM(MINFO, "EVENT: AWAKE\n"); + PRINTM(MEVENT, "|"); + if (!pmadapter->pps_uapsd_mode && + pmpriv->media_connected && + (pmpriv->port_open || !pmpriv->port_ctrl_mode) && + pmadapter->sleep_period.period) { + pmadapter->pps_uapsd_mode = MTRUE; + PRINTM(MEVENT, "PPS/UAPSD mode activated\n"); + } + /* Handle unexpected PS AWAKE event */ + if (pmadapter->ps_state == PS_STATE_SLEEP_CFM) + break; + pmadapter->tx_lock_flag = MFALSE; + if (pmadapter->pps_uapsd_mode && pmadapter->gen_null_pkt) { + if (MTRUE == wlan_check_last_packet_indication(pmpriv)) { + if (!pmadapter->data_sent) { + if (wlan_send_null_packet(pmpriv, + MRVDRV_TxPD_POWER_MGMT_NULL_PACKET + | + MRVDRV_TxPD_POWER_MGMT_LAST_PACKET) + == MLAN_STATUS_SUCCESS) { + LEAVE(); + return MLAN_STATUS_SUCCESS; + } + } + } + } + pmadapter->ps_state = PS_STATE_AWAKE; + pmadapter->pm_wakeup_card_req = MFALSE; + pmadapter->pm_wakeup_fw_try = MFALSE; + break; + + case EVENT_HS_ACT_REQ: + PRINTM(MEVENT, "EVENT: HS_ACT_REQ\n"); + ret = wlan_prepare_cmd(priv, + HostCmd_CMD_802_11_HS_CFG_ENH, + 0, 0, MNULL, MNULL); + break; + + case EVENT_MIC_ERR_UNICAST: + PRINTM(MEVENT, "EVENT: UNICAST MIC ERROR\n"); + wlan_recv_event(pmpriv, MLAN_EVENT_ID_FW_MIC_ERR_UNI, MNULL); + break; + + case EVENT_MIC_ERR_MULTICAST: + PRINTM(MEVENT, "EVENT: MULTICAST MIC ERROR\n"); + wlan_recv_event(pmpriv, MLAN_EVENT_ID_FW_MIC_ERR_MUL, MNULL); + break; + case EVENT_MIB_CHANGED: + case EVENT_INIT_DONE: + break; + + case EVENT_ADHOC_BCN_LOST: + PRINTM(MEVENT, "EVENT: ADHOC_BCN_LOST\n"); + pmpriv->adhoc_is_link_sensed = MFALSE; + wlan_clean_txrx(pmpriv); + wlan_recv_event(pmpriv, MLAN_EVENT_ID_FW_ADHOC_LINK_LOST, + MNULL); + break; + + case EVENT_FW_DEBUG_INFO: + /* Allocate memory for event buffer */ + ret = pcb->moal_malloc(pmadapter->pmoal_handle, + MAX_EVENT_SIZE, MLAN_MEM_DEF, &evt_buf); + if ((ret == MLAN_STATUS_SUCCESS) && evt_buf) { + pevent = (pmlan_event)evt_buf; + pevent->bss_index = pmpriv->bss_index; + pevent->event_id = MLAN_EVENT_ID_FW_DEBUG_INFO; + pevent->event_len = + pmbuf->data_len - sizeof(eventcause); + memcpy(pmadapter, (t_u8 *)pevent->event_buf, + pmbuf->pbuf + pmbuf->data_offset + + sizeof(eventcause), pevent->event_len); + PRINTM(MEVENT, "EVENT: FW Debug Info %s\n", + (t_u8 *)pevent->event_buf); + wlan_recv_event(pmpriv, pevent->event_id, pevent); + pcb->moal_mfree(pmadapter->pmoal_handle, evt_buf); + } + break; + + case EVENT_BG_SCAN_REPORT: + PRINTM(MEVENT, "EVENT: BGS_REPORT\n"); + pmadapter->bgscan_reported = MTRUE; + wlan_recv_event(pmpriv, MLAN_EVENT_ID_FW_BG_SCAN, MNULL); + break; + case EVENT_BG_SCAN_STOPPED: + PRINTM(MEVENT, "EVENT: BGS_STOPPED\n"); + wlan_recv_event(pmpriv, MLAN_EVENT_ID_FW_BG_SCAN_STOPPED, + MNULL); + break; + + case EVENT_PORT_RELEASE: + PRINTM(MEVENT, "EVENT: PORT RELEASE\n"); + /* Open the port for e-supp mode */ + if (pmpriv->port_ctrl_mode == MTRUE) { + PRINTM(MINFO, "PORT_REL: port_status = OPEN\n"); + pmpriv->port_open = MTRUE; + } + pmadapter->scan_block = MFALSE; + wlan_recv_event(pmpriv, MLAN_EVENT_ID_FW_PORT_RELEASE, MNULL); + break; + + case EVENT_STOP_TX: + PRINTM(MEVENT, "EVENT: Stop Tx (%#x)\n", eventcause); + wlan_11h_tx_disable(pmpriv); /* this fn will send event up to MOAL */ + break; + case EVENT_START_TX: + PRINTM(MEVENT, "EVENT: Start Tx (%#x)\n", eventcause); + wlan_11h_tx_enable(pmpriv); /* this fn will send event up to MOAL */ + break; + case EVENT_CHANNEL_SWITCH: + PRINTM(MEVENT, "EVENT: Channel Switch (%#x)\n", eventcause); + if (pmadapter->ecsa_enable) { + MrvlIEtypes_channel_band_t *pchan_info = + (MrvlIEtypes_channel_band_t *)(pmadapter-> + event_body); + t_u8 channel = pchan_info->channel; + chan_freq_power_t *cfp = MNULL; + DBG_HEXDUMP(MCMD_D, "chan band config", + (t_u8 *)pchan_info, + sizeof(MrvlIEtypes_channel_band_t)); + PRINTM(MEVENT, "Switch to channel %d success!\n", + channel); +#define MAX_CHANNEL_BAND_B 14 + if (channel <= MAX_CHANNEL_BAND_B) + cfp = wlan_find_cfp_by_band_and_channel + (pmadapter, BAND_B, channel); + else + cfp = wlan_find_cfp_by_band_and_channel + (pmadapter, BAND_A, channel); + pmpriv->curr_bss_params.bss_descriptor.channel = + channel; + if (cfp) + pmpriv->curr_bss_params.bss_descriptor.freq = + cfp->freq; + else + pmpriv->curr_bss_params.bss_descriptor.freq = 0; + if (pmpriv->adapter->state_rdh.stage == + RDH_SET_CUSTOM_IE) { + pmadapter->state_rdh.stage = + RDH_RESTART_TRAFFIC; + wlan_11h_radar_detected_handling(pmadapter, + pmpriv); + } + pmadapter->state_rdh.tx_block = MFALSE; + } + break; + case EVENT_CHANNEL_SWITCH_ANN: + PRINTM(MEVENT, "EVENT: Channel Switch Announcement\n"); + /* Here, pass up event first, as handling will send deauth */ + wlan_recv_event(pmpriv, + MLAN_EVENT_ID_FW_CHANNEL_SWITCH_ANN, MNULL); + if (pmadapter->ecsa_enable) { + t_u8 stop_tx = *(t_u8 *)pmadapter->event_body; + if (stop_tx) + pmadapter->state_rdh.tx_block = MTRUE; + } else + wlan_11h_handle_event_chanswann(pmpriv); + break; + case EVENT_RADAR_DETECTED: + PRINTM(MEVENT, "EVENT: Radar Detected\n"); + + /* Send as passthru first, this event can cause other events */ + memset(pmadapter, pevent, 0x00, sizeof(event_buf)); + pevent->bss_index = pmpriv->bss_index; + pevent->event_id = MLAN_EVENT_ID_DRV_PASSTHRU; + pevent->event_len = pmbuf->data_len; + memcpy(pmadapter, (t_u8 *)pevent->event_buf, + pmbuf->pbuf + pmbuf->data_offset, pevent->event_len); + wlan_recv_event(pmpriv, pevent->event_id, pevent); + + if (pmadapter->state_rdh.stage == RDH_OFF) { + pmadapter->state_rdh.stage = RDH_CHK_INTFS; + wlan_11h_radar_detected_handling(pmadapter, pmpriv); + } else { + PRINTM(MEVENT, "Ignore Event Radar Detected - handling" + " already in progress.\n"); + } + + break; + + case EVENT_CHANNEL_REPORT_RDY: + PRINTM(MEVENT, "EVENT: Channel Report Ready\n"); + /* Allocate memory for event buffer */ + ret = pcb->moal_malloc(pmadapter->pmoal_handle, + MAX_EVENT_SIZE, MLAN_MEM_DEF, &evt_buf); + if ((ret == MLAN_STATUS_SUCCESS) && evt_buf) { + memset(pmadapter, evt_buf, 0x00, MAX_EVENT_SIZE); + /* Setup event buffer */ + pevent = (pmlan_event)evt_buf; + pevent->bss_index = pmpriv->bss_index; + pevent->event_id = MLAN_EVENT_ID_FW_CHANNEL_REPORT_RDY; + pevent->event_len = + pmbuf->data_len - sizeof(eventcause); + /* Copy event data */ + memcpy(pmadapter, (t_u8 *)pevent->event_buf, + pmbuf->pbuf + pmbuf->data_offset + + sizeof(eventcause), pevent->event_len); + /* Handle / pass event data */ + ret = wlan_11h_handle_event_chanrpt_ready(pmpriv, + pevent); + + /* Also send this event as passthru */ + pevent->event_id = MLAN_EVENT_ID_DRV_PASSTHRU; + pevent->event_len = pmbuf->data_len; + memcpy(pmadapter, (t_u8 *)pevent->event_buf, + pmbuf->pbuf + pmbuf->data_offset, + pevent->event_len); + wlan_recv_event(pmpriv, pevent->event_id, pevent); + /* Now done with buffer */ + pcb->moal_mfree(pmadapter->pmoal_handle, evt_buf); + } + /* Send up this Event to unblock MOAL waitqueue */ + wlan_recv_event(pmpriv, MLAN_EVENT_ID_DRV_MEAS_REPORT, MNULL); + break; + case EVENT_EXT_SCAN_REPORT: + PRINTM(MEVENT, "EVENT: EXT_SCAN Report (%d)\n", + pmbuf->data_len); + if (pmadapter->pscan_ioctl_req && pmadapter->ext_scan) + ret = wlan_handle_event_ext_scan_report(priv, pmbuf); + break; + case EVENT_EXT_SCAN_STATUS_REPORT: + PRINTM(MEVENT, "EVENT: EXT_SCAN status report (%d)\n", + pmbuf->data_len); + pmadapter->ext_scan_timeout = MFALSE; + ret = wlan_handle_event_ext_scan_status(priv, pmbuf); + break; + case EVENT_MEAS_REPORT_RDY: + PRINTM(MEVENT, "EVENT: Measurement Report Ready (%#x)\n", + eventcause); + ret = wlan_prepare_cmd(priv, HostCmd_CMD_MEASUREMENT_REPORT, + HostCmd_ACT_GEN_SET, 0, 0, MNULL); + break; + case EVENT_WMM_STATUS_CHANGE: + if (pmbuf && pmbuf->data_len + > sizeof(eventcause) + sizeof(MrvlIEtypesHeader_t)) { + PRINTM(MEVENT, "EVENT: WMM status changed: %d\n", + pmbuf->data_len); + + evt_buf = (pmbuf->pbuf + + pmbuf->data_offset + sizeof(eventcause)); + + wlan_ret_wmm_get_status(pmpriv, + evt_buf, + pmbuf->data_len - + sizeof(eventcause)); + } else { + PRINTM(MEVENT, "EVENT: WMM status changed\n"); + ret = wlan_cmd_wmm_status_change(pmpriv); + } + break; + + case EVENT_RSSI_LOW: + PRINTM(MEVENT, "EVENT: Beacon RSSI_LOW\n"); + /* Allocate memory for event buffer */ + ret = pcb->moal_malloc(pmadapter->pmoal_handle, MAX_EVENT_SIZE, + MLAN_MEM_DEF, &evt_buf); + if ((ret == MLAN_STATUS_SUCCESS) && evt_buf) { + memset(pmadapter, evt_buf, 0x00, MAX_EVENT_SIZE); + pevent = (pmlan_event)evt_buf; + pevent->bss_index = pmpriv->bss_index; + pevent->event_id = MLAN_EVENT_ID_FW_BCN_RSSI_LOW; + pevent->event_len = sizeof(t_u16); + /** Fw send bcnRssi low value in event reason field*/ + memcpy(pmadapter, (t_u8 *)pevent->event_buf, + (t_u8 *)&pmadapter->event_body, + pevent->event_len); + wlan_recv_event(pmpriv, pevent->event_id, pevent); + pcb->moal_mfree(pmadapter->pmoal_handle, evt_buf); + } + break; + case EVENT_SNR_LOW: + PRINTM(MEVENT, "EVENT: Beacon SNR_LOW\n"); + wlan_recv_event(pmpriv, MLAN_EVENT_ID_FW_BCN_SNR_LOW, MNULL); + break; + case EVENT_MAX_FAIL: + PRINTM(MEVENT, "EVENT: MAX_FAIL\n"); + wlan_recv_event(pmpriv, MLAN_EVENT_ID_FW_MAX_FAIL, MNULL); + break; + case EVENT_RSSI_HIGH: + PRINTM(MEVENT, "EVENT: Beacon RSSI_HIGH\n"); + /* Allocate memory for event buffer */ + ret = pcb->moal_malloc(pmadapter->pmoal_handle, MAX_EVENT_SIZE, + MLAN_MEM_DEF, &evt_buf); + if ((ret == MLAN_STATUS_SUCCESS) && evt_buf) { + memset(pmadapter, evt_buf, 0x00, MAX_EVENT_SIZE); + pevent = (pmlan_event)evt_buf; + pevent->bss_index = pmpriv->bss_index; + pevent->event_id = MLAN_EVENT_ID_FW_BCN_RSSI_HIGH; + pevent->event_len = sizeof(t_u16); + /** Fw send bcnRssi high value in event reason field*/ + memcpy(pmadapter, (t_u8 *)pevent->event_buf, + (t_u8 *)&pmadapter->event_body, + pevent->event_len); + wlan_recv_event(pmpriv, pevent->event_id, pevent); + pcb->moal_mfree(pmadapter->pmoal_handle, evt_buf); + } + break; + case EVENT_SNR_HIGH: + PRINTM(MEVENT, "EVENT: Beacon SNR_HIGH\n"); + wlan_recv_event(pmpriv, MLAN_EVENT_ID_FW_BCN_SNR_HIGH, MNULL); + break; + case EVENT_DATA_RSSI_LOW: + PRINTM(MEVENT, "EVENT: Data RSSI_LOW\n"); + wlan_recv_event(pmpriv, MLAN_EVENT_ID_FW_DATA_RSSI_LOW, MNULL); + break; + case EVENT_DATA_SNR_LOW: + PRINTM(MEVENT, "EVENT: Data SNR_LOW\n"); + wlan_recv_event(pmpriv, MLAN_EVENT_ID_FW_DATA_SNR_LOW, MNULL); + break; + case EVENT_DATA_RSSI_HIGH: + PRINTM(MEVENT, "EVENT: Data RSSI_HIGH\n"); + wlan_recv_event(pmpriv, MLAN_EVENT_ID_FW_DATA_RSSI_HIGH, MNULL); + break; + case EVENT_DATA_SNR_HIGH: + PRINTM(MEVENT, "EVENT: Data SNR_HIGH\n"); + wlan_recv_event(pmpriv, MLAN_EVENT_ID_FW_DATA_SNR_HIGH, MNULL); + break; + case EVENT_LINK_QUALITY: + PRINTM(MEVENT, "EVENT: Link Quality\n"); + wlan_recv_event(pmpriv, MLAN_EVENT_ID_FW_LINK_QUALITY, MNULL); + break; + case EVENT_PRE_BEACON_LOST: + PRINTM(MEVENT, "EVENT: Pre-Beacon Lost\n"); + wlan_recv_event(pmpriv, MLAN_EVENT_ID_FW_PRE_BCN_LOST, MNULL); + break; + case EVENT_IBSS_COALESCED: + PRINTM(MEVENT, "EVENT: IBSS_COALESCED\n"); + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_802_11_IBSS_COALESCING_STATUS, + HostCmd_ACT_GEN_GET, 0, MNULL, MNULL); + break; + case EVENT_ADDBA: + PRINTM(MEVENT, "EVENT: ADDBA Request\n"); + if (pmpriv->media_connected == MTRUE) + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_11N_ADDBA_RSP, + HostCmd_ACT_GEN_SET, 0, MNULL, + pmadapter->event_body); + else + PRINTM(MERROR, + "Ignore ADDBA Request event in disconnected state\n"); + break; + case EVENT_DELBA: + PRINTM(MEVENT, "EVENT: DELBA Request\n"); + if (pmpriv->media_connected == MTRUE) + wlan_11n_delete_bastream(pmpriv, pmadapter->event_body); + else + PRINTM(MERROR, + "Ignore DELBA Request event in disconnected state\n"); + break; + case EVENT_BA_STREAM_TIMEOUT: + PRINTM(MEVENT, "EVENT: BA Stream timeout\n"); + if (pmpriv->media_connected == MTRUE) + wlan_11n_ba_stream_timeout(pmpriv, + (HostCmd_DS_11N_BATIMEOUT *) + pmadapter->event_body); + else + PRINTM(MERROR, + "Ignore BA Stream timeout event in disconnected state\n"); + break; + case EVENT_RXBA_SYNC: + PRINTM(MEVENT, "EVENT: RXBA_SYNC\n"); + wlan_11n_rxba_sync_event(pmpriv, pmadapter->event_body, + pmbuf->data_len - sizeof(eventcause)); + break; + case EVENT_AMSDU_AGGR_CTRL: + PRINTM(MEVENT, "EVENT: AMSDU_AGGR_CTRL %d\n", + *(t_u16 *)pmadapter->event_body); + pmadapter->tx_buf_size = + MIN(pmadapter->curr_tx_buf_size, + wlan_le16_to_cpu(*(t_u16 *)pmadapter->event_body)); + PRINTM(MEVENT, "tx_buf_size %d\n", pmadapter->tx_buf_size); + break; + + case EVENT_WEP_ICV_ERR: + PRINTM(MEVENT, "EVENT: WEP ICV error\n"); + pevent->bss_index = pmpriv->bss_index; + pevent->event_id = MLAN_EVENT_ID_FW_WEP_ICV_ERR; + pevent->event_len = sizeof(Event_WEP_ICV_ERR); + memcpy(pmadapter, (t_u8 *)pevent->event_buf, + pmadapter->event_body, pevent->event_len); + wlan_recv_event(pmpriv, MLAN_EVENT_ID_FW_WEP_ICV_ERR, pevent); + break; + + case EVENT_BW_CHANGE: + PRINTM(MEVENT, "EVENT: BW Change\n"); + pevent->bss_index = pmpriv->bss_index; + pevent->event_id = MLAN_EVENT_ID_FW_BW_CHANGED; + pevent->event_len = sizeof(t_u8); + /* Copy event body from the event buffer */ + memcpy(pmadapter, (t_u8 *)pevent->event_buf, + pmadapter->event_body, pevent->event_len); + if (pmadapter->dfs_repeater) + wlan_dfs_rep_bw_change(pmadapter); + wlan_recv_event(pmpriv, MLAN_EVENT_ID_FW_BW_CHANGED, pevent); + break; + +#ifdef WIFI_DIRECT_SUPPORT + case EVENT_WIFIDIRECT_GENERIC_EVENT: + PRINTM(MEVENT, "EVENT: WIFIDIRECT event %d\n", eventcause); + /* Allocate memory for event buffer */ + ret = pcb->moal_malloc(pmadapter->pmoal_handle, MAX_EVENT_SIZE, + MLAN_MEM_DEF, &evt_buf); + if ((ret == MLAN_STATUS_SUCCESS) && evt_buf) { + pevent = (pmlan_event)evt_buf; + pevent->bss_index = pmpriv->bss_index; + pevent->event_id = MLAN_EVENT_ID_DRV_PASSTHRU; + pevent->event_len = pmbuf->data_len; + memcpy(pmadapter, (t_u8 *)pevent->event_buf, + pmbuf->pbuf + pmbuf->data_offset, + pevent->event_len); + wlan_recv_event(pmpriv, pevent->event_id, pevent); + pcb->moal_mfree(pmadapter->pmoal_handle, evt_buf); + } + break; + case EVENT_WIFIDIRECT_SERVICE_DISCOVERY: + PRINTM(MEVENT, "EVENT: WIFIDIRECT service discovery event %d\n", + eventcause); + /* Allocate large memory for service discovery */ + if (pmbuf->data_len < MAX_EVENT_SIZE) + ret = pcb->moal_malloc(pmadapter->pmoal_handle, + MAX_EVENT_SIZE, MLAN_MEM_DEF, + &evt_buf); + else + ret = pcb->moal_malloc(pmadapter->pmoal_handle, + MAX_EVENT_SIZE * 2, MLAN_MEM_DEF, + &evt_buf); + if ((ret == MLAN_STATUS_SUCCESS) && evt_buf) { + pevent = (pmlan_event)evt_buf; + pevent->bss_index = pmpriv->bss_index; + pevent->event_id = MLAN_EVENT_ID_DRV_PASSTHRU; + pevent->event_len = pmbuf->data_len; + memcpy(pmadapter, (t_u8 *)pevent->event_buf, + pmbuf->pbuf + pmbuf->data_offset, + pevent->event_len); + wlan_recv_event(pmpriv, pevent->event_id, pevent); + pcb->moal_mfree(pmadapter->pmoal_handle, evt_buf); + } + break; + case EVENT_REMAIN_ON_CHANNEL_EXPIRED: + PRINTM(MEVENT, "EVENT: REMAIN_ON_CHANNEL_EXPIRED reason=%d\n", + *(t_u16 *)pmadapter->event_body); + wlan_recv_event(pmpriv, MLAN_EVENT_ID_DRV_FLUSH_RX_WORK, MNULL); + wlan_recv_event(pmpriv, MLAN_EVENT_ID_FW_REMAIN_ON_CHAN_EXPIRED, + MNULL); + break; +#endif /* WIFI_DIRECT_SUPPORT */ + + case EVENT_TDLS_GENERIC_EVENT: + PRINTM(MEVENT, "EVENT: TDLS event %d\n", eventcause); + wlan_parse_tdls_event(pmpriv, pmbuf); + /* Allocate memory for event buffer */ + ret = pcb->moal_malloc(pmadapter->pmoal_handle, MAX_EVENT_SIZE, + MLAN_MEM_DEF, &evt_buf); + if ((ret == MLAN_STATUS_SUCCESS) && evt_buf) { + pevent = (pmlan_event)evt_buf; + pevent->bss_index = pmpriv->bss_index; + pevent->event_id = MLAN_EVENT_ID_DRV_PASSTHRU; + pevent->event_len = pmbuf->data_len; + memcpy(pmadapter, (t_u8 *)pevent->event_buf, + pmbuf->pbuf + pmbuf->data_offset, + pevent->event_len); + wlan_recv_event(pmpriv, pevent->event_id, pevent); + pcb->moal_mfree(pmadapter->pmoal_handle, evt_buf); + } + break; + + case EVENT_TX_DATA_PAUSE: + PRINTM(MEVENT, "EVENT: TX_DATA_PAUSE\n"); + wlan_process_sta_tx_pause_event(priv, pmbuf); + break; + + case EVENT_IBSS_STATION_CONNECT: + memcpy(pmadapter, sta_addr, pmadapter->event_body + 2, + MLAN_MAC_ADDR_LENGTH); + sta_ptr = wlan_add_station_entry(pmpriv, sta_addr); + if (sta_ptr) { + PRINTM(MMSG, + "wlan: EVENT: IBSS_STA_CONNECT " MACSTR "\n", + MAC2STR(sta_addr)); + if (pmpriv->adapter->adhoc_11n_enabled) { + wlan_check_sta_capability(pmpriv, pmbuf, + sta_ptr); + for (i = 0; i < MAX_NUM_TID; i++) { + if (sta_ptr->is_11n_enabled) + sta_ptr->ampdu_sta[i] = + pmpriv->ibss_ampdu[i]; + else + sta_ptr->ampdu_sta[i] = + BA_STREAM_NOT_ALLOWED; + } + memset(pmadapter, sta_ptr->rx_seq, 0xff, + sizeof(sta_ptr->rx_seq)); + } + } + break; + case EVENT_IBSS_STATION_DISCONNECT: + memcpy(pmadapter, sta_addr, pmadapter->event_body + 2, + MLAN_MAC_ADDR_LENGTH); + PRINTM(MMSG, "wlan: EVENT: IBSS_STA_DISCONNECT " MACSTR "\n", + MAC2STR(sta_addr)); + + if (pmpriv->adapter->adhoc_11n_enabled) { + wlan_cleanup_reorder_tbl(pmpriv, sta_addr); + wlan_11n_cleanup_txbastream_tbl(pmpriv, sta_addr); + } + wlan_wmm_delete_peer_ralist(pmpriv, sta_addr); + wlan_delete_station_entry(pmpriv, sta_addr); + break; + case EVENT_SAD_REPORT: + { +#ifdef DEBUG_LEVEL1 + t_u8 *pevt_dat = + pmbuf->pbuf + pmbuf->data_offset + + sizeof(t_u32); +#endif + PRINTM(MEVENT, + "EVENT: Antenna Diversity %d (%d, %d, %d, %d)\n", + eventcause, pevt_dat[0] + 1, pevt_dat[1] + 1, + pevt_dat[2], pevt_dat[3]); + } + break; + case EVENT_MULTI_CHAN_INFO: + PRINTM(MEVENT, "EVENT: MULTI_CHAN_INFO\n"); + wlan_handle_event_multi_chan_info(pmpriv, pmbuf); + break; + + case EVENT_FW_DUMP_INFO: + PRINTM(MEVENT, "EVENT: Dump FW info\n"); + /* Allocate memory for event buffer */ + ret = pcb->moal_malloc(pmadapter->pmoal_handle, MAX_EVENT_SIZE, + MLAN_MEM_DEF, &evt_buf); + if ((ret == MLAN_STATUS_SUCCESS) && evt_buf) { + pevent = (pmlan_event)evt_buf; + pevent->bss_index = pmpriv->bss_index; + pevent->event_id = MLAN_EVENT_ID_FW_DUMP_INFO; + pevent->event_len = pmbuf->data_len; + memcpy(pmadapter, (t_u8 *)pevent->event_buf, + pmbuf->pbuf + pmbuf->data_offset, + pevent->event_len); + wlan_recv_event(pmpriv, pevent->event_id, pevent); + pcb->moal_mfree(pmadapter->pmoal_handle, evt_buf); + } + break; + case EVENT_TX_STATUS_REPORT: + PRINTM(MINFO, "EVENT: TX_STATUS\n"); + pevent->bss_index = pmpriv->bss_index; + pevent->event_id = MLAN_EVENT_ID_FW_TX_STATUS; + pevent->event_len = pmbuf->data_len; + memcpy(pmadapter, (t_u8 *)pevent->event_buf, + pmbuf->pbuf + pmbuf->data_offset, MIN(pevent->event_len, + sizeof + (event_buf))); + wlan_recv_event(pmpriv, pevent->event_id, pevent); + break; + case EVENT_BT_COEX_WLAN_PARA_CHANGE: + PRINTM(MEVENT, "EVENT: BT coex wlan param update\n"); + wlan_bt_coex_wlan_param_update_event(pmpriv, pmbuf); + break; + case EVENT_NLIST_REPORT: + PRINTM(MEVENT, "EVENT: 11K NLIST REPORT: len=%d\n", + pmbuf->data_len); + ret = pcb->moal_malloc(pmadapter->pmoal_handle, MAX_EVENT_SIZE, + MLAN_MEM_DEF, &evt_buf); + if ((ret == MLAN_STATUS_SUCCESS) && evt_buf) { + pevent = (pmlan_event)evt_buf; + pevent->bss_index = pmpriv->bss_index; + pevent->event_id = MLAN_EVENT_ID_DRV_PASSTHRU; + pevent->event_len = pmbuf->data_len; + memcpy(pmadapter, (t_u8 *)pevent->event_buf, + pmbuf->pbuf + pmbuf->data_offset, + pevent->event_len); + wlan_recv_event(pmpriv, pevent->event_id, pevent); + pcb->moal_mfree(pmadapter->pmoal_handle, evt_buf); + } + break; + case EVENT_NAN_GENERIC: + PRINTM(MEVENT, "EVENT: NAN_GENERIC_EVENT\n"); + wlan_process_nan_event(pmpriv, pmbuf); + break; + + case EVENT_MANAGEMENT_FRAME_WAKEUP: + PRINTM(MEVENT, "EVENT: EVENT_MANAGEMENT_FRAME_WAKEUP HOST\n"); + break; + case EVENT_ROAM_OFFLOAD: + memcpy(pmadapter, + pmpriv->curr_bss_params.bss_descriptor.mac_address, + (t_u8 *)(pmadapter->event_body + 2), + MLAN_MAC_ADDR_LENGTH); + /*Update the BSS for inform kernel, otherwise kernel will give warning for not find BSS */ + memcpy(pmadapter, (t_u8 *)&pmadapter->pscan_table[0], + (t_u8 *)&pmpriv->curr_bss_params.bss_descriptor, + sizeof(BSSDescriptor_t)); + if (!pmadapter->num_in_scan_table) + pmadapter->num_in_scan_table = 1; + PRINTM(MEVENT, "EVENT: ROAM OFFLOAD IN FW SUCCESS\n"); + /* Allocate memory for event buffer */ + ret = pcb->moal_malloc(pmadapter->pmoal_handle, MAX_EVENT_SIZE, + MLAN_MEM_DEF, &evt_buf); + if ((ret == MLAN_STATUS_SUCCESS) && evt_buf) { + pevent = (pmlan_event)evt_buf; + pevent->bss_index = pmpriv->bss_index; + pevent->event_id = MLAN_EVENT_ID_FW_ROAM_OFFLOAD_RESULT; + /** Drop event id length and 2 bytes reverved length*/ + pevent->event_len = + pmbuf->data_len - sizeof(eventcause) - 2; + memcpy(pmadapter, (t_u8 *)pevent->event_buf, + pmadapter->event_body + 2, MIN(pevent->event_len, + MAX_EVENT_SIZE)); + wlan_recv_event(pmpriv, pevent->event_id, pevent); + pcb->moal_mfree(pmadapter->pmoal_handle, evt_buf); + } + break; + case EVENT_WLS_FTM_COMPLETE: + PRINTM(MEVENT, "EVENT: FTM_GENERIC_EVENT\n"); + /* Allocate memory for event buffer */ + ret = pcb->moal_malloc(pmadapter->pmoal_handle, MAX_EVENT_SIZE, + MLAN_MEM_DEF, &evt_buf); + if ((ret == MLAN_STATUS_SUCCESS) && evt_buf) { + pevent = (pmlan_event)evt_buf; + pevent->bss_index = pmpriv->bss_index; + pevent->event_id = MLAN_EVENT_ID_DRV_PASSTHRU; + pevent->event_len = pmbuf->data_len; + memcpy(pmadapter, (t_u8 *)pevent->event_buf, + pmbuf->pbuf + pmbuf->data_offset, + pevent->event_len); + wlan_recv_event(pmpriv, pevent->event_id, pevent); + pcb->moal_mfree(pmadapter->pmoal_handle, evt_buf); + } + break; + default: + PRINTM(MEVENT, "EVENT: unknown event id: %#x\n", eventcause); + wlan_recv_event(pmpriv, MLAN_EVENT_ID_FW_UNKNOWN, MNULL); + break; + } + + LEAVE(); + return ret; +} diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_sta_ioctl.c b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_sta_ioctl.c new file mode 100644 index 000000000000..0a65ecc54a36 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_sta_ioctl.c @@ -0,0 +1,6090 @@ +/** @file mlan_sta_ioctl.c + * + * @brief This file contains the functions for station ioctl. + * + * Copyright (C) 2008-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 10/21/2008: initial version +******************************************************/ + +#include "mlan.h" +#include "mlan_join.h" +#include "mlan_util.h" +#include "mlan_fw.h" +#include "mlan_main.h" +#include "mlan_wmm.h" +#include "mlan_11n.h" +#include "mlan_sdio.h" +#include "mlan_11h.h" +#ifdef DRV_EMBEDDED_SUPPLICANT +#include "authenticator_api.h" +#endif + +/******************************************************** + Local Variables +********************************************************/ + +/******************************************************** + Global Variables +********************************************************/ + +/******************************************************** + Local Functions +********************************************************/ +/** + * @brief enable adhoc aes key + * + * @param pmpriv A pointer to mlan_private structure + * + * @return N/A + */ +static void +wlan_enable_aes_key(pmlan_private pmpriv) +{ + + ENTER(); + + if (pmpriv->aes_key.key_len != WPA_AES_KEY_LEN) { + LEAVE(); + return; + } + wlan_prepare_cmd(pmpriv, HostCmd_CMD_802_11_KEY_MATERIAL, + HostCmd_ACT_GEN_SET, 0, MNULL, &pmpriv->aes_key); + + LEAVE(); + return; +} + +/** + * @brief Get signal information + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status +wlan_get_info_signal(IN pmlan_adapter pmadapter, IN pmlan_ioctl_req pioctl_req) +{ + pmlan_private pmpriv = MNULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (pioctl_req != MNULL) { + pmpriv = pmadapter->priv[pioctl_req->bss_index]; + } else { + PRINTM(MERROR, "MLAN IOCTL information is not present\n"); + ret = MLAN_STATUS_FAILURE; + goto exit; + } + + /* Check information buffer length of MLAN IOCTL */ + if (pioctl_req->buf_len < sizeof(mlan_ds_get_signal)) { + PRINTM(MWARN, + "MLAN IOCTL information buffer length is too short.\n"); + pioctl_req->data_read_written = 0; + pioctl_req->buf_len_needed = sizeof(mlan_ds_get_signal); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_RESOURCE; + goto exit; + } + + /* Signal info can be obtained only if connected */ + if (pmpriv->media_connected == MFALSE) { + PRINTM(MINFO, "Can not get signal in disconnected state\n"); + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + ret = MLAN_STATUS_FAILURE; + goto exit; + } + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_RSSI_INFO, + HostCmd_ACT_GEN_GET, + 0, (t_void *)pioctl_req, MNULL); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + +exit: + LEAVE(); + return ret; +} + +/** + * @brief Get signal information + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status +wlan_get_info_signal_ext(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + pmlan_private pmpriv = MNULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_get_info *info = MNULL; + + ENTER(); + + if (pioctl_req != MNULL) { + pmpriv = pmadapter->priv[pioctl_req->bss_index]; + } else { + PRINTM(MERROR, "MLAN IOCTL information is not present\n"); + ret = MLAN_STATUS_FAILURE; + goto exit; + } + info = (mlan_ds_get_info *)pioctl_req->pbuf; + + /* Check information buffer length of MLAN IOCTL */ + if (pioctl_req->buf_len < sizeof(mlan_ds_get_info)) { + PRINTM(MWARN, + "MLAN IOCTL information buffer length is too short.\n"); + pioctl_req->data_read_written = 0; + pioctl_req->buf_len_needed = sizeof(mlan_ds_get_info); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_RESOURCE; + goto exit; + } + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_RSSI_INFO_EXT, + HostCmd_ACT_GEN_GET, + 0, (t_void *)pioctl_req, (t_void *)info); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + +exit: + LEAVE(); + return ret; +} + +/** + * @brief Get statistics information + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status +wlan_get_info_stats(IN pmlan_adapter pmadapter, IN pmlan_ioctl_req pioctl_req) +{ + pmlan_private pmpriv = MNULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (pioctl_req != MNULL) { + pmpriv = pmadapter->priv[pioctl_req->bss_index]; + } else { + PRINTM(MERROR, "MLAN IOCTL information is not present\n"); + ret = MLAN_STATUS_FAILURE; + goto exit; + } + + /* Check information buffer length of MLAN IOCTL */ + if (pioctl_req->buf_len < sizeof(mlan_ds_get_stats)) { + PRINTM(MWARN, + "MLAN IOCTL information buffer length is too short.\n"); + pioctl_req->data_read_written = 0; + pioctl_req->buf_len_needed = sizeof(mlan_ds_get_stats); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_RESOURCE; + goto exit; + } + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_802_11_GET_LOG, + HostCmd_ACT_GEN_GET, + 0, (t_void *)pioctl_req, MNULL); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + +exit: + LEAVE(); + return ret; +} + +/** + * @brief Get sta channel information + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status +wlan_bss_ioctl_get_chan_info(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + pmlan_private pmpriv = MNULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (pioctl_req != MNULL) { + pmpriv = pmadapter->priv[pioctl_req->bss_index]; + } else { + PRINTM(MERROR, "MLAN IOCTL information is not present\n"); + ret = MLAN_STATUS_FAILURE; + goto exit; + } + + /* Check information buffer length of MLAN IOCTL */ + if (pioctl_req->buf_len < sizeof(chan_band_info)) { + PRINTM(MWARN, + "MLAN IOCTL information buffer length is too short.\n"); + pioctl_req->data_read_written = 0; + pioctl_req->buf_len_needed = sizeof(chan_band_info); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_RESOURCE; + goto exit; + } + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_STA_CONFIGURE, + HostCmd_ACT_GEN_GET, + 0, (t_void *)pioctl_req, MNULL); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + +exit: + LEAVE(); + return ret; +} + +/** + * @brief Get BSS information + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success + */ +static mlan_status +wlan_get_info_bss_info(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + pmlan_private pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_get_info *info; + BSSDescriptor_t *pbss_desc; + t_s32 tbl_idx = 0; + + ENTER(); + + /* Get current BSS info */ + pbss_desc = &pmpriv->curr_bss_params.bss_descriptor; + info = (mlan_ds_get_info *)pioctl_req->pbuf; + + /* BSS mode */ + info->param.bss_info.bss_mode = pmpriv->bss_mode; + + /* SSID */ + memcpy(pmadapter, &info->param.bss_info.ssid, &pbss_desc->ssid, + sizeof(mlan_802_11_ssid)); + + /* BSSID */ + memcpy(pmadapter, &info->param.bss_info.bssid, &pbss_desc->mac_address, + MLAN_MAC_ADDR_LENGTH); + + /* Channel */ + info->param.bss_info.bss_chan = pbss_desc->channel; + + /* Beacon interval */ + info->param.bss_info.beacon_interval = pbss_desc->beacon_period; + + /* Band */ + info->param.bss_info.bss_band = (t_u8)pbss_desc->bss_band; + + /* Region code */ + info->param.bss_info.region_code = pmadapter->region_code; + + /* Scan table index if connected */ + info->param.bss_info.scan_table_idx = 0; + info->param.bss_info.scan_block = pmadapter->scan_block; + if (pmpriv->media_connected == MTRUE) { + tbl_idx = + wlan_find_ssid_in_list(pmpriv, &pbss_desc->ssid, + pbss_desc->mac_address, + pmpriv->bss_mode); + if (tbl_idx >= 0) + info->param.bss_info.scan_table_idx = tbl_idx; + } + + /* Connection status */ + info->param.bss_info.media_connected = pmpriv->media_connected; + + /* Radio status */ + info->param.bss_info.radio_on = pmadapter->radio_on; + + /* Tx power information */ + info->param.bss_info.max_power_level = pmpriv->max_tx_power_level; + info->param.bss_info.min_power_level = pmpriv->min_tx_power_level; + + /* AdHoc state */ + info->param.bss_info.adhoc_state = pmpriv->adhoc_state; + + /* Last beacon NF */ + info->param.bss_info.bcn_nf_last = pmpriv->bcn_nf_last; + + /* wep status */ + if (pmpriv->sec_info.wep_status == Wlan802_11WEPEnabled) + info->param.bss_info.wep_status = MTRUE; + else + info->param.bss_info.wep_status = MFALSE; + + info->param.bss_info.is_hs_configured = pmadapter->is_hs_configured; + info->param.bss_info.is_deep_sleep = pmadapter->is_deep_sleep; + + /* Capability Info */ + info->param.bss_info.capability_info = 0; + memcpy(pmadapter, &info->param.bss_info.capability_info, + &pbss_desc->cap_info, + sizeof(info->param.bss_info.capability_info)); + + memset(pmadapter, &info->param.bss_info.ext_cap, 0, sizeof(ExtCap_t)); + if (pbss_desc->pext_cap) { + memcpy(pmadapter, &info->param.bss_info.ext_cap, + (t_u8 *)pbss_desc->pext_cap + sizeof(IEEEtypes_Header_t), + pbss_desc->pext_cap->ieee_hdr.len); + } + + /* Listen Interval */ + info->param.bss_info.listen_interval = pmpriv->listen_interval; + + /* Association ID */ + if (pmpriv->assoc_rsp_buf) + info->param.bss_info.assoc_id = + (t_u16)((IEEEtypes_AssocRsp_t *)pmpriv->assoc_rsp_buf)-> + a_id; + else + info->param.bss_info.assoc_id = 0; + + /* AP/Peer supported rates */ + memset(pmadapter, info->param.bss_info.peer_supp_rates, 0, + sizeof(info->param.bss_info.peer_supp_rates)); + memcpy(pmadapter, info->param.bss_info.peer_supp_rates, + pbss_desc->supported_rates, + MIN(sizeof(info->param.bss_info.peer_supp_rates), + sizeof(pbss_desc->supported_rates))); + if (pbss_desc->pmd_ie) { + info->param.bss_info.mdid = pbss_desc->pmd_ie->mdid; + info->param.bss_info.ft_cap = pbss_desc->pmd_ie->ft_cap; + } + pioctl_req->data_read_written = + sizeof(mlan_bss_info) + MLAN_SUB_COMMAND_SIZE; + + LEAVE(); + return ret; +} + +/** + * @brief Get information handler + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +static mlan_status +wlan_get_info_ioctl(IN pmlan_adapter pmadapter, IN pmlan_ioctl_req pioctl_req) +{ + mlan_status status = MLAN_STATUS_SUCCESS; + mlan_ds_get_info *pget_info = MNULL; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + + ENTER(); + + pget_info = (mlan_ds_get_info *)pioctl_req->pbuf; + + switch (pget_info->sub_command) { + case MLAN_OID_GET_STATS: + status = wlan_get_info_stats(pmadapter, pioctl_req); + break; + case MLAN_OID_GET_SIGNAL: + status = wlan_get_info_signal(pmadapter, pioctl_req); + break; + case MLAN_OID_GET_SIGNAL_EXT: + status = wlan_get_info_signal_ext(pmadapter, pioctl_req); + break; + case MLAN_OID_GET_FW_INFO: + pioctl_req->data_read_written = + sizeof(mlan_fw_info) + MLAN_SUB_COMMAND_SIZE; + pget_info->param.fw_info.fw_ver = pmadapter->fw_release_number; + memcpy(pmadapter, &pget_info->param.fw_info.mac_addr, + pmpriv->curr_addr, MLAN_MAC_ADDR_LENGTH); + pget_info->param.fw_info.fw_bands = pmadapter->fw_bands; + pget_info->param.fw_info.region_code = pmadapter->region_code; + pget_info->param.fw_info.ecsa_enable = pmadapter->ecsa_enable; + pget_info->param.fw_info.getlog_enable = + pmadapter->getlog_enable; + pget_info->param.fw_info.hw_dev_mcs_support = + pmadapter->hw_dev_mcs_support; + pget_info->param.fw_info.hw_dot_11n_dev_cap = + pmadapter->hw_dot_11n_dev_cap; + pget_info->param.fw_info.usr_dev_mcs_support = + pmpriv->usr_dev_mcs_support; + pget_info->param.fw_info.fw_supplicant_support = + IS_FW_SUPPORT_SUPPLICANT(pmadapter) ? 0x01 : 0x00; + pget_info->param.fw_info.antinfo = pmadapter->antinfo; + pget_info->param.fw_info.max_ap_assoc_sta = + pmadapter->max_sta_conn; + pget_info->param.fw_info.fw_roaming_support = + (pmadapter-> + fw_cap_info & FW_ROAMING_SUPPORT) ? 0x01 : 0x00; + break; + case MLAN_OID_GET_BSS_INFO: + status = wlan_get_info_bss_info(pmadapter, pioctl_req); + break; + case MLAN_OID_GET_DEBUG_INFO: + status = wlan_get_info_debug_info(pmadapter, pioctl_req); + break; + case MLAN_OID_GET_VER_EXT: + status = wlan_get_info_ver_ext(pmadapter, pioctl_req); + break; + default: + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + status = MLAN_STATUS_FAILURE; + break; + } + + LEAVE(); + return status; +} + +/** + * @brief Set/Get SNMP MIB handler + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status +wlan_snmp_mib_ioctl(IN pmlan_adapter pmadapter, IN pmlan_ioctl_req pioctl_req) +{ + pmlan_private pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u16 cmd_action = 0; + t_u16 cmd_oid = 0; + mlan_ds_snmp_mib *mib = MNULL; + t_u32 value = 0; + + ENTER(); + + if (pioctl_req->buf_len < sizeof(mlan_ds_snmp_mib)) { + PRINTM(MWARN, + "MLAN IOCTL information buffer length is too short.\n"); + pioctl_req->data_read_written = 0; + pioctl_req->buf_len_needed = sizeof(mlan_ds_snmp_mib); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_RESOURCE; + goto exit; + } + + mib = (mlan_ds_snmp_mib *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_SET) + cmd_action = HostCmd_ACT_GEN_SET; + else + cmd_action = HostCmd_ACT_GEN_GET; + + switch (mib->sub_command) { + case MLAN_OID_SNMP_MIB_RTS_THRESHOLD: + value = mib->param.rts_threshold; + cmd_oid = RtsThresh_i; + break; + case MLAN_OID_SNMP_MIB_FRAG_THRESHOLD: + value = mib->param.frag_threshold; + cmd_oid = FragThresh_i; + break; + case MLAN_OID_SNMP_MIB_RETRY_COUNT: + value = mib->param.retry_count; + cmd_oid = ShortRetryLim_i; + break; + case MLAN_OID_SNMP_MIB_DTIM_PERIOD: + value = mib->param.dtim_period; + cmd_oid = DtimPeriod_i; + break; + case MLAN_OID_SNMP_MIB_SIGNALEXT_ENABLE: + value = mib->param.signalext_enable; + cmd_oid = SignalextEnable_i; + break; + } + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_802_11_SNMP_MIB, + cmd_action, + cmd_oid, (t_void *)pioctl_req, &value); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + +exit: + LEAVE(); + return ret; +} + +/** + * @brief Set/Get Infra/Ad-hoc band configuration + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +static mlan_status +wlan_radio_ioctl_band_cfg(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + t_u8 i, global_band = 0; + t_u8 chan_offset; + t_u8 infra_band = 0; + t_u8 adhoc_band = 0; + t_u32 adhoc_channel = 0; + mlan_ds_radio_cfg *radio_cfg = MNULL; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + + ENTER(); + + radio_cfg = (mlan_ds_radio_cfg *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_SET) { + infra_band = (t_u8)radio_cfg->param.band_cfg.config_bands; + adhoc_band = (t_u8)radio_cfg->param.band_cfg.adhoc_start_band; + adhoc_channel = radio_cfg->param.band_cfg.adhoc_channel; + + /* SET Infra band */ + if ((infra_band | pmadapter->fw_bands) & ~pmadapter->fw_bands) { + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + /* SET Ad-hoc Band */ + if ((adhoc_band | pmadapter->fw_bands) & ~pmadapter->fw_bands) { + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + LEAVE(); + return MLAN_STATUS_FAILURE; + } + if (!adhoc_band) + adhoc_band = pmadapter->adhoc_start_band; + + for (i = 0; i < pmadapter->priv_num; i++) { + if (pmadapter->priv[i] && pmadapter->priv[i] != pmpriv + && GET_BSS_ROLE(pmadapter->priv[i]) == + MLAN_BSS_ROLE_STA) + global_band |= pmadapter->priv[i]->config_bands; + } + global_band |= infra_band; + + if (wlan_set_regiontable + (pmpriv, (t_u8)pmadapter->region_code, + global_band | adhoc_band)) { + pioctl_req->status_code = MLAN_ERROR_IOCTL_FAIL; + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + if (wlan_11d_set_universaltable + (pmpriv, global_band | adhoc_band)) { + pioctl_req->status_code = MLAN_ERROR_IOCTL_FAIL; + LEAVE(); + return MLAN_STATUS_FAILURE; + } + pmpriv->config_bands = infra_band; + pmadapter->config_bands = global_band; + + pmadapter->adhoc_start_band = adhoc_band; + pmpriv->intf_state_11h.adhoc_auto_sel_chan = MFALSE; + pmadapter->chan_bandwidth = + (t_u8)radio_cfg->param.band_cfg.adhoc_chan_bandwidth; + /* + * If no adhoc_channel is supplied verify if the existing + * adhoc channel compiles with new adhoc_band + */ + if (!adhoc_channel) { + if (!wlan_find_cfp_by_band_and_channel + (pmadapter, pmadapter->adhoc_start_band, + pmpriv->adhoc_channel)) { + /* Pass back the default channel */ + radio_cfg->param.band_cfg.adhoc_channel = + DEFAULT_AD_HOC_CHANNEL; + if ((pmadapter->adhoc_start_band & BAND_A) + || (pmadapter->adhoc_start_band & BAND_AN) + ) { + radio_cfg->param.band_cfg. + adhoc_channel = + DEFAULT_AD_HOC_CHANNEL_A; + } + } + } else { + /* Return error if adhoc_band and adhoc_channel + * combination is invalid + */ + if (!wlan_find_cfp_by_band_and_channel + (pmadapter, pmadapter->adhoc_start_band, + (t_u16)adhoc_channel)) { + pioctl_req->status_code = + MLAN_ERROR_INVALID_PARAMETER; + LEAVE(); + return MLAN_STATUS_FAILURE; + } + pmpriv->adhoc_channel = (t_u8)adhoc_channel; + } + + /* validate the channel offset */ + chan_offset = wlan_validate_chan_offset(pmpriv, + pmadapter-> + adhoc_start_band, + pmpriv->adhoc_channel, + pmadapter-> + chan_bandwidth); + + if (chan_offset != SEC_CHAN_NONE) { + if (chan_offset == SEC_CHAN_ABOVE) + pmadapter->chan_bandwidth = + CHANNEL_BW_40MHZ_ABOVE; + else + pmadapter->chan_bandwidth = + CHANNEL_BW_40MHZ_BELOW; + } + if ((adhoc_band & BAND_GN) + || (adhoc_band & BAND_AN) + ) { + pmadapter->adhoc_11n_enabled = MTRUE; + } else { + pmadapter->adhoc_11n_enabled = MFALSE; + } + } else { + /* Infra Bands */ + radio_cfg->param.band_cfg.config_bands = pmpriv->config_bands; + /* Adhoc Band */ + radio_cfg->param.band_cfg.adhoc_start_band = + pmadapter->adhoc_start_band; + /* Adhoc Channel */ + radio_cfg->param.band_cfg.adhoc_channel = pmpriv->adhoc_channel; + /* FW support Bands */ + radio_cfg->param.band_cfg.fw_bands = pmadapter->fw_bands; + PRINTM(MINFO, "Global config band = %d\n", + pmadapter->config_bands); + /* adhoc channel bandwidth */ + radio_cfg->param.band_cfg.adhoc_chan_bandwidth = + pmadapter->chan_bandwidth; + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Radio command handler + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +static mlan_status +wlan_radio_ioctl(IN pmlan_adapter pmadapter, IN pmlan_ioctl_req pioctl_req) +{ + mlan_status status = MLAN_STATUS_SUCCESS; + mlan_ds_radio_cfg *radio_cfg = MNULL; + + ENTER(); + + if (pioctl_req->buf_len < sizeof(mlan_ds_radio_cfg)) { + PRINTM(MWARN, + "MLAN IOCTL information buffer length is too short.\n"); + pioctl_req->data_read_written = 0; + pioctl_req->buf_len_needed = sizeof(mlan_ds_radio_cfg); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + LEAVE(); + return MLAN_STATUS_RESOURCE; + } + radio_cfg = (mlan_ds_radio_cfg *)pioctl_req->pbuf; + switch (radio_cfg->sub_command) { + case MLAN_OID_RADIO_CTRL: + status = wlan_radio_ioctl_radio_ctl(pmadapter, pioctl_req); + break; + case MLAN_OID_BAND_CFG: + status = wlan_radio_ioctl_band_cfg(pmadapter, pioctl_req); + break; + case MLAN_OID_ANT_CFG: + status = wlan_radio_ioctl_ant_cfg(pmadapter, pioctl_req); + break; + case MLAN_OID_REMAIN_CHAN_CFG: + status = wlan_radio_ioctl_remain_chan_cfg(pmadapter, + pioctl_req); + break; + default: + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + status = MLAN_STATUS_FAILURE; + break; + } + + LEAVE(); + return status; +} + +/** + * @brief Set/Get MAC address + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status +wlan_bss_ioctl_mac_address(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_bss *bss = MNULL; + t_u16 cmd_action; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + bss = (mlan_ds_bss *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_GET) { + cmd_action = HostCmd_ACT_GEN_GET; + } else { + cmd_action = HostCmd_ACT_GEN_SET; + memcpy(pmadapter, pmpriv->curr_addr, &bss->param.mac_addr, + MLAN_MAC_ADDR_LENGTH); + } + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_802_11_MAC_ADDRESS, + cmd_action, 0, (t_void *)pioctl_req, MNULL); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Set multicast list + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status +wlan_bss_ioctl_set_multicast_list(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_bss *bss = MNULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u16 old_pkt_filter; + + ENTER(); + + old_pkt_filter = pmpriv->curr_pkt_filter; + bss = (mlan_ds_bss *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_GET) { + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + ret = MLAN_STATUS_FAILURE; + goto exit; + } + pioctl_req->data_read_written = + sizeof(mlan_multicast_list) + MLAN_SUB_COMMAND_SIZE; + if (bss->param.multicast_list.mode == MLAN_PROMISC_MODE) { + PRINTM(MINFO, "Enable Promiscuous mode\n"); + pmpriv->curr_pkt_filter |= HostCmd_ACT_MAC_PROMISCUOUS_ENABLE; + pmpriv->curr_pkt_filter &= + ~HostCmd_ACT_MAC_ALL_MULTICAST_ENABLE; + } else { + /* Multicast */ + pmpriv->curr_pkt_filter &= ~HostCmd_ACT_MAC_PROMISCUOUS_ENABLE; + if (bss->param.multicast_list.mode == MLAN_ALL_MULTI_MODE) { + PRINTM(MINFO, "Enabling All Multicast!\n"); + pmpriv->curr_pkt_filter |= + HostCmd_ACT_MAC_ALL_MULTICAST_ENABLE; + } else { + pmpriv->curr_pkt_filter &= + ~HostCmd_ACT_MAC_ALL_MULTICAST_ENABLE; + if (bss->param.multicast_list.num_multicast_addr) { + PRINTM(MINFO, "Set multicast list=%d\n", + bss->param.multicast_list. + num_multicast_addr); + /* Set multicast addresses to firmware */ + if (old_pkt_filter == pmpriv->curr_pkt_filter) { + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_MAC_MULTICAST_ADR, + HostCmd_ACT_GEN_SET, + 0, + (t_void *) + pioctl_req, + &bss->param. + multicast_list); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + } else { + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_MAC_MULTICAST_ADR, + HostCmd_ACT_GEN_SET, + 0, MNULL, + &bss->param. + multicast_list); + } + if (ret) + goto exit; + } + } + } + PRINTM(MINFO, "old_pkt_filter=0x%x, curr_pkt_filter=0x%x\n", + old_pkt_filter, pmpriv->curr_pkt_filter); + if (old_pkt_filter != pmpriv->curr_pkt_filter) { + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_MAC_CONTROL, + HostCmd_ACT_GEN_SET, + 0, + (t_void *)pioctl_req, + &pmpriv->curr_pkt_filter); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + } + +exit: + LEAVE(); + return ret; +} + +/** + * @brief Get channel list + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +static mlan_status +wlan_bss_ioctl_get_channel_list(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_bss *bss = MNULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + chan_freq_power_t *cfp; + t_u32 i, j; + + ENTER(); + + bss = (mlan_ds_bss *)pioctl_req->pbuf; + if (pioctl_req->action != MLAN_ACT_GET) { + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + LEAVE(); + return MLAN_STATUS_FAILURE; + } + if ((wlan_11d_is_enabled(pmpriv) && + pmpriv->media_connected == MTRUE) && + ((pmpriv->bss_mode == MLAN_BSS_MODE_INFRA) || + (pmpriv->bss_mode == MLAN_BSS_MODE_IBSS && + pmpriv->adhoc_state != ADHOC_STARTED)) + ) { + t_u8 chan_no; + t_u8 band; + + parsed_region_chan_11d_t *parsed_region_chan = MNULL; + parsed_region_chan_11d_t region_chan; + + BSSDescriptor_t *pbss_desc = + &pmpriv->curr_bss_params.bss_descriptor; + + memset(pmadapter, ®ion_chan, 0, + sizeof(parsed_region_chan_11d_t)); + + /*If country IE is present in the associated AP then return the channel list from country IE + else return it from the learning table */ + + if (wlan_11d_parse_domain_info + (pmadapter, &pbss_desc->country_info, + (t_u8)pbss_desc->bss_band, + ®ion_chan) == MLAN_STATUS_SUCCESS) { + + parsed_region_chan = ®ion_chan; + } else { + parsed_region_chan = &pmadapter->parsed_region_chan; + } + + PRINTM(MINFO, "no_of_chan=%d\n", + parsed_region_chan->no_of_chan); + + for (i = 0; + (bss->param.chanlist.num_of_chan < MLAN_MAX_CHANNEL_NUM) + && (i < parsed_region_chan->no_of_chan); i++) { + chan_no = parsed_region_chan->chan_pwr[i].chan; + band = parsed_region_chan->chan_pwr[i].band; + PRINTM(MINFO, "band=%d, chan_no=%d\n", band, chan_no); + bss->param.chanlist.cf[bss->param.chanlist.num_of_chan]. + channel = (t_u32)chan_no; + bss->param.chanlist.cf[bss->param.chanlist.num_of_chan]. + freq = + (t_u32)wlan_11d_chan_2_freq(pmadapter, chan_no, + band); + bss->param.chanlist.num_of_chan++; + } + } else { + for (j = 0; + (bss->param.chanlist.num_of_chan < MLAN_MAX_CHANNEL_NUM) + && (j < MAX_REGION_CHANNEL_NUM); j++) { + cfp = pmadapter->region_channel[j].pcfp; + for (i = 0; + (bss->param.chanlist.num_of_chan < + MLAN_MAX_CHANNEL_NUM) + && pmadapter->region_channel[j].valid && cfp && + (i < pmadapter->region_channel[j].num_cfp); i++) { + bss->param.chanlist.cf[bss->param.chanlist. + num_of_chan].channel = + (t_u32)cfp->channel; + bss->param.chanlist.cf[bss->param.chanlist. + num_of_chan].freq = + (t_u32)cfp->freq; + bss->param.chanlist.num_of_chan++; + cfp++; + } + } + } + + PRINTM(MINFO, "num of channel=%d\n", bss->param.chanlist.num_of_chan); + + LEAVE(); + return ret; +} + +/** Highest channel used in 2.4GHz band */ +#define MAX_CHANNEL_BAND_B (14) + +/** Highest frequency used in 2.4GHz band */ +#define MAX_FREQUENCY_BAND_B (2484) + +/** + * @brief Set/Get BSS channel + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +static mlan_status +wlan_bss_ioctl_channel(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = MNULL; + mlan_ds_bss *bss = MNULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + chan_freq_power_t *cfp = MNULL; + ENTER(); + + if ((pioctl_req == MNULL) || (pioctl_req->pbuf == MNULL)) { + PRINTM(MERROR, "Request buffer not found!\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + pmpriv = pmadapter->priv[pioctl_req->bss_index]; + bss = (mlan_ds_bss *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_GET) { + cfp = wlan_find_cfp_by_band_and_channel(pmadapter, + pmpriv->curr_bss_params. + band, + (t_u16)pmpriv-> + curr_bss_params. + bss_descriptor.channel); + if (cfp) { + bss->param.bss_chan.channel = cfp->channel; + bss->param.bss_chan.freq = cfp->freq; + } else { + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_FAILURE; + } + pioctl_req->data_read_written = + sizeof(chan_freq) + MLAN_SUB_COMMAND_SIZE; + LEAVE(); + return ret; + } + if (!bss->param.bss_chan.channel && !bss->param.bss_chan.freq) { + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + LEAVE(); + return MLAN_STATUS_FAILURE; + } + if (pmadapter->adhoc_start_band & BAND_AN) + pmadapter->adhoc_start_band = BAND_G | BAND_B | BAND_GN; + else if (pmadapter->adhoc_start_band & BAND_A) + pmadapter->adhoc_start_band = BAND_G | BAND_B; + if (bss->param.bss_chan.channel) { + if (bss->param.bss_chan.channel <= MAX_CHANNEL_BAND_B) + cfp = wlan_find_cfp_by_band_and_channel(pmadapter, + BAND_B, + (t_u16)bss-> + param.bss_chan. + channel); + if (!cfp) { + cfp = wlan_find_cfp_by_band_and_channel(pmadapter, + BAND_A, + (t_u16)bss-> + param.bss_chan. + channel); + if (cfp) { + if (pmadapter->adhoc_11n_enabled) + pmadapter->adhoc_start_band = + BAND_A | BAND_AN; + else + pmadapter->adhoc_start_band = BAND_A; + } + } + } else { + if (bss->param.bss_chan.freq <= MAX_FREQUENCY_BAND_B) + cfp = wlan_find_cfp_by_band_and_freq(pmadapter, BAND_B, + bss->param. + bss_chan.freq); + if (!cfp) { + cfp = wlan_find_cfp_by_band_and_freq(pmadapter, BAND_A, + bss->param. + bss_chan.freq); + if (cfp) { + if (pmadapter->adhoc_11n_enabled) + pmadapter->adhoc_start_band = + BAND_A | BAND_AN; + else + pmadapter->adhoc_start_band = BAND_A; + } + } + } + if (!cfp || !cfp->channel) { + PRINTM(MERROR, "Invalid channel/freq\n"); + if (pioctl_req) + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + LEAVE(); + return MLAN_STATUS_FAILURE; + + } + pmpriv->adhoc_channel = (t_u8)cfp->channel; + pmpriv->intf_state_11h.adhoc_auto_sel_chan = MFALSE; + bss->param.bss_chan.channel = cfp->channel; + bss->param.bss_chan.freq = cfp->freq; + + LEAVE(); + return ret; +} + +/** + * @brief Set/Get BSS mode + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status +wlan_bss_ioctl_mode(IN pmlan_adapter pmadapter, IN pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_bss *bss = MNULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + bss = (mlan_ds_bss *)pioctl_req->pbuf; + + ENTER(); + + if (pioctl_req->action == MLAN_ACT_GET) { + bss->param.bss_mode = pmpriv->bss_mode; + pioctl_req->data_read_written = + sizeof(t_u32) + MLAN_SUB_COMMAND_SIZE; + goto exit; + } + + if ((pmpriv->bss_mode == bss->param.bss_mode) || + (bss->param.bss_mode == MLAN_BSS_MODE_AUTO)) { + PRINTM(MINFO, "Already set to required mode! No change!\n"); + pmpriv->bss_mode = bss->param.bss_mode; + goto exit; + } + + if (pmpriv->bss_mode != MLAN_BSS_MODE_AUTO) + ret = wlan_disconnect(pmpriv, MNULL, MNULL); + else + ret = wlan_disconnect(pmpriv, pioctl_req, MNULL); + + if (pmpriv->sec_info.authentication_mode != MLAN_AUTH_MODE_AUTO) + pmpriv->sec_info.authentication_mode = MLAN_AUTH_MODE_OPEN; + pmpriv->bss_mode = bss->param.bss_mode; + + if (pmpriv->bss_mode == MLAN_BSS_MODE_INFRA) + pmpriv->port_ctrl_mode = MTRUE; + else + pmpriv->port_ctrl_mode = MFALSE; + + if ((pmpriv->bss_mode != MLAN_BSS_MODE_AUTO) + ) { + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_SET_BSS_MODE, + HostCmd_ACT_GEN_SET, + 0, (t_void *)pioctl_req, MNULL); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief Start BSS + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status +wlan_bss_ioctl_start(IN pmlan_adapter pmadapter, IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_bss *bss = (mlan_ds_bss *)pioctl_req->pbuf; + t_s32 i = -1; + t_u8 zero_mac[] = { 0, 0, 0, 0, 0, 0 }; + + ENTER(); + + if (pmadapter->enable_net_mon == CHANNEL_SPEC_SNIFFER_MODE) { + PRINTM(MINFO, + "Association is blocked in Channel Specified Network Monitor mode...\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + /* Before ASSOC REQ, If "port ctrl" mode is enabled, + * move the port to CLOSED state */ + if (pmpriv->port_ctrl_mode == MTRUE) { + PRINTM(MINFO, "bss_ioctl_start(): port_state=CLOSED\n"); + pmpriv->prior_port_status = pmpriv->port_open; + pmpriv->port_open = MFALSE; + } + pmadapter->scan_block = MFALSE; + + if (pmpriv->bss_mode == MLAN_BSS_MODE_INFRA) { + if (!bss->param.ssid_bssid.idx || + bss->param.ssid_bssid.idx > pmadapter->num_in_scan_table) { + /* Search for the requested SSID in the scan table */ + if (bss->param.ssid_bssid.ssid.ssid_len) { + if (memcmp + (pmadapter, &bss->param.ssid_bssid.bssid, + zero_mac, sizeof(zero_mac))) + i = wlan_find_ssid_in_list(pmpriv, + &bss->param. + ssid_bssid. + ssid, + (t_u8 *) + &bss->param. + ssid_bssid. + bssid, + MLAN_BSS_MODE_INFRA); + else + i = wlan_find_ssid_in_list(pmpriv, + &bss->param. + ssid_bssid. + ssid, MNULL, + MLAN_BSS_MODE_INFRA); + } else { + i = wlan_find_bssid_in_list(pmpriv, + (t_u8 *)&bss->param. + ssid_bssid.bssid, + MLAN_BSS_MODE_INFRA); + } + } else { + /* use bsslist index number to assoicate */ + i = wlan_is_network_compatible(pmpriv, + bss->param.ssid_bssid. + idx - 1, + pmpriv->bss_mode); + } + if (i >= 0) { + /* block if upper-layer tries to reconnect before new scan */ + if (wlan_11h_get_csa_closed_channel(pmpriv) == + (t_u8)pmadapter->pscan_table[i].channel) { + PRINTM(MINFO, + "Attempt to reconnect on csa_closed_chan(%d)\n", + pmadapter->pscan_table[i].channel); + pioctl_req->status_code = + MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_FAILURE; + goto start_ssid_done; + } + PRINTM(MINFO, + "SSID found in scan list ... associating...\n"); + + /* Clear any past association response stored for application retrieval */ + pmpriv->assoc_rsp_size = 0; + pmpriv->curr_chan_flags = + bss->param.ssid_bssid.channel_flags; + ret = wlan_associate(pmpriv, pioctl_req, + &pmadapter->pscan_table[i]); + if (ret) + goto start_ssid_done; + } else { /* i >= 0 */ + PRINTM(MERROR, + "SSID not found in scan list: ssid=%s, " MACSTR + ", idx=%d\n", bss->param.ssid_bssid.ssid.ssid, + MAC2STR(bss->param.ssid_bssid.bssid), + (int)bss->param.ssid_bssid.idx); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_FAILURE; + goto start_ssid_done; + } + } else { + /* Adhoc mode */ + /* If the requested SSID matches current SSID, return */ + if (bss->param.ssid_bssid.ssid.ssid_len && + (!wlan_ssid_cmp + (pmadapter, &pmpriv->curr_bss_params.bss_descriptor.ssid, + &bss->param.ssid_bssid.ssid))) { + ret = MLAN_STATUS_SUCCESS; + goto start_ssid_done; + } + + /* Exit Adhoc mode first */ + PRINTM(MINFO, "Sending Adhoc Stop\n"); + ret = wlan_disconnect(pmpriv, MNULL, MNULL); + if (ret) + goto start_ssid_done; + + pmpriv->adhoc_is_link_sensed = MFALSE; + + if (!bss->param.ssid_bssid.idx || + bss->param.ssid_bssid.idx > pmadapter->num_in_scan_table) { + /* Search for the requested network in the scan table */ + if (bss->param.ssid_bssid.ssid.ssid_len) { + i = wlan_find_ssid_in_list(pmpriv, + &bss->param. + ssid_bssid.ssid, + MNULL, + MLAN_BSS_MODE_IBSS); + } else { + i = wlan_find_bssid_in_list(pmpriv, + (t_u8 *)&bss->param. + ssid_bssid.bssid, + MLAN_BSS_MODE_IBSS); + } + } else { + /* use bsslist index number to assoicate */ + i = wlan_is_network_compatible(pmpriv, + bss->param.ssid_bssid. + idx - 1, + pmpriv->bss_mode); + } + + if (i >= 0) { + PRINTM(MINFO, + "Network found in scan list ... joining ...\n"); + pmpriv->curr_chan_flags = + bss->param.ssid_bssid.channel_flags; + ret = wlan_adhoc_join(pmpriv, pioctl_req, + &pmadapter->pscan_table[i]); + if (ret) + goto start_ssid_done; + if (pmpriv->adhoc_aes_enabled) + wlan_enable_aes_key(pmpriv); + } else { /* i >= 0 */ + PRINTM(MINFO, "Network not found in the list, " + "creating adhoc with ssid = %s\n", + bss->param.ssid_bssid.ssid.ssid); + pmpriv->curr_chan_flags = + bss->param.ssid_bssid.channel_flags; + ret = wlan_adhoc_start(pmpriv, pioctl_req, + &bss->param.ssid_bssid.ssid); + if (ret) + goto start_ssid_done; + if (pmpriv->adhoc_aes_enabled) + wlan_enable_aes_key(pmpriv); + } + } + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + +start_ssid_done: + LEAVE(); + return ret; +} + +/** + * @brief Stop BSS + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status +wlan_bss_ioctl_stop(IN pmlan_adapter pmadapter, IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_private pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_bss *bss = (mlan_ds_bss *)pioctl_req->pbuf; + + ENTER(); + + ret = wlan_disconnect(pmpriv, pioctl_req, &bss->param.deauth_param); + + LEAVE(); + return ret; +} + +/** + * @brief Set/Get IBSS channel + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status +wlan_bss_ioctl_ibss_channel(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_bss *bss = MNULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u16 cmd_action; + + ENTER(); + + bss = (mlan_ds_bss *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_GET) { + if (pmpriv->media_connected == MFALSE) { + bss->param.bss_chan.channel = pmpriv->adhoc_channel; + goto exit; + } + cmd_action = HostCmd_ACT_GEN_GET; + } else { + cmd_action = HostCmd_ACT_GEN_SET; + pmpriv->adhoc_channel = (t_u8)bss->param.bss_chan.channel; + pmpriv->intf_state_11h.adhoc_auto_sel_chan = MFALSE; + } + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_802_11_RF_CHANNEL, + cmd_action, + 0, + (t_void *)pioctl_req, + &bss->param.bss_chan.channel); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + +exit: + LEAVE(); + return ret; +} + +/** + * @brief Set/Get Listen Interval + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status +wlan_bss_ioctl_listen_interval(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_bss *bss = MNULL; + + ENTER(); + + bss = (mlan_ds_bss *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_GET) + bss->param.listen_interval = pmpriv->listen_interval; + else + pmpriv->listen_interval = bss->param.listen_interval; + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/* + * @brief Set/Get beacon interval + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success + */ +static mlan_status +wlan_bss_ioctl_beacon_interval(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_bss *bss = MNULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + ENTER(); + bss = (mlan_ds_bss *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_GET) { + bss->param.bcn_interval = pmpriv->beacon_period; + if (pmpriv->media_connected == MTRUE) + bss->param.bcn_interval = + pmpriv->curr_bss_params.bss_descriptor. + beacon_period; + } else + pmpriv->beacon_period = (t_u16)bss->param.bcn_interval; + pioctl_req->data_read_written = sizeof(t_u32) + MLAN_SUB_COMMAND_SIZE; + LEAVE(); + return ret; +} + +/** + * @brief Set/Get ATIM window + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success + */ +static mlan_status +wlan_bss_ioctl_atim_window(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_bss *bss = MNULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + ENTER(); + bss = (mlan_ds_bss *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_GET) { + bss->param.atim_window = pmpriv->atim_window; + if (pmpriv->media_connected == MTRUE) + bss->param.atim_window = + pmpriv->curr_bss_params.bss_descriptor. + atim_window; + } else + pmpriv->atim_window = (t_u16)bss->param.atim_window; + + pioctl_req->data_read_written = sizeof(t_u32) + MLAN_SUB_COMMAND_SIZE; + LEAVE(); + return ret; +} + +/** + * @brief Query passphrase + * + * @param priv A pointer to mlan_private structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +static mlan_status +wlan_query_passphrase(mlan_private *priv, pmlan_ioctl_req pioctl_req) +{ + mlan_adapter *pmadapter = priv->adapter; + mlan_ds_bss *bss = MNULL; + mlan_ssid_bssid *ssid_bssid = MNULL; + mlan_ds_sec_cfg sec; + mlan_ds_passphrase *sec_pp; + int i = 0; + BSSDescriptor_t *pbss_desc; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + bss = (mlan_ds_bss *)pioctl_req->pbuf; + ssid_bssid = &bss->param.ssid_bssid; + + memset(pmadapter, &sec, 0, sizeof(mlan_ds_sec_cfg)); + sec_pp = (mlan_ds_passphrase *)&sec.param.passphrase; + sec_pp->psk_type = MLAN_PSK_QUERY; + if (ssid_bssid->ssid.ssid_len == 0) { + i = wlan_find_bssid_in_list(priv, (t_u8 *)&ssid_bssid->bssid, + MLAN_BSS_MODE_AUTO); + if (i >= 0) { + pbss_desc = &pmadapter->pscan_table[i]; + memcpy(pmadapter, (t_u8 *)&sec_pp->ssid, + &pbss_desc->ssid, sizeof(mlan_802_11_ssid)); + } else + memcpy(pmadapter, (t_u8 *)&sec_pp->bssid, + &ssid_bssid->bssid, MLAN_MAC_ADDR_LENGTH); + } else { + memcpy(pmadapter, (t_u8 *)&sec_pp->ssid, &ssid_bssid->ssid, + sizeof(mlan_802_11_ssid)); + } + + /* Send request to firmware */ + ret = wlan_prepare_cmd(priv, + HostCmd_CMD_SUPPLICANT_PMK, + HostCmd_ACT_GEN_GET, + 0, (t_void *)pioctl_req, (t_void *)&sec); + + LEAVE(); + return ret; +} + +/** + * @brief Search for a BSS + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING --success, otherwise fail + */ +mlan_status +wlan_bss_ioctl_find_bss(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_status ret = MLAN_STATUS_SUCCESS; +#ifdef DRV_EMBEDDED_SUPPLICANT + mlan_ds_bss *bss = MNULL; + mlan_ssid_bssid *ssid_bssid = MNULL; +#endif + + ENTER(); + + if (pmpriv->ewpa_query) { + if (wlan_query_passphrase(pmpriv, pioctl_req) == + MLAN_STATUS_SUCCESS) { + PRINTM(MINFO, "Find BSS ioctl: query passphrase\n"); + LEAVE(); + return MLAN_STATUS_PENDING; + } + } +#ifdef DRV_EMBEDDED_SUPPLICANT + if (!IS_FW_SUPPORT_SUPPLICANT(pmpriv->adapter)) { + bss = (mlan_ds_bss *)pioctl_req->pbuf; + ssid_bssid = &bss->param.ssid_bssid; + supplicantQueryPassphraseAndEnable(pmpriv->psapriv, + (t_u8 *)ssid_bssid); + } +#endif + + ret = wlan_find_bss(pmpriv, pioctl_req); + + LEAVE(); + return ret; +} + +/** + * @brief Check if BSS channel is valid for Station's region + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING --success, otherwise fail + */ +mlan_status +wlan_bss_ioctl_bss_11d_check_channel(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_bss *bss = MNULL; + mlan_ssid_bssid *ssid_bssid = MNULL; + + ENTER(); + + bss = (mlan_ds_bss *)pioctl_req->pbuf; + ssid_bssid = &bss->param.ssid_bssid; + + PRINTM(MINFO, "ssid: %s idx:%d\n", ssid_bssid->ssid.ssid, + ssid_bssid->idx); + PRINTM(MINFO, "band:%d channel:%d\n", (t_u8)ssid_bssid->bss_band, + (t_u32)ssid_bssid->channel); + + /* check if this channel is supported in the region */ + if (!wlan_find_cfp_by_band_and_channel(pmadapter, + (t_u8)ssid_bssid->bss_band, + (t_u32)ssid_bssid->channel)) { + PRINTM(MERROR, "Unsupported Channel for region 0x%x\n", + pmadapter->region_code); + ret = MLAN_STATUS_FAILURE; + } + + LEAVE(); + return ret; +} + +/** + * @brief BSS command handler + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +static mlan_status +wlan_bss_ioctl(IN pmlan_adapter pmadapter, IN pmlan_ioctl_req pioctl_req) +{ + mlan_status status = MLAN_STATUS_SUCCESS; + mlan_ds_bss *bss = MNULL; + + ENTER(); + + if (pioctl_req->buf_len < sizeof(mlan_ds_bss)) { + PRINTM(MWARN, "MLAN bss IOCTL length is too short.\n"); + pioctl_req->data_read_written = 0; + pioctl_req->buf_len_needed = sizeof(mlan_ds_bss); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + LEAVE(); + return MLAN_STATUS_RESOURCE; + } + + bss = (mlan_ds_bss *)pioctl_req->pbuf; + + switch (bss->sub_command) { + case MLAN_OID_BSS_START: + status = wlan_bss_ioctl_start(pmadapter, pioctl_req); + break; + case MLAN_OID_BSS_STOP: + status = wlan_bss_ioctl_stop(pmadapter, pioctl_req); + break; + case MLAN_OID_BSS_MODE: + status = wlan_bss_ioctl_mode(pmadapter, pioctl_req); + break; + case MLAN_OID_BSS_CHANNEL: + status = wlan_bss_ioctl_channel(pmadapter, pioctl_req); + break; + case MLAN_OID_BSS_CHANNEL_LIST: + status = wlan_bss_ioctl_get_channel_list(pmadapter, pioctl_req); + break; + case MLAN_OID_BSS_MAC_ADDR: + status = wlan_bss_ioctl_mac_address(pmadapter, pioctl_req); + break; + case MLAN_OID_BSS_MULTICAST_LIST: + status = wlan_bss_ioctl_set_multicast_list(pmadapter, + pioctl_req); + break; + case MLAN_OID_BSS_FIND_BSS: + status = wlan_bss_ioctl_find_bss(pmadapter, pioctl_req); + break; + case MLAN_OID_IBSS_BCN_INTERVAL: + status = wlan_bss_ioctl_beacon_interval(pmadapter, pioctl_req); + break; + case MLAN_OID_IBSS_ATIM_WINDOW: + status = wlan_bss_ioctl_atim_window(pmadapter, pioctl_req); + break; + case MLAN_OID_IBSS_CHANNEL: + status = wlan_bss_ioctl_ibss_channel(pmadapter, pioctl_req); + break; +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) + case MLAN_OID_BSS_ROLE: + util_enqueue_list_tail(pmadapter->pmoal_handle, + &pmadapter->ioctl_pending_q, + (pmlan_linked_list)pioctl_req, + pmadapter->callbacks.moal_spin_lock, + pmadapter->callbacks.moal_spin_unlock); + pmadapter->pending_ioctl = MTRUE; + status = MLAN_STATUS_PENDING; + break; +#endif +#ifdef WIFI_DIRECT_SUPPORT + case MLAN_OID_WIFI_DIRECT_MODE: + status = wlan_bss_ioctl_wifi_direct_mode(pmadapter, pioctl_req); + break; +#endif + case MLAN_OID_BSS_LISTEN_INTERVAL: + status = wlan_bss_ioctl_listen_interval(pmadapter, pioctl_req); + break; + case MLAN_OID_BSS_REMOVE: + status = wlan_bss_ioctl_bss_remove(pmadapter, pioctl_req); + break; + + case MLAN_OID_BSS_11D_CHECK_CHANNEL: + status = wlan_bss_ioctl_bss_11d_check_channel(pmadapter, + pioctl_req); + break; + case MLAN_OID_BSS_CHAN_INFO: + status = wlan_bss_ioctl_get_chan_info(pmadapter, pioctl_req); + break; + default: + status = MLAN_STATUS_FAILURE; + break; + } + + LEAVE(); + return status; +} + +/** + * @brief Get supported rates + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +static mlan_status +wlan_rate_ioctl_get_supported_rate(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_rate *rate = MNULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + if (pioctl_req->action != MLAN_ACT_GET) { + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + LEAVE(); + return MLAN_STATUS_FAILURE; + } + rate = (mlan_ds_rate *)pioctl_req->pbuf; + if (rate->param.rate_band_cfg.config_bands && + rate->param.rate_band_cfg.bss_mode) + wlan_get_active_data_rates(pmpriv, + rate->param.rate_band_cfg.bss_mode, + rate->param.rate_band_cfg. + config_bands, rate->param.rates); + else + wlan_get_active_data_rates(pmpriv, pmpriv->bss_mode, + (pmpriv->bss_mode == + MLAN_BSS_MODE_INFRA) ? pmpriv-> + config_bands : pmadapter-> + adhoc_start_band, rate->param.rates); + pioctl_req->data_read_written = + MLAN_SUPPORTED_RATES + MLAN_SUB_COMMAND_SIZE; + LEAVE(); + return ret; +} + +/** + * @brief Rate command handler + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status +wlan_rate_ioctl(IN pmlan_adapter pmadapter, IN pmlan_ioctl_req pioctl_req) +{ + mlan_status status = MLAN_STATUS_SUCCESS; + mlan_ds_rate *rate = MNULL; + + ENTER(); + + if (pioctl_req->buf_len < sizeof(mlan_ds_rate)) { + PRINTM(MWARN, "MLAN bss IOCTL length is too short.\n"); + pioctl_req->data_read_written = 0; + pioctl_req->buf_len_needed = sizeof(mlan_ds_rate); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + LEAVE(); + return MLAN_STATUS_RESOURCE; + } + rate = (mlan_ds_rate *)pioctl_req->pbuf; + switch (rate->sub_command) { + case MLAN_OID_RATE_CFG: + status = wlan_rate_ioctl_cfg(pmadapter, pioctl_req); + break; + case MLAN_OID_GET_DATA_RATE: + status = wlan_rate_ioctl_get_data_rate(pmadapter, pioctl_req); + break; + case MLAN_OID_SUPPORTED_RATES: + status = wlan_rate_ioctl_get_supported_rate(pmadapter, + pioctl_req); + break; + default: + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + status = MLAN_STATUS_FAILURE; + break; + } + LEAVE(); + return status; +} + +/** + * @brief Get Tx power configuration + * + * @param pmadapter A pointer to mlan_adapter structure + * @param cmd_no Firmware command number used to retrieve power values + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status +wlan_power_ioctl_get_power(IN pmlan_adapter pmadapter, + IN t_u16 cmd_no, IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + + ENTER(); + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, + cmd_no, + HostCmd_ACT_GEN_GET, + 0, (t_void *)pioctl_req, MNULL); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Set Tx power configuration + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status +wlan_power_ioctl_set_power(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_ds_power_cfg *power = MNULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + + HostCmd_DS_TXPWR_CFG *txp_cfg = MNULL; + MrvlTypes_Power_Group_t *pg_tlv = MNULL; + Power_Group_t *pg = MNULL; + pmlan_callbacks pcb = &pmadapter->callbacks; + t_u8 *buf = MNULL; + t_s8 dbm = 0; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + + ENTER(); + + power = (mlan_ds_power_cfg *)pioctl_req->pbuf; + if (!power->param.power_cfg.is_power_auto) { + dbm = (t_s8)power->param.power_cfg.power_level; + if ((dbm < pmpriv->min_tx_power_level) || + (dbm > pmpriv->max_tx_power_level)) { + PRINTM(MERROR, + "The set txpower value %d dBm is out of range (%d dBm-%d dBm)!\n", + dbm, pmpriv->min_tx_power_level, + pmpriv->max_tx_power_level); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_FAILURE; + goto exit; + } + } + + ret = pcb->moal_malloc(pmadapter->pmoal_handle, + MRVDRV_SIZE_OF_CMD_BUFFER, MLAN_MEM_DEF, &buf); + if (ret != MLAN_STATUS_SUCCESS || buf == MNULL) { + PRINTM(MERROR, + "ALLOC_CMD_BUF: Failed to allocate command buffer\n"); + pioctl_req->status_code = MLAN_ERROR_NO_MEM; + ret = MLAN_STATUS_FAILURE; + goto exit; + } + memset(pmadapter, buf, 0, MRVDRV_SIZE_OF_CMD_BUFFER); + txp_cfg = (HostCmd_DS_TXPWR_CFG *)buf; + txp_cfg->action = HostCmd_ACT_GEN_SET; + if (!power->param.power_cfg.is_power_auto) { + txp_cfg->mode = 1; + pg_tlv = (MrvlTypes_Power_Group_t *)(buf + + sizeof + (HostCmd_DS_TXPWR_CFG)); + pg_tlv->type = TLV_TYPE_POWER_GROUP; + pg_tlv->length = 4 * sizeof(Power_Group_t); + pg = (Power_Group_t *)(buf + sizeof(HostCmd_DS_TXPWR_CFG) + + sizeof(MrvlTypes_Power_Group_t)); + /* Power group for modulation class HR/DSSS */ + pg->first_rate_code = 0x00; + pg->last_rate_code = 0x03; + pg->modulation_class = MOD_CLASS_HR_DSSS; + pg->power_step = 0; + pg->power_min = (t_s8)dbm; + pg->power_max = (t_s8)dbm; + pg++; + /* Power group for modulation class OFDM */ + pg->first_rate_code = 0x00; + pg->last_rate_code = 0x07; + pg->modulation_class = MOD_CLASS_OFDM; + pg->power_step = 0; + pg->power_min = (t_s8)dbm; + pg->power_max = (t_s8)dbm; + pg++; + /* Power group for modulation class HTBW20 */ + pg->first_rate_code = 0x00; + pg->last_rate_code = 0x20; + pg->modulation_class = MOD_CLASS_HT; + pg->power_step = 0; + pg->power_min = (t_s8)dbm; + pg->power_max = (t_s8)dbm; + pg->ht_bandwidth = HT_BW_20; + pg++; + /* Power group for modulation class HTBW40 */ + pg->first_rate_code = 0x00; + pg->last_rate_code = 0x20; + pg->modulation_class = MOD_CLASS_HT; + pg->power_step = 0; + pg->power_min = (t_s8)dbm; + pg->power_max = (t_s8)dbm; + pg->ht_bandwidth = HT_BW_40; + } + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_TXPWR_CFG, + HostCmd_ACT_GEN_SET, + 0, (t_void *)pioctl_req, buf); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + if (buf) + pcb->moal_mfree(pmadapter->pmoal_handle, buf); + +exit: + LEAVE(); + return ret; +} + +/** + * @brief Set extended power configuration + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status +wlan_power_ioctl_set_power_ext(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_power_cfg *power = MNULL; + pmlan_callbacks pcb = &pmadapter->callbacks; + t_u8 *buf = MNULL; + HostCmd_DS_TXPWR_CFG *txp_cfg = MNULL; + MrvlTypes_Power_Group_t *pg_tlv = MNULL; + Power_Group_t *pg = MNULL; + mlan_power_group *pwr_grp = MNULL; + + ENTER(); + + power = (mlan_ds_power_cfg *)pioctl_req->pbuf; + ret = pcb->moal_malloc(pmadapter->pmoal_handle, + MRVDRV_SIZE_OF_CMD_BUFFER, MLAN_MEM_DEF, &buf); + if (ret != MLAN_STATUS_SUCCESS || buf == MNULL) { + PRINTM(MERROR, + "ALLOC_CMD_BUF: Failed to allocate command buffer\n"); + pioctl_req->status_code = MLAN_ERROR_NO_MEM; + ret = MLAN_STATUS_FAILURE; + goto exit; + } + memset(pmadapter, buf, 0, MRVDRV_SIZE_OF_CMD_BUFFER); + txp_cfg = (HostCmd_DS_TXPWR_CFG *)buf; + txp_cfg->action = HostCmd_ACT_GEN_SET; + pwr_grp = &power->param.power_ext.power_group[0]; + if (pwr_grp->rate_format == TX_PWR_CFG_AUTO_CTRL_OFF) + txp_cfg->mode = 0; + else { + txp_cfg->mode = 1; + + pg_tlv = (MrvlTypes_Power_Group_t *)(buf + + sizeof + (HostCmd_DS_TXPWR_CFG)); + pg_tlv->type = TLV_TYPE_POWER_GROUP; + pg_tlv->length = sizeof(Power_Group_t); + pg = (Power_Group_t *)(buf + sizeof(HostCmd_DS_TXPWR_CFG) + + sizeof(MrvlTypes_Power_Group_t)); + pg->ht_bandwidth = pwr_grp->bandwidth; + pg->power_min = (t_s8)pwr_grp->power_min; + pg->power_max = (t_s8)pwr_grp->power_max; + pg->power_step = (t_s8)pwr_grp->power_step; + + if (pwr_grp->rate_format == MLAN_RATE_FORMAT_LG) { + if (pwr_grp->first_rate_ind <= MLAN_RATE_INDEX_HRDSSS3) { + pg->modulation_class = MOD_CLASS_HR_DSSS; + } else { + pg->modulation_class = MOD_CLASS_OFDM; + pwr_grp->first_rate_ind -= + MLAN_RATE_INDEX_OFDM0; + pwr_grp->last_rate_ind -= MLAN_RATE_INDEX_OFDM0; + } + pg->first_rate_code = (t_u8)pwr_grp->first_rate_ind; + pg->last_rate_code = (t_u8)pwr_grp->last_rate_ind; + } else if (pwr_grp->rate_format == MLAN_RATE_FORMAT_HT) { + pg->modulation_class = MOD_CLASS_HT; + pg->first_rate_code = (t_u8)pwr_grp->first_rate_ind; + pg->last_rate_code = (t_u8)pwr_grp->last_rate_ind; + } else { + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_FAILURE; + } + } + if (ret == MLAN_STATUS_FAILURE) { + if (buf) + pcb->moal_mfree(pmadapter->pmoal_handle, buf); + goto exit; + } + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_TXPWR_CFG, + HostCmd_ACT_GEN_SET, + 0, (t_void *)pioctl_req, buf); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + if (buf) + pcb->moal_mfree(pmadapter->pmoal_handle, buf); + +exit: + LEAVE(); + return ret; +} + +/** + * @brief Power configuration command handler + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +static mlan_status +wlan_power_ioctl(IN pmlan_adapter pmadapter, IN pmlan_ioctl_req pioctl_req) +{ + mlan_status status = MLAN_STATUS_SUCCESS; + mlan_ds_power_cfg *power = MNULL; + + ENTER(); + + if (pioctl_req->buf_len < sizeof(mlan_ds_power_cfg)) { + PRINTM(MWARN, "MLAN bss IOCTL length is too short.\n"); + pioctl_req->data_read_written = 0; + pioctl_req->buf_len_needed = sizeof(mlan_ds_power_cfg); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + LEAVE(); + return MLAN_STATUS_RESOURCE; + } + power = (mlan_ds_power_cfg *)pioctl_req->pbuf; + switch (power->sub_command) { + case MLAN_OID_POWER_CFG: + if (pioctl_req->action == MLAN_ACT_GET) + status = wlan_power_ioctl_get_power(pmadapter, + HostCmd_CMD_TXPWR_CFG, + pioctl_req); + else + status = wlan_power_ioctl_set_power(pmadapter, + pioctl_req); + break; + + case MLAN_OID_POWER_CFG_EXT: + if (pioctl_req->action == MLAN_ACT_GET) + status = wlan_power_ioctl_get_power(pmadapter, + HostCmd_CMD_TXPWR_CFG, + pioctl_req); + else + status = wlan_power_ioctl_set_power_ext(pmadapter, + pioctl_req); + break; + default: + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + status = MLAN_STATUS_FAILURE; + break; + } + LEAVE(); + return status; +} + +/** + * @brief Set power save configurations + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * @param ps_mode Power save mode + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status +wlan_pm_ioctl_ps_mode(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req, IN t_u16 ps_mode) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + t_u16 sub_cmd; + + ENTER(); + + if (pioctl_req->action == MLAN_ACT_SET) { + sub_cmd = (pmadapter->ps_mode == Wlan802_11PowerModePSP) ? + EN_AUTO_PS : DIS_AUTO_PS; + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_802_11_PS_MODE_ENH, sub_cmd, + BITMAP_STA_PS, (t_void *)pioctl_req, + MNULL); + if ((ret == MLAN_STATUS_SUCCESS) && (sub_cmd == DIS_AUTO_PS)) { + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_802_11_PS_MODE_ENH, + GET_PS, 0, MNULL, MNULL); + } + } else { + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_802_11_PS_MODE_ENH, GET_PS, + 0, (t_void *)pioctl_req, MNULL); + } + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Set/Get Inactivity timeout extend + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status +wlan_pm_ioctl_inactivity_timeout(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_pm_cfg *pmcfg = MNULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u16 cmd_action = 0; + + ENTER(); + + pmcfg = (mlan_ds_pm_cfg *)pioctl_req->pbuf; + cmd_action = HostCmd_ACT_GEN_GET; + if (pioctl_req->action == MLAN_ACT_SET) + cmd_action = HostCmd_ACT_GEN_SET; + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_INACTIVITY_TIMEOUT_EXT, + cmd_action, 0, (t_void *)pioctl_req, + (t_void *)&pmcfg->param.inactivity_to); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Enable/Disable Auto Deep Sleep + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status +wlan_set_auto_deep_sleep(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_private pmpriv = + (pmlan_private)pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_auto_ds auto_ds; + t_u32 mode; + + ENTER(); + + if (((mlan_ds_pm_cfg *)pioctl_req->pbuf)->param.auto_deep_sleep. + auto_ds == DEEP_SLEEP_ON) { + auto_ds.auto_ds = DEEP_SLEEP_ON; + PRINTM(MINFO, "Auto Deep Sleep: on\n"); + mode = EN_AUTO_PS; + } else { + auto_ds.auto_ds = DEEP_SLEEP_OFF; + PRINTM(MINFO, "Auto Deep Sleep: off\n"); + mode = DIS_AUTO_PS; + } + if (((mlan_ds_pm_cfg *)pioctl_req->pbuf)->param.auto_deep_sleep. + idletime) + auto_ds.idletime = + ((mlan_ds_pm_cfg *)pioctl_req->pbuf)->param. + auto_deep_sleep.idletime; + else + auto_ds.idletime = pmadapter->idle_time; + /* note: the command could be queued and executed + later if there is command in progress. */ + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_802_11_PS_MODE_ENH, + (t_u16)mode, + BITMAP_AUTO_DS, (t_void *)pioctl_req, &auto_ds); + if (ret) { + LEAVE(); + return ret; + } + ret = MLAN_STATUS_PENDING; + LEAVE(); + return ret; +} + +/** + * @brief Set/Get sleep period + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status +wlan_set_get_sleep_pd(IN pmlan_adapter pmadapter, IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_private pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_pm_cfg *pm_cfg = MNULL; + t_u16 cmd_action = 0, sleep_pd = 0; + + ENTER(); + + pm_cfg = (mlan_ds_pm_cfg *)pioctl_req->pbuf; + cmd_action = HostCmd_ACT_GEN_GET; + if (pioctl_req->action == MLAN_ACT_SET) { + cmd_action = HostCmd_ACT_GEN_SET; + sleep_pd = (t_u16)pm_cfg->param.sleep_period; + } + + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_802_11_SLEEP_PERIOD, + cmd_action, 0, (t_void *)pioctl_req, &sleep_pd); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Set/Get PS configuration parameter + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success + */ +static mlan_status +wlan_set_get_ps_cfg(IN pmlan_adapter pmadapter, IN pmlan_ioctl_req pioctl_req) +{ + mlan_ds_pm_cfg *pm_cfg = MNULL; + + ENTER(); + + pm_cfg = (mlan_ds_pm_cfg *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_GET) { + pm_cfg->param.ps_cfg.ps_null_interval = + (t_u32)pmadapter->null_pkt_interval; + pm_cfg->param.ps_cfg.multiple_dtim_interval = + (t_u32)pmadapter->multiple_dtim; + pm_cfg->param.ps_cfg.listen_interval = + (t_u32)pmadapter->local_listen_interval; + pm_cfg->param.ps_cfg.adhoc_awake_period = + (t_u32)pmadapter->adhoc_awake_period; + pm_cfg->param.ps_cfg.bcn_miss_timeout = + (t_u32)pmadapter->bcn_miss_time_out; + pm_cfg->param.ps_cfg.delay_to_ps = + (t_u32)pmadapter->delay_to_ps; + pm_cfg->param.ps_cfg.ps_mode = + (t_u32)pmadapter->enhanced_ps_mode; + } else { + if (pm_cfg->param.ps_cfg.ps_null_interval) + pmadapter->null_pkt_interval = + (t_u16)pm_cfg->param.ps_cfg.ps_null_interval; + else + pm_cfg->param.ps_cfg.ps_null_interval = + (t_u32)pmadapter->null_pkt_interval; + if (pm_cfg->param.ps_cfg.multiple_dtim_interval) + pmadapter->multiple_dtim = + (t_u16)pm_cfg->param.ps_cfg. + multiple_dtim_interval; + else + pm_cfg->param.ps_cfg.multiple_dtim_interval = + (t_u32)pmadapter->multiple_dtim; + if (((t_s32)pm_cfg->param.ps_cfg.listen_interval) == + MRVDRV_LISTEN_INTERVAL_DISABLE) + pmadapter->local_listen_interval = 0; + else if (pm_cfg->param.ps_cfg.listen_interval) + pmadapter->local_listen_interval = + (t_u16)pm_cfg->param.ps_cfg.listen_interval; + else + pm_cfg->param.ps_cfg.listen_interval = + (t_u32)pmadapter->local_listen_interval; + if (pm_cfg->param.ps_cfg.adhoc_awake_period) + pmadapter->adhoc_awake_period = + (t_u16)pm_cfg->param.ps_cfg.adhoc_awake_period; + else + pm_cfg->param.ps_cfg.adhoc_awake_period = + (t_u32)pmadapter->adhoc_awake_period; + if (pm_cfg->param.ps_cfg.bcn_miss_timeout) + pmadapter->bcn_miss_time_out = + (t_u16)pm_cfg->param.ps_cfg.bcn_miss_timeout; + else + pm_cfg->param.ps_cfg.bcn_miss_timeout = + (t_u32)pmadapter->bcn_miss_time_out; + if (pm_cfg->param.ps_cfg.delay_to_ps != DELAY_TO_PS_UNCHANGED) + pmadapter->delay_to_ps = + (t_u16)pm_cfg->param.ps_cfg.delay_to_ps; + else + pm_cfg->param.ps_cfg.delay_to_ps = + (t_u32)pmadapter->delay_to_ps; + if (pm_cfg->param.ps_cfg.ps_mode) + pmadapter->enhanced_ps_mode = + (t_u16)pm_cfg->param.ps_cfg.ps_mode; + else + pm_cfg->param.ps_cfg.ps_mode = + (t_u32)pmadapter->enhanced_ps_mode; + } + pioctl_req->data_read_written = sizeof(t_u32) + MLAN_SUB_COMMAND_SIZE; + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Set/Get PS configuration parameter + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success + */ +static mlan_status +wlan_set_get_bcn_timeout(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_pm_cfg *pm_cfg = MNULL; + + ENTER(); + + pm_cfg = (mlan_ds_pm_cfg *)pioctl_req->pbuf; + + /* Send command to firmware */ + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_802_11_PS_MODE_ENH, + EN_AUTO_PS, BITMAP_BCN_TMO, (t_void *)pioctl_req, + &pm_cfg->param.bcn_timeout); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Get/Set the sleep parameters + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status +wlan_set_get_sleep_params(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_pm_cfg *pm_cfg = MNULL; + t_u16 cmd_action = 0; + + ENTER(); + + pm_cfg = (mlan_ds_pm_cfg *)pioctl_req->pbuf; + cmd_action = HostCmd_ACT_GEN_GET; + if (pioctl_req->action == MLAN_ACT_SET) + cmd_action = HostCmd_ACT_GEN_SET; + + /* Send command to firmware */ + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_802_11_SLEEP_PARAMS, + cmd_action, 0, (t_void *)pioctl_req, + &pm_cfg->param.sleep_params); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief config management frame wakeup filter + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +mlan_status +wlan_config_mgmt_filter(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_pm_cfg *pm_cfg = MNULL; + int i = 0; + + ENTER(); + + memset(pmadapter, pmadapter->mgmt_filter, 0, + sizeof(mlan_mgmt_frame_wakeup) * MAX_MGMT_FRAME_FILTER); + pm_cfg = (mlan_ds_pm_cfg *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_SET) { + for (i = 0; i < MAX_MGMT_FRAME_FILTER; i++) + if (!pm_cfg->param.mgmt_filter[i].type) + break; + memcpy(pmadapter, (t_u8 *)pmadapter->mgmt_filter, + (t_u8 *)pm_cfg->param.mgmt_filter, + (i + 1) * sizeof(mlan_mgmt_frame_wakeup)); + } else if (pioctl_req->action == MLAN_ACT_GET) + PRINTM(MERROR, "Get not support\n"); + LEAVE(); + return ret; +} + +/** + * @brief Power save command handler + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +static mlan_status +wlan_pm_ioctl(IN pmlan_adapter pmadapter, IN pmlan_ioctl_req pioctl_req) +{ + mlan_status status = MLAN_STATUS_SUCCESS; + mlan_ds_pm_cfg *pm = MNULL; + + ENTER(); + + if (pioctl_req->buf_len < sizeof(mlan_ds_pm_cfg)) { + PRINTM(MWARN, "MLAN bss IOCTL length is too short.\n"); + pioctl_req->data_read_written = 0; + pioctl_req->buf_len_needed = sizeof(mlan_ds_pm_cfg); + LEAVE(); + return MLAN_STATUS_RESOURCE; + } + pm = (mlan_ds_pm_cfg *)pioctl_req->pbuf; + switch (pm->sub_command) { + case MLAN_OID_PM_CFG_IEEE_PS: + switch (pioctl_req->action) { + case MLAN_ACT_SET: + /**Block ieee power save disable command when bt coex enable*/ + if (pmadapter->coex_scan && !pm->param.ps_mode) + break; + if (pm->param.ps_mode) + pmadapter->ps_mode = Wlan802_11PowerModePSP; + else + pmadapter->ps_mode = Wlan802_11PowerModeCAM; + status = wlan_pm_ioctl_ps_mode(pmadapter, pioctl_req, + pmadapter->ps_mode); + break; + case MLAN_ACT_GET: + status = wlan_pm_ioctl_ps_mode(pmadapter, pioctl_req, + pmadapter->ps_mode); + break; + default: + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + status = MLAN_STATUS_FAILURE; + break; + } + break; + case MLAN_OID_PM_CFG_HS_CFG: + status = wlan_pm_ioctl_hscfg(pmadapter, pioctl_req); + break; + case MLAN_OID_PM_CFG_INACTIVITY_TO: + status = wlan_pm_ioctl_inactivity_timeout(pmadapter, + pioctl_req); + break; + case MLAN_OID_PM_CFG_DEEP_SLEEP: + switch (pioctl_req->action) { + case MLAN_ACT_SET: + if (pmadapter->is_deep_sleep && + pm->param.auto_deep_sleep.auto_ds == + DEEP_SLEEP_ON) { + PRINTM(MMSG, + "Station already in enhanced deep sleep mode\n"); + status = MLAN_STATUS_FAILURE; + break; + } else if (!pmadapter->is_deep_sleep && + pm->param.auto_deep_sleep.auto_ds == + DEEP_SLEEP_OFF) { + PRINTM(MMSG, + "Station already not in enhanced deep sleep mode\n"); + status = MLAN_STATUS_FAILURE; + break; + } + status = wlan_set_auto_deep_sleep(pmadapter, + pioctl_req); + break; + case MLAN_ACT_GET: + if (pmadapter->is_deep_sleep) { + pm->param.auto_deep_sleep.auto_ds = + DEEP_SLEEP_ON; + pm->param.auto_deep_sleep.idletime = + pmadapter->idle_time; + } else + pm->param.auto_deep_sleep.auto_ds = + DEEP_SLEEP_OFF; + break; + default: + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + status = MLAN_STATUS_FAILURE; + break; + } + break; + case MLAN_OID_PM_CFG_PS_CFG: + status = wlan_set_get_ps_cfg(pmadapter, pioctl_req); + break; + case MLAN_OID_PM_CFG_SLEEP_PD: + status = wlan_set_get_sleep_pd(pmadapter, pioctl_req); + break; + case MLAN_OID_PM_CFG_FW_WAKEUP_METHOD: + status = wlan_fw_wakeup_method(pmadapter, pioctl_req); + break; + case MLAN_OID_PM_CFG_SLEEP_PARAMS: + status = wlan_set_get_sleep_params(pmadapter, pioctl_req); + break; + case MLAN_OID_PM_INFO: + status = wlan_get_pm_info(pmadapter, pioctl_req); + break; + case MLAN_OID_PM_HS_WAKEUP_REASON: + status = wlan_get_hs_wakeup_reason(pmadapter, pioctl_req); + break; + case MLAN_OID_PM_MGMT_FILTER: + status = wlan_config_mgmt_filter(pmadapter, pioctl_req); + break; + case MLAN_OID_PM_CFG_BCN_TIMEOUT: + status = wlan_set_get_bcn_timeout(pmadapter, pioctl_req); + break; + default: + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + status = MLAN_STATUS_FAILURE; + break; + } + LEAVE(); + return status; +} + +/** + * @brief Set/Get WPA IE + * + * @param priv A pointer to mlan_private structure + * @param ie_data_ptr A pointer to IE + * @param ie_len Length of the IE + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +static mlan_status +wlan_set_wpa_ie_helper(mlan_private *priv, t_u8 *ie_data_ptr, t_u16 ie_len) +{ + ENTER(); + + if (ie_len) { + if (ie_len > sizeof(priv->wpa_ie)) { + PRINTM(MERROR, "failed to copy, WPA IE is too big\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + memcpy(priv->adapter, priv->wpa_ie, ie_data_ptr, ie_len); + priv->wpa_ie_len = (t_u8)ie_len; + PRINTM(MIOCTL, "Set Wpa_ie_len=%d IE=%#x\n", priv->wpa_ie_len, + priv->wpa_ie[0]); + DBG_HEXDUMP(MCMD_D, "Wpa_ie", priv->wpa_ie, priv->wpa_ie_len); + if (priv->wpa_ie[0] == WPA_IE) { + priv->sec_info.wpa_enabled = MTRUE; + } else if (priv->wpa_ie[0] == RSN_IE) { + priv->sec_info.wpa2_enabled = MTRUE; + } else { + priv->sec_info.wpa_enabled = MFALSE; + priv->sec_info.wpa2_enabled = MFALSE; + } + } else { + memset(priv->adapter, priv->wpa_ie, 0, sizeof(priv->wpa_ie)); + priv->wpa_ie_len = 0; + PRINTM(MINFO, "Reset Wpa_ie_len=%d IE=%#x\n", priv->wpa_ie_len, + priv->wpa_ie[0]); + priv->sec_info.wpa_enabled = MFALSE; + priv->sec_info.wpa2_enabled = MFALSE; + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Set WAPI IE + * + * @param priv A pointer to mlan_private structure + * @param ie_data_ptr A pointer to IE + * @param ie_len Length of the IE + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +static mlan_status +wlan_set_wapi_ie(mlan_private *priv, t_u8 *ie_data_ptr, t_u16 ie_len) +{ + ENTER(); + if (ie_len) { + if (ie_len > sizeof(priv->wapi_ie)) { + PRINTM(MWARN, "failed to copy, WAPI IE is too big\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + memcpy(priv->adapter, priv->wapi_ie, ie_data_ptr, ie_len); + priv->wapi_ie_len = (t_u8)ie_len; + PRINTM(MIOCTL, "Set wapi_ie_len=%d IE=%#x\n", priv->wapi_ie_len, + priv->wapi_ie[0]); + DBG_HEXDUMP(MCMD_D, "wapi_ie", priv->wapi_ie, + priv->wapi_ie_len); + if (priv->wapi_ie[0] == WAPI_IE) + priv->sec_info.wapi_enabled = MTRUE; + } else { + memset(priv->adapter, priv->wapi_ie, 0, sizeof(priv->wapi_ie)); + priv->wapi_ie_len = (t_u8)ie_len; + PRINTM(MINFO, "Reset wapi_ie_len=%d IE=%#x\n", + priv->wapi_ie_len, priv->wapi_ie[0]); + priv->sec_info.wapi_enabled = MFALSE; + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Set/Get WAPI status + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success + */ +static mlan_status +wlan_sec_ioctl_wapi_enable(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_sec_cfg *sec = MNULL; + ENTER(); + sec = (mlan_ds_sec_cfg *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_GET) { + if (pmpriv->wapi_ie_len) + sec->param.wapi_enabled = MTRUE; + else + sec->param.wapi_enabled = MFALSE; + } else { + if (sec->param.wapi_enabled == MFALSE) + wlan_set_wapi_ie(pmpriv, MNULL, 0); + } + pioctl_req->data_read_written = sizeof(t_u32) + MLAN_SUB_COMMAND_SIZE; + LEAVE(); + return ret; +} + +/** + * @brief Set WAPI key + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status +wlan_sec_ioctl_set_wapi_key(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_sec_cfg *sec = MNULL; + ENTER(); + + sec = (mlan_ds_sec_cfg *)pioctl_req->pbuf; + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_802_11_KEY_MATERIAL, + HostCmd_ACT_GEN_SET, + KEY_INFO_ENABLED, + (t_void *)pioctl_req, &sec->param.encrypt_key); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Set/Get Port Control status + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +static mlan_status +wlan_sec_ioctl_port_ctrl_enable(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_sec_cfg *sec = MNULL; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + + ENTER(); + + sec = (mlan_ds_sec_cfg *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_GET) { + if (pmpriv->port_ctrl_mode) + sec->param.port_ctrl_enabled = MTRUE; + else + sec->param.port_ctrl_enabled = MFALSE; + } else { + if (sec->param.port_ctrl_enabled) { + pmpriv->port_ctrl_mode = MTRUE; + pmpriv->port_open = MFALSE; + } else { + if (pmpriv->port_ctrl_mode == MTRUE) { + pmpriv->port_ctrl_mode = MFALSE; + /* Cleanup the bypass TX queue */ + wlan_cleanup_bypass_txq(pmpriv); + } + } + } + PRINTM(MINFO, "port_ctrl: port_ctrl_mode=%d port_open=%d\n", + pmpriv->port_ctrl_mode, pmpriv->port_open); + + LEAVE(); + return ret; +} + +/** + * @brief Set/Get authentication mode + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success + */ +static mlan_status +wlan_sec_ioctl_auth_mode(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_sec_cfg *sec = MNULL; + ENTER(); + sec = (mlan_ds_sec_cfg *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_GET) + sec->param.auth_mode = pmpriv->sec_info.authentication_mode; + else + pmpriv->sec_info.authentication_mode = sec->param.auth_mode; + + pioctl_req->data_read_written = sizeof(t_u32) + MLAN_SUB_COMMAND_SIZE; + LEAVE(); + return ret; +} + +/** + * @brief Set/Get encryption mode + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success + */ +static mlan_status +wlan_sec_ioctl_encrypt_mode(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_sec_cfg *sec = MNULL; + ENTER(); + sec = (mlan_ds_sec_cfg *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_GET) + sec->param.encrypt_mode = pmpriv->sec_info.encryption_mode; + else + pmpriv->sec_info.encryption_mode = sec->param.encrypt_mode; + + pioctl_req->data_read_written = sizeof(t_u32) + MLAN_SUB_COMMAND_SIZE; + LEAVE(); + return ret; +} + +/** + * @brief Get Random charactor + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return random charactor + */ +t_u8 +wlan_get_random_charactor(pmlan_adapter pmadapter) +{ + t_u32 sec, usec; + t_u8 ch = 0; + + ENTER(); + + pmadapter->callbacks.moal_get_system_time(pmadapter->pmoal_handle, &sec, + &usec); + sec = (sec & 0xFFFF) + (sec >> 16); + usec = (usec & 0xFFFF) + (usec >> 16); + ch = (((sec << 16) + usec) % 26) + 'a'; + LEAVE(); + return ch; +} + +/** + * @brief Set/Get WPA status + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success + */ +static mlan_status +wlan_sec_ioctl_wpa_enable(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_sec_cfg *sec = MNULL; + ENTER(); + sec = (mlan_ds_sec_cfg *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_GET) { + if (pmpriv->wpa_ie_len) + sec->param.wpa_enabled = MTRUE; + else + sec->param.wpa_enabled = MFALSE; + } else { + if (sec->param.wpa_enabled == MFALSE) + wlan_set_wpa_ie_helper(pmpriv, MNULL, 0); + /** clear adhoc aes flag, when WPA enabled */ + pmpriv->adhoc_aes_enabled = MFALSE; + pmpriv->aes_key.key_len = 0; + } + pioctl_req->data_read_written = sizeof(t_u32) + MLAN_SUB_COMMAND_SIZE; + LEAVE(); + return ret; +} + +/** + * @brief Set WEP keys + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status +wlan_sec_ioctl_set_wep_key(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_sec_cfg *sec = MNULL; + mrvl_wep_key_t *pwep_key = MNULL; + int index; + int i = 0; + + ENTER(); + + if (pmpriv->wep_key_curr_index >= MRVL_NUM_WEP_KEY) + pmpriv->wep_key_curr_index = 0; + pwep_key = &pmpriv->wep_key[pmpriv->wep_key_curr_index]; + sec = (mlan_ds_sec_cfg *)pioctl_req->pbuf; + if (sec->param.encrypt_key.key_index == MLAN_KEY_INDEX_DEFAULT) { + index = pmpriv->wep_key_curr_index; + sec->param.encrypt_key.key_index = index; + } else { + if (sec->param.encrypt_key.key_index >= MRVL_NUM_WEP_KEY) { + if ((sec->param.encrypt_key.key_remove == MTRUE)&& + (sec->param.encrypt_key.key_index <= 5)) { + /* call firmware remove key */ + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_802_11_KEY_MATERIAL, + HostCmd_ACT_GEN_SET, + 0, + MNULL, + &sec->param.encrypt_key); + goto exit; + } + PRINTM(MERROR, "Key_index is invalid\n"); + ret = MLAN_STATUS_FAILURE; + goto exit; + } + index = sec->param.encrypt_key.key_index; + } + + if ((sec->param.encrypt_key.key_disable == MTRUE)|| + (sec->param.encrypt_key.key_remove == MTRUE)) { + pmpriv->sec_info.wep_status = Wlan802_11WEPDisabled; + /* remove key */ + if (sec->param.encrypt_key.key_remove == MTRUE) { + memset(pmadapter, &pmpriv->wep_key[index], 0, + sizeof(mrvl_wep_key_t)); + /* call firmware remove key */ + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_802_11_KEY_MATERIAL, + HostCmd_ACT_GEN_SET, + 0, + MNULL, &sec->param.encrypt_key); + if (ret) + goto exit; + } + } else { + if (sec->param.encrypt_key.key_len) { + if ((sec->param.encrypt_key.key_len != + WEP_104_BIT_LEN)&&(sec->param.encrypt_key. + key_len != WEP_40_BIT_LEN)) { + PRINTM(MERROR, "Invalid wep key len=%d\n", + sec->param.encrypt_key.key_len); + /* We will use random key to clear the key buffer in FW */ + if (sec->param.encrypt_key.key_len < + WEP_40_BIT_LEN) + sec->param.encrypt_key.key_len = + WEP_40_BIT_LEN; + else + sec->param.encrypt_key.key_len = + WEP_104_BIT_LEN; + for (i = 0; i < sec->param.encrypt_key.key_len; + i++) + sec->param.encrypt_key.key_material[i] = + wlan_get_random_charactor + (pmadapter); + } + pwep_key = &pmpriv->wep_key[index]; + /* Cleanup */ + memset(pmadapter, pwep_key, 0, sizeof(mrvl_wep_key_t)); + /* Copy the key in the driver */ + + memcpy(pmadapter, pwep_key->key_material, + sec->param.encrypt_key.key_material, + sec->param.encrypt_key.key_len); + pwep_key->key_index = index; + pwep_key->key_length = sec->param.encrypt_key.key_len; + if (pmpriv->sec_info.wep_status != Wlan802_11WEPEnabled) { + /* + * The status is set as Key Absent + * so as to make sure we display the + * keys when iwlist mlanX key is used + */ + pmpriv->sec_info.wep_status = + Wlan802_11WEPKeyAbsent; + } + } + if (sec->param.encrypt_key.is_current_wep_key == MTRUE) { + /* Copy the required key as the current key */ + pwep_key = &pmpriv->wep_key[index]; + if (!pwep_key->key_length) { + if (0 + || &pmpriv->sec_info.wpa_enabled + || &pmpriv->sec_info.wpa2_enabled + || &pmpriv->sec_info.wapi_enabled) { + ret = MLAN_STATUS_SUCCESS; + goto exit; + } + PRINTM(MERROR, + "Key %d not set,so cannot enable it\n", + index); + pioctl_req->status_code = + MLAN_ERROR_CMD_RESP_FAIL; + ret = MLAN_STATUS_FAILURE; + goto exit; + } + pmpriv->wep_key_curr_index = (t_u16)index; + pmpriv->sec_info.wep_status = Wlan802_11WEPEnabled; + } + if (sec->param.encrypt_key.key_flags && pwep_key->key_length) { + pmpriv->wep_key_curr_index = (t_u16)index; + // Only do this if the key is an xmit key. If the key is a group key, + // we might be in wpa/wep mixed mode in which case we don't want to + // set wep_status = Wlan802_11WEPEnabled because that enables WEP + // at the MAC controller level and WPA stops working properly. + if (sec->param.encrypt_key. + key_flags & KEY_FLAG_SET_TX_KEY) { + pmpriv->sec_info.wep_status = + Wlan802_11WEPEnabled; + } + } + } + if (pmpriv->sec_info.wep_status == Wlan802_11WEPEnabled) + pmpriv->curr_pkt_filter |= HostCmd_ACT_MAC_WEP_ENABLE; + else + pmpriv->curr_pkt_filter &= ~HostCmd_ACT_MAC_WEP_ENABLE; + + /* Send request to firmware */ + if (pmpriv->sec_info.wep_status == Wlan802_11WEPEnabled + && pwep_key->key_length) { + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_MAC_CONTROL, + HostCmd_ACT_GEN_SET, + 0, MNULL, &pmpriv->curr_pkt_filter); + if (ret) + goto exit; + if (!sec->param.encrypt_key.key_len) { + sec->param.encrypt_key.key_index = pwep_key->key_index; + sec->param.encrypt_key.key_len = pwep_key->key_length; + memcpy(pmadapter, sec->param.encrypt_key.key_material, + pwep_key->key_material, + sec->param.encrypt_key.key_len); + } + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_802_11_KEY_MATERIAL, + HostCmd_ACT_GEN_SET, + 0, + (t_void *)pioctl_req, + &sec->param.encrypt_key); + } else { + if (pwep_key->key_length) { + if (!sec->param.encrypt_key.key_len) { + sec->param.encrypt_key.key_index = + pwep_key->key_index; + sec->param.encrypt_key.key_len = + pwep_key->key_length; + memcpy(pmadapter, + sec->param.encrypt_key.key_material, + pwep_key->key_material, + sec->param.encrypt_key.key_len); + } + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_802_11_KEY_MATERIAL, + HostCmd_ACT_GEN_SET, + 0, + MNULL, &sec->param.encrypt_key); + if (ret) + goto exit; + } + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_MAC_CONTROL, + HostCmd_ACT_GEN_SET, + 0, + (t_void *)pioctl_req, + &pmpriv->curr_pkt_filter); + } + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + +exit: + LEAVE(); + return ret; +} + +/** + * @brief Set WPA key + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status +wlan_sec_ioctl_set_wpa_key(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_sec_cfg *sec = MNULL; + + ENTER(); + + sec = (mlan_ds_sec_cfg *)pioctl_req->pbuf; + /* Current driver only supports key length of up to 32 bytes */ + if (sec->param.encrypt_key.key_len > MLAN_MAX_KEY_LENGTH) { + PRINTM(MERROR, "Key length is incorrect\n"); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_FAILURE; + goto exit; + } + + if (sec->param.encrypt_key.key_len == WPA_AES_KEY_LEN) { + /** back up adhoc AES key */ + memset(pmpriv->adapter, &pmpriv->aes_key, 0, + sizeof(pmpriv->aes_key)); + memcpy(pmpriv->adapter, (t_u8 *)&pmpriv->aes_key, + (t_u8 *)&sec->param.encrypt_key, + sizeof(pmpriv->aes_key)); + } + + /** only adhoc aes key_index = MLAN_KEY_INDEX_UNICAST */ + if (pmpriv->bss_mode == MLAN_BSS_MODE_IBSS && + sec->param.encrypt_key.key_len == WPA_AES_KEY_LEN + && sec->param.encrypt_key.key_index & MLAN_KEY_INDEX_UNICAST) { + t_u8 zero_key_material[WPA_AES_KEY_LEN]; + memset(pmadapter, zero_key_material, 0, + sizeof(zero_key_material)); + if (memcmp + (pmadapter, sec->param.encrypt_key.key_material, + zero_key_material, WPA_AES_KEY_LEN)) { + PRINTM(MINFO, "Adhoc AES Enabled.\n"); + pmpriv->adhoc_aes_enabled = MTRUE; + } else { + PRINTM(MINFO, "Adhoc AES Disabled.\n"); + pmpriv->adhoc_aes_enabled = MFALSE; + /** clear adhoc AES key */ + pmpriv->aes_key.key_len = 0; + } + } + + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_802_11_KEY_MATERIAL, + HostCmd_ACT_GEN_SET, + 0, + (t_void *)pioctl_req, &sec->param.encrypt_key); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + +exit: + LEAVE(); + return ret; +} + +/** + * @brief Get security keys + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +static mlan_status +wlan_sec_ioctl_get_key(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_sec_cfg *sec = MNULL; + int index; + ENTER(); + + sec = (mlan_ds_sec_cfg *)pioctl_req->pbuf; + + if ((sec->param.encrypt_key.key_index == MLAN_KEY_INDEX_UNICAST)&& + (sec->param.encrypt_key.key_len == WPA_AES_KEY_LEN)) { + if (pmpriv->adhoc_aes_enabled == MTRUE && + (pmpriv->aes_key.key_len == WPA_AES_KEY_LEN)) { + HEXDUMP("Get ADHOCAES Key", + pmpriv->aes_key.key_material, WPA_AES_KEY_LEN); + memcpy(pmadapter, sec->param.encrypt_key.key_material, + pmpriv->aes_key.key_material, WPA_AES_KEY_LEN); + LEAVE(); + return ret; + } else { + PRINTM(MERROR, " ADHOCAES key is not set yet!\n"); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_FAILURE; + LEAVE(); + return ret; + } + } + if (pmpriv->wep_key_curr_index >= MRVL_NUM_WEP_KEY) + pmpriv->wep_key_curr_index = 0; + + if ((pmpriv->sec_info.wep_status == Wlan802_11WEPEnabled) + || (pmpriv->sec_info.wep_status == Wlan802_11WEPKeyAbsent) + || pmpriv->sec_info.ewpa_enabled + || pmpriv->sec_info.wpa_enabled + || pmpriv->sec_info.wpa2_enabled || pmpriv->adhoc_aes_enabled) { + sec->param.encrypt_key.key_disable = MFALSE; + } else { + sec->param.encrypt_key.key_disable = MTRUE; + } + if (sec->param.encrypt_key.key_index == MLAN_KEY_INDEX_DEFAULT) { + if ((pmpriv->wep_key[pmpriv->wep_key_curr_index].key_length)&& + (pmpriv->sec_info.wep_status == Wlan802_11WEPEnabled)) { + index = pmpriv->wep_key_curr_index; + sec->param.encrypt_key.key_index = + pmpriv->wep_key[index].key_index; + memcpy(pmadapter, sec->param.encrypt_key.key_material, + pmpriv->wep_key[index].key_material, + MIN(MLAN_MAX_KEY_LENGTH, + pmpriv->wep_key[index].key_length)); + sec->param.encrypt_key.key_len = + MIN(MLAN_MAX_KEY_LENGTH, + pmpriv->wep_key[index].key_length); + } else if ((pmpriv->sec_info.wpa_enabled) + || (pmpriv->sec_info.ewpa_enabled) + || (pmpriv->sec_info.wpa2_enabled) + || (pmpriv->sec_info.wapi_enabled) + || (pmpriv->adhoc_aes_enabled) + ) { + /* Return WPA enabled */ + sec->param.encrypt_key.key_disable = MFALSE; + + memcpy(pmadapter, sec->param.encrypt_key.key_material, + pmpriv->aes_key.key_material, + MIN(MLAN_MAX_KEY_LENGTH, + pmpriv->aes_key.key_len)); + sec->param.encrypt_key.key_len = + MIN(MLAN_MAX_KEY_LENGTH, + pmpriv->aes_key.key_len); + } else { + sec->param.encrypt_key.key_disable = MTRUE; + } + } else { + index = sec->param.encrypt_key.key_index; + if (pmpriv->wep_key[index].key_length) { + sec->param.encrypt_key.key_index = + pmpriv->wep_key[index].key_index; + memcpy(pmadapter, sec->param.encrypt_key.key_material, + pmpriv->wep_key[index].key_material, + MIN(MLAN_MAX_KEY_LENGTH, + pmpriv->wep_key[index].key_length)); + sec->param.encrypt_key.key_len = + MIN(MLAN_MAX_KEY_LENGTH, + pmpriv->wep_key[index].key_length); + } else if ((pmpriv->sec_info.wpa_enabled) + || (pmpriv->sec_info.ewpa_enabled) + || (pmpriv->sec_info.wpa2_enabled) + || (pmpriv->sec_info.wapi_enabled) + || (pmpriv->adhoc_aes_enabled) + ) { + /* Return WPA enabled */ + sec->param.encrypt_key.key_disable = MFALSE; + } else { + sec->param.encrypt_key.key_disable = MTRUE; + } + } + + LEAVE(); + return ret; +} + +/** + * @brief Set security key(s) + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status +wlan_sec_ioctl_encrypt_key(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_status status = MLAN_STATUS_SUCCESS; + mlan_ds_sec_cfg *sec = MNULL; + ENTER(); + sec = (mlan_ds_sec_cfg *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_SET) { + if (sec->param.encrypt_key.is_wapi_key) + status = wlan_sec_ioctl_set_wapi_key(pmadapter, + pioctl_req); + else if (sec->param.encrypt_key.key_len > MAX_WEP_KEY_SIZE) + status = wlan_sec_ioctl_set_wpa_key(pmadapter, + pioctl_req); + else + status = wlan_sec_ioctl_set_wep_key(pmadapter, + pioctl_req); + } else { + status = wlan_sec_ioctl_get_key(pmadapter, pioctl_req); + } + LEAVE(); + return status; +} + +/** + * @brief Set/Get esupplicant status + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success + */ +static mlan_status +wlan_sec_ioctl_ewpa_enable(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_sec_cfg *sec = MNULL; + ENTER(); + sec = (mlan_ds_sec_cfg *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_GET) { + sec->param.ewpa_enabled = pmpriv->sec_info.ewpa_enabled; + } else { + pmpriv->sec_info.ewpa_enabled = (t_u8)sec->param.ewpa_enabled; + PRINTM(MINFO, "Set: ewpa_enabled = %d\n", + (int)pmpriv->sec_info.ewpa_enabled); + } + pioctl_req->data_read_written = sizeof(t_u32) + MLAN_SUB_COMMAND_SIZE; + LEAVE(); + return ret; +} + +/** + * @brief Get esupplicant mode + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status +wlan_sec_ioctl_esupp_mode(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + t_u16 cmd_action = 0; + mlan_ds_sec_cfg *sec = MNULL; + + ENTER(); + + sec = (mlan_ds_sec_cfg *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_SET) { + cmd_action = HostCmd_ACT_GEN_SET; + if (pmpriv->media_connected == MTRUE) { + PRINTM(MERROR, + "Cannot set esupplicant mode configuration while connected.\n"); + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + ret = MLAN_STATUS_FAILURE; + goto exit; + } + if (!sec->param.esupp_mode.rsn_mode || + (sec->param.esupp_mode.rsn_mode & RSN_TYPE_VALID_BITS) + != sec->param.esupp_mode.rsn_mode) { + PRINTM(MERROR, "Invalid RSN mode\n"); + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + ret = MLAN_STATUS_FAILURE; + goto exit; + } + if (!sec->param.esupp_mode.act_paircipher || + (sec->param.esupp_mode. + act_paircipher & EMBED_CIPHER_VALID_BITS) + != sec->param.esupp_mode.act_paircipher) { + PRINTM(MERROR, "Invalid pairwise cipher\n"); + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + ret = MLAN_STATUS_FAILURE; + goto exit; + } + if (!sec->param.esupp_mode.act_groupcipher || + (sec->param.esupp_mode. + act_groupcipher & EMBED_CIPHER_VALID_BITS) + != sec->param.esupp_mode.act_groupcipher) { + PRINTM(MERROR, "Invalid group cipher\n"); + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + ret = MLAN_STATUS_FAILURE; + goto exit; + } + } else { + cmd_action = HostCmd_ACT_GEN_GET_CURRENT; + } + + /* Send request to firmware */ + if (sec) { + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_SUPPLICANT_PROFILE, + cmd_action, + 0, + (t_void *)pioctl_req, + &sec->param.esupp_mode); + } else { + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_SUPPLICANT_PROFILE, + cmd_action, + 0, (t_void *)pioctl_req, MNULL); + } + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + +exit: + LEAVE(); + return ret; +} + +/** + * @brief Security configuration handler + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status +wlan_sec_cfg_ioctl(IN pmlan_adapter pmadapter, IN pmlan_ioctl_req pioctl_req) +{ + mlan_status status = MLAN_STATUS_SUCCESS; + mlan_ds_sec_cfg *sec = MNULL; + + ENTER(); + + if (pioctl_req->buf_len < sizeof(mlan_ds_sec_cfg)) { + PRINTM(MWARN, "MLAN bss IOCTL length is too short.\n"); + pioctl_req->data_read_written = 0; + pioctl_req->buf_len_needed = sizeof(mlan_ds_sec_cfg); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + LEAVE(); + return MLAN_STATUS_RESOURCE; + } + sec = (mlan_ds_sec_cfg *)pioctl_req->pbuf; + switch (sec->sub_command) { + case MLAN_OID_SEC_CFG_AUTH_MODE: + status = wlan_sec_ioctl_auth_mode(pmadapter, pioctl_req); + break; + case MLAN_OID_SEC_CFG_ENCRYPT_MODE: + status = wlan_sec_ioctl_encrypt_mode(pmadapter, pioctl_req); + break; + case MLAN_OID_SEC_CFG_WPA_ENABLED: + status = wlan_sec_ioctl_wpa_enable(pmadapter, pioctl_req); + break; + case MLAN_OID_SEC_CFG_WAPI_ENABLED: + status = wlan_sec_ioctl_wapi_enable(pmadapter, pioctl_req); + break; + case MLAN_OID_SEC_CFG_PORT_CTRL_ENABLED: + status = wlan_sec_ioctl_port_ctrl_enable(pmadapter, pioctl_req); + break; + case MLAN_OID_SEC_CFG_ENCRYPT_KEY: + status = wlan_sec_ioctl_encrypt_key(pmadapter, pioctl_req); + break; + case MLAN_OID_SEC_CFG_PASSPHRASE: + status = wlan_sec_ioctl_passphrase(pmadapter, pioctl_req); + break; + case MLAN_OID_SEC_CFG_EWPA_ENABLED: + status = wlan_sec_ioctl_ewpa_enable(pmadapter, pioctl_req); + break; + case MLAN_OID_SEC_CFG_ESUPP_MODE: + status = wlan_sec_ioctl_esupp_mode(pmadapter, pioctl_req); + break; + + default: + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + status = MLAN_STATUS_FAILURE; + break; + } + LEAVE(); + return status; +} + +/** + * @brief Append/Reset IE buffer. + * + * Pass an opaque block of data, expected to be IEEE IEs, to the driver + * for eventual passthrough to the firmware in an associate/join + * (and potentially start) command. This function is the main body + * for both wlan_set_gen_ie_ioctl and wlan_set_gen_ie + * + * Data is appended to an existing buffer and then wrapped in a passthrough + * TLV in the command API to the firmware. The firmware treats the data + * as a transparent passthrough to the transmitted management frame. + * + * @param priv A pointer to mlan_private structure + * @param ie_data_ptr A pointer to iwreq structure + * @param ie_len Length of the IE or IE block passed in ie_data_ptr + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +static int +wlan_set_gen_ie_helper(mlan_private *priv, t_u8 *ie_data_ptr, t_u16 ie_len) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + IEEEtypes_VendorHeader_t *pvendor_ie; + const t_u8 wpa_oui[] = { 0x00, 0x50, 0xf2, 0x01 }; + const t_u8 wps_oui[] = { 0x00, 0x50, 0xf2, 0x04 }; + + ENTER(); + + /* If the passed length is zero, reset the buffer */ + if (!ie_len) { + priv->gen_ie_buf_len = 0; + priv->wps.session_enable = MFALSE; + wlan_set_wpa_ie_helper(priv, MNULL, 0); + wlan_set_wapi_ie(priv, MNULL, 0); + } else if (!ie_data_ptr) { + /* MNULL check */ + ret = MLAN_STATUS_FAILURE; + } else { + + pvendor_ie = (IEEEtypes_VendorHeader_t *)ie_data_ptr; + if (pvendor_ie->element_id == EXT_CAPABILITY) { + t_u8 len = sizeof(priv->ext_cap); + if (len > pvendor_ie->len) + len = pvendor_ie->len; + memcpy(priv->adapter, &priv->ext_cap, &ie_data_ptr[2], + len); + } else + /* Test to see if it is a WPA IE, if not, then it is a gen IE */ + if (((pvendor_ie->element_id == WPA_IE) + && + (!memcmp + (priv->adapter, pvendor_ie->oui, wpa_oui, + sizeof(wpa_oui)))) + || (pvendor_ie->element_id == RSN_IE) + ) { + + /* IE is a WPA/WPA2 IE so call set_wpa function */ + ret = wlan_set_wpa_ie_helper(priv, ie_data_ptr, ie_len); + priv->wps.session_enable = MFALSE; + } else if (pvendor_ie->element_id == WAPI_IE) { + /* IE is a WAPI IE so call set_wapi function */ + ret = wlan_set_wapi_ie(priv, ie_data_ptr, ie_len); + } else if ((pvendor_ie->element_id == WPS_IE) && + (priv->wps.session_enable == MFALSE) && + (!memcmp + (priv->adapter, pvendor_ie->oui, wps_oui, + sizeof(wps_oui)))) { + /* + * Discard first two byte (Element ID and Length) + * because they are not needed in the case of setting + * WPS_IE + */ + if (pvendor_ie->len > 4) { + memcpy(priv->adapter, (t_u8 *)&priv->wps.wps_ie, + ie_data_ptr, ie_len); + HEXDUMP("wps_ie", (t_u8 *)&priv->wps.wps_ie, + priv->wps.wps_ie.vend_hdr.len + 2); + } else { + /* Only wps oui exist, reset driver wps buffer */ + memset(priv->adapter, (t_u8 *)&priv->wps.wps_ie, + 0x00, sizeof(priv->wps.wps_ie)); + PRINTM(MINFO, "wps_ie cleared\n"); + } + } else { + /* + * Verify that the passed length is not larger than + * the available space remaining in the buffer + */ + if (ie_len < + (sizeof(priv->gen_ie_buf) - priv->gen_ie_buf_len)) { + + /* Test to see if it is a WPS IE, if so, enable wps session flag */ + pvendor_ie = + (IEEEtypes_VendorHeader_t *)ie_data_ptr; + if ((pvendor_ie->element_id == WPS_IE) + && + (!memcmp + (priv->adapter, pvendor_ie->oui, wps_oui, + sizeof(wps_oui)))) { + priv->wps.session_enable = MTRUE; + PRINTM(MINFO, "WPS Session Enabled.\n"); + } + + /* Append the passed data to the end of + * the genIeBuffer */ + memcpy(priv->adapter, + priv->gen_ie_buf + priv->gen_ie_buf_len, + ie_data_ptr, ie_len); + /* Increment the stored buffer length by + * the size passed */ + priv->gen_ie_buf_len += ie_len; + } else { + /* Passed data does not fit in the + * remaining buffer space */ + ret = MLAN_STATUS_FAILURE; + } + } + } + + /* Return MLAN_STATUS_SUCCESS, or MLAN_STATUS_FAILURE for error case */ + LEAVE(); + return ret; +} + +/** + * @brief Set/Get WWS mode + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status +wlan_misc_ioctl_wws_cfg(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + pmlan_private pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_misc_cfg *misc_cfg = MNULL; + t_u16 cmd_action = 0; + t_u32 enable = 0; + + ENTER(); + + if (pioctl_req->buf_len < sizeof(mlan_ds_misc_cfg)) { + PRINTM(MWARN, + "MLAN IOCTL information buffer length is too short.\n"); + pioctl_req->data_read_written = 0; + pioctl_req->buf_len_needed = sizeof(mlan_ds_misc_cfg); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_RESOURCE; + goto exit; + } + + misc_cfg = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_SET) + cmd_action = HostCmd_ACT_GEN_SET; + else + cmd_action = HostCmd_ACT_GEN_GET; + + enable = misc_cfg->param.wws_cfg; + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_802_11_SNMP_MIB, + cmd_action, + WwsMode_i, (t_void *)pioctl_req, &enable); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + +exit: + LEAVE(); + return ret; + +} + +/** + * @brief Set/Get 11D status + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status +wlan_11d_cfg_ioctl_enable(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_11d_cfg *pcfg_11d = MNULL; + + ENTER(); + + pcfg_11d = (mlan_ds_11d_cfg *)pioctl_req->pbuf; + + if (pioctl_req->action == MLAN_ACT_SET) { + if (pmpriv->media_connected == MTRUE) { + PRINTM(MIOCTL, + "11D setting cannot be changed while interface is active.\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + PRINTM(MINFO, "11D: 11dcfg SET=%d\n", + pcfg_11d->param.enable_11d); + + /* Compare with current settings */ + if (pmpriv->state_11d.user_enable_11d != + pcfg_11d->param.enable_11d) { + ret = wlan_11d_enable(pmpriv, pioctl_req, + (state_11d_t)pcfg_11d->param. + enable_11d); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + } else { + PRINTM(MINFO, + "11D: same as current setting, do nothing\n"); + } + } else { + pcfg_11d->param.enable_11d = + (t_u32)pmpriv->state_11d.user_enable_11d; + pioctl_req->data_read_written = + sizeof(t_u32) + MLAN_SUB_COMMAND_SIZE; + PRINTM(MINFO, "11D: 11dcfg GET=%d\n", + pcfg_11d->param.enable_11d); + } + +done: + LEAVE(); + return ret; +} + +/** + * @brief Clear 11D chan table + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success + */ +static mlan_status +wlan_11d_clr_chan_table(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + + ENTER(); + + if (pioctl_req->action == MLAN_ACT_SET) { + PRINTM(MINFO, "11D: 11dclrtbl SET\n"); + + if (wlan_11d_clear_parsedtable(pmpriv) == MLAN_STATUS_SUCCESS) + PRINTM(MINFO, + "11D: cleared parsed_region_chan (now no_of_chan=%d)\n", + pmadapter->parsed_region_chan.no_of_chan); + } + + LEAVE(); + return ret; +} + +/** + * @brief 11D configuration handler + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status +wlan_11d_cfg_ioctl(IN pmlan_adapter pmadapter, IN pmlan_ioctl_req pioctl_req) +{ + mlan_status status = MLAN_STATUS_SUCCESS; + mlan_ds_11d_cfg *pcfg_11d = MNULL; + + ENTER(); + + if (pioctl_req->buf_len < sizeof(mlan_ds_11d_cfg)) { + PRINTM(MWARN, "MLAN bss IOCTL length is too short.\n"); + pioctl_req->data_read_written = 0; + pioctl_req->buf_len_needed = sizeof(mlan_ds_11d_cfg); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + status = MLAN_STATUS_RESOURCE; + goto exit; + } + + pcfg_11d = (mlan_ds_11d_cfg *)pioctl_req->pbuf; + switch (pcfg_11d->sub_command) { + case MLAN_OID_11D_CFG_ENABLE: + status = wlan_11d_cfg_ioctl_enable(pmadapter, pioctl_req); + break; + case MLAN_OID_11D_CLR_CHAN_TABLE: + status = wlan_11d_clr_chan_table(pmadapter, pioctl_req); + break; + case MLAN_OID_11D_DOMAIN_INFO: + status = wlan_11d_cfg_domain_info(pmadapter, pioctl_req); + break; + default: + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + status = MLAN_STATUS_FAILURE; + break; + } + +exit: + LEAVE(); + return status; +} + +/** + * @brief WPS configuration handler + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +static mlan_status +wlan_wps_cfg_ioctl(IN pmlan_adapter pmadapter, IN pmlan_ioctl_req pioctl_req) +{ + mlan_status status = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_wps_cfg *pwps = MNULL; + + ENTER(); + + if (pioctl_req->buf_len < sizeof(mlan_ds_wps_cfg)) { + PRINTM(MWARN, "MLAN bss IOCTL length is too short.\n"); + pioctl_req->data_read_written = 0; + pioctl_req->buf_len_needed = sizeof(mlan_ds_wps_cfg); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + LEAVE(); + return MLAN_STATUS_RESOURCE; + } + + pwps = (mlan_ds_wps_cfg *)pioctl_req->pbuf; + switch (pwps->sub_command) { + case MLAN_OID_WPS_CFG_SESSION: + if (pioctl_req->action == MLAN_ACT_SET) { + if (pwps->param.wps_session == + MLAN_WPS_CFG_SESSION_START) + pmpriv->wps.session_enable = MTRUE; + else + pmpriv->wps.session_enable = MFALSE; + } else { + pwps->param.wps_session = + (t_u32)pmpriv->wps.session_enable; + pioctl_req->data_read_written = sizeof(t_u32); + PRINTM(MINFO, "wpscfg GET=%d\n", + pwps->param.wps_session); + } + break; + default: + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + status = MLAN_STATUS_FAILURE; + break; + } + + LEAVE(); + return status; +} + +/** + * @brief register memory access handler + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status +wlan_reg_mem_ioctl(IN pmlan_adapter pmadapter, IN pmlan_ioctl_req pioctl_req) +{ + mlan_status status = MLAN_STATUS_SUCCESS; + mlan_ds_reg_mem *reg_mem = MNULL; + + ENTER(); + + if (pioctl_req->buf_len < sizeof(mlan_ds_reg_mem)) { + PRINTM(MWARN, "MLAN REG_MEM IOCTL length is too short\n"); + pioctl_req->data_read_written = 0; + pioctl_req->buf_len_needed = sizeof(mlan_ds_reg_mem); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + LEAVE(); + return MLAN_STATUS_RESOURCE; + } + reg_mem = (mlan_ds_reg_mem *)pioctl_req->pbuf; + switch (reg_mem->sub_command) { + case MLAN_OID_REG_RW: + status = wlan_reg_mem_ioctl_reg_rw(pmadapter, pioctl_req); + break; + case MLAN_OID_EEPROM_RD: + status = wlan_reg_mem_ioctl_read_eeprom(pmadapter, pioctl_req); + break; + case MLAN_OID_MEM_RW: + status = wlan_reg_mem_ioctl_mem_rw(pmadapter, pioctl_req); + break; + default: + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + status = MLAN_STATUS_FAILURE; + break; + } + LEAVE(); + return status; +} + +/** + * @brief 802.11h ad-hoc start channel check + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status +wlan_11h_channel_check_req(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + pmlan_private pmpriv = MNULL; + mlan_status ret = MLAN_STATUS_FAILURE; + t_u8 chan_width = CHAN_BW_20MHZ; + Band_Config_t bandcfg; + + ENTER(); + + if (pioctl_req != MNULL) { + pmpriv = pmadapter->priv[pioctl_req->bss_index]; + } else { + PRINTM(MERROR, "MLAN IOCTL information is not present\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + memset(pmadapter, &bandcfg, 0, sizeof(Band_Config_t)); + pmpriv->adhoc_state = ADHOC_STARTING; + + if ((pmadapter->adhoc_start_band & BAND_A) + || (pmadapter->adhoc_start_band & BAND_AN) + ) { + if (pmpriv->intf_state_11h.adhoc_auto_sel_chan) + pmpriv->adhoc_channel = + wlan_11h_get_adhoc_start_channel(pmpriv); + + /* + * Check if the region and channel requires a channel availability + * check. + */ + if (wlan_11h_radar_detect_required + (pmpriv, pmpriv->adhoc_channel) + && !wlan_11h_is_channel_under_nop(pmadapter, + pmpriv->adhoc_channel) + ) { + /* + * Radar detection is required for this channel, make sure + * 11h is activated in the firmware + */ + ret = wlan_11h_activate(pmpriv, MNULL, MTRUE); + ret = wlan_11h_config_master_radar_det(pmpriv, MTRUE); + ret = wlan_11h_check_update_radar_det_state(pmpriv); + + /* Check for radar on the channel */ + if ((pmadapter->chan_bandwidth == + CHANNEL_BW_40MHZ_ABOVE) || + (pmadapter->chan_bandwidth == + CHANNEL_BW_40MHZ_BELOW)) { + chan_width = CHAN_BW_40MHZ; + if (pmadapter->chanrpt_param_bandcfg) { + bandcfg.chan2Offset = + pmadapter->chan_bandwidth; + } + } + if (pmadapter->chanrpt_param_bandcfg) { + bandcfg.chanWidth = chan_width; + bandcfg.chanBand = BAND_5GHZ; + } else { + *((t_u8 *)&bandcfg) = chan_width; + } + + ret = wlan_11h_issue_radar_detect(pmpriv, pioctl_req, + pmpriv->adhoc_channel, + bandcfg); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + } + } + + LEAVE(); + return ret; +} + +/** + * @brief 802.11h set/get local power constraint + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_11h_ioctl_local_power_constraint(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_ds_11h_cfg *ds_11hcfg = MNULL; + t_s8 *plocalpower = &pmadapter->state_11h.usr_def_power_constraint; + + ENTER(); + + ds_11hcfg = (mlan_ds_11h_cfg *)pioctl_req->pbuf; + + if (pioctl_req->action == MLAN_ACT_GET) + ds_11hcfg->param.usr_local_power_constraint = *plocalpower; + else + *plocalpower = ds_11hcfg->param.usr_local_power_constraint; + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief 11h configuration handler + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status +wlan_11h_cfg_ioctl(IN pmlan_adapter pmadapter, IN pmlan_ioctl_req pioctl_req) +{ + mlan_status status = MLAN_STATUS_SUCCESS; + mlan_ds_11h_cfg *ds_11hcfg = MNULL; + + ENTER(); + + if (pioctl_req->buf_len < sizeof(mlan_ds_11h_cfg)) { + PRINTM(MWARN, "MLAN 11H IOCTL length is too short.\n"); + pioctl_req->data_read_written = 0; + pioctl_req->buf_len_needed = sizeof(mlan_ds_11h_cfg); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + LEAVE(); + return MLAN_STATUS_RESOURCE; + } + ds_11hcfg = (mlan_ds_11h_cfg *)pioctl_req->pbuf; + + switch (ds_11hcfg->sub_command) { + case MLAN_OID_11H_CHANNEL_CHECK: + status = wlan_11h_channel_check_req(pmadapter, pioctl_req); + break; + case MLAN_OID_11H_LOCAL_POWER_CONSTRAINT: + status = wlan_11h_ioctl_local_power_constraint(pmadapter, + pioctl_req); + break; +#if defined(DFS_TESTING_SUPPORT) + case MLAN_OID_11H_DFS_TESTING: + status = wlan_11h_ioctl_dfs_testing(pmadapter, pioctl_req); + break; +#endif + default: + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + status = MLAN_STATUS_FAILURE; + break; + } + + LEAVE(); + return status; +} + +/** + * @brief 11k enable + * + * @param pmpriv A pointer to mlan_private structure + * @param pioctl_buf A pointer to ioctl request buffer + * @param enable_11k 11k enable flag. + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +mlan_status +wlan_11k_enable(mlan_private *pmpriv, t_void *pioctl_buf, t_u8 enable_11k) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + HostCmd_OFFLOAD_FEATURE_CTRL fctrl; + + ENTER(); + + /* Send cmd HostCmd_CMD_OFFLOAD_FEATURE_CONTROL to FW to enable/disable 11K function */ + /* need to active 11h first when enable 11k */ + if (!wlan_11h_is_active(pmpriv)) { + ret = wlan_11h_activate(pmpriv, MNULL, MTRUE); + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "11h enable failed \n"); + goto done; + } + } + memset(pmpriv->adapter, &fctrl, 0, sizeof(fctrl)); + fctrl.featureSelect = 0; /* Std feature */ + + /* enable or disable Neighbor AP list Report */ + fctrl.control.std.dot11k_nbor_support = enable_11k; + /* enable or disable Traffic Stream Measurement */ + fctrl.control.std.dot11k_tsm = enable_11k; + /* enable or disable Link Measurement */ + fctrl.control.std.dot11k_lm = enable_11k; + /* enable or disable Radio Measurement (Beacon Report) */ + fctrl.control.std.dot11k_rm = enable_11k; + + /* enable 11v BSS Transition with 11k */ + fctrl.control.std.dot11v_bss_trans = enable_11k; + + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_OFFLOAD_FEATURE_CONTROL, + HostCmd_ACT_GEN_SET, + 0, (t_void *)pioctl_buf, &fctrl); + + if (ret) { + PRINTM(MERROR, "11K: Failed to %s 11K\n", + (enable_11k) ? "enable" : "disable"); + } + +done: + LEAVE(); + return ret; +} + +/** + * @brief 11k enable ioctl + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +static mlan_status +wlan_11k_cfg_ioctl_enable(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_11k_cfg *pcfg_11k = MNULL; + + ENTER(); + + pcfg_11k = (mlan_ds_11k_cfg *) pioctl_req->pbuf; + + if (pioctl_req->action == MLAN_ACT_SET) { + if (pmpriv->media_connected == MTRUE + && pcfg_11k->param.enable_11k != pmpriv->enable_11k) { + PRINTM(MERROR, + "11K setting cannot be changed while connecting with AP.\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + PRINTM(MINFO, "11K: 11kcfg SET=%d\n", + pcfg_11k->param.enable_11k); + + /* Compare with current settings */ + if (pmpriv->enable_11k != pcfg_11k->param.enable_11k) { + ret = wlan_11k_enable(pmpriv, pioctl_req, + pcfg_11k->param.enable_11k); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + } else { + PRINTM(MINFO, + "11K: same as current setting, do nothing\n"); + } + } else { + pcfg_11k->param.enable_11k = pmpriv->enable_11k; + pioctl_req->data_read_written = + sizeof(t_u32) + MLAN_SUB_COMMAND_SIZE; + PRINTM(MINFO, "11K: 11kcfg GET=%d\n", + pcfg_11k->param.enable_11k); + } +done: + LEAVE(); + return ret; +} + +/** + * @brief 11k get nlist + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +static mlan_status +wlan_11k_get_nlist(IN pmlan_adapter pmadapter, IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + + ENTER(); + + if (pioctl_req->action == MLAN_ACT_GET && pmpriv->media_connected) { + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_802_11K_GET_NLIST, + HostCmd_ACT_GEN_GET, + 0, pioctl_req, MNULL); + if (ret) { + PRINTM(MERROR, "11K: prepare get_nlist failed\n"); + ret = MLAN_STATUS_FAILURE; + } else { + /* start excute command */ + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + } + } else { + PRINTM(MERROR, " 11k get_nlist only support GET oper\n"); + ret = MLAN_STATUS_FAILURE; + } + + LEAVE(); + return ret; +} + +/** + * @brief 11K cfg ioctl + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +static mlan_status +wlan_11k_cfg_ioctl(IN pmlan_adapter pmadapter, IN pmlan_ioctl_req pioctl_req) +{ + mlan_status status = MLAN_STATUS_SUCCESS; + mlan_ds_11k_cfg *ds_11kcfg = MNULL; + + ENTER(); + + if (pioctl_req->buf_len < sizeof(mlan_ds_11k_cfg)) { + PRINTM(MWARN, "MLAN 11K IOCTL length is too short.\n"); + pioctl_req->data_read_written = 0; + pioctl_req->buf_len_needed = sizeof(mlan_ds_11k_cfg); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + LEAVE(); + return MLAN_STATUS_RESOURCE; + } + + ds_11kcfg = (mlan_ds_11k_cfg *) pioctl_req->pbuf; + switch (ds_11kcfg->sub_command) { + case MLAN_OID_11K_CFG_ENABLE: + status = wlan_11k_cfg_ioctl_enable(pmadapter, pioctl_req); + break; + case MLAN_OID_11K_GET_NLIST: + status = wlan_11k_get_nlist(pmadapter, pioctl_req); + break; + default: + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + status = MLAN_STATUS_FAILURE; + break; + } + + LEAVE(); + return status; +} + +/** + * @brief Set/Get generic IE + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +static mlan_status +wlan_misc_ioctl_gen_ie(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_misc_cfg *misc = MNULL; + + ENTER(); + + misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + switch (misc->param.gen_ie.type) { + case MLAN_IE_TYPE_GEN_IE: + if (pioctl_req->action == MLAN_ACT_GET) { + misc->param.gen_ie.len = pmpriv->wpa_ie_len; + memcpy(pmadapter, misc->param.gen_ie.ie_data, + pmpriv->wpa_ie, misc->param.gen_ie.len); + } else { + wlan_set_gen_ie_helper(pmpriv, + misc->param.gen_ie.ie_data, + (t_u16)misc->param.gen_ie.len); + } + break; + case MLAN_IE_TYPE_ARP_FILTER: + memset(pmadapter, pmadapter->arp_filter, 0, + sizeof(pmadapter->arp_filter)); + if (misc->param.gen_ie.len > ARP_FILTER_MAX_BUF_SIZE) { + pmadapter->arp_filter_size = 0; + PRINTM(MERROR, "Invalid ARP Filter Size\n"); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_FAILURE; + } else if (misc->param.gen_ie.len <= + sizeof(MrvlIEtypesHeader_t)) { + pmadapter->arp_filter_size = 0; + PRINTM(MINFO, "Clear ARP filter\n"); + } else { + memcpy(pmadapter, pmadapter->arp_filter, + misc->param.gen_ie.ie_data, + misc->param.gen_ie.len); + pmadapter->arp_filter_size = misc->param.gen_ie.len; + HEXDUMP("ArpFilter", pmadapter->arp_filter, + pmadapter->arp_filter_size); + } + break; + default: + PRINTM(MERROR, "Invalid IE type\n"); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_FAILURE; + } + pioctl_req->data_read_written = + sizeof(mlan_ds_misc_gen_ie) + MLAN_SUB_COMMAND_SIZE; + LEAVE(); + return ret; +} + +/** + * @brief Perform warm reset + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, MLAN_STATUS_FAILURE + */ +mlan_status +wlan_misc_ioctl_warm_reset(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + pmlan_private pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_callbacks pcb = &pmadapter->callbacks; + pmlan_buffer pmbuf; + t_s32 i = 0; + t_u16 mc_policy = pmadapter->mc_policy; + mlan_ds_misc_cfg *misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + + ENTER(); + mlan_block_rx_process(pmadapter, MTRUE); + /* Cancel all pending commands and complete ioctls */ + if (misc->param.fw_reload) + wlan_cancel_all_pending_cmd(pmadapter); + /** Init all the head nodes and free all the locks here */ + for (i = 0; i < pmadapter->priv_num; i++) + wlan_free_priv(pmadapter->priv[i]); + + while ((pmbuf = (pmlan_buffer)util_dequeue_list(pmadapter->pmoal_handle, + &pmadapter-> + rx_data_queue, + pcb->moal_spin_lock, + pcb-> + moal_spin_unlock))) { + wlan_free_mlan_buffer(pmadapter, pmbuf); + } + pmadapter->rx_pkts_queued = 0; + + /* Initialize adapter structure */ + wlan_init_adapter(pmadapter); + pmadapter->hw_status = WlanHardwareStatusInitializing; + + /* Initialize private structures */ + for (i = 0; i < pmadapter->priv_num; i++) { + if (pmadapter->priv[i]) + wlan_init_priv(pmadapter->priv[i]); + } + mlan_block_rx_process(pmadapter, MFALSE); + if (misc->param.fw_reload != MTRUE) { + /* Restart the firmware */ + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_FUNC_SHUTDOWN, + HostCmd_ACT_GEN_SET, 0, MNULL, MNULL); + if (ret) + goto done; + } + + /* Issue firmware initialize commands for first BSS, + * for other interfaces it will be called after getting + * the last init command response of previous interface + */ + pmpriv = wlan_get_priv(pmadapter, MLAN_BSS_ROLE_ANY); + if (!pmpriv) { + ret = MLAN_STATUS_FAILURE; + LEAVE(); + return ret; + } + ret = wlan_adapter_get_hw_spec(pmpriv->adapter); + if (ret == MLAN_STATUS_FAILURE) { + LEAVE(); + return ret; + } + ret = pmpriv->ops.init_cmd(pmpriv, MTRUE); + if (ret == MLAN_STATUS_FAILURE) { + LEAVE(); + return ret; + } + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_MULTI_CHAN_POLICY, + HostCmd_ACT_GEN_SET, 0, MNULL, &mc_policy); + if (ret == MLAN_STATUS_FAILURE) { + LEAVE(); + return ret; + } + if (ret == MLAN_STATUS_PENDING) + pmadapter->pwarm_reset_ioctl_req = pioctl_req; + +done: + LEAVE(); + return ret; +} + +#if defined(SDIO_MULTI_PORT_TX_AGGR) || defined(SDIO_MULTI_PORT_RX_AGGR) +/** + * @brief Reconfigure SDIO multiport aggregation parameters + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +static mlan_status +wlan_misc_ioctl_sdio_mpa_ctrl(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_private pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_misc_cfg *misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + mlan_ds_misc_sdio_mpa_ctrl *mpa_ctrl = MNULL; + + ENTER(); + + mpa_ctrl = &misc->param.mpa_ctrl; + + if (pioctl_req->action == MLAN_ACT_SET) { + + if (pmpriv->media_connected == MTRUE) { + PRINTM(MMSG, + "SDIO MPA CTRL: not allowed in connected state\n"); + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + ret = MLAN_STATUS_FAILURE; + goto exit; + } + + if (mpa_ctrl->tx_enable > 1) { + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_FAILURE; + goto exit; + } + + if (mpa_ctrl->rx_enable > 1) { + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_FAILURE; + goto exit; + } + + if (mpa_ctrl->tx_max_ports > SDIO_MP_AGGR_DEF_PKT_LIMIT) { + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_FAILURE; + goto exit; + } + + if (mpa_ctrl->rx_max_ports > SDIO_MP_AGGR_DEF_PKT_LIMIT) { + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_FAILURE; + goto exit; + } + + if (mpa_ctrl->tx_buf_size || mpa_ctrl->rx_buf_size) { + + wlan_free_sdio_mpa_buffers(pmadapter); + + if (mpa_ctrl->tx_buf_size > 0) + pmadapter->mpa_tx.buf_size = + mpa_ctrl->tx_buf_size; + + if (mpa_ctrl->rx_buf_size > 0) + pmadapter->mpa_rx.buf_size = + mpa_ctrl->rx_buf_size; + + if (wlan_alloc_sdio_mpa_buffers(pmadapter, + pmadapter->mpa_tx. + buf_size, + pmadapter->mpa_rx. + buf_size) != + MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, + "Failed to allocate sdio mp-a buffers\n"); + pioctl_req->status_code = MLAN_ERROR_NO_MEM; + ret = MLAN_STATUS_FAILURE; + goto exit; + } + } + + if (mpa_ctrl->tx_max_ports > 0) + pmadapter->mpa_tx.pkt_aggr_limit = + mpa_ctrl->tx_max_ports; + if (mpa_ctrl->rx_max_ports > 0) + pmadapter->mpa_rx.pkt_aggr_limit = + mpa_ctrl->rx_max_ports; + + pmadapter->mpa_tx.enabled = (t_u8)mpa_ctrl->tx_enable; + pmadapter->mpa_rx.enabled = (t_u8)mpa_ctrl->rx_enable; + + } else { + mpa_ctrl->tx_enable = (t_u16)pmadapter->mpa_tx.enabled; + mpa_ctrl->rx_enable = (t_u16)pmadapter->mpa_rx.enabled; + mpa_ctrl->tx_buf_size = (t_u16)pmadapter->mpa_tx.buf_size; + mpa_ctrl->rx_buf_size = (t_u16)pmadapter->mpa_rx.buf_size; + mpa_ctrl->tx_max_ports = + (t_u16)pmadapter->mpa_tx.pkt_aggr_limit; + mpa_ctrl->rx_max_ports = + (t_u16)pmadapter->mpa_rx.pkt_aggr_limit; + } + +exit: + LEAVE(); + return ret; +} +#endif /* SDIO_MULTI_PORT_TX_AGGR || SDIO_MULTI_PORT_RX_AGGR */ + +/** + * @brief Set/Get system clock configuration + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status +wlan_misc_ioctl_sysclock(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_misc_cfg *misc = MNULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u16 cmd_action = 0; + + ENTER(); + + misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_GET) + cmd_action = HostCmd_ACT_GEN_GET; + else + cmd_action = HostCmd_ACT_GEN_SET; + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_ECL_SYSTEM_CLOCK_CONFIG, + cmd_action, + 0, + (t_void *)pioctl_req, + (t_void *)&misc->param.sys_clock); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Get the associate response + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success + */ +static mlan_status +wlan_misc_ioctl_get_assoc_rsp(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_ds_misc_cfg *misc = MNULL; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + if ((pioctl_req->action == MLAN_ACT_GET) && pmpriv->assoc_rsp_size) { + memcpy(pmadapter, misc->param.assoc_resp.assoc_resp_buf, + pmpriv->assoc_rsp_buf, MIN(ASSOC_RSP_BUF_SIZE, + pmpriv->assoc_rsp_size)); + misc->param.assoc_resp.assoc_resp_len = + MIN(ASSOC_RSP_BUF_SIZE, pmpriv->assoc_rsp_size); + + } + + LEAVE(); + return ret; +} + +/** + * @brief Send function softreset command to firmware + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status +wlan_misc_ioctl_soft_reset(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + /* Send command to firmware */ + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_SOFT_RESET, + HostCmd_ACT_GEN_SET, + 0, (t_void *)pioctl_req, MNULL); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + LEAVE(); + return ret; +} + +/** + * @brief Get the thermal reading + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING -- success, otherwise fail + */ +mlan_status +wlan_misc_ioctl_thermal(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_misc_cfg *misc = MNULL; + t_u16 cmd_action = 0; + + ENTER(); + + if (pioctl_req->buf_len < sizeof(mlan_ds_misc_cfg)) { + PRINTM(MWARN, + "MLAN IOCTL information buffer length is too short.\n"); + pioctl_req->data_read_written = 0; + pioctl_req->buf_len_needed = sizeof(mlan_ds_misc_cfg); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + LEAVE(); + return MLAN_STATUS_RESOURCE; + } + + misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_SET) { + PRINTM(MERROR, "Thermal reading setting is not allowed!\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } else { + cmd_action = HostCmd_ACT_GEN_GET; + } + + /* Send command to firmware */ + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_802_11_SNMP_MIB, + cmd_action, + Thermal_i, (t_void *)pioctl_req, MNULL); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Get/Set subscribe event + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING -- success, otherwise fail + */ +mlan_status +wlan_misc_ioctl_subscribe_evt(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_misc_cfg *misc = MNULL; + t_u16 cmd_action = 0; + + ENTER(); + + misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_SET) + cmd_action = HostCmd_ACT_GEN_SET; + else + cmd_action = HostCmd_ACT_GEN_GET; + + /* Send command to firmware */ + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_802_11_SUBSCRIBE_EVENT, + cmd_action, + 0, + (t_void *)pioctl_req, + &misc->param.subscribe_event); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Set ARP filter based on IP address + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * @param ipv4_addr ipv4 Address + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +mlan_status +wlan_ipaddr_arp_filter(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req, IN t_u32 ipv4_addr) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_callbacks pcb = &pmadapter->callbacks; + t_u8 *buf; + arpfilter_header *arpfilter = MNULL; + filter_entry *entry = MNULL; + t_u32 len; + + ENTER(); + + pcb->moal_malloc(pmadapter->pmoal_handle, MRVDRV_SIZE_OF_CMD_BUFFER, + MLAN_MEM_DEF, &buf); + if (!buf) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* Construct the ARP filter TLV */ + arpfilter = (arpfilter_header *)buf; + arpfilter->type = wlan_cpu_to_le16(TLV_TYPE_ARP_FILTER); + + if (ipv4_addr) { + arpfilter->len = wlan_cpu_to_le16(sizeof(filter_entry) * 3); + entry = (filter_entry *)(buf + sizeof(arpfilter_header)); + entry->addr_type = wlan_cpu_to_le16(ADDR_TYPE_BROADCAST); + entry->eth_type = wlan_cpu_to_le16(ETHER_TYPE_ARP); + entry->ipv4_addr = wlan_cpu_to_le32(ipv4_addr); + entry++; + entry->addr_type = wlan_cpu_to_le16(ADDR_TYPE_UNICAST); + entry->eth_type = wlan_cpu_to_le16(ETHER_TYPE_ANY); + entry->ipv4_addr = wlan_cpu_to_le32(IPV4_ADDR_ANY); + entry++; + entry->addr_type = wlan_cpu_to_le16(ADDR_TYPE_MULTICAST); + entry->eth_type = wlan_cpu_to_le16(ETHER_TYPE_ANY); + entry->ipv4_addr = wlan_cpu_to_le32(IPV4_ADDR_ANY); + } else + arpfilter->len = 0; + + /* Update the total length */ + len = sizeof(arpfilter_header) + wlan_le16_to_cpu(arpfilter->len); + + memset(pmadapter, pmadapter->arp_filter, 0, + sizeof(pmadapter->arp_filter)); + if (len > ARP_FILTER_MAX_BUF_SIZE) { + pmadapter->arp_filter_size = 0; + PRINTM(MERROR, "Invalid ARP Filter Size\n"); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_FAILURE; + } else if (len <= sizeof(MrvlIEtypesHeader_t)) { + pmadapter->arp_filter_size = 0; + PRINTM(MINFO, "Clear ARP filter\n"); + } else { + memcpy(pmadapter, pmadapter->arp_filter, buf, len); + pmadapter->arp_filter_size = len; + HEXDUMP("ArpFilter", pmadapter->arp_filter, + pmadapter->arp_filter_size); + } + +done: + if (buf) + pcb->moal_mfree(pmadapter->pmoal_handle, buf); + LEAVE(); + return ret; +} + +#define FLTR_BUF_IP_OFFSET 24 +#define FLTR_BUF_IP_OFFSET_2_IP_1 9 +#define FLTR_BUF_IP_OFFSET_2_IP_2 26 + +/** + * @brief Enable/Disable Auto ARP resonse + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * @param ipv4_addr ipv4 Address + * @param num_ipv4 Number of ipv4 Addresses + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +mlan_status +wlan_ipaddr_auto_arp_resp(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req, + IN t_u32 *ipv4_addr, IN t_u8 num_ipv4) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_status ret = MLAN_STATUS_SUCCESS; + HostCmd_DS_GEN *hostcmd_hdr; + HostCmd_DS_MEF_CFG *mefcmd; + pmlan_callbacks pcb = &pmadapter->callbacks; + mlan_ds_misc_cmd *hostcmd; + t_u32 buf_len = 0; + t_u8 *buf, *filter; + + t_u8 fltr_buf[] = { 0x01, 0x10, 0x21, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x08, 0x06, 0x02, 0x02, 0x14, 0x00, 0x00, + 0x00, 0x01, 0x41, 0x01, 0x00, 0x00, 0x00, 0x01, + 0xc0, 0xa8, 0x01, 0x6d, 0x04, 0x02, 0x2e, 0x00, + 0x00, 0x00, 0x01, 0x41, 0x44 + }; + t_u8 fltr_buf_2_ip[] = { 0x01, 0x10, 0x33, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0xc0, 0xa8, 0x01, 0x6d, 0x04, 0x02, 0x2e, + 0x00, 0x00, 0x00, 0x01, 0x41, 0x01, 0x00, 0x00, + 0x00, 0x01, 0xc0, 0xa8, 0x02, 0x6d, 0x04, 0x02, + 0x2e, 0x00, 0x00, 0x00, 0x01, 0x41, 0x45, 0x01, + 0x00, 0x00, 0x00, 0x01, 0x08, 0x06, 0x02, 0x02, + 0x14, 0x00, 0x00, 0x00, 0x01, 0x41, 0x44 + }; + + ENTER(); + + ret = pcb->moal_malloc(pmadapter->pmoal_handle, + sizeof(mlan_ds_misc_cmd), + MLAN_MEM_DEF, (t_u8 **)&hostcmd); + + if (ret != MLAN_STATUS_SUCCESS || hostcmd == MNULL) { + PRINTM(MERROR, "Failed to allocate hostcmd buffer\n"); + pioctl_req->status_code = MLAN_ERROR_NO_MEM; + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + memset(pmpriv->adapter, hostcmd, 0, sizeof(mlan_ds_misc_cmd)); + buf = hostcmd->cmd; + + /* Prepare hostcmd buffer */ + hostcmd_hdr = (HostCmd_DS_GEN *)(buf); + hostcmd_hdr->command = wlan_cpu_to_le16(HostCmd_CMD_MEF_CFG); + mefcmd = (HostCmd_DS_MEF_CFG *)(buf + S_DS_GEN); + buf_len = S_DS_GEN; + + if (!ipv4_addr) { + PRINTM(MINFO, "Disable Auto ARP Response\n"); + mefcmd->criteria = wlan_cpu_to_le32(0); + mefcmd->nentries = wlan_cpu_to_le16(0); + buf_len += sizeof(HostCmd_DS_MEF_CFG); + } else { + /* Event bit (bit2) of HS conditions should be masked out */ + mefcmd->criteria = + wlan_cpu_to_le32(pmpriv->adapter->hs_cfg. + conditions & ~MBIT(2)); + mefcmd->nentries = wlan_cpu_to_le16(1); + buf_len += sizeof(HostCmd_DS_MEF_CFG); + filter = buf + buf_len; + if (num_ipv4 == 1) { + memcpy(pmpriv->adapter, filter, fltr_buf, + sizeof(fltr_buf)); + memcpy(pmpriv->adapter, &filter[FLTR_BUF_IP_OFFSET], + &ipv4_addr[0], sizeof(t_u32)); + buf_len += sizeof(fltr_buf); + } else if (num_ipv4 >= 2) { + memcpy(pmpriv->adapter, filter, fltr_buf_2_ip, + sizeof(fltr_buf_2_ip)); + memcpy(pmpriv->adapter, + &filter[FLTR_BUF_IP_OFFSET_2_IP_1], + &ipv4_addr[0], sizeof(t_u32)); + memcpy(pmpriv->adapter, + &filter[FLTR_BUF_IP_OFFSET_2_IP_2], + &ipv4_addr[1], sizeof(t_u32)); + buf_len += sizeof(fltr_buf_2_ip); + } + } + hostcmd_hdr->size = wlan_cpu_to_le16(buf_len); + hostcmd->len = buf_len; + + /* Send command to firmware */ + ret = wlan_prepare_cmd(pmpriv, + 0, + 0, 0, (t_void *)pioctl_req, (t_void *)hostcmd); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + pcb->moal_mfree(pmadapter->pmoal_handle, (t_u8 *)hostcmd); + + LEAVE(); + return ret; +} + +/** + * @brief MEF configuration + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +mlan_status +wlan_misc_ioctl_mef_cfg(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_misc_mef_cfg *mef_cfg = + &((mlan_ds_misc_cfg *)pioctl_req->pbuf)->param.mef_cfg; + pmlan_callbacks pcb = &pmadapter->callbacks; + HostCmd_DS_GEN *hostcmd_hdr; + HostCmd_DS_MEF_CFG *mefcmd; + mlan_ds_misc_cmd *hostcmd = MNULL; + t_u32 buf_len = 0; + t_u8 *buf, *filter; + t_u8 fltr_buf[] = { 0x02, 0x00, 0x2f, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x5e, 0x03, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x41, 0x06, 0x00, 0x00, 0x00, + 0x01, 0xff, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x41, 0x45, 0x01, 0x00, 0x00, 0x00, 0x01, + 0x33, 0x33, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x41, 0x45 + }; + + ENTER(); + + /* GET operation */ + if (pioctl_req->action == MLAN_ACT_GET) { + /* TODO: need to store for get operation */ + goto done; + } + + ret = pcb->moal_malloc(pmadapter->pmoal_handle, + sizeof(mlan_ds_misc_cmd), + MLAN_MEM_DEF, (t_u8 **)&hostcmd); + + if (ret != MLAN_STATUS_SUCCESS || hostcmd == MNULL) { + PRINTM(MERROR, "Failed to allocate hostcmd buffer\n"); + pioctl_req->status_code = MLAN_ERROR_NO_MEM; + ret = MLAN_STATUS_FAILURE; + goto done; + } + + memset(pmpriv->adapter, hostcmd, 0, sizeof(mlan_ds_misc_cmd)); + buf = hostcmd->cmd; + + /* Prepare hostcmd buffer */ + hostcmd_hdr = (HostCmd_DS_GEN *)(buf); + hostcmd_hdr->command = wlan_cpu_to_le16(HostCmd_CMD_MEF_CFG); + mefcmd = (HostCmd_DS_MEF_CFG *)(buf + S_DS_GEN); + buf_len = S_DS_GEN; + + switch (mef_cfg->sub_id) { + case MEF_CFG_DISABLE: + PRINTM(MINFO, "Disable MEF\n"); + mefcmd->criteria = wlan_cpu_to_le32(0); + mefcmd->nentries = wlan_cpu_to_le16(0); + buf_len += sizeof(HostCmd_DS_MEF_CFG); + break; + case MEF_CFG_RX_FILTER_ENABLE: + PRINTM(MINFO, "Enable Rx filter\n"); + mefcmd->criteria = wlan_cpu_to_le32((MBIT(3) | MBIT(0))); + mefcmd->nentries = wlan_cpu_to_le16(1); + buf_len += sizeof(HostCmd_DS_MEF_CFG); + filter = buf + buf_len; + memcpy(pmpriv->adapter, filter, fltr_buf, sizeof(fltr_buf)); + buf_len += sizeof(fltr_buf); + break; + case MEF_CFG_AUTO_ARP_RESP: + PRINTM(MINFO, "Enable auto ARP response\n"); + /* TODO */ + break; + case MEF_CFG_HOSTCMD: + PRINTM(MINFO, "MEF hostcmd from MOAL\n"); + filter = buf + buf_len; + memcpy(pmpriv->adapter, filter, mef_cfg->param.cmd_buf.cmd, + mef_cfg->param.cmd_buf.len); + buf_len += mef_cfg->param.cmd_buf.len; + break; + default: + PRINTM(MERROR, "Invalid sub ID parameter\n"); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_FAILURE; + goto done; + break; + } + hostcmd_hdr->size = wlan_cpu_to_le16(buf_len); + hostcmd->len = buf_len; + + /* Send command to firmware */ + ret = wlan_prepare_cmd(pmpriv, + 0, + 0, 0, (t_void *)pioctl_req, (t_void *)hostcmd); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + +done: + if (hostcmd) + pcb->moal_mfree(pmadapter->pmoal_handle, (t_u8 *)hostcmd); + + LEAVE(); + return ret; +} + +/** + * @brief ipaddr configuration + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +mlan_status +wlan_misc_ioctl_ipaddr_cfg(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_misc_cfg *misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u32 ipv4_addr[MAX_IPADDR]; + int i = 0; + + ENTER(); + + /* GET operation */ + if (pioctl_req->action == MLAN_ACT_GET) { + memcpy(pmadapter, misc->param.ipaddr_cfg.ip_addr, + pmpriv->ip_addr, IPADDR_LEN); + misc->param.ipaddr_cfg.op_code = pmpriv->op_code; + goto done; + } + /* only one IP is supported in current firmware */ + for (i = 0; i < misc->param.ipaddr_cfg.ip_addr_num; i++) { + memcpy(pmadapter, &ipv4_addr[i], + misc->param.ipaddr_cfg.ip_addr[i], sizeof(t_u32)); + } + + if (misc->param.ipaddr_cfg.op_code != MLAN_IPADDR_OP_IP_REMOVE && + !misc->param.ipaddr_cfg.ip_addr_num) { + PRINTM(MERROR, "Invalid IPv4 address\n"); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_FAILURE; + goto done; + } + if (misc->param.ipaddr_cfg.op_code & MLAN_IPADDR_OP_ARP_FILTER) + ret = wlan_ipaddr_arp_filter(pmadapter, pioctl_req, + ipv4_addr[0]); + else if (pmpriv->op_code & MLAN_IPADDR_OP_ARP_FILTER) + ret = wlan_ipaddr_arp_filter(pmadapter, pioctl_req, 0); + if (ret == MLAN_STATUS_FAILURE) + goto done; + if (misc->param.ipaddr_cfg.op_code & MLAN_IPADDR_OP_AUTO_ARP_RESP) + ret = wlan_ipaddr_auto_arp_resp(pmadapter, pioctl_req, + ipv4_addr, + misc->param.ipaddr_cfg. + ip_addr_num); + else if (pmpriv->op_code & MLAN_IPADDR_OP_AUTO_ARP_RESP) + ret = wlan_ipaddr_auto_arp_resp(pmadapter, pioctl_req, MNULL, + 0); + if (ret == MLAN_STATUS_FAILURE) + goto done; + + /* Save the values in MLAN */ + if (pioctl_req->action == MLAN_ACT_SET) { + pmpriv->op_code = misc->param.ipaddr_cfg.op_code; + memcpy(pmadapter, pmpriv->ip_addr, + misc->param.ipaddr_cfg.ip_addr, IPADDR_LEN); + } + +done: + LEAVE(); + return ret; +} + +/** + * @brief CFP code configuration + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +mlan_status +wlan_misc_ioctl_cfp_code_cfg(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_misc_cfg *misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + mlan_ds_misc_cfp_code *cfp_code = MNULL; + t_u32 region_bg = 0; + t_u32 region_a = 0; + int i; + + ENTER(); + + cfp_code = &misc->param.cfp_code; + if (pioctl_req->action == MLAN_ACT_SET) { + if (pmadapter->otp_region && pmadapter->otp_region->force_reg) { + PRINTM(MERROR, + "ForceRegionRule is set in the on-chip OTP" + "memory\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + /* Save the values in MLAN */ + if (!cfp_code->cfp_code_bg) + cfp_code->cfp_code_bg = pmadapter->cfp_code_bg; + for (i = 0; i < MRVDRV_MAX_REGION_CODE; i++) { + /* Use the region code to search for the index */ + if (cfp_code->cfp_code_bg == region_code_index[i]) { + region_bg = cfp_code->cfp_code_bg; + break; + } + } + if (!cfp_code->cfp_code_a) + cfp_code->cfp_code_a = pmadapter->cfp_code_a; + for (i = 0; i < MRVDRV_MAX_REGION_CODE; i++) { + /* Use the region code to search for the index */ + if (cfp_code->cfp_code_a == region_code_index[i]) { + region_a = cfp_code->cfp_code_a; + break; + } + } + if (!region_a) { + for (i = 0; i < MRVDRV_MAX_CFP_CODE_A; i++) { + /* Use the CFP code to search for the index */ + if (cfp_code->cfp_code_a == cfp_code_index_a[i]) + break; + } + if (i >= MRVDRV_MAX_CFP_CODE_A) { + PRINTM(MERROR, + "CFP Code not identified for A\n"); + pioctl_req->status_code = + MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_FAILURE; + goto done; + } + } + pmadapter->cfp_code_bg = (t_u8)cfp_code->cfp_code_bg; + pmadapter->cfp_code_a = (t_u8)cfp_code->cfp_code_a; + if (region_bg && region_a && (region_bg == region_a)) + pmadapter->region_code = pmadapter->cfp_code_a; + else + pmadapter->region_code = 0; + if (wlan_set_regiontable(pmpriv, (t_u8)pmadapter->region_code, + pmadapter->config_bands | pmadapter-> + adhoc_start_band)) { + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_FAILURE; + goto done; + } + } else { + /* GET operation */ + cfp_code->cfp_code_bg = pmadapter->cfp_code_bg; + cfp_code->cfp_code_a = pmadapter->cfp_code_a; + } + +done: + LEAVE(); + return ret; +} + +/** + * @brief This function sets up country code and downloads CMD to FW + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req Pointer to the IOCTL request buffer + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_misc_ioctl_country_code(IN pmlan_adapter pmadapter, + IN mlan_ioctl_req *pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_misc_country_code *country_code = MNULL; + mlan_ds_misc_cfg *cfg_misc = MNULL; + t_u8 cfp_bg = 0, cfp_a = 0; + + ENTER(); + + cfg_misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + country_code = &cfg_misc->param.country_code; + + if (pioctl_req->action == MLAN_ACT_SET) { + if (pmadapter->otp_region && pmadapter->otp_region->force_reg) { + PRINTM(MERROR, + "ForceRegionRule is set in the on-chip OTP" + "memory\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + /* Update region code and table based on country code */ + if (wlan_misc_country_2_cfp_table_code(pmadapter, + country_code-> + country_code, &cfp_bg, + &cfp_a)) { + PRINTM(MERROR, "Country code not found!\n"); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_FAILURE; + goto done; + } + pmadapter->cfp_code_bg = cfp_bg; + pmadapter->cfp_code_a = cfp_a; + if (cfp_bg && cfp_a && (cfp_bg == cfp_a)) + pmadapter->region_code = cfp_a; + else + pmadapter->region_code = 0; + if (wlan_set_regiontable(pmpriv, pmadapter->region_code, + pmadapter->config_bands | pmadapter-> + adhoc_start_band)) { + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_FAILURE; + goto done; + } + memcpy(pmadapter, pmadapter->country_code, + country_code->country_code, COUNTRY_CODE_LEN); + } else { + /* GET operation */ + memcpy(pmadapter, country_code->country_code, + pmadapter->country_code, COUNTRY_CODE_LEN); + } + +done: + LEAVE(); + return ret; +} + +/** + * @brief Configure MFPC and MFPR for management frame protection + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req Pointer to the IOCTL request buffer + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_misc_pmfcfg(IN pmlan_adapter pmadapter, IN mlan_ioctl_req *pioctl_req) +{ + pmlan_private pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_misc_cfg *cfg_misc = MNULL; + mlan_ds_misc_pmfcfg *pmfcfg; + + cfg_misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + pmfcfg = &cfg_misc->param.pmfcfg; + + if (pioctl_req->action == MLAN_ACT_SET) { + pmpriv->pmfcfg.mfpc = pmfcfg->mfpc; + pmpriv->pmfcfg.mfpr = pmfcfg->mfpr; + } else { + /* GET operation */ + pmfcfg->mfpc = pmpriv->pmfcfg.mfpc; + pmfcfg->mfpr = pmpriv->pmfcfg.mfpr; + } + + LEAVE(); + return ret; +} + +/** + * @brief get sensor temperature + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +mlan_status +wlan_misc_ioctl_get_sensor_temp(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_misc_cfg *misc = MNULL; + t_u16 cmd_action = 0; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + + ENTER(); + + misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + + if (pioctl_req->action == MLAN_ACT_GET) + cmd_action = HostCmd_ACT_GEN_GET; + else { + PRINTM(MERROR, " sensor temp only support get operation \n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, + HostCmd_DS_GET_SENSOR_TEMP, + cmd_action, 0, (t_void *)pioctl_req, MNULL); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Gtk Rekey Offload + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +mlan_status +wlan_misc_ioctl_gtk_rekey_offload(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_misc_cfg *misc_cfg = MNULL; + t_u16 cmd_action = 0; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + + ENTER(); + misc_cfg = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_SET) + cmd_action = HostCmd_ACT_GEN_SET; + else if (pioctl_req->action == MLAN_ACT_CLEAR) + cmd_action = HostCmd_ACT_GEN_REMOVE; + else + cmd_action = HostCmd_ACT_GEN_GET; + + if (!pmpriv->wpa_is_gtk_set) { + /* Store the gtk rekey data if it has already set gtk */ + memcpy(pmadapter, &pmpriv->gtk_rekey, + &misc_cfg->param.gtk_rekey, + sizeof(mlan_ds_misc_gtk_rekey_data)); + LEAVE(); + return ret; + } + /* Send request to firmware if it hasn't set gtk yet */ + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_GTK_REKEY_OFFLOAD_CFG, + cmd_action, + 0, + (t_void *)pioctl_req, + &misc_cfg->param.gtk_rekey); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief enable/disable roam offload in firmware + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req Pointer to the IOCTL request buffer + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_misc_roam_offload(IN pmlan_adapter pmadapter, + IN mlan_ioctl_req *pioctl_req) +{ + pmlan_private pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_misc_cfg *misc = MNULL; + t_u16 cmd_action = 0; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + + if (!(pmadapter->fw_cap_info & FW_ROAMING_SUPPORT)) { + PRINTM(MERROR, "Firmware roaming not support\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + if (!IS_FW_SUPPORT_SUPPLICANT(pmadapter)) { + PRINTM(MERROR, "Embedded supplicant do not enable\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + if ((misc->param.roam_offload.config_mode == ROAM_OFFLOAD_ENABLE) && + misc->param.roam_offload.userset_passphrase) { + pmpriv->adapter->userset_passphrase = + misc->param.roam_offload.userset_passphrase; + if (!misc->param.roam_offload.enable) { + LEAVE(); + return MLAN_STATUS_SUCCESS; + } + } + + if (pioctl_req->action == MLAN_ACT_SET) + cmd_action = HostCmd_ACT_GEN_SET; + else { + PRINTM(MERROR, "Unsupported cmd_action\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_ROAM_OFFLOAD, + cmd_action, + 0, + (t_void *)pioctl_req, &misc->param.roam_offload); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief set roam offload aplist to firmware + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req Pointer to the IOCTL request buffer + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_misc_roam_offload_aplist(IN pmlan_adapter pmadapter, + IN mlan_ioctl_req *pioctl_req) +{ + pmlan_private pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_misc_cfg *misc = MNULL; + t_u16 cmd_action = 0; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + + if (pioctl_req->action == MLAN_ACT_SET) + cmd_action = HostCmd_ACT_GEN_SET; + else { + PRINTM(MERROR, "Unsupported cmd_action\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_ROAM_OFFLOAD, + cmd_action, + 0, + (t_void *)pioctl_req, &misc->param.roam_offload); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Miscellaneous configuration handler + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status +wlan_misc_cfg_ioctl(IN pmlan_adapter pmadapter, IN pmlan_ioctl_req pioctl_req) +{ + mlan_status status = MLAN_STATUS_SUCCESS; + mlan_ds_misc_cfg *misc = MNULL; + + ENTER(); + + if ((pioctl_req == MNULL) || (pioctl_req->pbuf == MNULL)) { + PRINTM(MERROR, "Request buffer not found!\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + if (pioctl_req->buf_len < sizeof(mlan_ds_misc_cfg)) { + PRINTM(MWARN, "MLAN bss IOCTL length is too short.\n"); + pioctl_req->data_read_written = 0; + pioctl_req->buf_len_needed = sizeof(mlan_ds_misc_cfg); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + LEAVE(); + return MLAN_STATUS_RESOURCE; + } + misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + switch (misc->sub_command) { + case MLAN_OID_MISC_GEN_IE: + status = wlan_misc_ioctl_gen_ie(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_REGION: + status = wlan_misc_ioctl_region(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_WARM_RESET: + util_enqueue_list_tail(pmadapter->pmoal_handle, + &pmadapter->ioctl_pending_q, + (pmlan_linked_list)pioctl_req, + pmadapter->callbacks.moal_spin_lock, + pmadapter->callbacks.moal_spin_unlock); + pmadapter->pending_ioctl = MTRUE; + status = MLAN_STATUS_PENDING; + break; +#if defined(SDIO_MULTI_PORT_TX_AGGR) || defined(SDIO_MULTI_PORT_RX_AGGR) + case MLAN_OID_MISC_SDIO_MPA_CTRL: + status = wlan_misc_ioctl_sdio_mpa_ctrl(pmadapter, pioctl_req); + break; +#endif + case MLAN_OID_MISC_HOST_CMD: + status = wlan_misc_ioctl_host_cmd(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_SYS_CLOCK: + status = wlan_misc_ioctl_sysclock(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_WWS: + status = wlan_misc_ioctl_wws_cfg(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_ASSOC_RSP: + status = wlan_misc_ioctl_get_assoc_rsp(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_INIT_SHUTDOWN: + status = wlan_misc_ioctl_init_shutdown(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_SOFT_RESET: + status = wlan_misc_ioctl_soft_reset(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_COALESCING_STATUS: + status = wlan_misc_ioctl_coalescing_status(pmadapter, + pioctl_req); + break; + case MLAN_OID_MISC_CUSTOM_IE: + status = wlan_misc_ioctl_custom_ie_list(pmadapter, pioctl_req, + MTRUE); + break; + case MLAN_OID_MISC_TDLS_CONFIG: + status = wlan_misc_ioctl_tdls_config(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_TDLS_OPER: + status = wlan_misc_ioctl_tdls_oper(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_GET_TDLS_IES: + status = wlan_misc_ioctl_tdls_get_ies(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_TDLS_CS_CHANNEL: + status = wlan_misc_ioctl_tdls_cs_channel(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_TDLS_IDLE_TIME: + status = wlan_misc_ioctl_tdls_idle_time(pmadapter, pioctl_req); + break; + + case MLAN_OID_MISC_NET_MONITOR: + status = wlan_misc_ioctl_net_monitor(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_MAC_CONTROL: + status = wlan_misc_ioctl_mac_control(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_MEF_CFG: + status = wlan_misc_ioctl_mef_cfg(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_RX_MGMT_IND: + status = wlan_reg_rx_mgmt_ind(pmadapter, pioctl_req); + break; +#ifdef DEBUG_LEVEL1 + case MLAN_OID_MISC_DRVDBG: + status = wlan_set_drvdbg(pmadapter, pioctl_req); + break; +#endif + case MLAN_OID_MISC_IP_ADDR: + status = wlan_misc_ioctl_ipaddr_cfg(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_CFP_CODE: + status = wlan_misc_ioctl_cfp_code_cfg(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_COUNTRY_CODE: + status = wlan_misc_ioctl_country_code(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_THERMAL: + status = wlan_misc_ioctl_thermal(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_SUBSCRIBE_EVENT: + status = wlan_misc_ioctl_subscribe_evt(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_OTP_USER_DATA: + status = wlan_misc_otp_user_data(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_TXCONTROL: + status = wlan_misc_ioctl_txcontrol(pmadapter, pioctl_req); + break; +#ifdef STA_SUPPORT + case MLAN_OID_MISC_EXT_CAP_CFG: + status = wlan_misc_ext_capa_cfg(pmadapter, pioctl_req); + break; +#endif + case MLAN_OID_MISC_PMFCFG: + status = wlan_misc_pmfcfg(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_MULTI_CHAN_CFG: + status = wlan_misc_ioctl_multi_chan_config(pmadapter, + pioctl_req); + break; + case MLAN_OID_MISC_MULTI_CHAN_POLICY: + status = wlan_misc_ioctl_multi_chan_policy(pmadapter, + pioctl_req); + break; + case MLAN_OID_MISC_DRCS_CFG: + status = wlan_misc_ioctl_drcs_config(pmadapter, pioctl_req); + break; +#ifdef RX_PACKET_COALESCE + case MLAN_OID_MISC_RX_PACKET_COALESCE: + status = wlan_misc_ioctl_rx_pkt_coalesce_config(pmadapter, + pioctl_req); + break; +#endif + case MLAN_OID_MISC_PMIC_CFG: + status = wlan_misc_ioctl_pmic_configure(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_CWMODE_CTRL: + status = wlan_misc_ioctl_cwmode_ctrl(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_DFS_REAPTER_MODE: + status = wlan_misc_ioctl_dfs_repeater_cfg(pmadapter, + pioctl_req); + break; + case MLAN_OID_MISC_COALESCE_CFG: + status = wlan_misc_ioctl_coalesce_cfg(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_GET_SENSOR_TEMP: + status = wlan_misc_ioctl_get_sensor_temp(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_OPER_CLASS: + status = wlan_misc_ioctl_oper_class(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_OPER_CLASS_CHECK: + status = wlan_misc_ioctl_operclass_validation(pmadapter, + pioctl_req); + break; + case MLAN_OID_MISC_GTK_REKEY_OFFLOAD: + status = wlan_misc_ioctl_gtk_rekey_offload(pmadapter, + pioctl_req); + break; + case MLAN_OID_MISC_IND_RST_CFG: + status = wlan_misc_ioctl_ind_rst_cfg(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_GET_TSF: + status = wlan_misc_ioctl_get_tsf(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_ROAM_OFFLOAD: + status = wlan_misc_roam_offload(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_ROAM_OFFLOAD_APLIST: + status = wlan_misc_roam_offload_aplist(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_GET_CHAN_REGION_CFG: + status = wlan_misc_chan_reg_cfg(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_FW_DUMP_EVENT: + status = wlan_misc_ioctl_fw_dump_event(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_PER_PKT_CFG: + status = wlan_misc_per_pkt_cfg(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_ROBUSTCOEX: + status = wlan_misc_robustcoex(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_GET_CORRELATED_TIME: + status = wlan_misc_get_correlated_time(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_BOOT_SLEEP: + status = wlan_misc_bootsleep(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_CFP_INFO: + status = wlan_get_cfpinfo(pmadapter, pioctl_req); + break; + default: + if (pioctl_req) + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + status = MLAN_STATUS_FAILURE; + break; + } + LEAVE(); + return status; +} + +/** + * @brief Set/Get scan configuration parameter + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * @param action Set/Get + * + * @return MLAN_STATUS_SUCCESS --success + */ +static mlan_status +wlan_set_get_scan_cfg(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req, IN t_u32 action) +{ + mlan_ds_scan *scan = MNULL; + + ENTER(); + + scan = (mlan_ds_scan *)pioctl_req->pbuf; + if (action == MLAN_ACT_GET) { + scan->param.scan_cfg.scan_type = (t_u32)pmadapter->scan_type; + scan->param.scan_cfg.scan_mode = pmadapter->scan_mode; + scan->param.scan_cfg.scan_probe = (t_u32)pmadapter->scan_probes; + scan->param.scan_cfg.scan_time.specific_scan_time = + (t_u32)pmadapter->specific_scan_time; + scan->param.scan_cfg.scan_time.active_scan_time = + (t_u32)pmadapter->active_scan_time; + scan->param.scan_cfg.scan_time.passive_scan_time = + (t_u32)pmadapter->passive_scan_time; + scan->param.scan_cfg.ext_scan = pmadapter->ext_scan; + } else { + if (scan->param.scan_cfg.scan_type) + pmadapter->scan_type = + (t_u8)scan->param.scan_cfg.scan_type; + if (scan->param.scan_cfg.scan_mode) + pmadapter->scan_mode = scan->param.scan_cfg.scan_mode; + if (scan->param.scan_cfg.scan_probe) + pmadapter->scan_probes = + (t_u16)scan->param.scan_cfg.scan_probe; + if (scan->param.scan_cfg.scan_time.specific_scan_time) + pmadapter->specific_scan_time = + (t_u16)scan->param.scan_cfg.scan_time. + specific_scan_time; + if (scan->param.scan_cfg.scan_time.active_scan_time) + pmadapter->active_scan_time = + (t_u16)scan->param.scan_cfg.scan_time. + active_scan_time; + if (scan->param.scan_cfg.scan_time.passive_scan_time) + pmadapter->passive_scan_time = + (t_u16)scan->param.scan_cfg.scan_time. + passive_scan_time; + pmadapter->ext_scan = scan->param.scan_cfg.ext_scan; + } + pioctl_req->data_read_written = sizeof(t_u32) + MLAN_SUB_COMMAND_SIZE; + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Set/Get scan + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status +wlan_scan_ioctl(IN pmlan_adapter pmadapter, IN pmlan_ioctl_req pioctl_req) +{ + pmlan_private pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_status status = MLAN_STATUS_SUCCESS; + mlan_ds_scan *pscan; + + ENTER(); + + pscan = (mlan_ds_scan *)pioctl_req->pbuf; + if (pscan->sub_command == MLAN_OID_SCAN_CONFIG + || pscan->sub_command == MLAN_OID_SCAN_BGSCAN_CONFIG) + goto start_config; + if (pmadapter->scan_processing && pioctl_req->action == MLAN_ACT_SET && + pscan->sub_command != MLAN_OID_SCAN_CANCEL) { + PRINTM(MINFO, "Scan already in process...\n"); + LEAVE(); + return status; + } + + if (pmadapter->enable_net_mon == CHANNEL_SPEC_SNIFFER_MODE) { + PRINTM(MINFO, + "Scan is blocked in Channel Specified Network Monitor mode...\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + if (pmadapter->scan_block && pioctl_req->action == MLAN_ACT_SET) { + PRINTM(MERROR, "Scan is blocked during association...\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } +start_config: + /* Set scan */ + if (pioctl_req->action == MLAN_ACT_SET) { + + switch (pscan->sub_command) { + case MLAN_OID_SCAN_NORMAL: + status = wlan_scan_networks(pmpriv, pioctl_req, MNULL); + break; + case MLAN_OID_SCAN_SPECIFIC_SSID: + status = wlan_scan_specific_ssid(pmpriv, pioctl_req, + &pscan->param.scan_req. + scan_ssid); + break; + case MLAN_OID_SCAN_USER_CONFIG: + status = wlan_scan_networks(pmpriv, pioctl_req, + (wlan_user_scan_cfg *) + pscan->param.user_scan. + scan_cfg_buf); + break; + case MLAN_OID_SCAN_CONFIG: + status = wlan_set_get_scan_cfg(pmadapter, + pioctl_req, + MLAN_ACT_SET); + break; + case MLAN_OID_SCAN_CANCEL: + status = wlan_cancel_pending_scan_cmd(pmadapter, + pioctl_req); + break; + case MLAN_OID_SCAN_TABLE_FLUSH: + status = wlan_flush_scan_table(pmadapter); + break; + case MLAN_OID_SCAN_BGSCAN_CONFIG: + /* Send request to firmware */ + status = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_802_11_BG_SCAN_CONFIG, + HostCmd_ACT_GEN_SET, + 0, + (t_void *)pioctl_req, + pscan->param.user_scan. + scan_cfg_buf); + break; + default: + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + status = MLAN_STATUS_FAILURE; + break; + } + + if ((status == MLAN_STATUS_SUCCESS) && + (pscan->sub_command != MLAN_OID_SCAN_TABLE_FLUSH) && + (pscan->sub_command != MLAN_OID_SCAN_CANCEL) && + (pscan->sub_command != MLAN_OID_SCAN_CONFIG)) { + PRINTM(MINFO, + "wlan_scan_ioctl: return MLAN_STATUS_PENDING\n"); + status = MLAN_STATUS_PENDING; + } + } + /* Get scan */ + else { + if (pscan->sub_command == MLAN_OID_SCAN_CONFIG) { + status = wlan_set_get_scan_cfg(pmadapter, pioctl_req, + MLAN_ACT_GET); + } else if (pscan->sub_command == MLAN_OID_SCAN_GET_CURRENT_BSS) { + pscan->param.scan_resp.num_in_scan_table = + pmadapter->num_in_scan_table; + pscan->param.scan_resp.pscan_table = + (t_u8 *)&pmpriv->curr_bss_params.bss_descriptor; + pioctl_req->data_read_written = + sizeof(mlan_scan_resp) + MLAN_SUB_COMMAND_SIZE; + } else { + if (pmadapter->bgscan_reported) { + pmadapter->bgscan_reported = MFALSE; + /* Clear the previous scan result */ + memset(pmadapter, pmadapter->pscan_table, 0x00, + sizeof(BSSDescriptor_t) * + MRVDRV_MAX_BSSID_LIST); + pmadapter->num_in_scan_table = 0; + pmadapter->pbcn_buf_end = pmadapter->bcn_buf; + status = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_802_11_BG_SCAN_QUERY, + HostCmd_ACT_GEN_GET, + 0, + (t_void *)pioctl_req, + MNULL); + if (status == MLAN_STATUS_SUCCESS) { + PRINTM(MINFO, + "wlan_scan_ioctl: return MLAN_STATUS_PENDING\n"); + status = MLAN_STATUS_PENDING; + } + } else { + pscan->param.scan_resp.pscan_table = + (t_u8 *)pmadapter->pscan_table; + pscan->param.scan_resp.num_in_scan_table = + pmadapter->num_in_scan_table; + pscan->param.scan_resp.age_in_secs = + pmadapter->age_in_secs; + pioctl_req->data_read_written = + sizeof(mlan_scan_resp) + + MLAN_SUB_COMMAND_SIZE; + pscan->param.scan_resp.pchan_stats = + (t_u8 *)pmadapter->pchan_stats; + pscan->param.scan_resp.num_in_chan_stats = + pmadapter->num_in_chan_stats; + } + } + } + + LEAVE(); + return status; +} + +/******************************************************** + Global Functions +********************************************************/ + +/** + * @brief Set ewpa mode + * + * @param priv A pointer to mlan_private structure + * @param psec_pp A pointer to mlan_ds_passphrase structure + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_set_ewpa_mode(mlan_private *priv, mlan_ds_passphrase *psec_pp) +{ + ENTER(); + + if ((psec_pp->psk_type == MLAN_PSK_PASSPHRASE && + psec_pp->psk.passphrase.passphrase_len > 0) || + (psec_pp->psk_type == MLAN_PSK_PMK)) + priv->sec_info.ewpa_enabled = MTRUE; + else + priv->sec_info.ewpa_enabled = MFALSE; + + PRINTM(MINFO, "Set ewpa mode = %d\n", priv->sec_info.ewpa_enabled); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Search for a BSS + * + * @param pmpriv A pointer to mlan_private structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +mlan_status +wlan_find_bss(mlan_private *pmpriv, pmlan_ioctl_req pioctl_req) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + mlan_ds_bss *bss = MNULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u8 zero_mac[] = { 0, 0, 0, 0, 0, 0 }; + t_u8 mac[MLAN_MAC_ADDR_LENGTH]; + int i = 0; + BSSDescriptor_t *pbss_desc = MNULL; + + ENTER(); + + bss = (mlan_ds_bss *)pioctl_req->pbuf; + + if (memcmp + (pmadapter, &bss->param.ssid_bssid.bssid, zero_mac, + sizeof(zero_mac))) { + if (bss->param.ssid_bssid.ssid.ssid_len) /* ssid & bssid */ + i = wlan_find_ssid_in_list(pmpriv, + &bss->param.ssid_bssid.ssid, + (t_u8 *)&bss->param. + ssid_bssid.bssid, + pmpriv->bss_mode); + else + i = wlan_find_bssid_in_list(pmpriv, + (t_u8 *)&bss->param. + ssid_bssid.bssid, + pmpriv->bss_mode); + if (i < 0) { + memcpy(pmadapter, mac, &bss->param.ssid_bssid.bssid, + sizeof(mac)); + PRINTM(MIOCTL, "Can not find bssid " MACSTR "\n", + MAC2STR(mac)); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + LEAVE(); + return MLAN_STATUS_FAILURE; + } + pbss_desc = &pmadapter->pscan_table[i]; + memcpy(pmadapter, &bss->param.ssid_bssid.ssid, &pbss_desc->ssid, + sizeof(mlan_802_11_ssid)); + bss->param.ssid_bssid.rssi = pbss_desc->rssi; + bss->param.ssid_bssid.channel = (t_u16)pbss_desc->channel; + + bss->param.ssid_bssid.bss_band = pbss_desc->bss_band; + /* index in bss list,start from 1 */ + bss->param.ssid_bssid.idx = i + 1; + } else if (bss->param.ssid_bssid.ssid.ssid_len) { + i = wlan_find_ssid_in_list(pmpriv, &bss->param.ssid_bssid.ssid, + MNULL, pmpriv->bss_mode); + if (i < 0) { + PRINTM(MIOCTL, "Can not find ssid %s\n", + bss->param.ssid_bssid.ssid.ssid); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + LEAVE(); + return MLAN_STATUS_FAILURE; + } + pbss_desc = &pmadapter->pscan_table[i]; + memcpy(pmadapter, (t_u8 *)&bss->param.ssid_bssid.bssid, + (t_u8 *)&pbss_desc->mac_address, MLAN_MAC_ADDR_LENGTH); + bss->param.ssid_bssid.rssi = pbss_desc->rssi; + bss->param.ssid_bssid.channel = (t_u16)pbss_desc->channel; + + bss->param.ssid_bssid.bss_band = pbss_desc->bss_band; + /* index in bss list, start from 1 */ + bss->param.ssid_bssid.idx = i + 1; + } else { + ret = wlan_find_best_network(pmpriv, &bss->param.ssid_bssid); + } + + if (pbss_desc) { + /**if rsn do not have ft akm, don't set ft cap and ft md*/ + if (pbss_desc->pmd_ie + && wlan_ft_akm_is_used(pmpriv, (t_u8 *)pbss_desc->prsn_ie) + ) { + bss->param.ssid_bssid.ft_md = pbss_desc->pmd_ie->mdid; + bss->param.ssid_bssid.ft_cap = + pbss_desc->pmd_ie->ft_cap; + } + } + + LEAVE(); + return ret; +} + +/** + * @brief MLAN station ioctl handler + * + * @param adapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING --success, otherwise fail + */ +mlan_status +wlan_ops_sta_ioctl(t_void *adapter, pmlan_ioctl_req pioctl_req) +{ + pmlan_adapter pmadapter = (pmlan_adapter)adapter; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + switch (pioctl_req->req_id) { + case MLAN_IOCTL_SCAN: + status = wlan_scan_ioctl(pmadapter, pioctl_req); + break; + case MLAN_IOCTL_BSS: + status = wlan_bss_ioctl(pmadapter, pioctl_req); + break; + case MLAN_IOCTL_RADIO_CFG: + status = wlan_radio_ioctl(pmadapter, pioctl_req); + break; + case MLAN_IOCTL_SNMP_MIB: + status = wlan_snmp_mib_ioctl(pmadapter, pioctl_req); + break; + case MLAN_IOCTL_GET_INFO: + status = wlan_get_info_ioctl(pmadapter, pioctl_req); + break; + case MLAN_IOCTL_SEC_CFG: + status = wlan_sec_cfg_ioctl(pmadapter, pioctl_req); + break; + case MLAN_IOCTL_RATE: + status = wlan_rate_ioctl(pmadapter, pioctl_req); + break; + case MLAN_IOCTL_POWER_CFG: + status = wlan_power_ioctl(pmadapter, pioctl_req); + break; + case MLAN_IOCTL_PM_CFG: + status = wlan_pm_ioctl(pmadapter, pioctl_req); + break; + case MLAN_IOCTL_WMM_CFG: + status = wlan_wmm_cfg_ioctl(pmadapter, pioctl_req); + break; + case MLAN_IOCTL_WPS_CFG: + status = wlan_wps_cfg_ioctl(pmadapter, pioctl_req); + break; + case MLAN_IOCTL_11N_CFG: + status = wlan_11n_cfg_ioctl(pmadapter, pioctl_req); + break; + case MLAN_IOCTL_11D_CFG: + status = wlan_11d_cfg_ioctl(pmadapter, pioctl_req); + break; + case MLAN_IOCTL_REG_MEM: + status = wlan_reg_mem_ioctl(pmadapter, pioctl_req); + break; + case MLAN_IOCTL_MISC_CFG: + status = wlan_misc_cfg_ioctl(pmadapter, pioctl_req); + break; + case MLAN_IOCTL_11H_CFG: + status = wlan_11h_cfg_ioctl(pmadapter, pioctl_req); + break; + case MLAN_IOCTL_11K_CFG: + status = wlan_11k_cfg_ioctl(pmadapter, pioctl_req); + break; + default: + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + status = MLAN_STATUS_FAILURE; + break; + } + LEAVE(); + return status; +} diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_sta_rx.c b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_sta_rx.c new file mode 100644 index 000000000000..65649331c754 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_sta_rx.c @@ -0,0 +1,646 @@ +/** @file mlan_sta_rx.c + * + * @brief This file contains the handling of RX in MLAN + * module. + * + * Copyright (C) 2008-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/******************************************************** +Change log: + 10/27/2008: initial version +********************************************************/ + +#include "mlan.h" +#include "mlan_join.h" +#include "mlan_util.h" +#include "mlan_fw.h" +#include "mlan_main.h" +#include "mlan_11n_aggr.h" +#include "mlan_11n_rxreorder.h" +#ifdef DRV_EMBEDDED_SUPPLICANT +#include "authenticator_api.h" +#endif + +/******************************************************** + Local Variables +********************************************************/ + +/** Ethernet II header */ +typedef struct { + /** Ethernet II header destination address */ + t_u8 dest_addr[MLAN_MAC_ADDR_LENGTH]; + /** Ethernet II header source address */ + t_u8 src_addr[MLAN_MAC_ADDR_LENGTH]; + /** Ethernet II header length */ + t_u16 ethertype; + +} EthII_Hdr_t; + +/******************************************************** + Global Variables +********************************************************/ + +/******************************************************** + Local Functions +********************************************************/ + +/******************************************************** + Global functions +********************************************************/ + +/** + * @brief This function process tdls action frame + * + * @param priv A pointer to mlan_private structure + * @param pbuf A pointer to tdls action frame buffer + * @param len len of tdls action frame buffer + * @return N/A + */ +void +wlan_process_tdls_action_frame(pmlan_private priv, t_u8 *pbuf, t_u32 len) +{ + sta_node *sta_ptr = MNULL; + IEEEtypes_VendorHeader_t *pvendor_ie = MNULL; + const t_u8 wmm_oui[] = { 0x00, 0x50, 0xf2, 0x02 }; + t_u8 *peer; + t_u8 *pos, *end; + t_u8 action; + int ie_len = 0; + t_u8 i; + +#define TDLS_PAYLOAD_TYPE 2 +#define TDLS_CATEGORY 0x0c +#define TDLS_REQ_FIX_LEN 6 +#define TDLS_RESP_FIX_LEN 8 +#define TDLS_CONFIRM_FIX_LEN 6 + if (len < (sizeof(EthII_Hdr_t) + 3)) + return; + if (*(t_u8 *)(pbuf + sizeof(EthII_Hdr_t)) != TDLS_PAYLOAD_TYPE) + /*TDLS payload type = 2 */ + return; + if (*(t_u8 *)(pbuf + sizeof(EthII_Hdr_t) + 1) != TDLS_CATEGORY) + /*TDLS category = 0xc */ + return; + peer = pbuf + MLAN_MAC_ADDR_LENGTH; + + action = *(t_u8 *)(pbuf + sizeof(EthII_Hdr_t) + 2); + /*2= payload type + category */ + + if (action > TDLS_SETUP_CONFIRM) { + /*just handle TDLS setup request/response/confirm */ + PRINTM(MMSG, "Recv TDLS Action: peer=" MACSTR ", action=%d\n", + MAC2STR(peer), action); + return; + } + + sta_ptr = wlan_add_station_entry(priv, peer); + if (!sta_ptr) + return; + if (action == TDLS_SETUP_REQUEST) { /*setup request */ + sta_ptr->status = TDLS_NOT_SETUP; + PRINTM(MMSG, "Recv TDLS SETUP Request: peer=" MACSTR "\n", + MAC2STR(peer)); + wlan_hold_tdls_packets(priv, peer); + if (len < (sizeof(EthII_Hdr_t) + TDLS_REQ_FIX_LEN)) + return; + pos = pbuf + sizeof(EthII_Hdr_t) + 4; + /*payload 1+ category 1 + action 1 +dialog 1 */ + sta_ptr->capability = mlan_ntohs(*(t_u16 *)pos); + ie_len = len - sizeof(EthII_Hdr_t) - TDLS_REQ_FIX_LEN; + pos += 2; + } else if (action == 1) { /*setup respons */ + PRINTM(MMSG, "Recv TDLS SETUP Response: peer=" MACSTR "\n", + MAC2STR(peer)); + if (len < (sizeof(EthII_Hdr_t) + TDLS_RESP_FIX_LEN)) + return; + pos = pbuf + sizeof(EthII_Hdr_t) + 6; + /*payload 1+ category 1 + action 1 +dialog 1 +status 2 */ + sta_ptr->capability = mlan_ntohs(*(t_u16 *)pos); + ie_len = len - sizeof(EthII_Hdr_t) - TDLS_RESP_FIX_LEN; + pos += 2; + } else { /*setup confirm */ + PRINTM(MMSG, "Recv TDLS SETUP Confirm: peer=" MACSTR "\n", + MAC2STR(peer)); + if (len < (sizeof(EthII_Hdr_t) + TDLS_CONFIRM_FIX_LEN)) + return; + pos = pbuf + sizeof(EthII_Hdr_t) + TDLS_CONFIRM_FIX_LEN; + /*payload 1+ category 1 + action 1 +dialog 1 + status 2 */ + ie_len = len - sizeof(EthII_Hdr_t) - TDLS_CONFIRM_FIX_LEN; + } + for (end = pos + ie_len; pos + 1 < end; pos += 2 + pos[1]) { + if (pos + 2 + pos[1] > end) + break; + switch (*pos) { + case SUPPORTED_RATES: + sta_ptr->rate_len = pos[1]; + for (i = 0; i < pos[1]; i++) + sta_ptr->support_rate[i] = pos[2 + i]; + break; + case EXTENDED_SUPPORTED_RATES: + for (i = 0; i < pos[1]; i++) + sta_ptr->support_rate[sta_ptr->rate_len + i] = + pos[2 + i]; + sta_ptr->rate_len += pos[1]; + break; + case HT_CAPABILITY: + memcpy(priv->adapter, (t_u8 *)&sta_ptr->HTcap, pos, + sizeof(IEEEtypes_HTCap_t)); + sta_ptr->is_11n_enabled = 1; + DBG_HEXDUMP(MDAT_D, "TDLS HT capability", + (t_u8 *)(&sta_ptr->HTcap), + MIN(sizeof(IEEEtypes_HTCap_t), + MAX_DATA_DUMP_LEN)); + break; + case HT_OPERATION: + memcpy(priv->adapter, &sta_ptr->HTInfo, pos, + sizeof(IEEEtypes_HTInfo_t)); + DBG_HEXDUMP(MDAT_D, "TDLS HT info", + (t_u8 *)(&sta_ptr->HTInfo), + MIN(sizeof(IEEEtypes_HTInfo_t), + MAX_DATA_DUMP_LEN)); + break; + case BSSCO_2040: + memcpy(priv->adapter, (t_u8 *)&sta_ptr->BSSCO_20_40, + pos, sizeof(IEEEtypes_2040BSSCo_t)); + break; + case EXT_CAPABILITY: + memcpy(priv->adapter, (t_u8 *)&sta_ptr->ExtCap, pos, + pos[1] + sizeof(IEEEtypes_Header_t)); + DBG_HEXDUMP(MDAT_D, "TDLS Extended capability", + (t_u8 *)(&sta_ptr->ExtCap), + sta_ptr->ExtCap.ieee_hdr.len + 2); + break; + case RSN_IE: + memcpy(priv->adapter, (t_u8 *)&sta_ptr->rsn_ie, pos, + pos[1] + sizeof(IEEEtypes_Header_t)); + DBG_HEXDUMP(MDAT_D, "TDLS Rsn ie ", + (t_u8 *)(&sta_ptr->rsn_ie), + pos[1] + sizeof(IEEEtypes_Header_t)); + break; + case QOS_INFO: + sta_ptr->qos_info = pos[2]; + PRINTM(MDAT_D, "TDLS qos info %x\n", sta_ptr->qos_info); + break; + case VENDOR_SPECIFIC_221: + pvendor_ie = (IEEEtypes_VendorHeader_t *)pos; + if (!memcmp + (priv->adapter, pvendor_ie->oui, wmm_oui, + sizeof(wmm_oui))) { + sta_ptr->qos_info = pos[8]; /** qos info in wmm parameters in response and confirm */ + PRINTM(MDAT_D, "TDLS qos info %x\n", + sta_ptr->qos_info); + } + break; + case LINK_ID: + memcpy(priv->adapter, (t_u8 *)&sta_ptr->link_ie, pos, + sizeof(IEEEtypes_LinkIDElement_t)); + break; + + default: + break; + } + } + return; +} + +/** + * @brief This function get pxpd info for radiotap info + * + * @param priv A pointer to pmlan_private + * @param prx_pd A pointer to RxPD + * @param prt_info A pointer to radiotap_info + * + * @return N/A + */ +void +wlan_rxpdinfo_to_radiotapinfo(pmlan_private priv, RxPD *prx_pd, + radiotap_info * prt_info) +{ + radiotap_info rt_info_tmp; + t_u8 rx_rate_info = 0; + t_u8 mcs_index = 0; + t_u8 format = 0; + t_u8 bw = 0; + t_u8 gi = 0; + t_u8 ldpc = 0; + + memset(priv->adapter, &rt_info_tmp, 0x00, sizeof(rt_info_tmp)); + rt_info_tmp.snr = prx_pd->snr; + rt_info_tmp.nf = prx_pd->nf; + rt_info_tmp.band_config = (prx_pd->rx_info & 0xf); + rt_info_tmp.chan_num = (prx_pd->rx_info & RXPD_CHAN_MASK) >> 5; + + rt_info_tmp.antenna = prx_pd->antenna; + rx_rate_info = prx_pd->rate_info; + if ((rx_rate_info & 0x3) == MLAN_RATE_FORMAT_HT) { + /* HT rate */ + format = MLAN_RATE_FORMAT_HT; + mcs_index = prx_pd->rx_rate; + /* 20M: bw=0, 40M: bw=1 */ + bw = (rx_rate_info & 0xC) >> 2; + /* LGI: gi =0, SGI: gi = 1 */ + gi = (rx_rate_info & 0x10) >> 4; + } else { + /* LG rate */ + format = MLAN_RATE_FORMAT_LG; + mcs_index = (prx_pd->rx_rate > MLAN_RATE_INDEX_OFDM0) ? + prx_pd->rx_rate - 1 : prx_pd->rx_rate; + } + ldpc = rx_rate_info & 0x40; + + rt_info_tmp.rate_info.mcs_index = mcs_index; + rt_info_tmp.rate_info.rate_info = + (ldpc << 5) | (format << 3) | (bw << 1) | gi; + rt_info_tmp.rate_info.bitrate = + wlan_index_to_data_rate(priv->adapter, prx_pd->rx_rate, + prx_pd->rate_info); + + if (prx_pd->flags & RXPD_FLAG_EXTRA_HEADER) + memcpy(priv->adapter, &rt_info_tmp.extra_info, + (t_u8 *)prx_pd + sizeof(*prx_pd), + sizeof(rt_info_tmp.extra_info)); + + memset(priv->adapter, prt_info, 0x00, sizeof(*prt_info)); + memcpy(priv->adapter, prt_info, &rt_info_tmp, sizeof(*prt_info)); + + return; +} + +/** + * @brief This function processes received packet and forwards it + * to kernel/upper layer + * + * @param pmadapter A pointer to mlan_adapter + * @param pmbuf A pointer to mlan_buffer which includes the received packet + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_process_rx_packet(pmlan_adapter pmadapter, pmlan_buffer pmbuf) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_private priv = pmadapter->priv[pmbuf->bss_index]; + RxPacketHdr_t *prx_pkt; + RxPD *prx_pd; + int hdr_chop; + EthII_Hdr_t *peth_hdr; + t_u8 rfc1042_eth_hdr[MLAN_MAC_ADDR_LENGTH] = { + 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 + }; + t_u8 snap_oui_802_h[MLAN_MAC_ADDR_LENGTH] = { + 0xaa, 0xaa, 0x03, 0x00, 0x00, 0xf8 + }; + t_u8 appletalk_aarp_type[2] = { 0x80, 0xf3 }; + t_u8 ipx_snap_type[2] = { 0x81, 0x37 }; + t_u8 tdls_action_type[2] = { 0x89, 0x0d }; +#ifdef DRV_EMBEDDED_SUPPLICANT + t_u8 eapol_type[2] = { 0x88, 0x8e }; +#endif + + ENTER(); + + prx_pd = (RxPD *)(pmbuf->pbuf + pmbuf->data_offset); + prx_pkt = (RxPacketHdr_t *)((t_u8 *)prx_pd + prx_pd->rx_pkt_offset); + +/** Small debug type */ +#define DBG_TYPE_SMALL 2 +/** Size of debugging structure */ +#define SIZE_OF_DBG_STRUCT 4 + if (prx_pd->rx_pkt_type == PKT_TYPE_DEBUG) { + t_u8 dbg_type; + dbg_type = *(t_u8 *)&prx_pkt->eth803_hdr; + if (dbg_type == DBG_TYPE_SMALL) { + PRINTM(MFW_D, "\n"); + DBG_HEXDUMP(MFW_D, "FWDBG", + (char *)((t_u8 *)&prx_pkt->eth803_hdr + + SIZE_OF_DBG_STRUCT), + prx_pd->rx_pkt_length); + PRINTM(MFW_D, "FWDBG::\n"); + } + goto done; + } + + PRINTM(MINFO, + "RX Data: data_len - prx_pd->rx_pkt_offset = %d - %d = %d\n", + pmbuf->data_len, prx_pd->rx_pkt_offset, + pmbuf->data_len - prx_pd->rx_pkt_offset); + + HEXDUMP("RX Data: Dest", prx_pkt->eth803_hdr.dest_addr, + sizeof(prx_pkt->eth803_hdr.dest_addr)); + HEXDUMP("RX Data: Src", prx_pkt->eth803_hdr.src_addr, + sizeof(prx_pkt->eth803_hdr.src_addr)); + + if ((memcmp(pmadapter, &prx_pkt->rfc1042_hdr, + snap_oui_802_h, sizeof(snap_oui_802_h)) == 0) || + ((memcmp(pmadapter, &prx_pkt->rfc1042_hdr, + rfc1042_eth_hdr, sizeof(rfc1042_eth_hdr)) == 0) && + memcmp(pmadapter, &prx_pkt->rfc1042_hdr.snap_type, + appletalk_aarp_type, sizeof(appletalk_aarp_type)) && + memcmp(pmadapter, &prx_pkt->rfc1042_hdr.snap_type, + ipx_snap_type, sizeof(ipx_snap_type)))) { + /* + * Replace the 803 header and rfc1042 header (llc/snap) with an + * EthernetII header, keep the src/dst and snap_type (ethertype). + * The firmware only passes up SNAP frames converting + * all RX Data from 802.11 to 802.2/LLC/SNAP frames. + * To create the Ethernet II, just move the src, dst address + * right before the snap_type. + */ + peth_hdr = (EthII_Hdr_t *) + ((t_u8 *)&prx_pkt->eth803_hdr + + sizeof(prx_pkt->eth803_hdr) + + sizeof(prx_pkt->rfc1042_hdr) + - sizeof(prx_pkt->eth803_hdr.dest_addr) + - sizeof(prx_pkt->eth803_hdr.src_addr) + - sizeof(prx_pkt->rfc1042_hdr.snap_type)); + + memcpy(pmadapter, peth_hdr->src_addr, + prx_pkt->eth803_hdr.src_addr, + sizeof(peth_hdr->src_addr)); + memcpy(pmadapter, peth_hdr->dest_addr, + prx_pkt->eth803_hdr.dest_addr, + sizeof(peth_hdr->dest_addr)); + + /* Chop off the RxPD + the excess memory from the 802.2/llc/snap + * header that was removed. + */ + hdr_chop = (t_u32)((t_ptr)peth_hdr - (t_ptr)prx_pd); + } else { + HEXDUMP("RX Data: LLC/SNAP", + (t_u8 *)&prx_pkt->rfc1042_hdr, + sizeof(prx_pkt->rfc1042_hdr)); + if (!memcmp + (pmadapter, &prx_pkt->eth803_hdr.h803_len, tdls_action_type, + sizeof(tdls_action_type))) { + wlan_process_tdls_action_frame(priv, + ((t_u8 *)prx_pd + + prx_pd->rx_pkt_offset), + prx_pd->rx_pkt_length); + } + /* Chop off the RxPD */ + hdr_chop = (t_u32)((t_ptr)&prx_pkt->eth803_hdr - (t_ptr)prx_pd); + } + + /* Chop off the leading header bytes so the it points to the start of + * either the reconstructed EthII frame or the 802.2/llc/snap frame + */ + pmbuf->data_len -= hdr_chop; + pmbuf->data_offset += hdr_chop; + pmbuf->pparent = MNULL; + DBG_HEXDUMP(MDAT_D, "RxPD", (t_u8 *)prx_pd, + MIN(sizeof(RxPD), MAX_DATA_DUMP_LEN)); + DBG_HEXDUMP(MDAT_D, "Rx Payload", + ((t_u8 *)prx_pd + prx_pd->rx_pkt_offset), + MIN(prx_pd->rx_pkt_length, MAX_DATA_DUMP_LEN)); + + priv->rxpd_rate = prx_pd->rx_rate; + + pmadapter->callbacks.moal_get_system_time(pmadapter->pmoal_handle, + &pmbuf->out_ts_sec, + &pmbuf->out_ts_usec); + PRINTM_NETINTF(MDATA, priv); + PRINTM(MDATA, "%lu.%06lu : Data => kernel seq_num=%d tid=%d\n", + pmbuf->out_ts_sec, pmbuf->out_ts_usec, prx_pd->seq_num, + prx_pd->priority); + + if (pmadapter->enable_net_mon) { + pmbuf->flags |= MLAN_BUF_FLAG_NET_MONITOR; + goto mon_process; + } + +#ifdef DRV_EMBEDDED_SUPPLICANT + if (supplicantIsEnabled(priv->psapriv) && + (!memcmp + (pmadapter, &prx_pkt->eth803_hdr.h803_len, eapol_type, + sizeof(eapol_type)))) { + //BML_SET_OFFSET(bufDesc, offset); + if (ProcessEAPoLPkt(priv->psapriv, pmbuf)) { + wlan_free_mlan_buffer(pmadapter, pmbuf); + ret = MLAN_STATUS_SUCCESS; + PRINTM(MMSG, + "host supplicant eapol pkt process done.\n"); + + LEAVE(); + return ret; + } + } +#endif + +mon_process: + if (pmbuf->flags & MLAN_BUF_FLAG_NET_MONITOR) { + //Use some rxpd space to save rxpd info for radiotap header + //We should insure radiotap_info is not bigger than RxPD + wlan_rxpdinfo_to_radiotapinfo(priv, prx_pd, + (radiotap_info *) (pmbuf->pbuf + + pmbuf-> + data_offset - + sizeof + (radiotap_info))); + } + if (MFALSE || priv->rx_pkt_info) { + pmbuf->u.rx_info.data_rate = + wlan_index_to_data_rate(priv->adapter, prx_pd->rx_rate, + prx_pd->rate_info); + pmbuf->u.rx_info.channel = + (prx_pd->rx_info & RXPD_CHAN_MASK) >> 5; + pmbuf->u.rx_info.antenna = prx_pd->antenna; + pmbuf->u.rx_info.rssi = prx_pd->snr - prx_pd->nf; + } + ret = pmadapter->callbacks.moal_recv_packet(pmadapter->pmoal_handle, + pmbuf); + if (ret == MLAN_STATUS_FAILURE) { + pmbuf->status_code = MLAN_ERROR_PKT_INVALID; + PRINTM(MERROR, + "STA Rx Error: moal_recv_packet returned error\n"); + } +done: + if (ret != MLAN_STATUS_PENDING) + wlan_free_mlan_buffer(pmadapter, pmbuf); + + LEAVE(); + + return ret; +} + +/** + * @brief This function processes the received buffer + * + * @param adapter A pointer to mlan_adapter + * @param pmbuf A pointer to the received buffer + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_ops_sta_process_rx_packet(IN t_void *adapter, IN pmlan_buffer pmbuf) +{ + pmlan_adapter pmadapter = (pmlan_adapter)adapter; + mlan_status ret = MLAN_STATUS_SUCCESS; + RxPD *prx_pd; + RxPacketHdr_t *prx_pkt; + pmlan_private priv = pmadapter->priv[pmbuf->bss_index]; + t_u8 ta[MLAN_MAC_ADDR_LENGTH]; + t_u16 rx_pkt_type = 0; + wlan_mgmt_pkt *pmgmt_pkt_hdr = MNULL; + + sta_node *sta_ptr = MNULL; + t_u8 adj_rx_rate = 0; + t_u8 antenna = 0; + rxpd_extra_info *pextra_info = MNULL; + ENTER(); + + prx_pd = (RxPD *)(pmbuf->pbuf + pmbuf->data_offset); + /* Endian conversion */ + endian_convert_RxPD(prx_pd); + + if (prx_pd->flags & RXPD_FLAG_EXTRA_HEADER) { + pextra_info = + (rxpd_extra_info *) ((t_u8 *)prx_pd + sizeof(*prx_pd)); + endian_convert_RxPD_extra_header(pextra_info); + } + rx_pkt_type = prx_pd->rx_pkt_type; + prx_pkt = (RxPacketHdr_t *)((t_u8 *)prx_pd + prx_pd->rx_pkt_offset); + + priv->rxpd_rate_info = prx_pd->rate_info; + if (priv->bss_type == MLAN_BSS_TYPE_STA) { + antenna = wlan_adjust_antenna(priv, prx_pd); + adj_rx_rate = + wlan_adjust_data_rate(priv, priv->rxpd_rate, + priv->rxpd_rate_info); + pmadapter->callbacks.moal_hist_data_add(pmadapter->pmoal_handle, + pmbuf->bss_index, + adj_rx_rate, + prx_pd->snr, prx_pd->nf, + antenna); + } + + if ((prx_pd->rx_pkt_offset + prx_pd->rx_pkt_length) != + (t_u16)pmbuf->data_len) { + PRINTM(MERROR, + "Wrong rx packet: len=%d,rx_pkt_offset=%d," + " rx_pkt_length=%d\n", pmbuf->data_len, + prx_pd->rx_pkt_offset, prx_pd->rx_pkt_length); + pmbuf->status_code = MLAN_ERROR_PKT_SIZE_INVALID; + ret = MLAN_STATUS_FAILURE; + wlan_free_mlan_buffer(pmadapter, pmbuf); + goto done; + } + pmbuf->data_len = prx_pd->rx_pkt_offset + prx_pd->rx_pkt_length; + + if (pmadapter->priv[pmbuf->bss_index]->mgmt_frame_passthru_mask && + prx_pd->rx_pkt_type == PKT_TYPE_MGMT_FRAME) { + /* Check if this is mgmt packet and needs to + * forwarded to app as an event + */ + pmgmt_pkt_hdr = + (wlan_mgmt_pkt *)((t_u8 *)prx_pd + + prx_pd->rx_pkt_offset); + pmgmt_pkt_hdr->frm_len = + wlan_le16_to_cpu(pmgmt_pkt_hdr->frm_len); + + if ((pmgmt_pkt_hdr->wlan_header.frm_ctl + & IEEE80211_FC_MGMT_FRAME_TYPE_MASK) == 0) + wlan_process_802dot11_mgmt_pkt(pmadapter-> + priv[pmbuf->bss_index], + (t_u8 *)&pmgmt_pkt_hdr-> + wlan_header, + pmgmt_pkt_hdr->frm_len + + sizeof(wlan_mgmt_pkt) + - + sizeof(pmgmt_pkt_hdr-> + frm_len), prx_pd); + wlan_free_mlan_buffer(pmadapter, pmbuf); + goto done; + } + + /* + * If the packet is not an unicast packet then send the packet + * directly to os. Don't pass thru rx reordering + */ + if ((!IS_11N_ENABLED(priv) + && !IS_11N_ADHOC_ENABLED(priv) + && !(prx_pd->flags & RXPD_FLAG_PKT_DIRECT_LINK) + ) || + memcmp(priv->adapter, priv->curr_addr, + prx_pkt->eth803_hdr.dest_addr, MLAN_MAC_ADDR_LENGTH)) { + wlan_process_rx_packet(pmadapter, pmbuf); + goto done; + } + + if (queuing_ra_based(priv) || + (prx_pd->flags & RXPD_FLAG_PKT_DIRECT_LINK)) { + memcpy(pmadapter, ta, prx_pkt->eth803_hdr.src_addr, + MLAN_MAC_ADDR_LENGTH); + if (prx_pd->priority < MAX_NUM_TID) { + PRINTM(MDATA, "adhoc/tdls packet %p " MACSTR "\n", + pmbuf, MAC2STR(ta)); + sta_ptr = wlan_get_station_entry(priv, ta); + if (sta_ptr) { + sta_ptr->rx_seq[prx_pd->priority] = + prx_pd->seq_num; + if (prx_pd->flags & RXPD_FLAG_PKT_DIRECT_LINK) { + sta_ptr->snr = prx_pd->snr; + sta_ptr->nf = prx_pd->nf; + pmadapter->callbacks. + moal_updata_peer_signal + (pmadapter->pmoal_handle, + pmbuf->bss_index, ta, + prx_pd->snr, prx_pd->nf); + } + } + if (!sta_ptr || !sta_ptr->is_11n_enabled) { + wlan_process_rx_packet(pmadapter, pmbuf); + goto done; + } + } + } else { + if ((rx_pkt_type != PKT_TYPE_BAR) && + (prx_pd->priority < MAX_NUM_TID)) + priv->rx_seq[prx_pd->priority] = prx_pd->seq_num; + memcpy(pmadapter, ta, + priv->curr_bss_params.bss_descriptor.mac_address, + MLAN_MAC_ADDR_LENGTH); + } + + if ((priv->port_ctrl_mode == MTRUE && priv->port_open == MFALSE) && + (rx_pkt_type != PKT_TYPE_BAR)) { + mlan_11n_rxreorder_pkt(priv, prx_pd->seq_num, prx_pd->priority, + ta, (t_u8)prx_pd->rx_pkt_type, + (t_void *)RX_PKT_DROPPED_IN_FW); + if (rx_pkt_type == PKT_TYPE_AMSDU) { + pmbuf->data_len = prx_pd->rx_pkt_length; + pmbuf->data_offset += prx_pd->rx_pkt_offset; + wlan_11n_deaggregate_pkt(priv, pmbuf); + } else { + wlan_process_rx_packet(pmadapter, pmbuf); + } + goto done; + } + + /* Reorder and send to OS */ + ret = mlan_11n_rxreorder_pkt(priv, prx_pd->seq_num, + prx_pd->priority, ta, + (t_u8)prx_pd->rx_pkt_type, (void *)pmbuf); + if (ret || (rx_pkt_type == PKT_TYPE_BAR) + ) { + wlan_free_mlan_buffer(pmadapter, pmbuf); + } + +done: + + LEAVE(); + return ret; +} diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_sta_tx.c b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_sta_tx.c new file mode 100644 index 000000000000..9b17e8c5d76c --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_sta_tx.c @@ -0,0 +1,325 @@ +/** @file mlan_sta_tx.c + * + * @brief This file contains the handling of data packet + * transmission in MLAN module. + * + * Copyright (C) 2008-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/******************************************************** +Change log: + 10/21/2008: initial version +********************************************************/ + +#include "mlan.h" +#include "mlan_join.h" +#include "mlan_util.h" +#include "mlan_fw.h" +#include "mlan_main.h" +#include "mlan_wmm.h" +#include "mlan_sdio.h" + +/******************************************************** + Local Variables +********************************************************/ + +/******************************************************** + Global Variables +********************************************************/ + +/******************************************************** + Local Functions +********************************************************/ + +/******************************************************** + Global functions +********************************************************/ +/** + * @brief This function fill the txpd for tx packet + * + * @param priv A pointer to mlan_private structure + * @param pmbuf A pointer to the mlan_buffer for process + * + * @return headptr or MNULL + */ +t_void * +wlan_ops_sta_process_txpd(IN t_void *priv, IN pmlan_buffer pmbuf) +{ + mlan_private *pmpriv = (mlan_private *)priv; + pmlan_adapter pmadapter = pmpriv->adapter; + TxPD *plocal_tx_pd; + t_u8 *head_ptr = MNULL; + t_u32 pkt_type; + t_u32 tx_control; + + ENTER(); + + if (!pmbuf->data_len) { + PRINTM(MERROR, "STA Tx Error: Invalid packet length: %d\n", + pmbuf->data_len); + pmbuf->status_code = MLAN_ERROR_PKT_SIZE_INVALID; + goto done; + } + if (pmbuf->buf_type == MLAN_BUF_TYPE_RAW_DATA) { + memcpy(pmpriv->adapter, &pkt_type, + pmbuf->pbuf + pmbuf->data_offset, sizeof(pkt_type)); + memcpy(pmpriv->adapter, &tx_control, + pmbuf->pbuf + pmbuf->data_offset + sizeof(pkt_type), + sizeof(tx_control)); + pmbuf->data_offset += sizeof(pkt_type) + sizeof(tx_control); + pmbuf->data_len -= sizeof(pkt_type) + sizeof(tx_control); + } + + if (pmbuf->data_offset < (sizeof(TxPD) + pmpriv->intf_hr_len + + DMA_ALIGNMENT)) { + PRINTM(MERROR, + "not enough space for TxPD: headroom=%d pkt_len=%d, required=%d\n", + pmbuf->data_offset, pmbuf->data_len, + sizeof(TxPD) + pmpriv->intf_hr_len + DMA_ALIGNMENT); + pmbuf->status_code = MLAN_ERROR_PKT_SIZE_INVALID; + goto done; + } + + /* head_ptr should be aligned */ + head_ptr = + pmbuf->pbuf + pmbuf->data_offset - sizeof(TxPD) - + pmpriv->intf_hr_len; + head_ptr = (t_u8 *)((t_ptr)head_ptr & ~((t_ptr)(DMA_ALIGNMENT - 1))); + + plocal_tx_pd = (TxPD *)(head_ptr + pmpriv->intf_hr_len); + memset(pmadapter, plocal_tx_pd, 0, sizeof(TxPD)); + /* Set the BSS number to TxPD */ + plocal_tx_pd->bss_num = GET_BSS_NUM(pmpriv); + plocal_tx_pd->bss_type = pmpriv->bss_type; + plocal_tx_pd->tx_pkt_length = (t_u16)pmbuf->data_len; + + plocal_tx_pd->priority = (t_u8)pmbuf->priority; + plocal_tx_pd->pkt_delay_2ms = + wlan_wmm_compute_driver_packet_delay(pmpriv, pmbuf); + + if (plocal_tx_pd->priority < + NELEMENTS(pmpriv->wmm.user_pri_pkt_tx_ctrl)) + /* + * Set the priority specific tx_control field, setting of 0 will + * cause the default value to be used later in this function + */ + plocal_tx_pd->tx_control + = + pmpriv->wmm.user_pri_pkt_tx_ctrl[plocal_tx_pd-> + priority]; + if (pmadapter->pps_uapsd_mode) { + if (MTRUE == wlan_check_last_packet_indication(pmpriv)) { + pmadapter->tx_lock_flag = MTRUE; + plocal_tx_pd->flags = + MRVDRV_TxPD_POWER_MGMT_LAST_PACKET; + } + } + if (pmbuf->flags & MLAN_BUF_FLAG_TDLS) + plocal_tx_pd->flags |= MRVDRV_TxPD_FLAGS_TDLS_PACKET; + /* Offset of actual data */ + plocal_tx_pd->tx_pkt_offset = + (t_u16)((t_ptr)pmbuf->pbuf + pmbuf->data_offset - + (t_ptr)plocal_tx_pd); + + if (!plocal_tx_pd->tx_control) { + /* TxCtrl set by user or default */ + plocal_tx_pd->tx_control = pmpriv->pkt_tx_ctrl; + } + + if (pmbuf->buf_type == MLAN_BUF_TYPE_RAW_DATA) { + plocal_tx_pd->tx_pkt_type = (t_u16)pkt_type; + plocal_tx_pd->tx_control = tx_control; + } + + if (pmbuf->flags & MLAN_BUF_FLAG_TX_STATUS) { + plocal_tx_pd->tx_control_1 |= pmbuf->tx_seq_num << 8; + plocal_tx_pd->flags |= MRVDRV_TxPD_FLAGS_TX_PACKET_STATUS; + } + if (pmbuf->flags & MLAN_BUF_FLAG_TX_CTRL) { + if (pmbuf->u.tx_info.data_rate) { + plocal_tx_pd->tx_control |= + (wlan_ieee_rateid_to_mrvl_rateid + (pmpriv, pmbuf->u.tx_info.data_rate, + MNULL) << 16); + plocal_tx_pd->tx_control |= TXPD_TXRATE_ENABLE; + } + plocal_tx_pd->tx_control_1 |= pmbuf->u.tx_info.channel << 21; + if (pmbuf->u.tx_info.bw) { + plocal_tx_pd->tx_control_1 |= pmbuf->u.tx_info.bw << 16; + plocal_tx_pd->tx_control_1 |= TXPD_BW_ENABLE; + } + if (pmbuf->u.tx_info.tx_power.tp.hostctl) + plocal_tx_pd->tx_control |= + pmbuf->u.tx_info.tx_power.val; + if (pmbuf->u.tx_info.retry_limit) { + plocal_tx_pd->tx_control |= + pmbuf->u.tx_info.retry_limit << 8; + plocal_tx_pd->tx_control |= TXPD_RETRY_ENABLE; + } + } + endian_convert_TxPD(plocal_tx_pd); + /* Adjust the data offset and length to include TxPD in pmbuf */ + pmbuf->data_len += pmbuf->data_offset; + pmbuf->data_offset = (t_u32)(head_ptr - pmbuf->pbuf); + pmbuf->data_len -= pmbuf->data_offset; + +done: + LEAVE(); + return head_ptr; +} + +/** + * @brief This function tells firmware to send a NULL data packet. + * + * @param priv A pointer to mlan_private structure + * @param flags Transmit Pkt Flags + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING --success, otherwise failure + */ +mlan_status +wlan_send_null_packet(pmlan_private priv, t_u8 flags) +{ + pmlan_adapter pmadapter = MNULL; + TxPD *ptx_pd; +/* sizeof(TxPD) + Interface specific header */ +#define NULL_PACKET_HDR 256 + t_u32 data_len = NULL_PACKET_HDR; + pmlan_buffer pmbuf = MNULL; + t_u8 *ptr; + mlan_status ret = MLAN_STATUS_SUCCESS; +#ifdef DEBUG_LEVEL1 + t_u32 sec = 0, usec = 0; +#endif + + ENTER(); + + if (!priv) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + pmadapter = priv->adapter; + + if (pmadapter->surprise_removed == MTRUE) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + if (priv->media_connected == MFALSE) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + if (pmadapter->data_sent == MTRUE) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + pmbuf = wlan_alloc_mlan_buffer(pmadapter, data_len, 0, + MOAL_MALLOC_BUFFER); + if (!pmbuf) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + memset(pmadapter, pmbuf->pbuf, 0, data_len); + pmbuf->bss_index = priv->bss_index; + pmbuf->buf_type = MLAN_BUF_TYPE_DATA; + pmbuf->flags |= MLAN_BUF_FLAG_NULL_PKT; + ptr = pmbuf->pbuf + pmbuf->data_offset; + pmbuf->data_len = sizeof(TxPD) + priv->intf_hr_len; + ptx_pd = (TxPD *)(ptr + priv->intf_hr_len); + ptx_pd->tx_control = priv->pkt_tx_ctrl; + ptx_pd->flags = flags; + ptx_pd->priority = WMM_HIGHEST_PRIORITY; + ptx_pd->tx_pkt_offset = sizeof(TxPD); + /* Set the BSS number to TxPD */ + ptx_pd->bss_num = GET_BSS_NUM(priv); + ptx_pd->bss_type = priv->bss_type; + + endian_convert_TxPD(ptx_pd); + + ret = wlan_sdio_host_to_card(pmadapter, MLAN_TYPE_DATA, pmbuf, MNULL); + + switch (ret) { + case MLAN_STATUS_RESOURCE: + wlan_free_mlan_buffer(pmadapter, pmbuf); + PRINTM(MERROR, "STA Tx Error: Failed to send NULL packet!\n"); + pmadapter->dbg.num_tx_host_to_card_failure++; + goto done; + case MLAN_STATUS_FAILURE: + pmadapter->data_sent = MFALSE; + wlan_free_mlan_buffer(pmadapter, pmbuf); + PRINTM(MERROR, "STA Tx Error: Failed to send NULL packet!\n"); + pmadapter->dbg.num_tx_host_to_card_failure++; + goto done; + case MLAN_STATUS_SUCCESS: + wlan_free_mlan_buffer(pmadapter, pmbuf); + PRINTM(MINFO, "STA Tx: Successfully send the NULL packet\n"); + pmadapter->tx_lock_flag = MTRUE; + break; + case MLAN_STATUS_PENDING: + pmadapter->tx_lock_flag = MTRUE; + break; + default: + break; + } + + PRINTM_GET_SYS_TIME(MDATA, &sec, &usec); + PRINTM_NETINTF(MDATA, priv); + PRINTM(MDATA, "%lu.%06lu : Null data => FW\n", sec, usec); + DBG_HEXDUMP(MDAT_D, "Null data", ptr, sizeof(TxPD) + priv->intf_hr_len); +done: + LEAVE(); + return ret; +} + +/** + * @brief This function checks if we need to send last packet indication. + * + * @param priv A pointer to mlan_private structure + * + * @return MTRUE or MFALSE + */ +t_u8 +wlan_check_last_packet_indication(pmlan_private priv) +{ + pmlan_adapter pmadapter = priv->adapter; + t_u8 ret = MFALSE; + t_u8 prop_ps = MTRUE; + + ENTER(); + + if (!pmadapter->sleep_period.period) { + LEAVE(); + return ret; + } + if (wlan_bypass_tx_list_empty(pmadapter) && + wlan_wmm_lists_empty(pmadapter)) { + if (((priv->curr_bss_params.wmm_uapsd_enabled == MTRUE) && + priv->wmm_qosinfo) || prop_ps) + + ret = MTRUE; + } + if (ret && !pmadapter->cmd_sent && !pmadapter->curr_cmd + && !wlan_is_cmd_pending(pmadapter)) { + pmadapter->delay_null_pkt = MFALSE; + ret = MTRUE; + } else { + ret = MFALSE; + pmadapter->delay_null_pkt = MTRUE; + } + LEAVE(); + return ret; +} diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_txrx.c b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_txrx.c new file mode 100644 index 000000000000..cfe2bfc9d2cb --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_txrx.c @@ -0,0 +1,395 @@ +/** + * @file mlan_txrx.c + * + * @brief This file contains the handling of TX/RX in MLAN + * + * Copyright (C) 2009-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/************************************************************* +Change Log: + 05/11/2009: initial version +************************************************************/ + +#include "mlan.h" +#ifdef STA_SUPPORT +#include "mlan_join.h" +#endif +#include "mlan_util.h" +#include "mlan_fw.h" +#include "mlan_main.h" +#include "mlan_wmm.h" +#include "mlan_sdio.h" + +/******************************************************** + Local Variables +********************************************************/ + +/******************************************************** + Global Variables +********************************************************/ + +/******************************************************** + Local Functions +********************************************************/ + +/******************************************************** + Global Functions +********************************************************/ +/** + * @brief This function processes the received buffer + * + * @param pmadapter A pointer to mlan_adapter + * @param pmbuf A pointer to the received buffer + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_handle_rx_packet(pmlan_adapter pmadapter, pmlan_buffer pmbuf) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_private priv = MNULL; + RxPD *prx_pd; +#ifdef DEBUG_LEVEL1 + t_u32 sec = 0, usec = 0; +#endif + + ENTER(); + + prx_pd = (RxPD *)(pmbuf->pbuf + pmbuf->data_offset); + /* Get the BSS number from RxPD, get corresponding priv */ + priv = wlan_get_priv_by_id(pmadapter, prx_pd->bss_num & BSS_NUM_MASK, + prx_pd->bss_type); + if (!priv) + priv = wlan_get_priv(pmadapter, MLAN_BSS_ROLE_ANY); + if (!priv) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + pmbuf->bss_index = priv->bss_index; + PRINTM_GET_SYS_TIME(MDATA, &sec, &usec); + PRINTM_NETINTF(MDATA, priv); + PRINTM(MDATA, "%lu.%06lu : Data <= FW\n", sec, usec); + ret = priv->ops.process_rx_packet(pmadapter, pmbuf); + +done: + LEAVE(); + return ret; +} + +/** + * @brief This function checks the conditions and sends packet to device + * + * @param priv A pointer to mlan_private structure + * @param pmbuf A pointer to the mlan_buffer for process + * @param tx_param A pointer to mlan_tx_param structure + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING --success, otherwise failure + */ +mlan_status +wlan_process_tx(pmlan_private priv, pmlan_buffer pmbuf, mlan_tx_param *tx_param) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_adapter pmadapter = priv->adapter; + t_u8 *head_ptr = MNULL; +#ifdef DEBUG_LEVEL1 + t_u32 sec = 0, usec = 0; +#endif +#ifdef STA_SUPPORT + TxPD *plocal_tx_pd = MNULL; +#endif + + ENTER(); + head_ptr = (t_u8 *)priv->ops.process_txpd(priv, pmbuf); + if (!head_ptr) { + pmbuf->status_code = MLAN_ERROR_PKT_INVALID; + ret = MLAN_STATUS_FAILURE; + goto done; + } +#ifdef STA_SUPPORT + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) + plocal_tx_pd = (TxPD *)(head_ptr + priv->intf_hr_len); +#endif + + ret = wlan_sdio_host_to_card(pmadapter, MLAN_TYPE_DATA, pmbuf, + tx_param); +done: + switch (ret) { + case MLAN_STATUS_RESOURCE: +#ifdef STA_SUPPORT + if ((GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) && + pmadapter->pps_uapsd_mode && + (pmadapter->tx_lock_flag == MTRUE)) { + pmadapter->tx_lock_flag = MFALSE; + plocal_tx_pd->flags = 0; + } +#endif + PRINTM(MINFO, "MLAN_STATUS_RESOURCE is returned\n"); + break; + case MLAN_STATUS_FAILURE: + pmadapter->data_sent = MFALSE; + PRINTM(MERROR, "Error: host_to_card failed: 0x%X\n", ret); + pmadapter->dbg.num_tx_host_to_card_failure++; + pmbuf->status_code = MLAN_ERROR_DATA_TX_FAIL; + wlan_write_data_complete(pmadapter, pmbuf, ret); + break; + case MLAN_STATUS_PENDING: + DBG_HEXDUMP(MDAT_D, "Tx", head_ptr + priv->intf_hr_len, + MIN(pmbuf->data_len + sizeof(TxPD), + MAX_DATA_DUMP_LEN)); + break; + case MLAN_STATUS_SUCCESS: + DBG_HEXDUMP(MDAT_D, "Tx", head_ptr + priv->intf_hr_len, + MIN(pmbuf->data_len + sizeof(TxPD), + MAX_DATA_DUMP_LEN)); + wlan_write_data_complete(pmadapter, pmbuf, ret); + break; + default: + break; + } + + if ((ret == MLAN_STATUS_SUCCESS) || (ret == MLAN_STATUS_PENDING)) { + PRINTM_GET_SYS_TIME(MDATA, &sec, &usec); + PRINTM_NETINTF(MDATA, priv); + PRINTM(MDATA, "%lu.%06lu : Data => FW\n", sec, usec); + } + LEAVE(); + return ret; +} + +/** + * @brief Packet send completion handling + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pmbuf A pointer to mlan_buffer structure + * @param status Callback status + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_write_data_complete(IN pmlan_adapter pmadapter, + IN pmlan_buffer pmbuf, IN mlan_status status) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_callbacks pcb; + + ENTER(); + + MASSERT(pmadapter && pmbuf); + if (!pmadapter || !pmbuf) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + pcb = &pmadapter->callbacks; + + if ((pmbuf->buf_type == MLAN_BUF_TYPE_DATA) || + (pmbuf->buf_type == MLAN_BUF_TYPE_RAW_DATA)) { + PRINTM(MINFO, "wlan_write_data_complete: DATA %p\n", pmbuf); + if (pmbuf->flags & MLAN_BUF_FLAG_MOAL_TX_BUF) { + /* pmbuf was allocated by MOAL */ + pcb->moal_send_packet_complete(pmadapter->pmoal_handle, + pmbuf, status); + } else { + /* pmbuf was allocated by MLAN */ + wlan_free_mlan_buffer(pmadapter, pmbuf); + } + } + + LEAVE(); + return ret; +} + +/** + * @brief Packet receive completion callback handler + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pmbuf A pointer to mlan_buffer structure + * @param status Callback status + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_recv_packet_complete(IN pmlan_adapter pmadapter, + IN pmlan_buffer pmbuf, IN mlan_status status) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_callbacks pcb; + + ENTER(); + + MASSERT(pmadapter && pmbuf); + if (!pmadapter || !pmbuf) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + pcb = &pmadapter->callbacks; + MASSERT(pmbuf->bss_index < pmadapter->priv_num); + + if (pmbuf->pparent) { + /** we will free the pparaent at the end of deaggr */ + wlan_free_mlan_buffer(pmadapter, pmbuf); + } else { + wlan_free_mlan_buffer(pmadapter, pmbuf); + } + + LEAVE(); + return ret; +} + +/** + * @brief Add packet to Bypass TX queue + * + * @param pmadapter Pointer to the mlan_adapter driver data struct + * @param pmbuf Pointer to the mlan_buffer data struct + * + * @return N/A + */ +t_void +wlan_add_buf_bypass_txqueue(mlan_adapter *pmadapter, pmlan_buffer pmbuf) +{ + pmlan_private priv = pmadapter->priv[pmbuf->bss_index]; + ENTER(); + + if (pmbuf->buf_type != MLAN_BUF_TYPE_RAW_DATA) + pmbuf->buf_type = MLAN_BUF_TYPE_DATA; + pmadapter->callbacks.moal_spin_lock(pmadapter->pmoal_handle, + priv->bypass_txq.plock); + pmadapter->bypass_pkt_count++; + util_enqueue_list_tail(pmadapter->pmoal_handle, &priv->bypass_txq, + (pmlan_linked_list)pmbuf, MNULL, MNULL); + pmadapter->callbacks.moal_spin_unlock(pmadapter->pmoal_handle, + priv->bypass_txq.plock); + LEAVE(); +} + +/** + * @brief Check if packets are available in Bypass TX queue + * + * @param pmadapter Pointer to the mlan_adapter driver data struct + * + * @return MFALSE if not empty; MTRUE if empty + */ +INLINE t_u8 +wlan_bypass_tx_list_empty(mlan_adapter *pmadapter) +{ + return (pmadapter->bypass_pkt_count) ? MFALSE : MTRUE; +} + +/** + * @brief Clean up the By-pass TX queue + * + * @param priv Pointer to the mlan_private data struct + * + * @return N/A + */ +t_void +wlan_cleanup_bypass_txq(mlan_private *priv) +{ + pmlan_buffer pmbuf; + mlan_adapter *pmadapter = priv->adapter; + ENTER(); + pmadapter->callbacks.moal_spin_lock(pmadapter->pmoal_handle, + priv->bypass_txq.plock); + while ((pmbuf = + (pmlan_buffer)util_peek_list(pmadapter->pmoal_handle, + &priv->bypass_txq, MNULL, + MNULL))) { + util_unlink_list(pmadapter->pmoal_handle, &priv->bypass_txq, + (pmlan_linked_list)pmbuf, MNULL, MNULL); + wlan_write_data_complete(pmadapter, pmbuf, MLAN_STATUS_FAILURE); + pmadapter->bypass_pkt_count--; + } + pmadapter->callbacks.moal_spin_unlock(pmadapter->pmoal_handle, + priv->bypass_txq.plock); + LEAVE(); +} + +/** + * @brief Transmit the By-passed packet awaiting in by-pass queue + * + * @param pmadapter Pointer to the mlan_adapter driver data struct + * + * @return N/A + */ +t_void +wlan_process_bypass_tx(pmlan_adapter pmadapter) +{ + pmlan_buffer pmbuf; + mlan_tx_param tx_param; + mlan_status status = MLAN_STATUS_SUCCESS; + pmlan_private priv; + int j = 0; + ENTER(); + for (j = 0; j < pmadapter->priv_num; ++j) { + priv = pmadapter->priv[j]; + if (priv) { + pmbuf = (pmlan_buffer)util_dequeue_list(pmadapter-> + pmoal_handle, + &priv-> + bypass_txq, + pmadapter-> + callbacks. + moal_spin_lock, + pmadapter-> + callbacks. + moal_spin_unlock); + if (pmbuf) { + PRINTM(MINFO, "Dequeuing bypassed packet %p\n", + pmbuf); + /* XXX: nex_pkt_len ??? */ + tx_param.next_pkt_len = 0; + status = wlan_process_tx(pmadapter-> + priv[pmbuf->bss_index], + pmbuf, &tx_param); + + if (status == MLAN_STATUS_RESOURCE) { + /* Queue the packet again so that it will be TX'ed later */ + util_enqueue_list_head(pmadapter-> + pmoal_handle, + &priv-> + bypass_txq, + (pmlan_linked_list) + pmbuf, + pmadapter-> + callbacks. + moal_spin_lock, + pmadapter-> + callbacks. + moal_spin_unlock); + } else { + pmadapter->callbacks. + moal_spin_lock(pmadapter-> + pmoal_handle, + priv->bypass_txq. + plock); + pmadapter->bypass_pkt_count--; + pmadapter->callbacks. + moal_spin_unlock(pmadapter-> + pmoal_handle, + priv-> + bypass_txq. + plock); + } + break; + } else { + PRINTM(MINFO, "Nothing to send\n"); + } + } + } + LEAVE(); +} diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_uap.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_uap.h new file mode 100644 index 000000000000..68f075715763 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_uap.h @@ -0,0 +1,87 @@ +/** @file mlan_uap.h + * + * @brief This file contains related macros, enum, and struct + * of uap functionalities + * + * Copyright (C) 2009-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/******************************************************** +Change log: + 02/05/2009: initial version +********************************************************/ + +#ifndef _MLAN_UAP_H_ +#define _MLAN_UAP_H_ + +#ifdef BIG_ENDIAN_SUPPORT +/** Convert TxPD to little endian format from CPU format */ +#define uap_endian_convert_TxPD(x) \ + { \ + (x)->tx_pkt_length = wlan_cpu_to_le16((x)->tx_pkt_length); \ + (x)->tx_pkt_offset = wlan_cpu_to_le16((x)->tx_pkt_offset); \ + (x)->tx_pkt_type = wlan_cpu_to_le16((x)->tx_pkt_type); \ + (x)->tx_control = wlan_cpu_to_le32((x)->tx_control); \ + (x)->tx_control_1 = wlan_cpu_to_le32((x)->tx_control_1); \ + } +/** Convert RxPD from little endian format to CPU format */ +#define uap_endian_convert_RxPD(x) \ + { \ + (x)->rx_pkt_length = wlan_le16_to_cpu((x)->rx_pkt_length); \ + (x)->rx_pkt_offset = wlan_le16_to_cpu((x)->rx_pkt_offset); \ + (x)->rx_pkt_type = wlan_le16_to_cpu((x)->rx_pkt_type); \ + (x)->seq_num = wlan_le16_to_cpu((x)->seq_num); \ + (x)->rx_info = wlan_le32_to_cpu((x)->rx_info); \ + } +#else +/** Convert TxPD to little endian format from CPU format */ +#define uap_endian_convert_TxPD(x) do {} while (0) +/** Convert RxPD from little endian format to CPU format */ +#define uap_endian_convert_RxPD(x) do {} while (0) +#endif /* BIG_ENDIAN_SUPPORT */ + +mlan_status wlan_uap_get_channel(IN pmlan_private pmpriv); + +mlan_status wlan_uap_set_channel(IN pmlan_private pmpriv, + IN Band_Config_t uap_band_cfg, + IN t_u8 channel); + +mlan_status wlan_uap_get_beacon_dtim(IN pmlan_private pmpriv); + +mlan_status wlan_ops_uap_ioctl(t_void *adapter, pmlan_ioctl_req pioctl_req); + +mlan_status wlan_ops_uap_prepare_cmd(IN t_void *priv, + IN t_u16 cmd_no, + IN t_u16 cmd_action, + IN t_u32 cmd_oid, + IN t_void *pioctl_buf, + IN t_void *pdata_buf, IN t_void *pcmd_buf); + +mlan_status wlan_ops_uap_process_cmdresp(IN t_void *priv, + IN t_u16 cmdresp_no, + IN t_void *pcmd_buf, + IN t_void *pioctl); + +mlan_status wlan_ops_uap_process_rx_packet(IN t_void *adapter, + IN pmlan_buffer pmbuf); + +mlan_status wlan_ops_uap_process_event(IN t_void *priv); + +t_void *wlan_ops_uap_process_txpd(IN t_void *priv, IN pmlan_buffer pmbuf); + +mlan_status wlan_ops_uap_init_cmd(IN t_void *priv, IN t_u8 first_bss); + +#endif /* _MLAN_UAP_H_ */ diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_uap_cmdevent.c b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_uap_cmdevent.c new file mode 100644 index 000000000000..781001531ba7 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_uap_cmdevent.c @@ -0,0 +1,4745 @@ +/** @file mlan_uap_cmdevent.c + * + * @brief This file contains the handling of AP mode command and event + * + * Copyright (C) 2009-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/******************************************************** +Change log: + 02/05/2009: initial version +********************************************************/ + +#include "mlan.h" +#include "mlan_util.h" +#include "mlan_fw.h" +#ifdef STA_SUPPORT +#include "mlan_join.h" +#endif +#include "mlan_main.h" +#include "mlan_uap.h" +#include "mlan_sdio.h" +#include "mlan_11n.h" +#include "mlan_11h.h" +#ifdef DRV_EMBEDDED_AUTHENTICATOR +#include "authenticator_api.h" +#endif + +/******************************************************** + Local Functions +********************************************************/ +/** + * @brief This function handles the command response error + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to command buffer + * + * @return N/A + */ +static mlan_status +uap_process_cmdresp_error(mlan_private *pmpriv, HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + mlan_status ret = MLAN_STATUS_FAILURE; + + ENTER(); + if (resp->command != HostCmd_CMD_WMM_PARAM_CONFIG + || resp->command != HostCmd_CMD_CHAN_REGION_CFG) + PRINTM(MERROR, "CMD_RESP: cmd %#x error, result=%#x\n", + resp->command, resp->result); + if (pioctl_buf) + pioctl_buf->status_code = resp->result; + /* + * Handling errors here + */ + switch (resp->command) { + case HostCmd_CMD_SDIO_SP_RX_AGGR_CFG: + pmadapter->sdio_rx_aggr_enable = MFALSE; + PRINTM(MMSG, "FW don't support SDIO single port rx aggr\n"); + break; + + case HOST_CMD_APCMD_SYS_CONFIGURE: + { + HostCmd_DS_SYS_CONFIG *sys_config = + (HostCmd_DS_SYS_CONFIG *)&resp->params. + sys_config; + t_u16 resp_len = 0, travel_len = 0, index; + mlan_ds_misc_custom_ie *cust_ie = MNULL; + mlan_ds_misc_cfg *misc = MNULL; + custom_ie *cptr; + + if (!pioctl_buf || + (pioctl_buf->req_id != MLAN_IOCTL_MISC_CFG)) + break; + misc = (mlan_ds_misc_cfg *)pioctl_buf->pbuf; + if ((pioctl_buf->action == MLAN_ACT_SET) && + (misc->sub_command == MLAN_OID_MISC_CUSTOM_IE)) { + cust_ie = + (mlan_ds_misc_custom_ie *)sys_config-> + tlv_buffer; + if (cust_ie) { + cust_ie->type = + wlan_le16_to_cpu(cust_ie->type); + resp_len = cust_ie->len = + wlan_le16_to_cpu(cust_ie->len); + travel_len = 0; + /* conversion for index, mask, len */ + if (resp_len == sizeof(t_u16)) + cust_ie->ie_data_list[0]. + ie_index = + wlan_cpu_to_le16 + (cust_ie-> + ie_data_list[0]. + ie_index); + + while (resp_len > sizeof(t_u16)) { + cptr = (custom_ie + *)(((t_u8 *)cust_ie-> + ie_data_list) + + travel_len); + index = cptr->ie_index = + wlan_le16_to_cpu(cptr-> + ie_index); + cptr->mgmt_subtype_mask = + wlan_le16_to_cpu(cptr-> + mgmt_subtype_mask); + cptr->ie_length = + wlan_le16_to_cpu(cptr-> + ie_length); + travel_len += + cptr->ie_length + + sizeof(custom_ie) - + MAX_IE_SIZE; + resp_len -= + cptr->ie_length + + sizeof(custom_ie) - + MAX_IE_SIZE; + if ((pmpriv->mgmt_ie[index]. + mgmt_subtype_mask == + cptr->mgmt_subtype_mask) && + (pmpriv->mgmt_ie[index]. + ie_length == + cptr->ie_length) && + !memcmp(pmpriv->adapter, + pmpriv-> + mgmt_ie[index]. + ie_buffer, + cptr->ie_buffer, + cptr->ie_length)) { + PRINTM(MERROR, + "set custom ie fail, remove ie index :%d\n", + index); + memset(pmadapter, + &pmpriv-> + mgmt_ie[index], + 0, + sizeof + (custom_ie)); + } + } + } + } + } + break; + case HostCmd_CMD_CHAN_REGION_CFG: + ret = MLAN_STATUS_SUCCESS; + PRINTM(MCMND, "FW don't support chan region cfg command!\n"); + break; + + default: + break; + } + + wlan_request_cmd_lock(pmadapter); + wlan_insert_cmd_to_free_q(pmadapter, pmadapter->curr_cmd); + pmadapter->curr_cmd = MNULL; + wlan_release_cmd_lock(pmadapter); + + LEAVE(); + return ret; +} + +/** + * @brief This function will return the pointer to station entry in station list + * table which matches the give mac address + * + * @param priv A pointer to mlan_private + * + * @return A pointer to structure sta_node + */ +void +wlan_notify_station_deauth(mlan_private *priv) +{ + sta_node *sta_ptr; + t_u8 event_buf[100]; + mlan_event *pevent = (mlan_event *)event_buf; + t_u8 *pbuf; + + ENTER(); + sta_ptr = (sta_node *)util_peek_list(priv->adapter->pmoal_handle, + &priv->sta_list, + priv->adapter->callbacks. + moal_spin_lock, + priv->adapter->callbacks. + moal_spin_unlock); + if (!sta_ptr) { + LEAVE(); + return; + } + while (sta_ptr != (sta_node *)&priv->sta_list) { + memset(priv->adapter, event_buf, 0, sizeof(event_buf)); + pevent->bss_index = priv->bss_index; + pevent->event_id = MLAN_EVENT_ID_UAP_FW_STA_DISCONNECT; + pevent->event_len = MLAN_MAC_ADDR_LENGTH + 2; + pbuf = (t_u8 *)pevent->event_buf; + /* reason field set to 0, Unspecified */ + memcpy(priv->adapter, pbuf + 2, sta_ptr->mac_addr, + MLAN_MAC_ADDR_LENGTH); + wlan_recv_event(priv, pevent->event_id, pevent); + sta_ptr = sta_ptr->pnext; + } + LEAVE(); + return; +} + +/** + * @brief This function prepares command of hs_cfg. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action The action: GET or SET + * @param pdata_buf A pointer to data buffer + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_uap_cmd_802_11_hs_cfg(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, IN hs_config_param *pdata_buf) +{ + pmlan_adapter pmadapter = pmpriv->adapter; + HostCmd_DS_802_11_HS_CFG_ENH *phs_cfg = + (HostCmd_DS_802_11_HS_CFG_ENH *)&(cmd->params.opt_hs_cfg); + t_u8 *tlv = (t_u8 *)phs_cfg + sizeof(HostCmd_DS_802_11_HS_CFG_ENH); + MrvlIEtypes_HsWakeHoldoff_t *holdoff_tlv = MNULL; + MrvlIEtypes_WakeupSourceGPIO_t *gpio_tlv = MNULL; + MrvlIEtypes_MgmtFrameFilter_t *mgmt_filter_tlv = MNULL; + MrvlIEtypes_WakeupExtend_t *ext_tlv = MNULL; + + ENTER(); + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_802_11_HS_CFG_ENH); + cmd->size = + wlan_cpu_to_le16(S_DS_GEN + + sizeof(HostCmd_DS_802_11_HS_CFG_ENH)); + + if (pdata_buf == MNULL) { + phs_cfg->action = wlan_cpu_to_le16(HS_ACTIVATE); + phs_cfg->params.hs_activate.resp_ctrl = + wlan_cpu_to_le16(RESP_NEEDED); + } else { + phs_cfg->action = wlan_cpu_to_le16(HS_CONFIGURE); + phs_cfg->params.hs_config.conditions = + wlan_cpu_to_le32(pdata_buf->conditions); + phs_cfg->params.hs_config.gpio = pdata_buf->gpio; + phs_cfg->params.hs_config.gap = pdata_buf->gap; + if (pmpriv->adapter->min_wake_holdoff) { + cmd->size = + wlan_cpu_to_le16(S_DS_GEN + + sizeof + (HostCmd_DS_802_11_HS_CFG_ENH) + + + sizeof + (MrvlIEtypes_HsWakeHoldoff_t)); + holdoff_tlv = (MrvlIEtypes_HsWakeHoldoff_t *)tlv; + holdoff_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_HS_WAKE_HOLDOFF); + holdoff_tlv->header.len = + wlan_cpu_to_le16(sizeof + (MrvlIEtypes_HsWakeHoldoff_t) - + sizeof(MrvlIEtypesHeader_t)); + holdoff_tlv->min_wake_holdoff = + wlan_cpu_to_le16(pmpriv->adapter-> + min_wake_holdoff); + tlv += sizeof(MrvlIEtypes_HsWakeHoldoff_t); + } + PRINTM(MCMND, + "HS_CFG_CMD: condition:0x%x gpio:0x%x gap:0x%x holdoff=%d\n", + phs_cfg->params.hs_config.conditions, + phs_cfg->params.hs_config.gpio, + phs_cfg->params.hs_config.gap, + pmpriv->adapter->min_wake_holdoff); + + if (pmadapter->param_type_ind == 1) { + cmd->size += sizeof(MrvlIEtypes_WakeupSourceGPIO_t); + gpio_tlv = (MrvlIEtypes_WakeupSourceGPIO_t *) tlv; + gpio_tlv->header.type = + wlan_cpu_to_le16 + (TLV_TYPE_HS_WAKEUP_SOURCE_GPIO); + gpio_tlv->header.len = + wlan_cpu_to_le16(sizeof + (MrvlIEtypes_WakeupSourceGPIO_t) + - sizeof(MrvlIEtypesHeader_t)); + gpio_tlv->ind_gpio = (t_u8)pmadapter->ind_gpio; + gpio_tlv->level = (t_u8)pmadapter->level; + tlv += sizeof(MrvlIEtypes_WakeupSourceGPIO_t); + } + if (pmadapter->param_type_ext == 2) { + cmd->size += sizeof(MrvlIEtypes_WakeupExtend_t); + ext_tlv = (MrvlIEtypes_WakeupExtend_t *) tlv; + ext_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_WAKEUP_EXTEND); + ext_tlv->header.len = + wlan_cpu_to_le16(sizeof + (MrvlIEtypes_WakeupExtend_t) - + sizeof(MrvlIEtypesHeader_t)); + ext_tlv->event_force_ignore = + wlan_cpu_to_le32(pmadapter->event_force_ignore); + ext_tlv->event_use_ext_gap = + wlan_cpu_to_le32(pmadapter->event_use_ext_gap); + ext_tlv->ext_gap = pmadapter->ext_gap; + ext_tlv->gpio_wave = pmadapter->gpio_wave; + tlv += sizeof(MrvlIEtypes_WakeupExtend_t); + } + if (pmadapter->mgmt_filter[0].type) { + int i = 0; + mgmt_frame_filter mgmt_filter[MAX_MGMT_FRAME_FILTER]; + memset(pmadapter, mgmt_filter, 0, + MAX_MGMT_FRAME_FILTER * + sizeof(mgmt_frame_filter)); + mgmt_filter_tlv = (MrvlIEtypes_MgmtFrameFilter_t *) tlv; + mgmt_filter_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_MGMT_FRAME_WAKEUP); + tlv += sizeof(MrvlIEtypesHeader_t); + while (i < MAX_MGMT_FRAME_FILTER && + pmadapter->mgmt_filter[i].type) { + mgmt_filter[i].action = + (t_u8)pmadapter->mgmt_filter[i].action; + mgmt_filter[i].type = + (t_u8)pmadapter->mgmt_filter[i].type; + mgmt_filter[i].frame_mask = + wlan_cpu_to_le32(pmadapter-> + mgmt_filter[i]. + frame_mask); + i++; + } + memcpy(pmadapter, (t_u8 *)mgmt_filter_tlv->filter, + (t_u8 *)mgmt_filter, + i * sizeof(mgmt_frame_filter)); + tlv += i * sizeof(mgmt_frame_filter); + mgmt_filter_tlv->header.len = + wlan_cpu_to_le16(i * sizeof(mgmt_frame_filter)); + cmd->size += + i * sizeof(mgmt_frame_filter) + + sizeof(MrvlIEtypesHeader_t); + } + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of Tx data pause + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action the action: GET or SET + * @param pdata_buf A pointer to data buffer + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_uap_cmd_txdatapause(pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, IN t_void *pdata_buf) +{ + HostCmd_DS_CMD_TX_DATA_PAUSE *pause_cmd = + (HostCmd_DS_CMD_TX_DATA_PAUSE *)&cmd->params.tx_data_pause; + mlan_ds_misc_tx_datapause *data_pause = + (mlan_ds_misc_tx_datapause *)pdata_buf; + + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_CFG_TX_DATA_PAUSE); + cmd->size = + wlan_cpu_to_le16(sizeof(HostCmd_DS_CMD_TX_DATA_PAUSE) + + S_DS_GEN); + pause_cmd->action = wlan_cpu_to_le16(cmd_action); + + if (cmd_action == HostCmd_ACT_GEN_SET) { + pause_cmd->enable_tx_pause = (t_u8)data_pause->tx_pause; + pause_cmd->pause_tx_count = (t_u8)data_pause->tx_buf_cnt; + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of Tx data pause + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_uap_ret_txdatapause(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf) +{ + HostCmd_DS_CMD_TX_DATA_PAUSE *pause_cmd = + (HostCmd_DS_CMD_TX_DATA_PAUSE *)&resp->params.tx_data_pause; + mlan_ds_misc_cfg *misc_cfg = MNULL; + + ENTER(); + + if (pioctl_buf) { + misc_cfg = (mlan_ds_misc_cfg *)pioctl_buf->pbuf; + misc_cfg->param.tx_datapause.tx_pause = + pause_cmd->enable_tx_pause; + misc_cfg->param.tx_datapause.tx_buf_cnt = + pause_cmd->pause_tx_count; + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function will process tx pause event + * + * @param priv A pointer to mlan_private + * @param pevent A pointer to event buf + * + * @return N/A + */ +static void +wlan_process_tx_pause_event(pmlan_private priv, pmlan_buffer pevent) +{ + t_u16 tlv_type, tlv_len; + int tlv_buf_left = pevent->data_len - sizeof(t_u32); + MrvlIEtypesHeader_t *tlv = + (MrvlIEtypesHeader_t *)(pevent->pbuf + pevent->data_offset + + sizeof(t_u32)); + MrvlIEtypes_tx_pause_t *tx_pause_tlv; + sta_node *sta_ptr = MNULL; + t_u8 bc_mac[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + + ENTER(); + + while (tlv_buf_left >= (int)sizeof(MrvlIEtypesHeader_t)) { + tlv_type = wlan_le16_to_cpu(tlv->type); + tlv_len = wlan_le16_to_cpu(tlv->len); + if ((sizeof(MrvlIEtypesHeader_t) + tlv_len) > + (unsigned int)tlv_buf_left) { + PRINTM(MERROR, "wrong tlv: tlvLen=%d, tlvBufLeft=%d\n", + tlv_len, tlv_buf_left); + break; + } + if (tlv_type == TLV_TYPE_TX_PAUSE) { + tx_pause_tlv = (MrvlIEtypes_tx_pause_t *)tlv; + PRINTM(MCMND, "TxPause: " MACSTR " pause=%d, pkts=%d\n", + MAC2STR(tx_pause_tlv->peermac), + tx_pause_tlv->tx_pause, tx_pause_tlv->pkt_cnt); + if (!memcmp + (priv->adapter, bc_mac, tx_pause_tlv->peermac, + MLAN_MAC_ADDR_LENGTH)) + wlan_update_ralist_tx_pause(priv, + tx_pause_tlv-> + peermac, + tx_pause_tlv-> + tx_pause); + else if (!memcmp + (priv->adapter, priv->curr_addr, + tx_pause_tlv->peermac, + MLAN_MAC_ADDR_LENGTH)) { + if (tx_pause_tlv->tx_pause) + priv->tx_pause = MTRUE; + else + priv->tx_pause = MFALSE; + } else { + sta_ptr = + wlan_get_station_entry(priv, + tx_pause_tlv-> + peermac); + if (sta_ptr) { + if (sta_ptr->tx_pause != + tx_pause_tlv->tx_pause) { + sta_ptr->tx_pause = + tx_pause_tlv->tx_pause; + wlan_update_ralist_tx_pause + (priv, + tx_pause_tlv->peermac, + tx_pause_tlv-> + tx_pause); + } + } + } + } + tlv_buf_left -= (sizeof(MrvlIEtypesHeader_t) + tlv_len); + tlv = (MrvlIEtypesHeader_t *)((t_u8 *)tlv + tlv_len + + sizeof(MrvlIEtypesHeader_t)); + } + + LEAVE(); + return; +} + +/** + * @brief This function prepares command for config uap settings + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action the action: GET or SET + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status +wlan_uap_cmd_ap_config(pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, IN pmlan_ioctl_req pioctl_buf) +{ + mlan_ds_bss *bss = MNULL; + HostCmd_DS_SYS_CONFIG *sys_config = + (HostCmd_DS_SYS_CONFIG *)&cmd->params.sys_config; + t_u8 *tlv = MNULL; + MrvlIEtypes_MacAddr_t *tlv_mac = MNULL; + MrvlIEtypes_SsIdParamSet_t *tlv_ssid = MNULL; + MrvlIEtypes_beacon_period_t *tlv_beacon_period = MNULL; + MrvlIEtypes_dtim_period_t *tlv_dtim_period = MNULL; + MrvlIEtypes_RatesParamSet_t *tlv_rates = MNULL; + MrvlIEtypes_tx_rate_t *tlv_txrate = MNULL; + MrvlIEtypes_mcbc_rate_t *tlv_mcbc_rate = MNULL; + MrvlIEtypes_tx_power_t *tlv_tx_power = MNULL; + MrvlIEtypes_bcast_ssid_t *tlv_bcast_ssid = MNULL; + MrvlIEtypes_antenna_mode_t *tlv_antenna = MNULL; + MrvlIEtypes_pkt_forward_t *tlv_pkt_forward = MNULL; + MrvlIEtypes_max_sta_count_t *tlv_sta_count = MNULL; + MrvlIEtypes_sta_ageout_t *tlv_sta_ageout = MNULL; + MrvlIEtypes_ps_sta_ageout_t *tlv_ps_sta_ageout = MNULL; + MrvlIEtypes_rts_threshold_t *tlv_rts_threshold = MNULL; + MrvlIEtypes_frag_threshold_t *tlv_frag_threshold = MNULL; + MrvlIEtypes_retry_limit_t *tlv_retry_limit = MNULL; + MrvlIEtypes_eapol_pwk_hsk_timeout_t *tlv_pairwise_timeout = MNULL; + MrvlIEtypes_eapol_pwk_hsk_retries_t *tlv_pairwise_retries = MNULL; + MrvlIEtypes_eapol_gwk_hsk_timeout_t *tlv_groupwise_timeout = MNULL; + MrvlIEtypes_eapol_gwk_hsk_retries_t *tlv_groupwise_retries = MNULL; + MrvlIEtypes_mgmt_ie_passthru_t *tlv_mgmt_ie_passthru = MNULL; + MrvlIEtypes_2040_coex_enable_t *tlv_2040_coex_enable = MNULL; + MrvlIEtypes_mac_filter_t *tlv_mac_filter = MNULL; + MrvlIEtypes_channel_band_t *tlv_chan_band = MNULL; + MrvlIEtypes_ChanListParamSet_t *tlv_chan_list = MNULL; + ChanScanParamSet_t *pscan_chan = MNULL; + MrvlIEtypes_auth_type_t *tlv_auth_type = MNULL; + MrvlIEtypes_encrypt_protocol_t *tlv_encrypt_protocol = MNULL; + MrvlIEtypes_akmp_t *tlv_akmp = MNULL; + MrvlIEtypes_pwk_cipher_t *tlv_pwk_cipher = MNULL; + MrvlIEtypes_gwk_cipher_t *tlv_gwk_cipher = MNULL; + MrvlIEtypes_rsn_replay_prot_t *tlv_rsn_prot = MNULL; + MrvlIEtypes_passphrase_t *tlv_passphrase = MNULL; + MrvlIEtypes_group_rekey_time_t *tlv_rekey_time = MNULL; + MrvlIEtypes_wep_key_t *tlv_wep_key = MNULL; + MrvlIETypes_HTCap_t *tlv_htcap = MNULL; + MrvlIEtypes_wmm_parameter_t *tlv_wmm_parameter = MNULL; + t_u32 cmd_size = 0; + t_u8 zero_mac[] = { 0, 0, 0, 0, 0, 0 }; + t_u16 i; + t_u16 ac; + + ENTER(); + if (pioctl_buf == MNULL) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + bss = (mlan_ds_bss *)pioctl_buf->pbuf; + + cmd->command = wlan_cpu_to_le16(HOST_CMD_APCMD_SYS_CONFIGURE); + sys_config->action = wlan_cpu_to_le16(cmd_action); + cmd_size = sizeof(HostCmd_DS_SYS_CONFIG) - 1 + S_DS_GEN; + + tlv = (t_u8 *)sys_config->tlv_buffer; + if (memcmp + (pmpriv->adapter, zero_mac, &bss->param.bss_config.mac_addr, + MLAN_MAC_ADDR_LENGTH)) { + tlv_mac = (MrvlIEtypes_MacAddr_t *)tlv; + tlv_mac->header.type = + wlan_cpu_to_le16(TLV_TYPE_UAP_MAC_ADDRESS); + tlv_mac->header.len = wlan_cpu_to_le16(MLAN_MAC_ADDR_LENGTH); + memcpy(pmpriv->adapter, tlv_mac->mac, + &bss->param.bss_config.mac_addr, MLAN_MAC_ADDR_LENGTH); + cmd_size += sizeof(MrvlIEtypes_MacAddr_t); + tlv += sizeof(MrvlIEtypes_MacAddr_t); + } + + if (bss->param.bss_config.bandcfg.scanMode == SCAN_MODE_ACS) { + /* ACS is not allowed when DFS repeater mode is on */ + if (pmpriv->adapter->dfs_repeater) { + PRINTM(MERROR, "ACS is not allowed when" + "DFS repeater mode is on.\n"); + return MLAN_STATUS_FAILURE; + } + } + + if (bss->param.bss_config.ssid.ssid_len) { + tlv_ssid = (MrvlIEtypes_SsIdParamSet_t *)tlv; + tlv_ssid->header.type = wlan_cpu_to_le16(TLV_TYPE_SSID); + tlv_ssid->header.len = + wlan_cpu_to_le16((t_u16)bss->param.bss_config.ssid. + ssid_len); + memcpy(pmpriv->adapter, tlv_ssid->ssid, + bss->param.bss_config.ssid.ssid, + bss->param.bss_config.ssid.ssid_len); + cmd_size += + sizeof(MrvlIEtypesHeader_t) + + bss->param.bss_config.ssid.ssid_len; + tlv += sizeof(MrvlIEtypesHeader_t) + + bss->param.bss_config.ssid.ssid_len; + } + + if ((bss->param.bss_config.beacon_period >= MIN_BEACON_PERIOD) && + (bss->param.bss_config.beacon_period <= MAX_BEACON_PERIOD)) { + tlv_beacon_period = (MrvlIEtypes_beacon_period_t *)tlv; + tlv_beacon_period->header.type = + wlan_cpu_to_le16(TLV_TYPE_UAP_BEACON_PERIOD); + tlv_beacon_period->header.len = wlan_cpu_to_le16(sizeof(t_u16)); + tlv_beacon_period->beacon_period = + wlan_cpu_to_le16(bss->param.bss_config.beacon_period); + cmd_size += sizeof(MrvlIEtypes_beacon_period_t); + tlv += sizeof(MrvlIEtypes_beacon_period_t); + } + + if ((bss->param.bss_config.dtim_period >= MIN_DTIM_PERIOD) && + (bss->param.bss_config.dtim_period <= MAX_DTIM_PERIOD)) { + tlv_dtim_period = (MrvlIEtypes_dtim_period_t *)tlv; + tlv_dtim_period->header.type = + wlan_cpu_to_le16(TLV_TYPE_UAP_DTIM_PERIOD); + tlv_dtim_period->header.len = wlan_cpu_to_le16(sizeof(t_u8)); + tlv_dtim_period->dtim_period = + bss->param.bss_config.dtim_period; + cmd_size += sizeof(MrvlIEtypes_dtim_period_t); + tlv += sizeof(MrvlIEtypes_dtim_period_t); + } + + if (bss->param.bss_config.rates[0]) { + tlv_rates = (MrvlIEtypes_RatesParamSet_t *)tlv; + tlv_rates->header.type = wlan_cpu_to_le16(TLV_TYPE_RATES); + for (i = 0; + i < MAX_DATA_RATES && bss->param.bss_config.rates[i]; + i++) { + tlv_rates->rates[i] = bss->param.bss_config.rates[i]; + } + tlv_rates->header.len = wlan_cpu_to_le16(i); + cmd_size += sizeof(MrvlIEtypesHeader_t) + i; + tlv += sizeof(MrvlIEtypesHeader_t) + i; + } + + if (bss->param.bss_config.tx_data_rate <= DATA_RATE_54M) { + tlv_txrate = (MrvlIEtypes_tx_rate_t *)tlv; + tlv_txrate->header.type = + wlan_cpu_to_le16(TLV_TYPE_UAP_TX_DATA_RATE); + tlv_txrate->header.len = wlan_cpu_to_le16(sizeof(t_u16)); + tlv_txrate->tx_data_rate = + wlan_cpu_to_le16(bss->param.bss_config.tx_data_rate); + cmd_size += sizeof(MrvlIEtypes_tx_rate_t); + tlv += sizeof(MrvlIEtypes_tx_rate_t); + } + + if (bss->param.bss_config.tx_beacon_rate <= DATA_RATE_54M) { + tlv_txrate = (MrvlIEtypes_tx_rate_t *)tlv; + tlv_txrate->header.type = + wlan_cpu_to_le16(TLV_TYPE_UAP_TX_BEACON_RATE); + tlv_txrate->header.len = wlan_cpu_to_le16(sizeof(t_u16)); + tlv_txrate->tx_data_rate = + wlan_cpu_to_le16(bss->param.bss_config.tx_beacon_rate); + cmd_size += sizeof(MrvlIEtypes_tx_rate_t); + tlv += sizeof(MrvlIEtypes_tx_rate_t); + } + + if (bss->param.bss_config.mcbc_data_rate <= DATA_RATE_54M) { + tlv_mcbc_rate = (MrvlIEtypes_mcbc_rate_t *)tlv; + tlv_mcbc_rate->header.type = + wlan_cpu_to_le16(TLV_TYPE_UAP_MCBC_DATA_RATE); + tlv_mcbc_rate->header.len = wlan_cpu_to_le16(sizeof(t_u16)); + tlv_mcbc_rate->mcbc_data_rate = + wlan_cpu_to_le16(bss->param.bss_config.mcbc_data_rate); + cmd_size += sizeof(MrvlIEtypes_mcbc_rate_t); + tlv += sizeof(MrvlIEtypes_mcbc_rate_t); + } + + if (bss->param.bss_config.tx_power_level <= MAX_TX_POWER) { + tlv_tx_power = (MrvlIEtypes_tx_power_t *)tlv; + tlv_tx_power->header.type = + wlan_cpu_to_le16(TLV_TYPE_UAP_TX_POWER); + tlv_tx_power->header.len = wlan_cpu_to_le16(sizeof(t_u8)); + tlv_tx_power->tx_power = bss->param.bss_config.tx_power_level; + cmd_size += sizeof(MrvlIEtypes_tx_power_t); + tlv += sizeof(MrvlIEtypes_tx_power_t); + } + + if (bss->param.bss_config.bcast_ssid_ctl <= MAX_BCAST_SSID_CTL) { + tlv_bcast_ssid = (MrvlIEtypes_bcast_ssid_t *)tlv; + tlv_bcast_ssid->header.type = + wlan_cpu_to_le16(TLV_TYPE_UAP_BCAST_SSID_CTL); + tlv_bcast_ssid->header.len = wlan_cpu_to_le16(sizeof(t_u8)); + tlv_bcast_ssid->bcast_ssid_ctl = + bss->param.bss_config.bcast_ssid_ctl; + cmd_size += sizeof(MrvlIEtypes_bcast_ssid_t); + tlv += sizeof(MrvlIEtypes_bcast_ssid_t); + } + + if ((bss->param.bss_config.tx_antenna == ANTENNA_MODE_A) || + (bss->param.bss_config.tx_antenna == ANTENNA_MODE_B)) { + tlv_antenna = (MrvlIEtypes_antenna_mode_t *)tlv; + tlv_antenna->header.type = + wlan_cpu_to_le16(TLV_TYPE_UAP_ANTENNA_CTL); + tlv_antenna->header.len = + wlan_cpu_to_le16(sizeof(t_u8) + sizeof(t_u8)); + tlv_antenna->which_antenna = TX_ANTENNA; + tlv_antenna->antenna_mode = bss->param.bss_config.tx_antenna; + cmd_size += sizeof(MrvlIEtypes_antenna_mode_t); + tlv += sizeof(MrvlIEtypes_antenna_mode_t); + } + + if ((bss->param.bss_config.rx_antenna == ANTENNA_MODE_A) || + (bss->param.bss_config.rx_antenna == ANTENNA_MODE_B)) { + tlv_antenna = (MrvlIEtypes_antenna_mode_t *)tlv; + tlv_antenna->header.type = + wlan_cpu_to_le16(TLV_TYPE_UAP_ANTENNA_CTL); + tlv_antenna->header.len = + wlan_cpu_to_le16(sizeof(t_u8) + sizeof(t_u8)); + tlv_antenna->which_antenna = RX_ANTENNA; + tlv_antenna->antenna_mode = bss->param.bss_config.rx_antenna; + cmd_size += sizeof(MrvlIEtypes_antenna_mode_t); + tlv += sizeof(MrvlIEtypes_antenna_mode_t); + } + + if (bss->param.bss_config.pkt_forward_ctl <= MAX_PKT_FWD_CTRL) { + tlv_pkt_forward = (MrvlIEtypes_pkt_forward_t *)tlv; + tlv_pkt_forward->header.type = + wlan_cpu_to_le16(TLV_TYPE_UAP_PKT_FWD_CTL); + tlv_pkt_forward->header.len = wlan_cpu_to_le16(sizeof(t_u8)); + tlv_pkt_forward->pkt_forward_ctl = + bss->param.bss_config.pkt_forward_ctl; + cmd_size += sizeof(MrvlIEtypes_pkt_forward_t); + tlv += sizeof(MrvlIEtypes_pkt_forward_t); + } + + if (bss->param.bss_config.max_sta_count <= MAX_STA_COUNT) { + tlv_sta_count = (MrvlIEtypes_max_sta_count_t *)tlv; + tlv_sta_count->header.type = + wlan_cpu_to_le16(TLV_TYPE_UAP_MAX_STA_CNT); + tlv_sta_count->header.len = wlan_cpu_to_le16(sizeof(t_u16)); + tlv_sta_count->max_sta_count = + wlan_cpu_to_le16(bss->param.bss_config.max_sta_count); + cmd_size += sizeof(MrvlIEtypes_max_sta_count_t); + tlv += sizeof(MrvlIEtypes_max_sta_count_t); + } + + if (((bss->param.bss_config.sta_ageout_timer >= MIN_STAGE_OUT_TIME) && + (bss->param.bss_config.sta_ageout_timer <= MAX_STAGE_OUT_TIME)) || + (bss->param.bss_config.sta_ageout_timer == 0)) { + tlv_sta_ageout = (MrvlIEtypes_sta_ageout_t *)tlv; + tlv_sta_ageout->header.type = + wlan_cpu_to_le16(TLV_TYPE_UAP_STA_AGEOUT_TIMER); + tlv_sta_ageout->header.len = wlan_cpu_to_le16(sizeof(t_u32)); + tlv_sta_ageout->sta_ageout_timer = + wlan_cpu_to_le32(bss->param.bss_config. + sta_ageout_timer); + cmd_size += sizeof(MrvlIEtypes_sta_ageout_t); + tlv += sizeof(MrvlIEtypes_sta_ageout_t); + } + + if (((bss->param.bss_config.ps_sta_ageout_timer >= MIN_STAGE_OUT_TIME) + && (bss->param.bss_config.ps_sta_ageout_timer <= + MAX_STAGE_OUT_TIME)) || + (bss->param.bss_config.ps_sta_ageout_timer == 0)) { + tlv_ps_sta_ageout = (MrvlIEtypes_ps_sta_ageout_t *)tlv; + tlv_ps_sta_ageout->header.type = + wlan_cpu_to_le16(TLV_TYPE_UAP_PS_STA_AGEOUT_TIMER); + tlv_ps_sta_ageout->header.len = wlan_cpu_to_le16(sizeof(t_u32)); + tlv_ps_sta_ageout->ps_sta_ageout_timer = + wlan_cpu_to_le32(bss->param.bss_config. + ps_sta_ageout_timer); + cmd_size += sizeof(MrvlIEtypes_ps_sta_ageout_t); + tlv += sizeof(MrvlIEtypes_ps_sta_ageout_t); + } + if (bss->param.bss_config.rts_threshold <= MAX_RTS_THRESHOLD) { + tlv_rts_threshold = (MrvlIEtypes_rts_threshold_t *)tlv; + tlv_rts_threshold->header.type = + wlan_cpu_to_le16(TLV_TYPE_UAP_RTS_THRESHOLD); + tlv_rts_threshold->header.len = wlan_cpu_to_le16(sizeof(t_u16)); + tlv_rts_threshold->rts_threshold = + wlan_cpu_to_le16(bss->param.bss_config.rts_threshold); + cmd_size += sizeof(MrvlIEtypes_rts_threshold_t); + tlv += sizeof(MrvlIEtypes_rts_threshold_t); + } + + if ((bss->param.bss_config.frag_threshold >= MIN_FRAG_THRESHOLD) && + (bss->param.bss_config.frag_threshold <= MAX_FRAG_THRESHOLD)) { + tlv_frag_threshold = (MrvlIEtypes_frag_threshold_t *)tlv; + tlv_frag_threshold->header.type = + wlan_cpu_to_le16(TLV_TYPE_UAP_FRAG_THRESHOLD); + tlv_frag_threshold->header.len = + wlan_cpu_to_le16(sizeof(t_u16)); + tlv_frag_threshold->frag_threshold = + wlan_cpu_to_le16(bss->param.bss_config.frag_threshold); + cmd_size += sizeof(MrvlIEtypes_frag_threshold_t); + tlv += sizeof(MrvlIEtypes_frag_threshold_t); + } + + if (bss->param.bss_config.retry_limit <= MAX_RETRY_LIMIT) { + tlv_retry_limit = (MrvlIEtypes_retry_limit_t *)tlv; + tlv_retry_limit->header.type = + wlan_cpu_to_le16(TLV_TYPE_UAP_RETRY_LIMIT); + tlv_retry_limit->header.len = wlan_cpu_to_le16(sizeof(t_u8)); + tlv_retry_limit->retry_limit = + (t_u8)bss->param.bss_config.retry_limit; + cmd_size += sizeof(MrvlIEtypes_retry_limit_t); + tlv += sizeof(MrvlIEtypes_retry_limit_t); + } +#ifdef DRV_EMBEDDED_AUTHENTICATOR + if (IS_FW_SUPPORT_AUTHENTICATOR(pmpriv->adapter)) { +#endif + if (bss->param.bss_config.pairwise_update_timeout < + (MAX_VALID_DWORD)) { + tlv_pairwise_timeout = + (MrvlIEtypes_eapol_pwk_hsk_timeout_t *)tlv; + tlv_pairwise_timeout->header.type = + wlan_cpu_to_le16 + (TLV_TYPE_UAP_EAPOL_PWK_HSK_TIMEOUT); + tlv_pairwise_timeout->header.len = + wlan_cpu_to_le16(sizeof(t_u32)); + tlv_pairwise_timeout->pairwise_update_timeout = + wlan_cpu_to_le32(bss->param.bss_config. + pairwise_update_timeout); + cmd_size += sizeof(MrvlIEtypes_eapol_pwk_hsk_timeout_t); + tlv += sizeof(MrvlIEtypes_eapol_pwk_hsk_timeout_t); + } + + if (bss->param.bss_config.pwk_retries < (MAX_VALID_DWORD)) { + tlv_pairwise_retries = + (MrvlIEtypes_eapol_pwk_hsk_retries_t *)tlv; + tlv_pairwise_retries->header.type = + wlan_cpu_to_le16 + (TLV_TYPE_UAP_EAPOL_PWK_HSK_RETRIES); + tlv_pairwise_retries->header.len = + wlan_cpu_to_le16(sizeof(t_u32)); + tlv_pairwise_retries->pwk_retries = + wlan_cpu_to_le32(bss->param.bss_config. + pwk_retries); + cmd_size += sizeof(MrvlIEtypes_eapol_pwk_hsk_retries_t); + tlv += sizeof(MrvlIEtypes_eapol_pwk_hsk_retries_t); + } + + if (bss->param.bss_config.groupwise_update_timeout < + (MAX_VALID_DWORD)) { + tlv_groupwise_timeout = + (MrvlIEtypes_eapol_gwk_hsk_timeout_t *)tlv; + tlv_groupwise_timeout->header.type = + wlan_cpu_to_le16 + (TLV_TYPE_UAP_EAPOL_GWK_HSK_TIMEOUT); + tlv_groupwise_timeout->header.len = + wlan_cpu_to_le16(sizeof(t_u32)); + tlv_groupwise_timeout->groupwise_update_timeout = + wlan_cpu_to_le32(bss->param.bss_config. + groupwise_update_timeout); + cmd_size += sizeof(MrvlIEtypes_eapol_gwk_hsk_timeout_t); + tlv += sizeof(MrvlIEtypes_eapol_gwk_hsk_timeout_t); + } + + if (bss->param.bss_config.gwk_retries < (MAX_VALID_DWORD)) { + tlv_groupwise_retries = + (MrvlIEtypes_eapol_gwk_hsk_retries_t *)tlv; + tlv_groupwise_retries->header.type = + wlan_cpu_to_le16 + (TLV_TYPE_UAP_EAPOL_GWK_HSK_RETRIES); + tlv_groupwise_retries->header.len = + wlan_cpu_to_le16(sizeof(t_u32)); + tlv_groupwise_retries->gwk_retries = + wlan_cpu_to_le32(bss->param.bss_config. + gwk_retries); + cmd_size += sizeof(MrvlIEtypes_eapol_gwk_hsk_retries_t); + tlv += sizeof(MrvlIEtypes_eapol_gwk_hsk_retries_t); + } +#ifdef DRV_EMBEDDED_AUTHENTICATOR + } +#endif + if ((bss->param.bss_config.filter.filter_mode <= + MAC_FILTER_MODE_BLOCK_MAC) + && (bss->param.bss_config.filter.mac_count <= MAX_MAC_FILTER_NUM)) { + tlv_mac_filter = (MrvlIEtypes_mac_filter_t *)tlv; + tlv_mac_filter->header.type = + wlan_cpu_to_le16(TLV_TYPE_UAP_STA_MAC_ADDR_FILTER); + tlv_mac_filter->header.len = + wlan_cpu_to_le16(2 + + MLAN_MAC_ADDR_LENGTH * + bss->param.bss_config.filter. + mac_count); + tlv_mac_filter->count = + (t_u8)bss->param.bss_config.filter.mac_count; + tlv_mac_filter->filter_mode = + (t_u8)bss->param.bss_config.filter.filter_mode; + memcpy(pmpriv->adapter, tlv_mac_filter->mac_address, + (t_u8 *)bss->param.bss_config.filter.mac_list, + MLAN_MAC_ADDR_LENGTH * + bss->param.bss_config.filter.mac_count); + cmd_size += + sizeof(MrvlIEtypesHeader_t) + 2 + + MLAN_MAC_ADDR_LENGTH * + bss->param.bss_config.filter.mac_count; + tlv += sizeof(MrvlIEtypesHeader_t) + 2 + + MLAN_MAC_ADDR_LENGTH * + bss->param.bss_config.filter.mac_count; + } + + if (((bss->param.bss_config.bandcfg.scanMode == SCAN_MODE_MANUAL) && + (bss->param.bss_config.channel > 0) && + (bss->param.bss_config.channel <= MLAN_MAX_CHANNEL)) || + (bss->param.bss_config.bandcfg.scanMode == SCAN_MODE_ACS)) { + tlv_chan_band = (MrvlIEtypes_channel_band_t *)tlv; + tlv_chan_band->header.type = + wlan_cpu_to_le16(TLV_TYPE_UAP_CHAN_BAND_CONFIG); + tlv_chan_band->header.len = + wlan_cpu_to_le16(sizeof(t_u8) + sizeof(t_u8)); + tlv_chan_band->bandcfg = bss->param.bss_config.bandcfg; + tlv_chan_band->channel = bss->param.bss_config.channel; + cmd_size += sizeof(MrvlIEtypes_channel_band_t); + tlv += sizeof(MrvlIEtypes_channel_band_t); + } + + if ((bss->param.bss_config.num_of_chan) && + (bss->param.bss_config.num_of_chan <= MLAN_MAX_CHANNEL)) { + tlv_chan_list = (MrvlIEtypes_ChanListParamSet_t *)tlv; + tlv_chan_list->header.type = + wlan_cpu_to_le16(TLV_TYPE_CHANLIST); + tlv_chan_list->header.len = + wlan_cpu_to_le16((t_u16) + (sizeof(ChanScanParamSet_t) * + bss->param.bss_config.num_of_chan)); + pscan_chan = tlv_chan_list->chan_scan_param; + for (i = 0; i < bss->param.bss_config.num_of_chan; i++) { + pscan_chan->chan_number = + bss->param.bss_config.chan_list[i].chan_number; + pscan_chan->bandcfg = + bss->param.bss_config.chan_list[i].bandcfg; + pscan_chan++; + } + cmd_size += sizeof(tlv_chan_list->header) + + (sizeof(ChanScanParamSet_t) * + bss->param.bss_config.num_of_chan); + tlv += sizeof(tlv_chan_list->header) + + (sizeof(ChanScanParamSet_t) * + bss->param.bss_config.num_of_chan); + } + + if ((bss->param.bss_config.auth_mode <= MLAN_AUTH_MODE_SHARED) || + (bss->param.bss_config.auth_mode == MLAN_AUTH_MODE_AUTO)) { + tlv_auth_type = (MrvlIEtypes_auth_type_t *)tlv; + tlv_auth_type->header.type = + wlan_cpu_to_le16(TLV_TYPE_AUTH_TYPE); + tlv_auth_type->header.len = wlan_cpu_to_le16(sizeof(t_u8)); + tlv_auth_type->auth_type = + (t_u8)bss->param.bss_config.auth_mode; + cmd_size += sizeof(MrvlIEtypes_auth_type_t); + tlv += sizeof(MrvlIEtypes_auth_type_t); + } + + if (bss->param.bss_config.protocol) { + tlv_encrypt_protocol = (MrvlIEtypes_encrypt_protocol_t *)tlv; + tlv_encrypt_protocol->header.type = + wlan_cpu_to_le16(TLV_TYPE_UAP_ENCRYPT_PROTOCOL); + tlv_encrypt_protocol->header.len = + wlan_cpu_to_le16(sizeof(t_u16)); + tlv_encrypt_protocol->protocol = + wlan_cpu_to_le16(bss->param.bss_config.protocol); + cmd_size += sizeof(MrvlIEtypes_encrypt_protocol_t); + tlv += sizeof(MrvlIEtypes_encrypt_protocol_t); + } + + if ((bss->param.bss_config.protocol & PROTOCOL_WPA) || + (bss->param.bss_config.protocol & PROTOCOL_WPA2) || + (bss->param.bss_config.protocol & PROTOCOL_EAP)) { + tlv_akmp = (MrvlIEtypes_akmp_t *)tlv; + tlv_akmp->header.type = wlan_cpu_to_le16(TLV_TYPE_UAP_AKMP); + tlv_akmp->key_mgmt = + wlan_cpu_to_le16(bss->param.bss_config.key_mgmt); + tlv_akmp->header.len = sizeof(t_u16); + tlv_akmp->key_mgmt_operation = + wlan_cpu_to_le16(bss->param.bss_config. + key_mgmt_operation); + tlv_akmp->header.len += sizeof(t_u16); + tlv_akmp->header.len = wlan_cpu_to_le16(tlv_akmp->header.len); + cmd_size += sizeof(MrvlIEtypes_akmp_t); + tlv += sizeof(MrvlIEtypes_akmp_t); + + if (bss->param.bss_config.wpa_cfg. + pairwise_cipher_wpa & VALID_CIPHER_BITMAP) { + tlv_pwk_cipher = (MrvlIEtypes_pwk_cipher_t *)tlv; + tlv_pwk_cipher->header.type = + wlan_cpu_to_le16(TLV_TYPE_PWK_CIPHER); + tlv_pwk_cipher->header.len = + wlan_cpu_to_le16(sizeof(t_u16) + sizeof(t_u8) + + sizeof(t_u8)); + tlv_pwk_cipher->protocol = + wlan_cpu_to_le16(PROTOCOL_WPA); + tlv_pwk_cipher->pairwise_cipher = + bss->param.bss_config.wpa_cfg. + pairwise_cipher_wpa; + cmd_size += sizeof(MrvlIEtypes_pwk_cipher_t); + tlv += sizeof(MrvlIEtypes_pwk_cipher_t); + } + + if (bss->param.bss_config.wpa_cfg. + pairwise_cipher_wpa2 & VALID_CIPHER_BITMAP) { + tlv_pwk_cipher = (MrvlIEtypes_pwk_cipher_t *)tlv; + tlv_pwk_cipher->header.type = + wlan_cpu_to_le16(TLV_TYPE_PWK_CIPHER); + tlv_pwk_cipher->header.len = + wlan_cpu_to_le16(sizeof(t_u16) + sizeof(t_u8) + + sizeof(t_u8)); + tlv_pwk_cipher->protocol = + wlan_cpu_to_le16(PROTOCOL_WPA2); + tlv_pwk_cipher->pairwise_cipher = + bss->param.bss_config.wpa_cfg. + pairwise_cipher_wpa2; + cmd_size += sizeof(MrvlIEtypes_pwk_cipher_t); + tlv += sizeof(MrvlIEtypes_pwk_cipher_t); + } + + if (bss->param.bss_config.wpa_cfg. + group_cipher & VALID_CIPHER_BITMAP) { + tlv_gwk_cipher = (MrvlIEtypes_gwk_cipher_t *)tlv; + tlv_gwk_cipher->header.type = + wlan_cpu_to_le16(TLV_TYPE_GWK_CIPHER); + tlv_gwk_cipher->header.len = + wlan_cpu_to_le16(sizeof(t_u8) + sizeof(t_u8)); + tlv_gwk_cipher->group_cipher = + bss->param.bss_config.wpa_cfg.group_cipher; + cmd_size += sizeof(MrvlIEtypes_gwk_cipher_t); + tlv += sizeof(MrvlIEtypes_gwk_cipher_t); + } + + if (bss->param.bss_config.wpa_cfg.rsn_protection <= MTRUE) { + tlv_rsn_prot = (MrvlIEtypes_rsn_replay_prot_t *)tlv; + tlv_rsn_prot->header.type = + wlan_cpu_to_le16 + (TLV_TYPE_UAP_RSN_REPLAY_PROTECT); + tlv_rsn_prot->header.len = + wlan_cpu_to_le16(sizeof(t_u8)); + tlv_rsn_prot->rsn_replay_prot = + bss->param.bss_config.wpa_cfg.rsn_protection; + cmd_size += sizeof(MrvlIEtypes_rsn_replay_prot_t); + tlv += sizeof(MrvlIEtypes_rsn_replay_prot_t); + } +#ifdef DRV_EMBEDDED_AUTHENTICATOR + if (IS_FW_SUPPORT_AUTHENTICATOR(pmpriv->adapter)) { +#endif + if (bss->param.bss_config.wpa_cfg.length) { + tlv_passphrase = + (MrvlIEtypes_passphrase_t *)tlv; + tlv_passphrase->header.type = + wlan_cpu_to_le16 + (TLV_TYPE_UAP_WPA_PASSPHRASE); + tlv_passphrase->header.len = + (t_u16)wlan_cpu_to_le16(bss->param. + bss_config. + wpa_cfg.length); + memcpy(pmpriv->adapter, + tlv_passphrase->passphrase, + bss->param.bss_config.wpa_cfg.passphrase, + bss->param.bss_config.wpa_cfg.length); + cmd_size += + sizeof(MrvlIEtypesHeader_t) + + bss->param.bss_config.wpa_cfg.length; + tlv += sizeof(MrvlIEtypesHeader_t) + + bss->param.bss_config.wpa_cfg.length; + } + + if (bss->param.bss_config.wpa_cfg.gk_rekey_time < + MAX_GRP_TIMER) { + tlv_rekey_time = + (MrvlIEtypes_group_rekey_time_t *)tlv; + tlv_rekey_time->header.type = + wlan_cpu_to_le16 + (TLV_TYPE_UAP_GRP_REKEY_TIME); + tlv_rekey_time->header.len = + wlan_cpu_to_le16(sizeof(t_u32)); + tlv_rekey_time->gk_rekey_time = + wlan_cpu_to_le32(bss->param.bss_config. + wpa_cfg.gk_rekey_time); + cmd_size += + sizeof(MrvlIEtypes_group_rekey_time_t); + tlv += sizeof(MrvlIEtypes_group_rekey_time_t); + } +#ifdef DRV_EMBEDDED_AUTHENTICATOR + } +#endif + } else { + if ((bss->param.bss_config.wep_cfg.key0.length) && + ((bss->param.bss_config.wep_cfg.key0.length == 5) || + (bss->param.bss_config.wep_cfg.key0.length == 10) || + (bss->param.bss_config.wep_cfg.key0.length == 13) || + (bss->param.bss_config.wep_cfg.key0.length == 26))) { + tlv_wep_key = (MrvlIEtypes_wep_key_t *)tlv; + tlv_wep_key->header.type = + wlan_cpu_to_le16(TLV_TYPE_UAP_WEP_KEY); + tlv_wep_key->header.len = + wlan_cpu_to_le16(2 + + bss->param.bss_config.wep_cfg. + key0.length); + tlv_wep_key->key_index = + bss->param.bss_config.wep_cfg.key0.key_index; + tlv_wep_key->is_default = + bss->param.bss_config.wep_cfg.key0.is_default; + memcpy(pmpriv->adapter, tlv_wep_key->key, + bss->param.bss_config.wep_cfg.key0.key, + bss->param.bss_config.wep_cfg.key0.length); + cmd_size += + sizeof(MrvlIEtypesHeader_t) + 2 + + bss->param.bss_config.wep_cfg.key0.length; + tlv += sizeof(MrvlIEtypesHeader_t) + 2 + + bss->param.bss_config.wep_cfg.key0.length; + } + + if ((bss->param.bss_config.wep_cfg.key1.length) && + ((bss->param.bss_config.wep_cfg.key1.length == 5) || + (bss->param.bss_config.wep_cfg.key1.length == 10) || + (bss->param.bss_config.wep_cfg.key1.length == 13) || + (bss->param.bss_config.wep_cfg.key1.length == 26))) { + tlv_wep_key = (MrvlIEtypes_wep_key_t *)tlv; + tlv_wep_key->header.type = + wlan_cpu_to_le16(TLV_TYPE_UAP_WEP_KEY); + tlv_wep_key->header.len = + wlan_cpu_to_le16(2 + + bss->param.bss_config.wep_cfg. + key1.length); + tlv_wep_key->key_index = + bss->param.bss_config.wep_cfg.key1.key_index; + tlv_wep_key->is_default = + bss->param.bss_config.wep_cfg.key1.is_default; + memcpy(pmpriv->adapter, tlv_wep_key->key, + bss->param.bss_config.wep_cfg.key1.key, + bss->param.bss_config.wep_cfg.key1.length); + cmd_size += + sizeof(MrvlIEtypesHeader_t) + 2 + + bss->param.bss_config.wep_cfg.key1.length; + tlv += sizeof(MrvlIEtypesHeader_t) + 2 + + bss->param.bss_config.wep_cfg.key1.length; + } + + if ((bss->param.bss_config.wep_cfg.key2.length) && + ((bss->param.bss_config.wep_cfg.key2.length == 5) || + (bss->param.bss_config.wep_cfg.key2.length == 10) || + (bss->param.bss_config.wep_cfg.key2.length == 13) || + (bss->param.bss_config.wep_cfg.key2.length == 26))) { + tlv_wep_key = (MrvlIEtypes_wep_key_t *)tlv; + tlv_wep_key->header.type = + wlan_cpu_to_le16(TLV_TYPE_UAP_WEP_KEY); + tlv_wep_key->header.len = + wlan_cpu_to_le16(2 + + bss->param.bss_config.wep_cfg. + key2.length); + tlv_wep_key->key_index = + bss->param.bss_config.wep_cfg.key2.key_index; + tlv_wep_key->is_default = + bss->param.bss_config.wep_cfg.key2.is_default; + memcpy(pmpriv->adapter, tlv_wep_key->key, + bss->param.bss_config.wep_cfg.key2.key, + bss->param.bss_config.wep_cfg.key2.length); + cmd_size += + sizeof(MrvlIEtypesHeader_t) + 2 + + bss->param.bss_config.wep_cfg.key2.length; + tlv += sizeof(MrvlIEtypesHeader_t) + 2 + + bss->param.bss_config.wep_cfg.key2.length; + } + + if ((bss->param.bss_config.wep_cfg.key3.length) && + ((bss->param.bss_config.wep_cfg.key3.length == 5) || + (bss->param.bss_config.wep_cfg.key3.length == 10) || + (bss->param.bss_config.wep_cfg.key3.length == 13) || + (bss->param.bss_config.wep_cfg.key3.length == 26))) { + tlv_wep_key = (MrvlIEtypes_wep_key_t *)tlv; + tlv_wep_key->header.type = + wlan_cpu_to_le16(TLV_TYPE_UAP_WEP_KEY); + tlv_wep_key->header.len = + wlan_cpu_to_le16(2 + + bss->param.bss_config.wep_cfg. + key3.length); + tlv_wep_key->key_index = + bss->param.bss_config.wep_cfg.key3.key_index; + tlv_wep_key->is_default = + bss->param.bss_config.wep_cfg.key3.is_default; + memcpy(pmpriv->adapter, tlv_wep_key->key, + bss->param.bss_config.wep_cfg.key3.key, + bss->param.bss_config.wep_cfg.key3.length); + cmd_size += + sizeof(MrvlIEtypesHeader_t) + 2 + + bss->param.bss_config.wep_cfg.key3.length; + tlv += sizeof(MrvlIEtypesHeader_t) + 2 + + bss->param.bss_config.wep_cfg.key3.length; + } + } + if ((bss->param.bss_config.ht_cap_info) + ) { + tlv_htcap = (MrvlIETypes_HTCap_t *)tlv; + tlv_htcap->header.type = wlan_cpu_to_le16(HT_CAPABILITY); + tlv_htcap->header.len = wlan_cpu_to_le16(sizeof(HTCap_t)); + tlv_htcap->ht_cap.ht_cap_info = + wlan_cpu_to_le16(bss->param.bss_config.ht_cap_info); + tlv_htcap->ht_cap.ampdu_param = + bss->param.bss_config.ampdu_param; + memcpy(pmpriv->adapter, tlv_htcap->ht_cap.supported_mcs_set, + bss->param.bss_config.supported_mcs_set, 16); + tlv_htcap->ht_cap.ht_ext_cap = + wlan_cpu_to_le16(bss->param.bss_config.ht_ext_cap); + tlv_htcap->ht_cap.tx_bf_cap = + wlan_cpu_to_le32(bss->param.bss_config.tx_bf_cap); + tlv_htcap->ht_cap.asel = bss->param.bss_config.asel; + cmd_size += sizeof(MrvlIETypes_HTCap_t); + tlv += sizeof(MrvlIETypes_HTCap_t); + } + if (bss->param.bss_config.mgmt_ie_passthru_mask < (MAX_VALID_DWORD)) { + tlv_mgmt_ie_passthru = (MrvlIEtypes_mgmt_ie_passthru_t *)tlv; + tlv_mgmt_ie_passthru->header.type = + wlan_cpu_to_le16(TLV_TYPE_UAP_MGMT_IE_PASSTHRU_MASK); + tlv_mgmt_ie_passthru->header.len = + wlan_cpu_to_le16(sizeof(t_u32)); + /* keep copy in private data */ + pmpriv->mgmt_frame_passthru_mask = + bss->param.bss_config.mgmt_ie_passthru_mask; + tlv_mgmt_ie_passthru->mgmt_ie_mask = + wlan_cpu_to_le32(bss->param.bss_config. + mgmt_ie_passthru_mask); + cmd_size += sizeof(MrvlIEtypes_mgmt_ie_passthru_t); + tlv += sizeof(MrvlIEtypes_mgmt_ie_passthru_t); + } + if (((bss->param.bss_config.enable_2040coex == 0) || + (bss->param.bss_config.enable_2040coex == 1)) + ) { + tlv_2040_coex_enable = (MrvlIEtypes_2040_coex_enable_t *)tlv; + tlv_2040_coex_enable->header.type = + wlan_cpu_to_le16(TLV_TYPE_2040_BSS_COEX_CONTROL); + tlv_2040_coex_enable->header.len = + wlan_cpu_to_le16(sizeof(t_u8)); + tlv_2040_coex_enable->enable_2040coex = + bss->param.bss_config.enable_2040coex; + cmd_size += sizeof(MrvlIEtypes_2040_coex_enable_t); + tlv += sizeof(MrvlIEtypes_2040_coex_enable_t); + } + if (bss->param.bss_config.wmm_para.qos_info == 0x80 || + bss->param.bss_config.wmm_para.qos_info == 0x00) { + tlv_wmm_parameter = (MrvlIEtypes_wmm_parameter_t *)tlv; + tlv_wmm_parameter->header.type = + wlan_cpu_to_le16(TLV_TYPE_VENDOR_SPECIFIC_IE); + tlv_wmm_parameter->header.len = + wlan_cpu_to_le16(sizeof + (bss->param.bss_config.wmm_para)); + memcpy(pmpriv->adapter, tlv_wmm_parameter->wmm_para.ouitype, + bss->param.bss_config.wmm_para.ouitype, + sizeof(tlv_wmm_parameter->wmm_para.ouitype)); + tlv_wmm_parameter->wmm_para.ouisubtype = + bss->param.bss_config.wmm_para.ouisubtype; + tlv_wmm_parameter->wmm_para.version = + bss->param.bss_config.wmm_para.version; + tlv_wmm_parameter->wmm_para.qos_info = + bss->param.bss_config.wmm_para.qos_info; + for (ac = 0; ac < 4; ac++) { + tlv_wmm_parameter->wmm_para.ac_params[ac].aci_aifsn. + aifsn = + bss->param.bss_config.wmm_para.ac_params[ac]. + aci_aifsn.aifsn; + tlv_wmm_parameter->wmm_para.ac_params[ac].aci_aifsn. + aci = + bss->param.bss_config.wmm_para.ac_params[ac]. + aci_aifsn.aci; + tlv_wmm_parameter->wmm_para.ac_params[ac].ecw.ecw_max = + bss->param.bss_config.wmm_para.ac_params[ac]. + ecw.ecw_max; + tlv_wmm_parameter->wmm_para.ac_params[ac].ecw.ecw_min = + bss->param.bss_config.wmm_para.ac_params[ac]. + ecw.ecw_min; + tlv_wmm_parameter->wmm_para.ac_params[ac].tx_op_limit = + wlan_cpu_to_le16(bss->param.bss_config.wmm_para. + ac_params[ac].tx_op_limit); + } + cmd_size += sizeof(MrvlIEtypes_wmm_parameter_t); + tlv += sizeof(MrvlIEtypes_wmm_parameter_t); + } +#ifdef DRV_EMBEDDED_AUTHENTICATOR + if (!IS_FW_SUPPORT_AUTHENTICATOR(pmpriv->adapter)) + AuthenticatorBssConfig(pmpriv->psapriv, + (t_u8 *)&bss->param.bss_config, 0, 0, 0); +#endif + cmd->size = (t_u16)wlan_cpu_to_le16(cmd_size); + PRINTM(MCMND, "AP config: cmd_size=%d\n", cmd_size); + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of sys_config + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action the action: GET or SET + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status +wlan_uap_cmd_sys_configure(pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, + IN pmlan_ioctl_req pioctl_buf, IN t_void *pdata_buf) +{ + mlan_ds_bss *bss = MNULL; + HostCmd_DS_SYS_CONFIG *sys_config = + (HostCmd_DS_SYS_CONFIG *)&cmd->params.sys_config; + MrvlIEtypes_MacAddr_t *mac_tlv = MNULL; + MrvlIEtypes_channel_band_t *pdat_tlv_cb = MNULL; + MrvlIEtypes_beacon_period_t *bcn_pd_tlv = MNULL, *pdat_tlv_bcnpd = + MNULL; + MrvlIEtypes_dtim_period_t *dtim_pd_tlv = MNULL, *pdat_tlv_dtimpd = + MNULL; + MrvlIEtypes_wmm_parameter_t *tlv_wmm_parameter = MNULL; + MrvlIEtypes_ChanListParamSet_t *tlv_chan_list = MNULL; + ChanScanParamSet_t *pscan_chan = MNULL; + MrvlIEtypes_channel_band_t *chan_band_tlv = MNULL; + MrvlIEtypes_chan_bw_oper_t *poper_class_tlv = MNULL; + t_u8 length = 0; + t_u8 curr_oper_class = 1; + t_u8 *oper_class_ie = (t_u8 *)sys_config->tlv_buffer; + t_u16 i = 0; + t_u8 ac = 0; + mlan_ds_misc_custom_ie *cust_ie = MNULL; + mlan_ds_misc_cfg *misc = MNULL; + MrvlIEtypesHeader_t *ie_header = + (MrvlIEtypesHeader_t *)sys_config->tlv_buffer; + MrvlIEtypesHeader_t *pdata_header = (MrvlIEtypesHeader_t *)pdata_buf; + t_u8 *ie = (t_u8 *)sys_config->tlv_buffer + sizeof(MrvlIEtypesHeader_t); + t_u16 req_len = 0, travel_len = 0; + custom_ie *cptr = MNULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + cmd->command = wlan_cpu_to_le16(HOST_CMD_APCMD_SYS_CONFIGURE); + sys_config->action = wlan_cpu_to_le16(cmd_action); + cmd->size = + wlan_cpu_to_le16(sizeof(HostCmd_DS_SYS_CONFIG) - 1 + S_DS_GEN); + if (pioctl_buf == MNULL) { + + if (pdata_buf) { + switch (pdata_header->type) { + case TLV_TYPE_UAP_CHAN_BAND_CONFIG: + pdat_tlv_cb = + (MrvlIEtypes_channel_band_t *)pdata_buf; + chan_band_tlv = + (MrvlIEtypes_channel_band_t *) + sys_config->tlv_buffer; + cmd->size = + wlan_cpu_to_le16(sizeof + (HostCmd_DS_SYS_CONFIG) + - 1 + S_DS_GEN + + sizeof + (MrvlIEtypes_channel_band_t)); + chan_band_tlv->header.type = + wlan_cpu_to_le16 + (TLV_TYPE_UAP_CHAN_BAND_CONFIG); + chan_band_tlv->header.len = + wlan_cpu_to_le16(sizeof + (MrvlIEtypes_channel_band_t) + - + sizeof + (MrvlIEtypesHeader_t)); + if (cmd_action) { + chan_band_tlv->bandcfg = + pdat_tlv_cb->bandcfg; + chan_band_tlv->channel = + pdat_tlv_cb->channel; + } + ret = MLAN_STATUS_SUCCESS; + break; + case TLV_TYPE_UAP_BEACON_PERIOD: + pdat_tlv_bcnpd = + (MrvlIEtypes_beacon_period_t *) + pdata_buf; + bcn_pd_tlv = + (MrvlIEtypes_beacon_period_t *) + sys_config->tlv_buffer; + cmd->size = + sizeof(HostCmd_DS_SYS_CONFIG) - 1 + + S_DS_GEN + + sizeof(MrvlIEtypes_beacon_period_t); + bcn_pd_tlv->header.type = + wlan_cpu_to_le16 + (TLV_TYPE_UAP_BEACON_PERIOD); + bcn_pd_tlv->header.len = + wlan_cpu_to_le16(sizeof + (MrvlIEtypes_beacon_period_t) + - + sizeof + (MrvlIEtypesHeader_t)); + if (cmd_action) { + bcn_pd_tlv->beacon_period = + wlan_cpu_to_le16 + (pdat_tlv_bcnpd->beacon_period); + } + /* Add TLV_UAP_DTIM_PERIOD if it follws in pdata_buf */ + pdat_tlv_dtimpd = + (MrvlIEtypes_dtim_period_t + *)(((t_u8 *)pdata_buf) + + + sizeof + (MrvlIEtypes_beacon_period_t)); + if (TLV_TYPE_UAP_DTIM_PERIOD == + pdat_tlv_dtimpd->header.type) { + dtim_pd_tlv = + (MrvlIEtypes_dtim_period_t + *)(sys_config->tlv_buffer + + sizeof + (MrvlIEtypes_beacon_period_t)); + cmd->size += + sizeof + (MrvlIEtypes_dtim_period_t); + dtim_pd_tlv->header.type = + wlan_cpu_to_le16 + (TLV_TYPE_UAP_DTIM_PERIOD); + dtim_pd_tlv->header.len = + wlan_cpu_to_le16(sizeof + (MrvlIEtypes_dtim_period_t) + - + sizeof + (MrvlIEtypesHeader_t)); + if (cmd_action) { + dtim_pd_tlv->dtim_period = + pdat_tlv_dtimpd-> + dtim_period; + } + } + /* Finalize cmd size */ + cmd->size = wlan_cpu_to_le16(cmd->size); + ret = MLAN_STATUS_SUCCESS; + break; + case TLV_TYPE_MGMT_IE: + cust_ie = (mlan_ds_misc_custom_ie *)pdata_buf; + cmd->size = + wlan_cpu_to_le16(sizeof + (HostCmd_DS_SYS_CONFIG) + - 1 + S_DS_GEN + + sizeof + (MrvlIEtypesHeader_t) + + cust_ie->len); + ie_header->type = + wlan_cpu_to_le16(TLV_TYPE_MGMT_IE); + ie_header->len = wlan_cpu_to_le16(cust_ie->len); + + if (ie && cust_ie->ie_data_list) { + req_len = cust_ie->len; + travel_len = 0; + /* conversion for index, mask, len */ + if (req_len == sizeof(t_u16)) + cust_ie->ie_data_list[0]. + ie_index = + wlan_cpu_to_le16 + (cust_ie-> + ie_data_list[0]. + ie_index); + while (req_len > sizeof(t_u16)) { + cptr = (custom_ie + *)(((t_u8 *)&cust_ie-> + ie_data_list) + + travel_len); + travel_len += + cptr->ie_length + + sizeof(custom_ie) - + MAX_IE_SIZE; + req_len -= + cptr->ie_length + + sizeof(custom_ie) - + MAX_IE_SIZE; + cptr->ie_index = + wlan_cpu_to_le16(cptr-> + ie_index); + cptr->mgmt_subtype_mask = + wlan_cpu_to_le16(cptr-> + mgmt_subtype_mask); + cptr->ie_length = + wlan_cpu_to_le16(cptr-> + ie_length); + } + memcpy(pmpriv->adapter, ie, + cust_ie->ie_data_list, + cust_ie->len); + } + break; + case REGULATORY_CLASS: + poper_class_tlv = + (MrvlIEtypes_chan_bw_oper_t *) + pdata_buf; + ret = wlan_get_curr_oper_class(pmpriv, + poper_class_tlv-> + ds_chan_bw_oper. + channel, + poper_class_tlv-> + ds_chan_bw_oper. + bandwidth, + &curr_oper_class); + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, + "Can not get current oper class! bandwidth = %d, channel = %d\n", + poper_class_tlv->ds_chan_bw_oper. + bandwidth, + poper_class_tlv->ds_chan_bw_oper. + channel); + } + + if (cmd_action == HostCmd_ACT_GEN_SET) + length = wlan_add_supported_oper_class_ie(pmpriv, &oper_class_ie, curr_oper_class); + cmd->size = + wlan_cpu_to_le16(sizeof + (HostCmd_DS_SYS_CONFIG) + - 1 + S_DS_GEN + + length); + break; + + default: + PRINTM(MERROR, + "Wrong data, or missing TLV_TYPE 0x%04x handler.\n", + *(t_u16 *)pdata_buf); + break; + } + goto done; + } else { + mac_tlv = + (MrvlIEtypes_MacAddr_t *)sys_config->tlv_buffer; + cmd->size = + wlan_cpu_to_le16(sizeof(HostCmd_DS_SYS_CONFIG) - + 1 + S_DS_GEN + + sizeof(MrvlIEtypes_MacAddr_t)); + mac_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_UAP_MAC_ADDRESS); + mac_tlv->header.len = + wlan_cpu_to_le16(MLAN_MAC_ADDR_LENGTH); + ret = MLAN_STATUS_SUCCESS; + goto done; + } + } + if (pioctl_buf->req_id == MLAN_IOCTL_BSS) { + bss = (mlan_ds_bss *)pioctl_buf->pbuf; + if (bss->sub_command == MLAN_OID_BSS_MAC_ADDR) { + mac_tlv = + (MrvlIEtypes_MacAddr_t *)sys_config->tlv_buffer; + cmd->size = + wlan_cpu_to_le16(sizeof(HostCmd_DS_SYS_CONFIG) - + 1 + S_DS_GEN + + sizeof(MrvlIEtypes_MacAddr_t)); + mac_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_UAP_MAC_ADDRESS); + mac_tlv->header.len = + wlan_cpu_to_le16(MLAN_MAC_ADDR_LENGTH); + if (cmd_action == HostCmd_ACT_GEN_SET) + memcpy(pmpriv->adapter, mac_tlv->mac, + &bss->param.mac_addr, + MLAN_MAC_ADDR_LENGTH); + } else if (bss->sub_command == MLAN_OID_UAP_CFG_WMM_PARAM) { + tlv_wmm_parameter = + (MrvlIEtypes_wmm_parameter_t *)sys_config-> + tlv_buffer; + cmd->size = + wlan_cpu_to_le16(sizeof(HostCmd_DS_SYS_CONFIG) - + 1 + S_DS_GEN + + sizeof + (MrvlIEtypes_wmm_parameter_t)); + tlv_wmm_parameter->header.type = + wlan_cpu_to_le16(TLV_TYPE_AP_WMM_PARAM); + tlv_wmm_parameter->header.len = + wlan_cpu_to_le16(sizeof + (bss->param.ap_wmm_para)); + if (cmd_action == HostCmd_ACT_GEN_SET) { + for (ac = 0; ac < 4; ac++) { + tlv_wmm_parameter->wmm_para. + ac_params[ac].aci_aifsn.aifsn = + bss->param.ap_wmm_para. + ac_params[ac].aci_aifsn.aifsn; + tlv_wmm_parameter->wmm_para. + ac_params[ac].aci_aifsn.aci = + bss->param.ap_wmm_para. + ac_params[ac].aci_aifsn.aci; + tlv_wmm_parameter->wmm_para. + ac_params[ac].ecw.ecw_max = + bss->param.ap_wmm_para. + ac_params[ac].ecw.ecw_max; + tlv_wmm_parameter->wmm_para. + ac_params[ac].ecw.ecw_min = + bss->param.ap_wmm_para. + ac_params[ac].ecw.ecw_min; + tlv_wmm_parameter->wmm_para. + ac_params[ac].tx_op_limit = + wlan_cpu_to_le16(bss->param. + ap_wmm_para. + ac_params[ac]. + tx_op_limit); + } + } + } else if (bss->sub_command == MLAN_OID_UAP_SCAN_CHANNELS) { + tlv_chan_list = + (MrvlIEtypes_ChanListParamSet_t *)sys_config-> + tlv_buffer; + tlv_chan_list->header.type = + wlan_cpu_to_le16(TLV_TYPE_CHANLIST); + if (bss->param.ap_scan_channels.num_of_chan && + bss->param.ap_scan_channels.num_of_chan <= + MLAN_MAX_CHANNEL) { + cmd->size = + wlan_cpu_to_le16(sizeof + (HostCmd_DS_SYS_CONFIG) + - 1 + S_DS_GEN + + sizeof(tlv_chan_list-> + header) + + sizeof + (ChanScanParamSet_t) * + bss->param. + ap_scan_channels. + num_of_chan); + tlv_chan_list->header.len = + wlan_cpu_to_le16((t_u16) + (sizeof + (ChanScanParamSet_t) * + bss->param. + ap_scan_channels. + num_of_chan)); + pscan_chan = tlv_chan_list->chan_scan_param; + for (i = 0; + i < + bss->param.ap_scan_channels.num_of_chan; + i++) { + pscan_chan->chan_number = + bss->param.ap_scan_channels. + chan_list[i].chan_number; + pscan_chan->bandcfg = + bss->param.ap_scan_channels. + chan_list[i].bandcfg; + pscan_chan++; + } + PRINTM(MCMND, + "Set AP scan channel list = %d\n", + bss->param.ap_scan_channels.num_of_chan); + } else { + tlv_chan_list->header.len = + wlan_cpu_to_le16((t_u16) + (sizeof + (ChanScanParamSet_t) * + MLAN_MAX_CHANNEL)); + cmd->size = + wlan_cpu_to_le16(sizeof + (HostCmd_DS_SYS_CONFIG) + - 1 + S_DS_GEN + + sizeof + (MrvlIEtypes_ChanListParamSet_t) + + + sizeof + (ChanScanParamSet_t) * + MLAN_MAX_CHANNEL); + } + } else if (bss->sub_command == MLAN_OID_UAP_CHANNEL) { + chan_band_tlv = + (MrvlIEtypes_channel_band_t *)sys_config-> + tlv_buffer; + cmd->size = + wlan_cpu_to_le16(sizeof(HostCmd_DS_SYS_CONFIG) - + 1 + S_DS_GEN + + sizeof + (MrvlIEtypes_channel_band_t)); + chan_band_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_UAP_CHAN_BAND_CONFIG); + chan_band_tlv->header.len = + wlan_cpu_to_le16(sizeof + (MrvlIEtypes_channel_band_t) + - sizeof(MrvlIEtypesHeader_t)); + if (cmd_action == HostCmd_ACT_GEN_SET) { + chan_band_tlv->bandcfg = + bss->param.ap_channel.bandcfg; + chan_band_tlv->channel = + bss->param.ap_channel.channel; + PRINTM(MCMND, + "Set AP channel, band=%d, channel=%d\n", + bss->param.ap_channel.bandcfg.chanBand, + bss->param.ap_channel.channel); + } + } else if ((bss->sub_command == MLAN_OID_UAP_BSS_CONFIG) && + (cmd_action == HostCmd_ACT_GEN_SET)) { + ret = wlan_uap_cmd_ap_config(pmpriv, cmd, cmd_action, + pioctl_buf); + goto done; + } + } else if (pioctl_buf->req_id == MLAN_IOCTL_MISC_CFG) { + misc = (mlan_ds_misc_cfg *)pioctl_buf->pbuf; + if ((misc->sub_command == MLAN_OID_MISC_GEN_IE) && + (misc->param.gen_ie.type == MLAN_IE_TYPE_GEN_IE) + ) { + cmd->size = + wlan_cpu_to_le16(sizeof(HostCmd_DS_SYS_CONFIG) - + 1 + S_DS_GEN + + sizeof(MrvlIEtypesHeader_t) + + misc->param.gen_ie.len); + ie_header->type = wlan_cpu_to_le16(TLV_TYPE_WAPI_IE); + ie_header->len = + wlan_cpu_to_le16(misc->param.gen_ie.len); + if (cmd_action == HostCmd_ACT_GEN_SET) + memcpy(pmpriv->adapter, ie, + misc->param.gen_ie.ie_data, + misc->param.gen_ie.len); + } + if ((misc->sub_command == MLAN_OID_MISC_CUSTOM_IE) && + (misc->param.cust_ie.type == TLV_TYPE_MGMT_IE)) { + cmd->size = + wlan_cpu_to_le16(sizeof(HostCmd_DS_SYS_CONFIG) - + 1 + S_DS_GEN + + sizeof(MrvlIEtypesHeader_t) + + misc->param.cust_ie.len); + ie_header->type = wlan_cpu_to_le16(TLV_TYPE_MGMT_IE); + ie_header->len = + wlan_cpu_to_le16(misc->param.cust_ie.len); + + if (ie && misc->param.cust_ie.ie_data_list) { + req_len = misc->param.cust_ie.len; + travel_len = 0; + /* conversion for index, mask, len */ + if (req_len == sizeof(t_u16)) + misc->param.cust_ie.ie_data_list[0]. + ie_index = + wlan_cpu_to_le16(misc->param. + cust_ie. + ie_data_list + [0].ie_index); + while (req_len > sizeof(t_u16)) { + cptr = (custom_ie + *)(((t_u8 *)&misc->param. + cust_ie.ie_data_list) + + travel_len); + travel_len += + cptr->ie_length + + sizeof(custom_ie) - MAX_IE_SIZE; + req_len -= + cptr->ie_length + + sizeof(custom_ie) - MAX_IE_SIZE; + cptr->ie_index = + wlan_cpu_to_le16(cptr-> + ie_index); + cptr->mgmt_subtype_mask = + wlan_cpu_to_le16(cptr-> + mgmt_subtype_mask); + cptr->ie_length = + wlan_cpu_to_le16(cptr-> + ie_length); + } + if (misc->param.cust_ie.len) + memcpy(pmpriv->adapter, ie, + misc->param.cust_ie.ie_data_list, + misc->param.cust_ie.len); + } + } + } +done: + LEAVE(); + return ret; +} + +/** + * @brief This function handles command resp for get uap settings + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_uap_ret_cmd_ap_config(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf) +{ + HostCmd_DS_SYS_CONFIG *sys_config = + (HostCmd_DS_SYS_CONFIG *)&resp->params.sys_config; + mlan_ds_bss *bss = MNULL; + MrvlIEtypesHeader_t *tlv = MNULL; + t_u16 tlv_buf_left = 0; + t_u16 tlv_type = 0; + t_u16 tlv_len = 0; + MrvlIEtypes_MacAddr_t *tlv_mac = MNULL; + MrvlIEtypes_SsIdParamSet_t *tlv_ssid = MNULL; + MrvlIEtypes_beacon_period_t *tlv_beacon_period = MNULL; + MrvlIEtypes_dtim_period_t *tlv_dtim_period = MNULL; + MrvlIEtypes_RatesParamSet_t *tlv_rates = MNULL; + MrvlIEtypes_tx_rate_t *tlv_txrate = MNULL; + MrvlIEtypes_mcbc_rate_t *tlv_mcbc_rate = MNULL; + MrvlIEtypes_tx_power_t *tlv_tx_power = MNULL; + MrvlIEtypes_bcast_ssid_t *tlv_bcast_ssid = MNULL; + MrvlIEtypes_antenna_mode_t *tlv_antenna = MNULL; + MrvlIEtypes_pkt_forward_t *tlv_pkt_forward = MNULL; + MrvlIEtypes_max_sta_count_t *tlv_sta_count = MNULL; + MrvlIEtypes_sta_ageout_t *tlv_sta_ageout = MNULL; + MrvlIEtypes_ps_sta_ageout_t *tlv_ps_sta_ageout = MNULL; + MrvlIEtypes_rts_threshold_t *tlv_rts_threshold = MNULL; + MrvlIEtypes_frag_threshold_t *tlv_frag_threshold = MNULL; + MrvlIEtypes_retry_limit_t *tlv_retry_limit = MNULL; + MrvlIEtypes_eapol_pwk_hsk_timeout_t *tlv_pairwise_timeout = MNULL; + MrvlIEtypes_eapol_pwk_hsk_retries_t *tlv_pairwise_retries = MNULL; + MrvlIEtypes_eapol_gwk_hsk_timeout_t *tlv_groupwise_timeout = MNULL; + MrvlIEtypes_eapol_gwk_hsk_retries_t *tlv_groupwise_retries = MNULL; + MrvlIEtypes_mgmt_ie_passthru_t *tlv_mgmt_ie_passthru = MNULL; + MrvlIEtypes_2040_coex_enable_t *tlv_2040_coex_enable = MNULL; + MrvlIEtypes_mac_filter_t *tlv_mac_filter = MNULL; + MrvlIEtypes_channel_band_t *tlv_chan_band = MNULL; + MrvlIEtypes_ChanListParamSet_t *tlv_chan_list = MNULL; + ChanScanParamSet_t *pscan_chan = MNULL; + MrvlIEtypes_auth_type_t *tlv_auth_type = MNULL; + MrvlIEtypes_encrypt_protocol_t *tlv_encrypt_protocol = MNULL; + MrvlIEtypes_akmp_t *tlv_akmp = MNULL; + MrvlIEtypes_pwk_cipher_t *tlv_pwk_cipher = MNULL; + MrvlIEtypes_gwk_cipher_t *tlv_gwk_cipher = MNULL; + MrvlIEtypes_rsn_replay_prot_t *tlv_rsn_prot = MNULL; + MrvlIEtypes_passphrase_t *tlv_passphrase = MNULL; +#ifdef WIFI_DIRECT_SUPPORT + MrvlIEtypes_psk_t *tlv_psk = MNULL; +#endif /* WIFI_DIRECT_SUPPORT */ + MrvlIEtypes_group_rekey_time_t *tlv_rekey_time = MNULL; + MrvlIEtypes_wep_key_t *tlv_wep_key = MNULL; + MrvlIEtypes_preamble_t *tlv_preamble = MNULL; + MrvlIEtypes_bss_status_t *tlv_bss_status = MNULL; + MrvlIETypes_HTCap_t *tlv_htcap = MNULL; + MrvlIEtypes_wmm_parameter_t *tlv_wmm_parameter = MNULL; + + wep_key *pkey = MNULL; + t_u16 i; + t_u16 ac; + + ENTER(); + + bss = (mlan_ds_bss *)pioctl_buf->pbuf; + tlv = (MrvlIEtypesHeader_t *)sys_config->tlv_buffer; + tlv_buf_left = + resp->size - (sizeof(HostCmd_DS_SYS_CONFIG) - 1 + S_DS_GEN); + + while (tlv_buf_left >= sizeof(MrvlIEtypesHeader_t)) { + + tlv_type = wlan_le16_to_cpu(tlv->type); + tlv_len = wlan_le16_to_cpu(tlv->len); + + if (tlv_buf_left < (tlv_len + sizeof(MrvlIEtypesHeader_t))) { + PRINTM(MERROR, + "Error processing uAP sys config TLVs, bytes left < TLV length\n"); + break; + } + + switch (tlv_type) { + case TLV_TYPE_UAP_MAC_ADDRESS: + tlv_mac = (MrvlIEtypes_MacAddr_t *)tlv; + memcpy(pmpriv->adapter, &bss->param.bss_config.mac_addr, + tlv_mac->mac, MLAN_MAC_ADDR_LENGTH); + break; + case TLV_TYPE_SSID: + tlv_ssid = (MrvlIEtypes_SsIdParamSet_t *)tlv; + bss->param.bss_config.ssid.ssid_len = + MIN(MLAN_MAX_SSID_LENGTH, tlv_len); + memcpy(pmpriv->adapter, bss->param.bss_config.ssid.ssid, + tlv_ssid->ssid, MIN(MLAN_MAX_SSID_LENGTH, + tlv_len)); + break; + case TLV_TYPE_UAP_BEACON_PERIOD: + tlv_beacon_period = (MrvlIEtypes_beacon_period_t *)tlv; + bss->param.bss_config.beacon_period = + wlan_le16_to_cpu(tlv_beacon_period-> + beacon_period); + pmpriv->uap_state_chan_cb.beacon_period = + wlan_le16_to_cpu(tlv_beacon_period-> + beacon_period); + break; + case TLV_TYPE_UAP_DTIM_PERIOD: + tlv_dtim_period = (MrvlIEtypes_dtim_period_t *)tlv; + bss->param.bss_config.dtim_period = + tlv_dtim_period->dtim_period; + pmpriv->uap_state_chan_cb.dtim_period = + tlv_dtim_period->dtim_period; + break; + case TLV_TYPE_RATES: + tlv_rates = (MrvlIEtypes_RatesParamSet_t *)tlv; + memcpy(pmpriv->adapter, bss->param.bss_config.rates, + tlv_rates->rates, MIN(MAX_DATA_RATES, tlv_len)); + break; + case TLV_TYPE_UAP_TX_DATA_RATE: + tlv_txrate = (MrvlIEtypes_tx_rate_t *)tlv; + bss->param.bss_config.tx_data_rate = + wlan_le16_to_cpu(tlv_txrate->tx_data_rate); + break; + case TLV_TYPE_UAP_TX_BEACON_RATE: + tlv_txrate = (MrvlIEtypes_tx_rate_t *)tlv; + bss->param.bss_config.tx_beacon_rate = + wlan_le16_to_cpu(tlv_txrate->tx_data_rate); + break; + case TLV_TYPE_UAP_MCBC_DATA_RATE: + tlv_mcbc_rate = (MrvlIEtypes_mcbc_rate_t *)tlv; + bss->param.bss_config.mcbc_data_rate = + wlan_le16_to_cpu(tlv_mcbc_rate->mcbc_data_rate); + break; + case TLV_TYPE_UAP_TX_POWER: + tlv_tx_power = (MrvlIEtypes_tx_power_t *)tlv; + bss->param.bss_config.tx_power_level = + tlv_tx_power->tx_power; + break; + case TLV_TYPE_UAP_BCAST_SSID_CTL: + tlv_bcast_ssid = (MrvlIEtypes_bcast_ssid_t *)tlv; + bss->param.bss_config.bcast_ssid_ctl = + tlv_bcast_ssid->bcast_ssid_ctl; + break; + case TLV_TYPE_UAP_ANTENNA_CTL: + tlv_antenna = (MrvlIEtypes_antenna_mode_t *)tlv; + if (tlv_antenna->which_antenna == TX_ANTENNA) + bss->param.bss_config.tx_antenna = + tlv_antenna->antenna_mode; + else if (tlv_antenna->which_antenna == RX_ANTENNA) + bss->param.bss_config.rx_antenna = + tlv_antenna->antenna_mode; + break; + case TLV_TYPE_UAP_PKT_FWD_CTL: + tlv_pkt_forward = (MrvlIEtypes_pkt_forward_t *)tlv; + bss->param.bss_config.pkt_forward_ctl = + tlv_pkt_forward->pkt_forward_ctl; + break; + case TLV_TYPE_UAP_MAX_STA_CNT: + tlv_sta_count = (MrvlIEtypes_max_sta_count_t *)tlv; + bss->param.bss_config.max_sta_count = + wlan_le16_to_cpu(tlv_sta_count->max_sta_count); + break; + case TLV_TYPE_UAP_STA_AGEOUT_TIMER: + tlv_sta_ageout = (MrvlIEtypes_sta_ageout_t *)tlv; + bss->param.bss_config.sta_ageout_timer = + wlan_le32_to_cpu(tlv_sta_ageout-> + sta_ageout_timer); + break; + case TLV_TYPE_UAP_PS_STA_AGEOUT_TIMER: + tlv_ps_sta_ageout = (MrvlIEtypes_ps_sta_ageout_t *)tlv; + bss->param.bss_config.ps_sta_ageout_timer = + wlan_le32_to_cpu(tlv_ps_sta_ageout-> + ps_sta_ageout_timer); + break; + case TLV_TYPE_UAP_RTS_THRESHOLD: + tlv_rts_threshold = (MrvlIEtypes_rts_threshold_t *)tlv; + bss->param.bss_config.rts_threshold = + wlan_le16_to_cpu(tlv_rts_threshold-> + rts_threshold); + break; + case TLV_TYPE_UAP_FRAG_THRESHOLD: + tlv_frag_threshold = + (MrvlIEtypes_frag_threshold_t *)tlv; + bss->param.bss_config.frag_threshold = + wlan_le16_to_cpu(tlv_frag_threshold-> + frag_threshold); + break; + case TLV_TYPE_UAP_RETRY_LIMIT: + tlv_retry_limit = (MrvlIEtypes_retry_limit_t *)tlv; + bss->param.bss_config.retry_limit = + tlv_retry_limit->retry_limit; + break; + case TLV_TYPE_UAP_EAPOL_PWK_HSK_TIMEOUT: + tlv_pairwise_timeout = + (MrvlIEtypes_eapol_pwk_hsk_timeout_t *)tlv; + bss->param.bss_config.pairwise_update_timeout = + wlan_le32_to_cpu(tlv_pairwise_timeout-> + pairwise_update_timeout); + break; + case TLV_TYPE_UAP_EAPOL_PWK_HSK_RETRIES: + tlv_pairwise_retries = + (MrvlIEtypes_eapol_pwk_hsk_retries_t *)tlv; + bss->param.bss_config.pwk_retries = + wlan_le32_to_cpu(tlv_pairwise_retries-> + pwk_retries); + break; + case TLV_TYPE_UAP_EAPOL_GWK_HSK_TIMEOUT: + tlv_groupwise_timeout = + (MrvlIEtypes_eapol_gwk_hsk_timeout_t *)tlv; + bss->param.bss_config.groupwise_update_timeout = + wlan_le32_to_cpu(tlv_groupwise_timeout-> + groupwise_update_timeout); + break; + case TLV_TYPE_UAP_EAPOL_GWK_HSK_RETRIES: + tlv_groupwise_retries = + (MrvlIEtypes_eapol_gwk_hsk_retries_t *)tlv; + bss->param.bss_config.gwk_retries = + wlan_le32_to_cpu(tlv_groupwise_retries-> + gwk_retries); + break; + case TLV_TYPE_UAP_MGMT_IE_PASSTHRU_MASK: + tlv_mgmt_ie_passthru = + (MrvlIEtypes_mgmt_ie_passthru_t *)tlv; + bss->param.bss_config.mgmt_ie_passthru_mask = + wlan_le32_to_cpu(tlv_mgmt_ie_passthru-> + mgmt_ie_mask); + break; + case TLV_TYPE_2040_BSS_COEX_CONTROL: + tlv_2040_coex_enable = + (MrvlIEtypes_2040_coex_enable_t *)tlv; + bss->param.bss_config.enable_2040coex = + tlv_2040_coex_enable->enable_2040coex; + break; + case TLV_TYPE_UAP_STA_MAC_ADDR_FILTER: + tlv_mac_filter = (MrvlIEtypes_mac_filter_t *)tlv; + bss->param.bss_config.filter.mac_count = + MIN(MAX_MAC_FILTER_NUM, tlv_mac_filter->count); + bss->param.bss_config.filter.filter_mode = + tlv_mac_filter->filter_mode; + memcpy(pmpriv->adapter, + (t_u8 *)bss->param.bss_config.filter.mac_list, + tlv_mac_filter->mac_address, + MLAN_MAC_ADDR_LENGTH * + bss->param.bss_config.filter.mac_count); + break; + case TLV_TYPE_UAP_CHAN_BAND_CONFIG: + tlv_chan_band = (MrvlIEtypes_channel_band_t *)tlv; + bss->param.bss_config.bandcfg = tlv_chan_band->bandcfg; + bss->param.bss_config.channel = tlv_chan_band->channel; + pmpriv->uap_state_chan_cb.bandcfg = + tlv_chan_band->bandcfg; + pmpriv->uap_state_chan_cb.channel = + tlv_chan_band->channel; + break; + case TLV_TYPE_CHANLIST: + tlv_chan_list = (MrvlIEtypes_ChanListParamSet_t *)tlv; + bss->param.bss_config.num_of_chan = + tlv_len / sizeof(ChanScanParamSet_t); + pscan_chan = tlv_chan_list->chan_scan_param; + for (i = 0; i < bss->param.bss_config.num_of_chan; i++) { + bss->param.bss_config.chan_list[i].chan_number = + pscan_chan->chan_number; + bss->param.bss_config.chan_list[i].bandcfg = + pscan_chan->bandcfg; + pscan_chan++; + } + break; + case TLV_TYPE_AUTH_TYPE: + tlv_auth_type = (MrvlIEtypes_auth_type_t *)tlv; + bss->param.bss_config.auth_mode = + tlv_auth_type->auth_type; + break; + case TLV_TYPE_UAP_ENCRYPT_PROTOCOL: + tlv_encrypt_protocol = + (MrvlIEtypes_encrypt_protocol_t *)tlv; + bss->param.bss_config.protocol = + wlan_le16_to_cpu(tlv_encrypt_protocol-> + protocol); + break; + case TLV_TYPE_UAP_AKMP: + tlv_akmp = (MrvlIEtypes_akmp_t *)tlv; + bss->param.bss_config.key_mgmt = + wlan_le16_to_cpu(tlv_akmp->key_mgmt); + if (tlv_len > sizeof(t_u16)) + bss->param.bss_config.key_mgmt_operation = + wlan_le16_to_cpu(tlv_akmp-> + key_mgmt_operation); + break; + case TLV_TYPE_PWK_CIPHER: + tlv_pwk_cipher = (MrvlIEtypes_pwk_cipher_t *)tlv; + if (wlan_le16_to_cpu(tlv_pwk_cipher->protocol) & + PROTOCOL_WPA) + bss->param.bss_config.wpa_cfg. + pairwise_cipher_wpa = + tlv_pwk_cipher->pairwise_cipher; + if (wlan_le16_to_cpu(tlv_pwk_cipher->protocol) & + PROTOCOL_WPA2) + bss->param.bss_config.wpa_cfg. + pairwise_cipher_wpa2 = + tlv_pwk_cipher->pairwise_cipher; + break; + case TLV_TYPE_GWK_CIPHER: + tlv_gwk_cipher = (MrvlIEtypes_gwk_cipher_t *)tlv; + bss->param.bss_config.wpa_cfg.group_cipher = + tlv_gwk_cipher->group_cipher; + break; + case TLV_TYPE_UAP_RSN_REPLAY_PROTECT: + tlv_rsn_prot = (MrvlIEtypes_rsn_replay_prot_t *)tlv; + bss->param.bss_config.wpa_cfg.rsn_protection = + tlv_rsn_prot->rsn_replay_prot; + break; + case TLV_TYPE_UAP_WPA_PASSPHRASE: + tlv_passphrase = (MrvlIEtypes_passphrase_t *)tlv; + bss->param.bss_config.wpa_cfg.length = + MIN(MLAN_PMK_HEXSTR_LENGTH, tlv_len); + memcpy(pmpriv->adapter, + bss->param.bss_config.wpa_cfg.passphrase, + tlv_passphrase->passphrase, + bss->param.bss_config.wpa_cfg.length); + break; +#ifdef WIFI_DIRECT_SUPPORT + case TLV_TYPE_UAP_PSK: + tlv_psk = (MrvlIEtypes_psk_t *)tlv; + memcpy(pmpriv->adapter, bss->param.bss_config.psk, + tlv_psk->psk, MIN(MLAN_MAX_KEY_LENGTH, tlv_len)); + break; +#endif /* WIFI_DIRECT_SUPPORT */ + case TLV_TYPE_UAP_GRP_REKEY_TIME: + tlv_rekey_time = (MrvlIEtypes_group_rekey_time_t *)tlv; + bss->param.bss_config.wpa_cfg.gk_rekey_time = + wlan_le32_to_cpu(tlv_rekey_time->gk_rekey_time); + break; + case TLV_TYPE_UAP_WEP_KEY: + tlv_wep_key = (MrvlIEtypes_wep_key_t *)tlv; + pkey = MNULL; + if (tlv_wep_key->key_index == 0) + pkey = &bss->param.bss_config.wep_cfg.key0; + else if (tlv_wep_key->key_index == 1) + pkey = &bss->param.bss_config.wep_cfg.key1; + else if (tlv_wep_key->key_index == 2) + pkey = &bss->param.bss_config.wep_cfg.key2; + else if (tlv_wep_key->key_index == 3) + pkey = &bss->param.bss_config.wep_cfg.key3; + if (pkey) { + pkey->key_index = tlv_wep_key->key_index; + pkey->is_default = tlv_wep_key->is_default; + pkey->length = + MIN(MAX_WEP_KEY_SIZE, (tlv_len - 2)); + memcpy(pmpriv->adapter, pkey->key, + tlv_wep_key->key, pkey->length); + } + break; + case TLV_TYPE_UAP_PREAMBLE_CTL: + tlv_preamble = (MrvlIEtypes_preamble_t *)tlv; + bss->param.bss_config.preamble_type = + tlv_preamble->preamble_type; + break; + case TLV_TYPE_BSS_STATUS: + tlv_bss_status = (MrvlIEtypes_bss_status_t *)tlv; + bss->param.bss_config.bss_status = + wlan_le16_to_cpu(tlv_bss_status->bss_status); + pmpriv->uap_bss_started = + (bss->param.bss_config. + bss_status) ? MTRUE : MFALSE; + break; + case TLV_TYPE_HT_CAPABILITY: + tlv_htcap = (MrvlIETypes_HTCap_t *)tlv; + bss->param.bss_config.ht_cap_info = + wlan_le16_to_cpu(tlv_htcap->ht_cap.ht_cap_info); + bss->param.bss_config.ampdu_param = + tlv_htcap->ht_cap.ampdu_param; + memcpy(pmpriv->adapter, + bss->param.bss_config.supported_mcs_set, + tlv_htcap->ht_cap.supported_mcs_set, 16); + bss->param.bss_config.ht_ext_cap = + wlan_le16_to_cpu(tlv_htcap->ht_cap.ht_ext_cap); + bss->param.bss_config.tx_bf_cap = + wlan_le32_to_cpu(tlv_htcap->ht_cap.tx_bf_cap); + bss->param.bss_config.asel = tlv_htcap->ht_cap.asel; + break; + case TLV_TYPE_VENDOR_SPECIFIC_IE: + tlv_wmm_parameter = (MrvlIEtypes_wmm_parameter_t *)tlv; + bss->param.bss_config.wmm_para.qos_info = + tlv_wmm_parameter->wmm_para.qos_info; + for (ac = 0; ac < 4; ac++) { + bss->param.bss_config.wmm_para.ac_params[ac]. + aci_aifsn.aifsn = + tlv_wmm_parameter->wmm_para. + ac_params[ac].aci_aifsn.aifsn; + bss->param.bss_config.wmm_para.ac_params[ac]. + aci_aifsn.aci = + tlv_wmm_parameter->wmm_para. + ac_params[ac].aci_aifsn.aci; + bss->param.bss_config.wmm_para.ac_params[ac]. + ecw.ecw_max = + tlv_wmm_parameter->wmm_para. + ac_params[ac].ecw.ecw_max; + bss->param.bss_config.wmm_para.ac_params[ac]. + ecw.ecw_min = + tlv_wmm_parameter->wmm_para. + ac_params[ac].ecw.ecw_min; + bss->param.bss_config.wmm_para.ac_params[ac]. + tx_op_limit = + wlan_le16_to_cpu(tlv_wmm_parameter-> + wmm_para.ac_params[ac]. + tx_op_limit); + } + break; + } + + tlv_buf_left -= tlv_len + sizeof(MrvlIEtypesHeader_t); + tlv = (MrvlIEtypesHeader_t *)((t_u8 *)tlv + tlv_len + + sizeof(MrvlIEtypesHeader_t)); + } +#ifdef DRV_EMBEDDED_AUTHENTICATOR + if (!IS_FW_SUPPORT_AUTHENTICATOR(pmpriv->adapter)) + AuthenticatorBssConfig(pmpriv->psapriv, + (t_u8 *)&bss->param.bss_config, 0, 0, 1); +#endif + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of sys_reset + * Clear various private state variables used by DFS. + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_uap_ret_sys_reset(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf) +{ + ENTER(); + + memset(pmpriv->adapter, &(pmpriv->uap_state_chan_cb.bandcfg), 0, + sizeof(pmpriv->uap_state_chan_cb.bandcfg)); + pmpriv->uap_state_chan_cb.channel = 0; + pmpriv->uap_state_chan_cb.beacon_period = 0; + pmpriv->uap_state_chan_cb.dtim_period = 0; + + /* assume default 11d/11h states are off, should check with FW */ + /* currently don't clear domain_info... global, could be from STA */ + wlan_11d_priv_init(pmpriv); + wlan_11h_priv_init(pmpriv); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of sys_config + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_uap_ret_sys_config(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf) +{ + int resp_len = 0, travel_len = 0; + int i = 0; + custom_ie *cptr; + HostCmd_DS_SYS_CONFIG *sys_config = + (HostCmd_DS_SYS_CONFIG *)&resp->params.sys_config; + mlan_ds_bss *bss = MNULL; + mlan_ds_misc_cfg *misc = MNULL; + MrvlIEtypes_MacAddr_t *tlv = + (MrvlIEtypes_MacAddr_t *)sys_config->tlv_buffer; + mlan_ds_misc_custom_ie *cust_ie = MNULL; + tlvbuf_max_mgmt_ie *max_mgmt_ie = MNULL; + MrvlIEtypes_wmm_parameter_t *tlv_wmm_parameter = + (MrvlIEtypes_wmm_parameter_t *)sys_config->tlv_buffer; + MrvlIEtypes_ChanListParamSet_t *tlv_chan_list = + (MrvlIEtypes_ChanListParamSet_t *)sys_config->tlv_buffer; + MrvlIEtypes_channel_band_t *chan_band_tlv = + (MrvlIEtypes_channel_band_t *)sys_config->tlv_buffer; + ChanScanParamSet_t *pscan_chan = MNULL; + t_u8 ac = 0; + MrvlIEtypes_channel_band_t *tlv_cb = MNULL; + MrvlIEtypes_beacon_period_t *tlv_bcnpd = MNULL; + MrvlIEtypes_dtim_period_t *tlv_dtimpd = MNULL; + + ENTER(); + if (pioctl_buf) { + if (pioctl_buf->req_id == MLAN_IOCTL_BSS) { + bss = (mlan_ds_bss *)pioctl_buf->pbuf; + if (bss->sub_command == MLAN_OID_BSS_MAC_ADDR) { + if (TLV_TYPE_UAP_MAC_ADDRESS == + wlan_le16_to_cpu(tlv->header.type)) { + memcpy(pmpriv->adapter, + &bss->param.mac_addr, tlv->mac, + MLAN_MAC_ADDR_LENGTH); + } + } else if (bss->sub_command == + MLAN_OID_UAP_CFG_WMM_PARAM) { + if (TLV_TYPE_AP_WMM_PARAM == + wlan_le16_to_cpu(tlv_wmm_parameter->header. + type)) { + if (wlan_le16_to_cpu + (tlv_wmm_parameter->header.len) < + sizeof(bss->param.ap_wmm_para)) { + PRINTM(MCMND, + "FW don't support AP WMM PARAM\n"); + } else { + bss->param.ap_wmm_para. + reserved = + MLAN_STATUS_COMPLETE; + for (ac = 0; ac < 4; ac++) { + bss->param.ap_wmm_para. + ac_params[ac]. + aci_aifsn. + aifsn = + tlv_wmm_parameter-> + wmm_para. + ac_params[ac]. + aci_aifsn.aifsn; + bss->param.ap_wmm_para. + ac_params[ac]. + aci_aifsn.aci = + tlv_wmm_parameter-> + wmm_para. + ac_params[ac]. + aci_aifsn.aci; + bss->param.ap_wmm_para. + ac_params[ac]. + ecw.ecw_max = + tlv_wmm_parameter-> + wmm_para. + ac_params[ac]. + ecw.ecw_max; + bss->param.ap_wmm_para. + ac_params[ac]. + ecw.ecw_min = + tlv_wmm_parameter-> + wmm_para. + ac_params[ac]. + ecw.ecw_min; + bss->param.ap_wmm_para. + ac_params[ac]. + tx_op_limit = + wlan_le16_to_cpu + (tlv_wmm_parameter-> + wmm_para. + ac_params[ac]. + tx_op_limit); + PRINTM(MCMND, + "ac=%d, aifsn=%d, aci=%d, ecw_max=%d, ecw_min=%d, tx_op=%d\n", + ac, + bss->param. + ap_wmm_para. + ac_params[ac]. + aci_aifsn.aifsn, + bss->param. + ap_wmm_para. + ac_params[ac]. + aci_aifsn.aci, + bss->param. + ap_wmm_para. + ac_params[ac]. + ecw.ecw_max, + bss->param. + ap_wmm_para. + ac_params[ac]. + ecw.ecw_min, + bss->param. + ap_wmm_para. + ac_params[ac]. + tx_op_limit); + } + } + } + } else if (bss->sub_command == + MLAN_OID_UAP_SCAN_CHANNELS) { + if (TLV_TYPE_CHANLIST == + wlan_le16_to_cpu(tlv_chan_list->header. + type)) { + pscan_chan = + tlv_chan_list->chan_scan_param; + bss->param.ap_scan_channels. + num_of_chan = 0; + for (i = 0; + i < + wlan_le16_to_cpu(tlv_chan_list-> + header.len) / + sizeof(ChanScanParamSet_t); i++) { + if (bss->param.ap_scan_channels. + remove_nop_channel && + wlan_11h_is_channel_under_nop + (pmpriv->adapter, + pscan_chan->chan_number)) { + bss->param. + ap_scan_channels. + num_remvoed_channel++; + PRINTM(MCMND, + "Remove nop channel=%d\n", + pscan_chan-> + chan_number); + pscan_chan++; + continue; + } + bss->param.ap_scan_channels. + chan_list[bss->param. + ap_scan_channels. + num_of_chan]. + chan_number = + pscan_chan->chan_number; + bss->param.ap_scan_channels. + chan_list[bss->param. + ap_scan_channels. + num_of_chan]. + bandcfg = + pscan_chan->bandcfg; + bss->param.ap_scan_channels. + num_of_chan++; + pscan_chan++; + } + PRINTM(MCMND, + "AP scan channel list=%d\n", + bss->param.ap_scan_channels. + num_of_chan); + } + } else if (bss->sub_command == MLAN_OID_UAP_CHANNEL) { + if (TLV_TYPE_UAP_CHAN_BAND_CONFIG == + wlan_le16_to_cpu(chan_band_tlv->header. + type)) { + bss->param.ap_channel.bandcfg = + chan_band_tlv->bandcfg; + bss->param.ap_channel.channel = + chan_band_tlv->channel; + bss->param.ap_channel.is_11n_enabled = + pmpriv->is_11n_enabled; + PRINTM(MCMND, + "AP channel, band=0x%x, channel=%d, is_11n_enabled=%d center_chan=%d\n", + bss->param.ap_channel.bandcfg. + chanBand, + bss->param.ap_channel.channel, + bss->param.ap_channel. + is_11n_enabled, + bss->param.ap_channel. + center_chan); + } + } else if ((bss->sub_command == MLAN_OID_UAP_BSS_CONFIG) + && (pioctl_buf->action == MLAN_ACT_GET)) { + wlan_uap_ret_cmd_ap_config(pmpriv, resp, + pioctl_buf); + } + } + if (pioctl_buf->req_id == MLAN_IOCTL_MISC_CFG) { + misc = (mlan_ds_misc_cfg *)pioctl_buf->pbuf; + cust_ie = + (mlan_ds_misc_custom_ie *)sys_config-> + tlv_buffer; + if ((pioctl_buf->action == MLAN_ACT_GET || + pioctl_buf->action == MLAN_ACT_SET) && + (misc->sub_command == MLAN_OID_MISC_CUSTOM_IE)) { + + cust_ie->type = wlan_le16_to_cpu(cust_ie->type); + resp_len = cust_ie->len = + wlan_le16_to_cpu(cust_ie->len); + travel_len = 0; + /* conversion for index, mask, len */ + if (resp_len == sizeof(t_u16)) + cust_ie->ie_data_list[0].ie_index = + wlan_cpu_to_le16(cust_ie-> + ie_data_list + [0].ie_index); + + while (resp_len > sizeof(t_u16)) { + cptr = (custom_ie + *)(((t_u8 *)cust_ie-> + ie_data_list) + travel_len); + cptr->ie_index = + wlan_le16_to_cpu(cptr-> + ie_index); + cptr->mgmt_subtype_mask = + wlan_le16_to_cpu(cptr-> + mgmt_subtype_mask); + cptr->ie_length = + wlan_le16_to_cpu(cptr-> + ie_length); + travel_len += + cptr->ie_length + + sizeof(custom_ie) - MAX_IE_SIZE; + resp_len -= + cptr->ie_length + + sizeof(custom_ie) - MAX_IE_SIZE; + } + memcpy(pmpriv->adapter, &misc->param.cust_ie, + cust_ie, + MIN(sizeof(mlan_ds_misc_custom_ie) - + sizeof(tlvbuf_max_mgmt_ie), + (cust_ie->len + + sizeof(MrvlIEtypesHeader_t)))); + max_mgmt_ie = + (tlvbuf_max_mgmt_ie *)(sys_config-> + tlv_buffer + + cust_ie->len + + sizeof + (MrvlIEtypesHeader_t)); + if (max_mgmt_ie) { + max_mgmt_ie->type = + wlan_le16_to_cpu(max_mgmt_ie-> + type); + if (max_mgmt_ie->type == + TLV_TYPE_MAX_MGMT_IE) { + max_mgmt_ie->len = + wlan_le16_to_cpu + (max_mgmt_ie->len); + max_mgmt_ie->count = + wlan_le16_to_cpu + (max_mgmt_ie->count); + for (i = 0; + i < max_mgmt_ie->count; + i++) { + max_mgmt_ie->info[i]. + buf_size = + wlan_le16_to_cpu + (max_mgmt_ie-> + info[i]. + buf_size); + max_mgmt_ie->info[i]. + buf_count = + wlan_le16_to_cpu + (max_mgmt_ie-> + info[i]. + buf_count); + } + /* Append max_mgmt_ie TLV after custom_ie */ + memcpy(pmpriv->adapter, + (t_u8 *)&misc->param. + cust_ie + (cust_ie->len + + sizeof + (MrvlIEtypesHeader_t)), + max_mgmt_ie, + MIN(sizeof + (tlvbuf_max_mgmt_ie), + max_mgmt_ie->len + + sizeof + (MrvlIEtypesHeader_t))); + } + } + } + } + } else { /* no ioctl: driver generated get/set */ + switch (wlan_le16_to_cpu(tlv->header.type)) { + case TLV_TYPE_UAP_MAC_ADDRESS: + memcpy(pmpriv->adapter, pmpriv->curr_addr, tlv->mac, + MLAN_MAC_ADDR_LENGTH); + break; + case TLV_TYPE_UAP_CHAN_BAND_CONFIG: + tlv_cb = (MrvlIEtypes_channel_band_t *)tlv; + pmpriv->uap_state_chan_cb.bandcfg = tlv_cb->bandcfg; + pmpriv->uap_state_chan_cb.channel = tlv_cb->channel; + /* call callback waiting for channel info */ + if (pmpriv->uap_state_chan_cb.get_chan_callback) + pmpriv->uap_state_chan_cb. + get_chan_callback(pmpriv); + break; + case TLV_TYPE_UAP_BEACON_PERIOD: + tlv_bcnpd = (MrvlIEtypes_beacon_period_t *)tlv; + pmpriv->uap_state_chan_cb.beacon_period = + wlan_le16_to_cpu(tlv_bcnpd->beacon_period); + /* copy dtim_period as well if it follows */ + tlv_dtimpd = + (MrvlIEtypes_dtim_period_t *)(((t_u8 *)tlv) + + sizeof + (MrvlIEtypes_beacon_period_t)); + if (TLV_TYPE_UAP_DTIM_PERIOD == + wlan_le16_to_cpu(tlv_dtimpd->header.type)) + pmpriv->uap_state_chan_cb.dtim_period = + tlv_dtimpd->dtim_period; + /* call callback waiting for beacon/dtim info */ + if (pmpriv->uap_state_chan_cb.get_chan_callback) + pmpriv->uap_state_chan_cb. + get_chan_callback(pmpriv); + break; + case TLV_TYPE_MGMT_IE: + if ((pmpriv->adapter->state_rdh.stage == + RDH_SET_CUSTOM_IE) || + (pmpriv->adapter->state_rdh.stage == + RDH_REM_CUSTOM_IE)) { + if (!pmpriv->adapter->ecsa_enable) + wlan_11h_radar_detected_callback((t_void + *) + pmpriv); + } + break; + } + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of snmp_mib + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action the action: GET or SET + * @param cmd_oid Cmd oid: treated as sub command + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * @param pdata_buf A pointer to information buffer + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status +wlan_uap_cmd_snmp_mib(pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, + IN t_u32 cmd_oid, + IN pmlan_ioctl_req pioctl_buf, IN t_void *pdata_buf) +{ + HostCmd_DS_802_11_SNMP_MIB *psnmp_mib = &cmd->params.smib; + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u8 *psnmp_oid = MNULL; + t_u32 ul_temp; + t_u8 i; + + t_u8 snmp_oids[] = { + tkip_mic_failures, + ccmp_decrypt_errors, + wep_undecryptable_count, + wep_icv_error_count, + decrypt_failure_count, + dot11_mcast_tx_count, + dot11_failed_count, + dot11_retry_count, + dot11_multi_retry_count, + dot11_frame_dup_count, + dot11_rts_success_count, + dot11_rts_failure_count, + dot11_ack_failure_count, + dot11_rx_fragment_count, + dot11_mcast_rx_frame_count, + dot11_fcs_error_count, + dot11_tx_frame_count, + dot11_rsna_tkip_cm_invoked, + dot11_rsna_4way_hshk_failures, + }; + + ENTER(); + + if (cmd_action == HostCmd_ACT_GEN_GET) { + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_802_11_SNMP_MIB); + psnmp_mib->query_type = wlan_cpu_to_le16(HostCmd_ACT_GEN_GET); + if (cmd_oid == StopDeauth_i) { + psnmp_mib->oid = wlan_cpu_to_le16((t_u16)StopDeauth_i); + psnmp_mib->buf_size = wlan_cpu_to_le16(sizeof(t_u8)); + cmd->size = + wlan_cpu_to_le16(sizeof + (HostCmd_DS_802_11_SNMP_MIB) + + S_DS_GEN); + } else { + cmd->size = + wlan_cpu_to_le16(sizeof(t_u16) + S_DS_GEN + + sizeof(snmp_oids) * + sizeof + (MrvlIEtypes_snmp_oid_t)); + psnmp_oid = (t_u8 *)&psnmp_mib->oid; + for (i = 0; i < sizeof(snmp_oids); i++) { + /* SNMP OID header type */ + *(t_u16 *)psnmp_oid = + wlan_cpu_to_le16(snmp_oids[i]); + psnmp_oid += sizeof(t_u16); + /* SNMP OID header length */ + *(t_u16 *)psnmp_oid = + wlan_cpu_to_le16(sizeof(t_u32)); + psnmp_oid += sizeof(t_u16) + sizeof(t_u32); + } + } + } else { /* cmd_action == ACT_SET */ + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_802_11_SNMP_MIB); + cmd->size = sizeof(HostCmd_DS_802_11_SNMP_MIB) - 1 + S_DS_GEN; + psnmp_mib->query_type = wlan_cpu_to_le16(HostCmd_ACT_GEN_SET); + + switch (cmd_oid) { + case Dot11D_i: + case Dot11H_i: + psnmp_mib->oid = wlan_cpu_to_le16((t_u16)cmd_oid); + psnmp_mib->buf_size = wlan_cpu_to_le16(sizeof(t_u16)); + ul_temp = *(t_u32 *)pdata_buf; + *((t_u16 *)(psnmp_mib->value)) = + wlan_cpu_to_le16((t_u16)ul_temp); + cmd->size += sizeof(t_u16); + break; + case ECSAEnable_i: + psnmp_mib->oid = wlan_cpu_to_le16((t_u16)cmd_oid); + psnmp_mib->buf_size = wlan_cpu_to_le16(sizeof(t_u8)); + psnmp_mib->value[0] = *((t_u8 *)pdata_buf); + cmd->size += sizeof(t_u8); + break; + case StopDeauth_i: + psnmp_mib->oid = wlan_cpu_to_le16((t_u16)cmd_oid); + psnmp_mib->buf_size = wlan_cpu_to_le16(sizeof(t_u8)); + psnmp_mib->value[0] = *((t_u8 *)pdata_buf); + cmd->size += sizeof(t_u8); + break; + default: + PRINTM(MERROR, "Unsupported OID.\n"); + ret = MLAN_STATUS_FAILURE; + break; + } + cmd->size = wlan_cpu_to_le16(cmd->size); + } + + LEAVE(); + return ret; +} + +/** + * @brief This function prepares command of get_log. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_uap_cmd_802_11_get_log(IN pmlan_private pmpriv, IN HostCmd_DS_COMMAND *cmd) +{ + ENTER(); + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_802_11_GET_LOG); + cmd->size = + wlan_cpu_to_le16(sizeof(HostCmd_DS_802_11_GET_LOG) + S_DS_GEN); + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of snmp_mib + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_uap_ret_snmp_mib(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf) +{ + pmlan_adapter pmadapter = pmpriv->adapter; + HostCmd_DS_802_11_SNMP_MIB *psnmp_mib = + (HostCmd_DS_802_11_SNMP_MIB *)&resp->params.smib; + mlan_ds_get_info *info; + mlan_ds_snmp_mib *mib = MNULL; + t_u16 oid = wlan_le16_to_cpu(psnmp_mib->oid); + t_u8 *psnmp_oid = MNULL; + t_u32 data; + t_u16 tlv_buf_left = 0; + t_u16 tlv_type = 0; + t_u16 query_type = wlan_le16_to_cpu(psnmp_mib->query_type); + + ENTER(); + if (query_type == HostCmd_ACT_GEN_GET) { + if (!pioctl_buf) { + LEAVE(); + return MLAN_STATUS_SUCCESS; + } + if (oid == StopDeauth_i) { + mib = (mlan_ds_snmp_mib *)pioctl_buf->pbuf; + if (mib) + mib->param.deauthctrl = psnmp_mib->value[0]; + LEAVE(); + return MLAN_STATUS_SUCCESS; + } + info = (mlan_ds_get_info *)pioctl_buf->pbuf; + tlv_buf_left = resp->size - (sizeof(t_u16) + S_DS_GEN); + psnmp_oid = (t_u8 *)&psnmp_mib->oid; + while (tlv_buf_left >= sizeof(MrvlIEtypes_snmp_oid_t)) { + tlv_type = wlan_le16_to_cpu(*(t_u16 *)psnmp_oid); + psnmp_oid += sizeof(t_u16) + sizeof(t_u16); + memcpy(pmadapter, &data, psnmp_oid, sizeof(t_u32)); + switch (tlv_type) { + case tkip_mic_failures: + info->param.ustats.tkip_mic_failures = + wlan_le32_to_cpu(data); + break; + case ccmp_decrypt_errors: + info->param.ustats.ccmp_decrypt_errors = + wlan_le32_to_cpu(data); + break; + case wep_undecryptable_count: + info->param.ustats.wep_undecryptable_count = + wlan_le32_to_cpu(data); + break; + case wep_icv_error_count: + info->param.ustats.wep_icv_error_count = + wlan_le32_to_cpu(data); + break; + case decrypt_failure_count: + info->param.ustats.decrypt_failure_count = + wlan_le32_to_cpu(data); + break; + case dot11_mcast_tx_count: + info->param.ustats.mcast_tx_count = + wlan_le32_to_cpu(data); + break; + case dot11_failed_count: + info->param.ustats.failed_count = + wlan_le32_to_cpu(data); + break; + case dot11_retry_count: + info->param.ustats.retry_count = + wlan_le32_to_cpu(data); + break; + case dot11_multi_retry_count: + info->param.ustats.multi_retry_count = + wlan_le32_to_cpu(data); + break; + case dot11_frame_dup_count: + info->param.ustats.frame_dup_count = + wlan_le32_to_cpu(data); + break; + case dot11_rts_success_count: + info->param.ustats.rts_success_count = + wlan_le32_to_cpu(data); + break; + case dot11_rts_failure_count: + info->param.ustats.rts_failure_count = + wlan_le32_to_cpu(data); + break; + case dot11_ack_failure_count: + info->param.ustats.ack_failure_count = + wlan_le32_to_cpu(data); + break; + case dot11_rx_fragment_count: + info->param.ustats.rx_fragment_count = + wlan_le32_to_cpu(data); + break; + case dot11_mcast_rx_frame_count: + info->param.ustats.mcast_rx_frame_count = + wlan_le32_to_cpu(data); + break; + case dot11_fcs_error_count: + info->param.ustats.fcs_error_count = + wlan_le32_to_cpu(data); + break; + case dot11_tx_frame_count: + info->param.ustats.tx_frame_count = + wlan_le32_to_cpu(data); + break; + case dot11_rsna_tkip_cm_invoked: + info->param.ustats.rsna_tkip_cm_invoked = + wlan_le32_to_cpu(data); + break; + case dot11_rsna_4way_hshk_failures: + info->param.ustats.rsna_4way_hshk_failures = + wlan_le32_to_cpu(data); + break; + } + tlv_buf_left -= sizeof(MrvlIEtypes_snmp_oid_t); + psnmp_oid += sizeof(t_u32); + } + } else { /* ACT_SET */ + switch (wlan_le16_to_cpu(psnmp_mib->oid)) { + case Dot11D_i: + data = wlan_le16_to_cpu(*((t_u16 *)(psnmp_mib->value))); + /* Set 11d state to private */ + pmpriv->state_11d.enable_11d = data; + /* Set user enable flag if called from ioctl */ + if (pioctl_buf) + pmpriv->state_11d.user_enable_11d = data; + break; + case Dot11H_i: + data = wlan_le16_to_cpu(*((t_u16 *)(psnmp_mib->value))); + /* Set 11h state to priv */ + pmpriv->intf_state_11h.is_11h_active = + (data & ENABLE_11H_MASK); + /* Set radar_det state to adapter */ + pmpriv->adapter->state_11h.is_master_radar_det_active + = + (data & MASTER_RADAR_DET_MASK) ? MTRUE : MFALSE; + pmpriv->adapter->state_11h.is_slave_radar_det_active = + (data & SLAVE_RADAR_DET_MASK) ? MTRUE : MFALSE; + break; + } + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of get_log + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_uap_ret_get_log(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, IN mlan_ioctl_req *pioctl_buf) +{ + HostCmd_DS_802_11_GET_LOG *pget_log = + (HostCmd_DS_802_11_GET_LOG *)&resp->params.get_log; + mlan_ds_get_info *pget_info = MNULL; + int i = 0; + + ENTER(); + + if (pioctl_buf) { + pget_info = (mlan_ds_get_info *)pioctl_buf->pbuf; + pget_info->param.stats.mcast_tx_frame = + wlan_le32_to_cpu(pget_log->mcast_tx_frame); + pget_info->param.stats.failed = + wlan_le32_to_cpu(pget_log->failed); + pget_info->param.stats.retry = + wlan_le32_to_cpu(pget_log->retry); + pget_info->param.stats.multi_retry = + wlan_le32_to_cpu(pget_log->multiretry); + pget_info->param.stats.frame_dup = + wlan_le32_to_cpu(pget_log->frame_dup); + pget_info->param.stats.rts_success = + wlan_le32_to_cpu(pget_log->rts_success); + pget_info->param.stats.rts_failure = + wlan_le32_to_cpu(pget_log->rts_failure); + pget_info->param.stats.ack_failure = + wlan_le32_to_cpu(pget_log->ack_failure); + pget_info->param.stats.rx_frag = + wlan_le32_to_cpu(pget_log->rx_frag); + pget_info->param.stats.mcast_rx_frame = + wlan_le32_to_cpu(pget_log->mcast_rx_frame); + pget_info->param.stats.fcs_error = + wlan_le32_to_cpu(pget_log->fcs_error); + pget_info->param.stats.tx_frame = + wlan_le32_to_cpu(pget_log->tx_frame); + pget_info->param.stats.wep_icv_error[0] = + wlan_le32_to_cpu(pget_log->wep_icv_err_cnt[0]); + pget_info->param.stats.wep_icv_error[1] = + wlan_le32_to_cpu(pget_log->wep_icv_err_cnt[1]); + pget_info->param.stats.wep_icv_error[2] = + wlan_le32_to_cpu(pget_log->wep_icv_err_cnt[2]); + pget_info->param.stats.wep_icv_error[3] = + wlan_le32_to_cpu(pget_log->wep_icv_err_cnt[3]); + pget_info->param.stats.bcn_rcv_cnt = + wlan_le32_to_cpu(pget_log->bcn_rcv_cnt); + pget_info->param.stats.bcn_miss_cnt = + wlan_le32_to_cpu(pget_log->bcn_miss_cnt); + pget_info->param.stats.amsdu_rx_cnt = pmpriv->amsdu_rx_cnt; + pget_info->param.stats.msdu_in_rx_amsdu_cnt = + pmpriv->msdu_in_rx_amsdu_cnt; + pget_info->param.stats.amsdu_tx_cnt = pmpriv->amsdu_tx_cnt; + pget_info->param.stats.msdu_in_tx_amsdu_cnt = + pmpriv->msdu_in_tx_amsdu_cnt; + if (pmpriv->adapter->getlog_enable) { + pget_info->param.stats.tx_frag_cnt = + wlan_le32_to_cpu(pget_log->tx_frag_cnt); + for (i = 0; i < 8; i++) { + pget_info->param.stats.qos_tx_frag_cnt[i] = + wlan_le32_to_cpu(pget_log-> + qos_tx_frag_cnt[i]); + pget_info->param.stats.qos_failed_cnt[i] = + wlan_le32_to_cpu(pget_log-> + qos_failed_cnt[i]); + pget_info->param.stats.qos_retry_cnt[i] = + wlan_le32_to_cpu(pget_log-> + qos_retry_cnt[i]); + pget_info->param.stats.qos_multi_retry_cnt[i] = + wlan_le32_to_cpu(pget_log-> + qos_multi_retry_cnt + [i]); + pget_info->param.stats.qos_frm_dup_cnt[i] = + wlan_le32_to_cpu(pget_log-> + qos_frm_dup_cnt[i]); + pget_info->param.stats.qos_rts_suc_cnt[i] = + wlan_le32_to_cpu(pget_log-> + qos_rts_suc_cnt[i]); + pget_info->param.stats.qos_rts_failure_cnt[i] = + wlan_le32_to_cpu(pget_log-> + qos_rts_failure_cnt + [i]); + pget_info->param.stats.qos_ack_failure_cnt[i] = + wlan_le32_to_cpu(pget_log-> + qos_ack_failure_cnt + [i]); + pget_info->param.stats.qos_rx_frag_cnt[i] = + wlan_le32_to_cpu(pget_log-> + qos_rx_frag_cnt[i]); + pget_info->param.stats.qos_tx_frm_cnt[i] = + wlan_le32_to_cpu(pget_log-> + qos_tx_frm_cnt[i]); + pget_info->param.stats. + qos_discarded_frm_cnt[i] = + wlan_le32_to_cpu(pget_log-> + qos_discarded_frm_cnt + [i]); + pget_info->param.stats.qos_mpdus_rx_cnt[i] = + wlan_le32_to_cpu(pget_log-> + qos_mpdus_rx_cnt[i]); + pget_info->param.stats.qos_retries_rx_cnt[i] = + wlan_le32_to_cpu(pget_log-> + qos_retries_rx_cnt[i]); + } + pget_info->param.stats.mgmt_ccmp_replays = + wlan_le32_to_cpu(pget_log->mgmt_ccmp_replays); + pget_info->param.stats.tx_amsdu_cnt = + wlan_le32_to_cpu(pget_log->tx_amsdu_cnt); + pget_info->param.stats.failed_amsdu_cnt = + wlan_le32_to_cpu(pget_log->failed_amsdu_cnt); + pget_info->param.stats.retry_amsdu_cnt = + wlan_le32_to_cpu(pget_log->retry_amsdu_cnt); + pget_info->param.stats.multi_retry_amsdu_cnt = + wlan_le32_to_cpu(pget_log-> + multi_retry_amsdu_cnt); + pget_info->param.stats.tx_octets_in_amsdu_cnt = + wlan_le64_to_cpu(pget_log-> + tx_octets_in_amsdu_cnt); + pget_info->param.stats.amsdu_ack_failure_cnt = + wlan_le32_to_cpu(pget_log-> + amsdu_ack_failure_cnt); + pget_info->param.stats.rx_amsdu_cnt = + wlan_le32_to_cpu(pget_log->rx_amsdu_cnt); + pget_info->param.stats.rx_octets_in_amsdu_cnt = + wlan_le64_to_cpu(pget_log-> + rx_octets_in_amsdu_cnt); + pget_info->param.stats.tx_ampdu_cnt = + wlan_le32_to_cpu(pget_log->tx_ampdu_cnt); + pget_info->param.stats.tx_mpdus_in_ampdu_cnt = + wlan_le32_to_cpu(pget_log-> + tx_mpdus_in_ampdu_cnt); + pget_info->param.stats.tx_octets_in_ampdu_cnt = + wlan_le64_to_cpu(pget_log-> + tx_octets_in_ampdu_cnt); + pget_info->param.stats.ampdu_rx_cnt = + wlan_le32_to_cpu(pget_log->ampdu_rx_cnt); + pget_info->param.stats.mpdu_in_rx_ampdu_cnt = + wlan_le32_to_cpu(pget_log-> + mpdu_in_rx_ampdu_cnt); + pget_info->param.stats.rx_octets_in_ampdu_cnt = + wlan_le64_to_cpu(pget_log-> + rx_octets_in_ampdu_cnt); + pget_info->param.stats.ampdu_delimiter_crc_error_cnt = + wlan_le32_to_cpu(pget_log-> + ampdu_delimiter_crc_error_cnt); + + /* Indicate ioctl complete */ + pioctl_buf->data_read_written = + sizeof(mlan_ds_get_info); + } else + pioctl_buf->data_read_written = + sizeof(mlan_ds_get_stats_org) + + sizeof(pget_info->sub_command); + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of deauth station + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param pdata_buf A pointer to data buffer + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_uap_cmd_sta_deauth(pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, IN t_void *pdata_buf) +{ + HostCmd_DS_STA_DEAUTH *pcmd_sta_deauth = + (HostCmd_DS_STA_DEAUTH *)&cmd->params.sta_deauth; + mlan_deauth_param *deauth = (mlan_deauth_param *)pdata_buf; + + ENTER(); + cmd->command = wlan_cpu_to_le16(HOST_CMD_APCMD_STA_DEAUTH); + cmd->size = wlan_cpu_to_le16(S_DS_GEN + sizeof(HostCmd_DS_STA_DEAUTH)); + memcpy(pmpriv->adapter, pcmd_sta_deauth->mac, deauth->mac_addr, + MLAN_MAC_ADDR_LENGTH); + pcmd_sta_deauth->reason = wlan_cpu_to_le16(deauth->reason_code); + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of key material + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action The action: GET or SET + * @param cmd_oid OID: ENABLE or DISABLE + * @param pdata_buf A pointer to data buffer + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_uap_cmd_key_material(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, + IN t_u16 cmd_oid, IN t_void *pdata_buf) +{ + HostCmd_DS_802_11_KEY_MATERIAL *pkey_material = + &cmd->params.key_material; + mlan_ds_encrypt_key *pkey = (mlan_ds_encrypt_key *)pdata_buf; + mlan_status ret = MLAN_STATUS_SUCCESS; + sta_node *sta_ptr = MNULL; + + ENTER(); + if (!pkey) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_802_11_KEY_MATERIAL); + pkey_material->action = wlan_cpu_to_le16(cmd_action); + if (cmd_action == HostCmd_ACT_GEN_GET) { + cmd->size = + wlan_cpu_to_le16(sizeof(pkey_material->action) + + S_DS_GEN); + goto done; + } + memset(pmpriv->adapter, &pkey_material->key_param_set, 0, + sizeof(MrvlIEtype_KeyParamSetV2_t)); + if (pkey->key_flags & KEY_FLAG_REMOVE_KEY) { + pkey_material->action = + wlan_cpu_to_le16(HostCmd_ACT_GEN_REMOVE); + pkey_material->key_param_set.type = + wlan_cpu_to_le16(TLV_TYPE_KEY_PARAM_V2); + pkey_material->key_param_set.length = + wlan_cpu_to_le16(KEY_PARAMS_FIXED_LEN); + pkey_material->key_param_set.key_idx = + pkey->key_index & KEY_INDEX_MASK; + pkey_material->key_param_set.key_info = + wlan_cpu_to_le16(KEY_INFO_MCAST_KEY | + KEY_INFO_UCAST_KEY); + memcpy(pmpriv->adapter, pkey_material->key_param_set.mac_addr, + pkey->mac_addr, MLAN_MAC_ADDR_LENGTH); + cmd->size = + wlan_cpu_to_le16(sizeof(MrvlIEtypesHeader_t) + + S_DS_GEN + KEY_PARAMS_FIXED_LEN + + sizeof(pkey_material->action)); + PRINTM(MCMND, "Remove Key\n"); + goto done; + } + pkey_material->action = wlan_cpu_to_le16(HostCmd_ACT_GEN_SET); + pkey_material->key_param_set.key_idx = pkey->key_index & KEY_INDEX_MASK; + pkey_material->key_param_set.type = + wlan_cpu_to_le16(TLV_TYPE_KEY_PARAM_V2); + pkey_material->key_param_set.key_info = KEY_INFO_ENABLE_KEY; + memcpy(pmpriv->adapter, pkey_material->key_param_set.mac_addr, + pkey->mac_addr, MLAN_MAC_ADDR_LENGTH); + if (pkey->key_len <= MAX_WEP_KEY_SIZE) { + pkey_material->key_param_set.length = + wlan_cpu_to_le16(KEY_PARAMS_FIXED_LEN + + sizeof(wep_param_t)); + pkey_material->key_param_set.key_type = KEY_TYPE_ID_WEP; + pkey_material->key_param_set.key_info |= + KEY_INFO_MCAST_KEY | KEY_INFO_UCAST_KEY; + if (pkey_material->key_param_set.key_idx == + (pmpriv->wep_key_curr_index & KEY_INDEX_MASK)) + pkey_material->key_param_set.key_info |= + KEY_INFO_DEFAULT_KEY; + pkey_material->key_param_set.key_info = + wlan_cpu_to_le16(pkey_material->key_param_set.key_info); + pkey_material->key_param_set.key_params.wep.key_len = + wlan_cpu_to_le16(pkey->key_len); + memcpy(pmpriv->adapter, + pkey_material->key_param_set.key_params.wep.key, + pkey->key_material, pkey->key_len); + cmd->size = + wlan_cpu_to_le16(sizeof(MrvlIEtypesHeader_t) + + S_DS_GEN + KEY_PARAMS_FIXED_LEN + + sizeof(wep_param_t) + + sizeof(pkey_material->action)); + PRINTM(MCMND, "Set WEP Key\n"); + goto done; + } + if (pkey->key_flags & KEY_FLAG_GROUP_KEY) + pkey_material->key_param_set.key_info |= KEY_INFO_MCAST_KEY; + else + pkey_material->key_param_set.key_info |= KEY_INFO_UCAST_KEY; + if (pkey->key_flags & KEY_FLAG_AES_MCAST_IGTK) + pkey_material->key_param_set.key_info = KEY_INFO_CMAC_AES_KEY; + if (pkey->key_flags & KEY_FLAG_SET_TX_KEY) + pkey_material->key_param_set.key_info |= + KEY_INFO_TX_KEY | KEY_INFO_RX_KEY; + else + pkey_material->key_param_set.key_info |= KEY_INFO_TX_KEY; + if (pkey->is_wapi_key) { + pkey_material->key_param_set.key_type = KEY_TYPE_ID_WAPI; + memcpy(pmpriv->adapter, + pkey_material->key_param_set.key_params.wapi.pn, + pkey->pn, PN_SIZE); + pkey_material->key_param_set.key_params.wapi.key_len = + wlan_cpu_to_le16(MIN(WAPI_KEY_SIZE, pkey->key_len)); + memcpy(pmpriv->adapter, + pkey_material->key_param_set.key_params.wapi.key, + pkey->key_material, MIN(WAPI_KEY_SIZE, pkey->key_len)); + if (!pmpriv->sec_info.wapi_key_on) + pkey_material->key_param_set.key_info |= + KEY_INFO_DEFAULT_KEY; + if (pkey->key_flags & KEY_FLAG_GROUP_KEY) { + pmpriv->sec_info.wapi_key_on = MTRUE; + } else { + /* WAPI pairwise key: unicast */ + sta_ptr = + wlan_add_station_entry(pmpriv, pkey->mac_addr); + if (sta_ptr) { + PRINTM(MCMND, "station: wapi_key_on\n"); + sta_ptr->wapi_key_on = MTRUE; + } + } + pkey_material->key_param_set.key_info = + wlan_cpu_to_le16(pkey_material->key_param_set.key_info); + pkey_material->key_param_set.length = + wlan_cpu_to_le16(KEY_PARAMS_FIXED_LEN + + sizeof(wapi_param)); + cmd->size = + wlan_cpu_to_le16(sizeof(MrvlIEtypesHeader_t) + + S_DS_GEN + KEY_PARAMS_FIXED_LEN + + sizeof(wapi_param) + + sizeof(pkey_material->action)); + PRINTM(MCMND, "Set WAPI Key\n"); + goto done; + } + pkey_material->key_param_set.key_info |= KEY_INFO_DEFAULT_KEY; + pkey_material->key_param_set.key_info = + wlan_cpu_to_le16(pkey_material->key_param_set.key_info); + if (pkey->key_len == WPA_AES_KEY_LEN && + !(pkey->key_flags & KEY_FLAG_AES_MCAST_IGTK)) { + if (pkey-> + key_flags & (KEY_FLAG_RX_SEQ_VALID | KEY_FLAG_TX_SEQ_VALID)) + memcpy(pmpriv->adapter, + pkey_material->key_param_set.key_params.aes.pn, + pkey->pn, SEQ_MAX_SIZE); + pkey_material->key_param_set.key_type = KEY_TYPE_ID_AES; + pkey_material->key_param_set.key_params.aes.key_len = + wlan_cpu_to_le16(pkey->key_len); + memcpy(pmpriv->adapter, + pkey_material->key_param_set.key_params.aes.key, + pkey->key_material, pkey->key_len); + pkey_material->key_param_set.length = + wlan_cpu_to_le16(KEY_PARAMS_FIXED_LEN + + sizeof(aes_param)); + cmd->size = + wlan_cpu_to_le16(sizeof(MrvlIEtypesHeader_t) + + S_DS_GEN + KEY_PARAMS_FIXED_LEN + + sizeof(aes_param) + + sizeof(pkey_material->action)); + PRINTM(MCMND, "Set AES Key\n"); + goto done; + } + if (pkey->key_len == WPA_IGTK_KEY_LEN && + (pkey->key_flags & KEY_FLAG_AES_MCAST_IGTK)) { + if (pkey-> + key_flags & (KEY_FLAG_RX_SEQ_VALID | KEY_FLAG_TX_SEQ_VALID)) + memcpy(pmpriv->adapter, + pkey_material->key_param_set.key_params.cmac_aes. + ipn, pkey->pn, SEQ_MAX_SIZE); + pkey_material->key_param_set.key_info &= + ~(wlan_cpu_to_le16(KEY_INFO_MCAST_KEY)); + pkey_material->key_param_set.key_info |= + wlan_cpu_to_le16(KEY_INFO_AES_MCAST_IGTK); + pkey_material->key_param_set.key_type = KEY_TYPE_ID_AES_CMAC; + pkey_material->key_param_set.key_params.cmac_aes.key_len = + wlan_cpu_to_le16(pkey->key_len); + memcpy(pmpriv->adapter, + pkey_material->key_param_set.key_params.cmac_aes.key, + pkey->key_material, pkey->key_len); + pkey_material->key_param_set.length = + wlan_cpu_to_le16(KEY_PARAMS_FIXED_LEN + + sizeof(cmac_aes_param)); + cmd->size = + wlan_cpu_to_le16(sizeof(MrvlIEtypesHeader_t) + + S_DS_GEN + KEY_PARAMS_FIXED_LEN + + sizeof(cmac_aes_param) + + sizeof(pkey_material->action)); + PRINTM(MCMND, "Set CMAC AES Key\n"); + goto done; + } + if (pkey->key_len == WPA_TKIP_KEY_LEN) { + if (pkey-> + key_flags & (KEY_FLAG_RX_SEQ_VALID | KEY_FLAG_TX_SEQ_VALID)) + memcpy(pmpriv->adapter, + pkey_material->key_param_set.key_params.tkip.pn, + pkey->pn, SEQ_MAX_SIZE); + pkey_material->key_param_set.key_type = KEY_TYPE_ID_TKIP; + pkey_material->key_param_set.key_params.tkip.key_len = + wlan_cpu_to_le16(pkey->key_len); + memcpy(pmpriv->adapter, + pkey_material->key_param_set.key_params.tkip.key, + pkey->key_material, pkey->key_len); + pkey_material->key_param_set.length = + wlan_cpu_to_le16(KEY_PARAMS_FIXED_LEN + + sizeof(tkip_param)); + cmd->size = + wlan_cpu_to_le16(sizeof(MrvlIEtypesHeader_t) + + S_DS_GEN + KEY_PARAMS_FIXED_LEN + + sizeof(tkip_param) + + sizeof(pkey_material->action)); + PRINTM(MCMND, "Set TKIP Key\n"); + } +done: + LEAVE(); + return ret; +} + +/** + * @brief This function handles the command response of sta_list + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_uap_ret_sta_list(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf) +{ + HostCmd_DS_STA_LIST *sta_list = + (HostCmd_DS_STA_LIST *)&resp->params.sta_list; + mlan_ds_get_info *info; + MrvlIEtypes_sta_info_t *tlv = MNULL; + t_u8 i = 0; + sta_node *sta_ptr; + + ENTER(); + if (pioctl_buf) { + info = (mlan_ds_get_info *)pioctl_buf->pbuf; + info->param.sta_list.sta_count = + wlan_le16_to_cpu(sta_list->sta_count); + tlv = (MrvlIEtypes_sta_info_t *)((t_u8 *)sta_list + + sizeof(HostCmd_DS_STA_LIST)); + info->param.sta_list.sta_count = + MIN(info->param.sta_list.sta_count, MAX_NUM_CLIENTS); + for (i = 0; i < info->param.sta_list.sta_count; i++) { + memcpy(pmpriv->adapter, + info->param.sta_list.info[i].mac_address, + tlv->mac_address, MLAN_MAC_ADDR_LENGTH); + info->param.sta_list.info[i].power_mfg_status = + tlv->power_mfg_status; + info->param.sta_list.info[i].rssi = tlv->rssi; + sta_ptr = + wlan_get_station_entry(pmpriv, + tlv->mac_address); + if (sta_ptr) + info->param.sta_list.info[i].bandmode = + sta_ptr->bandmode; + else + info->param.sta_list.info[i].bandmode = 0xFF; + tlv++; + } + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** Fixed size of bss start event */ +#define BSS_START_EVENT_FIX_SIZE 12 + +/** + * @brief This function will search for the specific ie + * + * @param priv A pointer to mlan_private + * @param pevent A pointer to event buf + * + * @return N/A + */ +static void +wlan_check_uap_capability(pmlan_private priv, pmlan_buffer pevent) +{ + t_u16 tlv_type, tlv_len; + int tlv_buf_left = pevent->data_len - BSS_START_EVENT_FIX_SIZE; + MrvlIEtypesHeader_t *tlv = + (MrvlIEtypesHeader_t *)(pevent->pbuf + pevent->data_offset + + BSS_START_EVENT_FIX_SIZE); + const t_u8 wmm_oui[4] = { 0x00, 0x50, 0xf2, 0x02 }; + IEEEtypes_WmmParameter_t wmm_param_ie; + MrvlIEtypes_channel_band_t *pchan_info; + t_u8 event_buf[100]; + mlan_event *event = (mlan_event *)event_buf; + chan_band_info *pchan_band_info = (chan_band_info *) event->event_buf; + priv->wmm_enabled = MFALSE; + priv->pkt_fwd = MFALSE; + priv->is_11n_enabled = MFALSE; + + ENTER(); + + while (tlv_buf_left >= (int)sizeof(MrvlIEtypesHeader_t)) { + tlv_type = wlan_le16_to_cpu(tlv->type); + tlv_len = wlan_le16_to_cpu(tlv->len); + if ((sizeof(MrvlIEtypesHeader_t) + tlv_len) > + (unsigned int)tlv_buf_left) { + PRINTM(MERROR, "wrong tlv: tlvLen=%d, tlvBufLeft=%d\n", + tlv_len, tlv_buf_left); + break; + } + if (tlv_type == HT_CAPABILITY) { + DBG_HEXDUMP(MCMD_D, "HT_CAP tlv", tlv, + tlv_len + sizeof(MrvlIEtypesHeader_t)); + priv->is_11n_enabled = MTRUE; + } + if (tlv_type == VENDOR_SPECIFIC_221) { + if (!memcmp + (priv->adapter, + (t_u8 *)tlv + sizeof(MrvlIEtypesHeader_t), wmm_oui, + sizeof(wmm_oui))) { + DBG_HEXDUMP(MCMD_D, "wmm ie tlv", tlv, + tlv_len + + sizeof(MrvlIEtypesHeader_t)); + priv->wmm_enabled = MFALSE; + wlan_wmm_setup_ac_downgrade(priv); + priv->wmm_enabled = MTRUE; + memcpy(priv->adapter, &wmm_param_ie, + ((t_u8 *)tlv + 2), + sizeof(IEEEtypes_WmmParameter_t)); + wmm_param_ie.vend_hdr.len = (t_u8)tlv_len; + wmm_param_ie.vend_hdr.element_id = WMM_IE; + wlan_wmm_setup_queue_priorities(priv, + &wmm_param_ie); + } + } + if (tlv_type == TLV_TYPE_UAP_PKT_FWD_CTL) { + DBG_HEXDUMP(MCMD_D, "pkt_fwd tlv", tlv, + tlv_len + sizeof(MrvlIEtypesHeader_t)); + priv->pkt_fwd = + *((t_u8 *)tlv + sizeof(MrvlIEtypesHeader_t)); + PRINTM(MCMND, "pkt_fwd FW: 0x%x\n", priv->pkt_fwd); + if (priv->pkt_fwd & PKT_FWD_FW_BIT) + priv->pkt_fwd = MFALSE; + else + priv->pkt_fwd |= PKT_FWD_ENABLE_BIT; + PRINTM(MCMND, "pkt_fwd DRV: 0x%x\n", priv->pkt_fwd); + } + if (tlv_type == TLV_TYPE_UAP_CHAN_BAND_CONFIG) { + DBG_HEXDUMP(MCMD_D, "chan_band_config tlv", tlv, + tlv_len + sizeof(MrvlIEtypesHeader_t)); + pchan_info = (MrvlIEtypes_channel_band_t *)tlv; + priv->uap_channel = pchan_info->channel; + PRINTM(MCMND, "uap_channel FW: 0x%x\n", + priv->uap_channel); + event->bss_index = priv->bss_index; + event->event_id = MLAN_EVENT_ID_DRV_UAP_CHAN_INFO; + event->event_len = sizeof(chan_band_info); + memcpy(priv->adapter, (t_u8 *)&pchan_band_info->bandcfg, + (t_u8 *)&pchan_info->bandcfg, tlv_len); + if (priv->adapter->ecsa_enable) { + int ret; + t_u8 bandwidth = BW_20MHZ; + + MrvlIEtypes_chan_bw_oper_t chan_bw_oper; + chan_bw_oper.header.type = REGULATORY_CLASS; + chan_bw_oper.header.len = + sizeof(MrvlIEtypes_chan_bw_oper_t); + chan_bw_oper.ds_chan_bw_oper.channel = + pchan_info->channel; + + if (pchan_band_info->bandcfg.chanWidth == + CHAN_BW_40MHZ) + bandwidth = BW_40MHZ; + chan_bw_oper.ds_chan_bw_oper.bandwidth = + bandwidth; + + ret = wlan_prepare_cmd(priv, + HOST_CMD_APCMD_SYS_CONFIGURE, + HostCmd_ACT_GEN_SET, 0, + MNULL, &chan_bw_oper); + if (ret != MLAN_STATUS_SUCCESS && + ret != MLAN_STATUS_PENDING) { + PRINTM(MERROR, + "%s(): Could not set supported operating class IE for priv=%p [priv_bss_idx=%d]!\n", + __func__, priv, priv->bss_index); + } + } + } + + tlv_buf_left -= (sizeof(MrvlIEtypesHeader_t) + tlv_len); + tlv = (MrvlIEtypesHeader_t *)((t_u8 *)tlv + tlv_len + + sizeof(MrvlIEtypesHeader_t)); + } + if (priv->wmm_enabled == MFALSE) { + /* Since WMM is not enabled, setup the queues with the defaults */ + wlan_wmm_setup_queues(priv); + } + if (event->event_id == MLAN_EVENT_ID_DRV_UAP_CHAN_INFO) { + pchan_band_info->is_11n_enabled = priv->is_11n_enabled; + wlan_recv_event(priv, MLAN_EVENT_ID_DRV_UAP_CHAN_INFO, event); + } + + LEAVE(); +} + +/** + * @brief This function will update WAPI PN in statation assoc event + * + * @param priv A pointer to mlan_private + * @param pevent A pointer to event buf + * + * @return MFALSE + */ +static t_u32 +wlan_update_wapi_info_tlv(pmlan_private priv, pmlan_buffer pevent) +{ + t_u32 ret = MFALSE; + t_u16 tlv_type, tlv_len; + t_u32 tx_pn[4]; + t_u32 i = 0; + int tlv_buf_left = pevent->data_len - ASSOC_EVENT_FIX_SIZE; + MrvlIEtypesHeader_t *tlv = (MrvlIEtypesHeader_t *) + (pevent->pbuf + pevent->data_offset + ASSOC_EVENT_FIX_SIZE); + MrvlIEtypes_wapi_info_t *wapi_tlv = MNULL; + + ENTER(); + while (tlv_buf_left >= (int)sizeof(MrvlIEtypesHeader_t)) { + tlv_type = wlan_le16_to_cpu(tlv->type); + tlv_len = wlan_le16_to_cpu(tlv->len); + if ((sizeof(MrvlIEtypesHeader_t) + tlv_len) > + (unsigned int)tlv_buf_left) { + PRINTM(MERROR, "wrong tlv: tlvLen=%d, tlvBufLeft=%d\n", + tlv_len, tlv_buf_left); + break; + } + if (tlv_type == TLV_TYPE_AP_WAPI_INFO) { + wapi_tlv = (MrvlIEtypes_wapi_info_t *)tlv; + DBG_HEXDUMP(MCMD_D, "Fw:multicast_PN", + wapi_tlv->multicast_PN, PN_SIZE); + memcpy(priv->adapter, (t_u8 *)tx_pn, + wapi_tlv->multicast_PN, PN_SIZE); + for (i = 0; i < 4; i++) + tx_pn[i] = mlan_ntohl(tx_pn[i]); + memcpy(priv->adapter, wapi_tlv->multicast_PN, + (t_u8 *)tx_pn, PN_SIZE); + DBG_HEXDUMP(MCMD_D, "Host:multicast_PN", + wapi_tlv->multicast_PN, PN_SIZE); + break; + } + tlv_buf_left -= (sizeof(MrvlIEtypesHeader_t) + tlv_len); + tlv = (MrvlIEtypesHeader_t *)((t_u8 *)tlv + tlv_len + + sizeof(MrvlIEtypesHeader_t)); + } + LEAVE(); + + return ret; +} + +/** + * @brief This function send sta_assoc_event to moal + * payload with sta mac address and assoc ie. + * + * @param priv A pointer to mlan_private + * @param pevent A pointer to mlan_event buffer + * @param pbuf A pointer to mlan_buffer which has event content. + * + * @return MFALSE + */ +static t_u32 +wlan_process_sta_assoc_event(pmlan_private priv, mlan_event *pevent, + pmlan_buffer pmbuf) +{ + t_u32 ret = MFALSE; + t_u16 tlv_type, tlv_len; + t_u16 frame_control, frame_sub_type = 0; + t_u8 *assoc_req_ie = MNULL; + t_u8 ie_len = 0, assoc_ie_len = 0; + int tlv_buf_left = pmbuf->data_len - ASSOC_EVENT_FIX_SIZE; + MrvlIEtypesHeader_t *tlv = (MrvlIEtypesHeader_t *) + (pmbuf->pbuf + pmbuf->data_offset + ASSOC_EVENT_FIX_SIZE); + MrvlIETypes_MgmtFrameSet_t *mgmt_tlv = MNULL; + + ENTER(); + pevent->event_id = MLAN_EVENT_ID_UAP_FW_STA_CONNECT; + pevent->bss_index = priv->bss_index; + pevent->event_len = MLAN_MAC_ADDR_LENGTH; + memcpy(priv->adapter, pevent->event_buf, + pmbuf->pbuf + pmbuf->data_offset + 6, pevent->event_len); + while (tlv_buf_left >= (int)sizeof(MrvlIEtypesHeader_t)) { + tlv_type = wlan_le16_to_cpu(tlv->type); + tlv_len = wlan_le16_to_cpu(tlv->len); + if ((sizeof(MrvlIEtypesHeader_t) + tlv_len) > + (unsigned int)tlv_buf_left) { + PRINTM(MERROR, "wrong tlv: tlvLen=%d, tlvBufLeft=%d\n", + tlv_len, tlv_buf_left); + break; + } + if (tlv_type == TLV_TYPE_MGMT_FRAME) { + mgmt_tlv = (MrvlIETypes_MgmtFrameSet_t *)tlv; + memcpy(priv->adapter, &frame_control, + (t_u8 *)&(mgmt_tlv->frame_control), + sizeof(frame_control)); + frame_sub_type = + IEEE80211_GET_FC_MGMT_FRAME_SUBTYPE + (frame_control); + if ((mgmt_tlv->frame_control.type == 0) && + ((frame_sub_type == SUBTYPE_ASSOC_REQUEST) || + (frame_sub_type == SUBTYPE_REASSOC_REQUEST))) { + + if (frame_sub_type == SUBTYPE_ASSOC_REQUEST) + assoc_ie_len = + sizeof(IEEEtypes_AssocRqst_t); + else if (frame_sub_type == + SUBTYPE_REASSOC_REQUEST) + assoc_ie_len = + sizeof(IEEEtypes_ReAssocRqst_t); + + ie_len = tlv_len - + sizeof(IEEEtypes_FrameCtl_t) - + assoc_ie_len; + assoc_req_ie = + (t_u8 *)tlv + + sizeof(MrvlIETypes_MgmtFrameSet_t) + + assoc_ie_len; + memcpy(priv->adapter, + pevent->event_buf + pevent->event_len, + assoc_req_ie, ie_len); + pevent->event_len += ie_len; + break; + } + } + tlv_buf_left -= (sizeof(MrvlIEtypesHeader_t) + tlv_len); + tlv = (MrvlIEtypesHeader_t *)((t_u8 *)tlv + tlv_len + + sizeof(MrvlIEtypesHeader_t)); + } + PRINTM(MEVENT, "STA assoc event len=%d\n", pevent->event_len); + DBG_HEXDUMP(MCMD_D, "STA assoc event", pevent->event_buf, + pevent->event_len); + wlan_recv_event(priv, pevent->event_id, pevent); + LEAVE(); + return ret; +} + +/** + * @brief This function prepares command of uap operation control + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action the action: GET or SET + * @param pdata_buf A pointer to data buffer + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_uap_cmd_oper_ctrl(pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, IN t_void *pdata_buf) +{ + HostCmd_DS_UAP_OPER_CTRL *poper_ctl = + (HostCmd_DS_UAP_OPER_CTRL *) & cmd->params.uap_oper_ctrl; + mlan_ds_bss *bss = (mlan_ds_bss *)pdata_buf; + mlan_uap_oper_ctrl *uap_oper_ctrl = &bss->param.ap_oper_ctrl; + Band_Config_t *bandcfg = MNULL; + + ENTER(); + + cmd->command = wlan_cpu_to_le16(HOST_CMD_APCMD_OPER_CTRL); + cmd->size = + wlan_cpu_to_le16(sizeof(HostCmd_DS_UAP_OPER_CTRL) + S_DS_GEN); + poper_ctl->action = wlan_cpu_to_le16(cmd_action); + + if (cmd_action == HostCmd_ACT_GEN_SET) { + poper_ctl->ctrl = wlan_cpu_to_le16(uap_oper_ctrl->ctrl_value); + if (uap_oper_ctrl->ctrl_value == 2) { + poper_ctl->chan_opt = + wlan_cpu_to_le16(uap_oper_ctrl->chan_opt); + if (uap_oper_ctrl->chan_opt == 3) { + poper_ctl->channel_band.header.type = + wlan_cpu_to_le16 + (TLV_TYPE_UAP_CHAN_BAND_CONFIG); + poper_ctl->channel_band.header.len = + wlan_cpu_to_le16(sizeof + (MrvlIEtypes_channel_band_t) + - + sizeof + (MrvlIEtypesHeader_t)); + bandcfg = &poper_ctl->channel_band.bandcfg; + if (uap_oper_ctrl->channel > 14) + bandcfg->chanBand = BAND_5GHZ; + bandcfg->chanWidth = uap_oper_ctrl->band_cfg; + if (bandcfg->chanWidth) + bandcfg->chan2Offset = + wlan_get_second_channel_offset + (uap_oper_ctrl->channel); + bandcfg->scanMode = SCAN_MODE_MANUAL; + poper_ctl->channel_band.channel = + uap_oper_ctrl->channel; + } + } + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of uap operation control + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_uap_ret_oper_ctrl(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + IN mlan_ioctl_req *pioctl_buf) +{ + HostCmd_DS_UAP_OPER_CTRL *poper_ctl = + (HostCmd_DS_UAP_OPER_CTRL *) & resp->params.uap_oper_ctrl; + mlan_ds_bss *bss = MNULL; + mlan_uap_oper_ctrl *uap_oper_ctrl = MNULL; + Band_Config_t *bandcfg = MNULL; + + ENTER(); + + if (pioctl_buf && pioctl_buf->action == MLAN_ACT_GET) { + bss = (mlan_ds_bss *)pioctl_buf->pbuf; + uap_oper_ctrl = + (mlan_uap_oper_ctrl *) & bss->param.ap_oper_ctrl; + uap_oper_ctrl->ctrl_value = wlan_le16_to_cpu(poper_ctl->ctrl); + uap_oper_ctrl->chan_opt = wlan_le16_to_cpu(poper_ctl->chan_opt); + uap_oper_ctrl->channel = poper_ctl->channel_band.channel; + bandcfg = &poper_ctl->channel_band.bandcfg; + uap_oper_ctrl->band_cfg = bandcfg->chanWidth; + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/******************************************************** + Global Functions +********************************************************/ +/** + * @brief This function prepare the command before sending to firmware. + * + * @param priv A pointer to mlan_private structure + * @param cmd_no Command number + * @param cmd_action Command action: GET or SET + * @param cmd_oid Cmd oid: treated as sub command + * @param pioctl_buf A pointer to MLAN IOCTL Request buffer + * @param pdata_buf A pointer to information buffer + * @param pcmd_buf A pointer to cmd buf + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_ops_uap_prepare_cmd(IN t_void *priv, + IN t_u16 cmd_no, + IN t_u16 cmd_action, + IN t_u32 cmd_oid, + IN t_void *pioctl_buf, + IN t_void *pdata_buf, IN t_void *pcmd_buf) +{ + HostCmd_DS_COMMAND *cmd_ptr = (HostCmd_DS_COMMAND *)pcmd_buf; + mlan_private *pmpriv = (mlan_private *)priv; + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_ioctl_req pioctl_req = (mlan_ioctl_req *)pioctl_buf; + + ENTER(); + + /* Prepare command */ + switch (cmd_no) { + case HostCmd_CMD_SOFT_RESET: + case HOST_CMD_APCMD_BSS_STOP: + case HOST_CMD_APCMD_BSS_START: + case HOST_CMD_APCMD_SYS_INFO: + case HOST_CMD_APCMD_SYS_RESET: + case HOST_CMD_APCMD_STA_LIST: + cmd_ptr->command = wlan_cpu_to_le16(cmd_no); + cmd_ptr->size = wlan_cpu_to_le16(S_DS_GEN); +#ifdef DRV_EMBEDDED_AUTHENTICATOR + if ((HOST_CMD_APCMD_BSS_START == cmd_no) && + IsAuthenticatorEnabled(pmpriv->psapriv)) + AuthenticatorBssConfig(pmpriv->psapriv, MNULL, 1, 0, 0); +#endif + break; + case HOST_CMD_APCMD_SYS_CONFIGURE: + ret = wlan_uap_cmd_sys_configure(pmpriv, cmd_ptr, cmd_action, + (pmlan_ioctl_req)pioctl_buf, + pdata_buf); + break; + case HostCmd_CMD_802_11_PS_MODE_ENH: + ret = wlan_cmd_enh_power_mode(pmpriv, cmd_ptr, cmd_action, + (t_u16)cmd_oid, pdata_buf); + break; + case HostCmd_CMD_SDIO_GPIO_INT_CONFIG: + ret = wlan_cmd_sdio_gpio_int(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_FUNC_INIT: + if (pmpriv->adapter->hw_status == WlanHardwareStatusReset) + pmpriv->adapter->hw_status = + WlanHardwareStatusInitializing; + cmd_ptr->command = wlan_cpu_to_le16(cmd_no); + cmd_ptr->size = wlan_cpu_to_le16(S_DS_GEN); + break; + case HostCmd_CMD_FUNC_SHUTDOWN: + pmpriv->adapter->hw_status = WlanHardwareStatusReset; + cmd_ptr->command = wlan_cpu_to_le16(cmd_no); + cmd_ptr->size = wlan_cpu_to_le16(S_DS_GEN); + break; + case HostCmd_CMD_CFG_DATA: + ret = wlan_cmd_cfg_data(pmpriv, cmd_ptr, cmd_action, cmd_oid, + pdata_buf); + break; + case HostCmd_CMD_MAC_CONTROL: + ret = wlan_cmd_mac_control(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_802_11_SNMP_MIB: + ret = wlan_uap_cmd_snmp_mib(pmpriv, cmd_ptr, cmd_action, + cmd_oid, + (pmlan_ioctl_req)pioctl_buf, + pdata_buf); + break; + case HostCmd_CMD_802_11_GET_LOG: + ret = wlan_uap_cmd_802_11_get_log(pmpriv, cmd_ptr); + break; + case HostCmd_CMD_802_11D_DOMAIN_INFO: + ret = wlan_cmd_802_11d_domain_info(pmpriv, cmd_ptr, cmd_action); + break; + case HostCmd_CMD_CHAN_REPORT_REQUEST: + ret = wlan_11h_cmd_process(pmpriv, cmd_ptr, pdata_buf); + break; + case HOST_CMD_APCMD_STA_DEAUTH: + ret = wlan_uap_cmd_sta_deauth(pmpriv, cmd_ptr, pdata_buf); + break; + case HostCmd_CMD_802_11_KEY_MATERIAL: + ret = wlan_uap_cmd_key_material(pmpriv, cmd_ptr, cmd_action, + cmd_oid, pdata_buf); + break; + case HostCmd_CMD_GET_HW_SPEC: + ret = wlan_cmd_get_hw_spec(pmpriv, cmd_ptr); + break; + case HostCmd_CMD_SDIO_SP_RX_AGGR_CFG: + ret = wlan_cmd_sdio_rx_aggr_cfg(cmd_ptr, cmd_action, pdata_buf); + break; + case HostCmd_CMD_802_11_HS_CFG_ENH: + ret = wlan_uap_cmd_802_11_hs_cfg(pmpriv, cmd_ptr, cmd_action, + (hs_config_param *)pdata_buf); + break; + case HostCmd_CMD_HS_WAKEUP_REASON: + ret = wlan_cmd_hs_wakeup_reason(pmpriv, cmd_ptr, pdata_buf); + break; + case HostCmd_CMD_802_11_FW_WAKE_METHOD: + ret = wlan_cmd_802_11_fw_wakeup_method(pmpriv, cmd_ptr, + cmd_action, + (t_u16 *)pdata_buf); + break; + case HostCmd_CMD_802_11_ROBUSTCOEX: + ret = wlan_cmd_robustcoex(pmpriv, cmd_ptr, cmd_action, + (t_u16 *)pdata_buf); + break; + case HostCmd_CMD_RECONFIGURE_TX_BUFF: + ret = wlan_cmd_recfg_tx_buf(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_AMSDU_AGGR_CTRL: + ret = wlan_cmd_amsdu_aggr_ctrl(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_11N_CFG: + ret = wlan_cmd_11n_cfg(pmpriv, cmd_ptr, cmd_action, pdata_buf); + break; + case HostCmd_CMD_11N_ADDBA_REQ: + ret = wlan_cmd_11n_addba_req(pmpriv, cmd_ptr, pdata_buf); + break; + case HostCmd_CMD_11N_DELBA: + ret = wlan_cmd_11n_delba(pmpriv, cmd_ptr, pdata_buf); + break; + case HostCmd_CMD_11N_ADDBA_RSP: + ret = wlan_cmd_11n_addba_rspgen(pmpriv, cmd_ptr, pdata_buf); + break; + case HostCmd_CMD_REJECT_ADDBA_REQ: + ret = wlan_cmd_reject_addba_req(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_TX_BF_CFG: + ret = wlan_cmd_tx_bf_cfg(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; +#if defined(WIFI_DIRECT_SUPPORT) + case HostCmd_CMD_SET_BSS_MODE: + cmd_ptr->command = wlan_cpu_to_le16(cmd_no); + if (pdata_buf) + cmd_ptr->params.bss_mode.con_type = *(t_u8 *)pdata_buf; + else + cmd_ptr->params.bss_mode.con_type = + BSS_MODE_WIFIDIRECT_GO; + cmd_ptr->size = + wlan_cpu_to_le16(sizeof(HostCmd_DS_SET_BSS_MODE) + + S_DS_GEN); + ret = MLAN_STATUS_SUCCESS; + break; +#endif + case HostCmd_CMD_VERSION_EXT: + cmd_ptr->command = wlan_cpu_to_le16(cmd_no); + cmd_ptr->params.verext.version_str_sel = + (t_u8)(*((t_u32 *)pdata_buf)); + cmd_ptr->size = + wlan_cpu_to_le16(sizeof(HostCmd_DS_VERSION_EXT) + + S_DS_GEN); + ret = MLAN_STATUS_SUCCESS; + break; + case HostCmd_CMD_RX_MGMT_IND: + cmd_ptr->command = wlan_cpu_to_le16(cmd_no); + cmd_ptr->params.rx_mgmt_ind.action = + wlan_cpu_to_le16(cmd_action); + cmd_ptr->params.rx_mgmt_ind.mgmt_subtype_mask = + (t_u32)(*((t_u32 *)pdata_buf)); + cmd_ptr->size = + wlan_cpu_to_le16(sizeof(HostCmd_DS_RX_MGMT_IND) + + S_DS_GEN); + break; + case HostCmd_CMD_CFG_TX_DATA_PAUSE: + ret = wlan_uap_cmd_txdatapause(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_802_11_RADIO_CONTROL: + ret = wlan_cmd_802_11_radio_control(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_TX_RATE_CFG: + ret = wlan_cmd_tx_rate_cfg(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_802_11_TX_RATE_QUERY: + cmd_ptr->command = + wlan_cpu_to_le16(HostCmd_CMD_802_11_TX_RATE_QUERY); + cmd_ptr->size = + wlan_cpu_to_le16(sizeof(HostCmd_TX_RATE_QUERY) + + S_DS_GEN); + pmpriv->tx_rate = 0; + ret = MLAN_STATUS_SUCCESS; + break; + case HostCmd_CMD_802_11_REMAIN_ON_CHANNEL: + ret = wlan_cmd_remain_on_channel(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + +#ifdef WIFI_DIRECT_SUPPORT + case HOST_CMD_WIFI_DIRECT_MODE_CONFIG: + ret = wlan_cmd_wifi_direct_mode(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HOST_CMD_P2P_PARAMS_CONFIG: + ret = wlan_cmd_p2p_params_config(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; +#endif + case HostCmd_CMD_MAC_REG_ACCESS: + case HostCmd_CMD_BBP_REG_ACCESS: + case HostCmd_CMD_RF_REG_ACCESS: + case HostCmd_CMD_CAU_REG_ACCESS: + case HostCmd_CMD_802_11_EEPROM_ACCESS: + ret = wlan_cmd_reg_access(cmd_ptr, cmd_action, pdata_buf); + break; + case HostCmd_CMD_MEM_ACCESS: + ret = wlan_cmd_mem_access(cmd_ptr, cmd_action, pdata_buf); + break; + case HostCmd_CMD_WMM_QUEUE_CONFIG: + ret = wlan_cmd_wmm_queue_config(pmpriv, cmd_ptr, pdata_buf); + break; + case HostCmd_CMD_MULTI_CHAN_CONFIG: + ret = wlan_cmd_multi_chan_cfg(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_MULTI_CHAN_POLICY: + ret = wlan_cmd_multi_chan_policy(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_DRCS_CONFIG: + ret = wlan_cmd_drcs_cfg(pmpriv, cmd_ptr, cmd_action, pdata_buf); + break; +#ifdef RX_PACKET_COALESCE + case HostCmd_CMD_RX_PKT_COALESCE_CFG: + ret = wlan_cmd_rx_pkt_coalesce_cfg(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; +#endif + case HOST_CMD_APCMD_OPER_CTRL: + ret = wlan_uap_cmd_oper_ctrl(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + + case HostCmd_CMD_INDEPENDENT_RESET_CFG: + ret = wlan_cmd_ind_rst_cfg(cmd_ptr, cmd_action, pdata_buf); + break; + case HostCmd_CMD_GET_TSF: + ret = wlan_cmd_get_tsf(pmpriv, cmd_ptr, cmd_action); + break; + + case HostCmd_CMD_802_11_PS_INACTIVITY_TIMEOUT: + ret = wlan_cmd_ps_inactivity_timeout(pmpriv, cmd_ptr, + cmd_action, pdata_buf); + break; + + case HostCmd_CMD_HOST_CLOCK_CFG: + ret = wlan_cmd_host_clock_cfg(cmd_ptr, cmd_action, pdata_buf); + break; + case HostCmd_CMD_CHAN_REGION_CFG: + cmd_ptr->command = wlan_cpu_to_le16(cmd_no); + cmd_ptr->size = + wlan_cpu_to_le16(sizeof(HostCmd_DS_CHAN_REGION_CFG) + + S_DS_GEN); + cmd_ptr->params.reg_cfg.action = wlan_cpu_to_le16(cmd_action); + break; + case HostCmd_CMD_802_11_NET_MONITOR: + ret = wlan_cmd_net_monitor(cmd_ptr, cmd_action, pdata_buf); + break; +#if defined(SYSKT_MULTI) && defined(OOB_WAKEUP) || defined(SUSPEND_SDIO_PULL_DOWN) + case HostCmd_CMD_SDIO_PULL_CTRL: + ret = wlan_cmd_sdio_pull_ctl(pmpriv, cmd_ptr, cmd_action); + break; +#endif + case HostCmd_CMD_FW_DUMP_EVENT: + ret = wlan_cmd_fw_dump_event(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_BOOT_SLEEP: + ret = wlan_cmd_boot_sleep(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + default: + PRINTM(MERROR, "PREP_CMD: unknown command- %#x\n", cmd_no); + if (pioctl_req) + pioctl_req->status_code = MLAN_ERROR_CMD_INVALID; + ret = MLAN_STATUS_FAILURE; + break; + } + LEAVE(); + return ret; +} + +/** + * @brief This function handles the AP mode command response + * + * @param priv A pointer to mlan_private structure + * @param cmdresp_no cmd no + * @param pcmd_buf cmdresp buf + * @param pioctl A pointer to ioctl buf + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_ops_uap_process_cmdresp(IN t_void *priv, + IN t_u16 cmdresp_no, + IN t_void *pcmd_buf, IN t_void *pioctl) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = (mlan_private *)priv; + HostCmd_DS_COMMAND *resp = (HostCmd_DS_COMMAND *)pcmd_buf; + mlan_ioctl_req *pioctl_buf = (mlan_ioctl_req *)pioctl; + mlan_adapter *pmadapter = pmpriv->adapter; + int ctr; + wlan_dfs_device_state_t *pstate_dfs = (wlan_dfs_device_state_t *) + &pmpriv->adapter->state_dfs; + t_u32 sec, usec; + ENTER(); + + /* If the command is not successful, cleanup and return failure */ + if (resp->result != HostCmd_RESULT_OK) { + ret = uap_process_cmdresp_error(pmpriv, resp, pioctl_buf); + LEAVE(); + return ret; + } + + /* Command successful, handle response */ + switch (cmdresp_no) { + case HOST_CMD_APCMD_BSS_STOP: + pmpriv->uap_bss_started = MFALSE; + /* Timestamp update is required because bss_start after skip_cac + * enabled should not select non-current channel just because + * timestamp got expired + */ + if (!pmpriv->intf_state_11h.is_11h_host && + !pstate_dfs->dfs_check_pending && + pstate_dfs->dfs_check_channel) { + pmpriv->adapter->callbacks.moal_get_system_time(pmpriv-> + adapter-> + pmoal_handle, + &sec, + &usec); + pstate_dfs->dfs_report_time_sec = sec; + } + if (pmpriv->intf_state_11h.is_11h_host) + pmpriv->intf_state_11h.tx_disabled = MFALSE; + else + wlan_11h_check_update_radar_det_state(pmpriv); + + if (pmpriv->adapter->state_rdh.stage == RDH_STOP_INTFS) + wlan_11h_radar_detected_callback((t_void *)pmpriv); + wlan_coex_ampdu_rxwinsize(pmadapter); +#ifdef DRV_EMBEDDED_AUTHENTICATOR + if (IsAuthenticatorEnabled(pmpriv->psapriv)) { + AuthenticatorBssConfig(pmpriv->psapriv, MNULL, 0, 1, 0); + AuthenticatorkeyClear(pmpriv->psapriv); + } +#endif + pmpriv->uap_host_based = MFALSE; + break; + case HOST_CMD_APCMD_BSS_START: + if (!pmpriv->intf_state_11h.is_11h_host && + pmpriv->adapter->state_rdh.stage == RDH_RESTART_INTFS) + wlan_11h_radar_detected_callback((t_void *)pmpriv); + /* Stop pps_uapsd_mode once bss_start */ + pmpriv->adapter->tx_lock_flag = MFALSE; + pmpriv->adapter->pps_uapsd_mode = MFALSE; + pmpriv->adapter->delay_null_pkt = MFALSE; + /* Clear AMSDU statistics */ + pmpriv->amsdu_rx_cnt = 0; + pmpriv->amsdu_tx_cnt = 0; + pmpriv->msdu_in_rx_amsdu_cnt = 0; + pmpriv->msdu_in_tx_amsdu_cnt = 0; + break; + case HOST_CMD_APCMD_SYS_RESET: + pmpriv->uap_bss_started = MFALSE; + pmpriv->uap_host_based = MFALSE; +#ifdef DRV_EMBEDDED_AUTHENTICATOR + AuthenitcatorInitBssConfig(pmpriv->psapriv); +#endif + ret = wlan_uap_ret_sys_reset(pmpriv, resp, pioctl_buf); + wlan_11h_check_update_radar_det_state(pmpriv); + wlan_coex_ampdu_rxwinsize(pmadapter); + break; + case HOST_CMD_APCMD_SYS_INFO: + break; + case HOST_CMD_APCMD_SYS_CONFIGURE: + ret = wlan_uap_ret_sys_config(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_802_11_PS_MODE_ENH: + ret = wlan_ret_enh_power_mode(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_SDIO_GPIO_INT_CONFIG: + break; + case HostCmd_CMD_FUNC_INIT: + case HostCmd_CMD_FUNC_SHUTDOWN: + break; + case HostCmd_CMD_802_11_SNMP_MIB: + ret = wlan_uap_ret_snmp_mib(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_802_11_GET_LOG: + ret = wlan_uap_ret_get_log(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_802_11D_DOMAIN_INFO: + ret = wlan_ret_802_11d_domain_info(pmpriv, resp); + break; + case HostCmd_CMD_CHAN_REPORT_REQUEST: + ret = wlan_11h_cmdresp_process(pmpriv, resp); + break; + case HOST_CMD_APCMD_STA_DEAUTH: + break; + case HostCmd_CMD_802_11_KEY_MATERIAL: + break; + case HOST_CMD_APCMD_STA_LIST: + ret = wlan_uap_ret_sta_list(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_GET_HW_SPEC: + ret = wlan_ret_get_hw_spec(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_SDIO_SP_RX_AGGR_CFG: + ret = wlan_ret_sdio_rx_aggr_cfg(pmpriv, resp); + break; + case HostCmd_CMD_CFG_DATA: + ret = wlan_ret_cfg_data(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_MAC_CONTROL: + ret = wlan_ret_mac_control(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_802_11_HS_CFG_ENH: + ret = wlan_ret_802_11_hs_cfg(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_HS_WAKEUP_REASON: + ret = wlan_ret_hs_wakeup_reason(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_802_11_FW_WAKE_METHOD: + ret = wlan_ret_fw_wakeup_method(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_802_11_ROBUSTCOEX: + break; + case HostCmd_CMD_11N_ADDBA_REQ: + ret = wlan_ret_11n_addba_req(pmpriv, resp); + break; + case HostCmd_CMD_11N_DELBA: + ret = wlan_ret_11n_delba(pmpriv, resp); + break; + case HostCmd_CMD_11N_ADDBA_RSP: + ret = wlan_ret_11n_addba_resp(pmpriv, resp); + break; + case HostCmd_CMD_SET_BSS_MODE: + break; + case HostCmd_CMD_802_11_NET_MONITOR: + ret = wlan_ret_net_monitor(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_RECONFIGURE_TX_BUFF: + wlan_set_tx_pause_flag(pmpriv, MFALSE); + + pmadapter->tx_buf_size = + (t_u16)wlan_le16_to_cpu(resp->params.tx_buf.buff_size); + pmadapter->tx_buf_size = + (pmadapter->tx_buf_size / MLAN_SDIO_BLOCK_SIZE) * + MLAN_SDIO_BLOCK_SIZE; + pmadapter->curr_tx_buf_size = pmadapter->tx_buf_size; + pmadapter->mp_end_port = + wlan_le16_to_cpu(resp->params.tx_buf.mp_end_port); + pmadapter->mp_data_port_mask = DATA_PORT_MASK; + + for (ctr = 1; ctr <= MAX_PORT - pmadapter->mp_end_port; ctr++) { + pmadapter->mp_data_port_mask &= + ~(1 << (MAX_PORT - ctr)); + } + pmadapter->curr_wr_port = 0; + pmadapter->mpa_tx.pkt_aggr_limit = + MIN(SDIO_MP_AGGR_DEF_PKT_LIMIT, + (pmadapter->mp_end_port >> 1)); + PRINTM(MCMND, "end port %d, data port mask %x\n", + wlan_le16_to_cpu(resp->params.tx_buf.mp_end_port), + pmadapter->mp_data_port_mask); + PRINTM(MCMND, "max_tx_buf_size=%d, tx_buf_size=%d\n", + pmadapter->max_tx_buf_size, pmadapter->tx_buf_size); + break; + case HostCmd_CMD_AMSDU_AGGR_CTRL: + ret = wlan_ret_amsdu_aggr_ctrl(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_11N_CFG: + ret = wlan_ret_11n_cfg(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_REJECT_ADDBA_REQ: + ret = wlan_ret_reject_addba_req(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_TX_BF_CFG: + ret = wlan_ret_tx_bf_cfg(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_VERSION_EXT: + ret = wlan_ret_ver_ext(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_RX_MGMT_IND: + ret = wlan_ret_rx_mgmt_ind(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_CFG_TX_DATA_PAUSE: + ret = wlan_uap_ret_txdatapause(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_802_11_RADIO_CONTROL: + ret = wlan_ret_802_11_radio_control(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_TX_RATE_CFG: + ret = wlan_ret_tx_rate_cfg(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_802_11_TX_RATE_QUERY: + ret = wlan_ret_802_11_tx_rate_query(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_802_11_REMAIN_ON_CHANNEL: + ret = wlan_ret_remain_on_channel(pmpriv, resp, pioctl_buf); + break; +#ifdef WIFI_DIRECT_SUPPORT + case HOST_CMD_WIFI_DIRECT_MODE_CONFIG: + ret = wlan_ret_wifi_direct_mode(pmpriv, resp, pioctl_buf); + break; + case HOST_CMD_P2P_PARAMS_CONFIG: + ret = wlan_ret_p2p_params_config(pmpriv, resp, pioctl_buf); + break; +#endif + case HostCmd_CMD_MAC_REG_ACCESS: + case HostCmd_CMD_BBP_REG_ACCESS: + case HostCmd_CMD_RF_REG_ACCESS: + case HostCmd_CMD_CAU_REG_ACCESS: + case HostCmd_CMD_802_11_EEPROM_ACCESS: + ret = wlan_ret_reg_access(pmpriv->adapter, cmdresp_no, resp, + pioctl_buf); + break; + case HostCmd_CMD_MEM_ACCESS: + ret = wlan_ret_mem_access(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_WMM_QUEUE_CONFIG: + ret = wlan_ret_wmm_queue_config(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_MULTI_CHAN_CONFIG: + ret = wlan_ret_multi_chan_cfg(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_MULTI_CHAN_POLICY: + ret = wlan_ret_multi_chan_policy(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_DRCS_CONFIG: + ret = wlan_ret_drcs_cfg(pmpriv, resp, pioctl_buf); + break; +#ifdef RX_PACKET_COALESCE + case HostCmd_CMD_RX_PKT_COALESCE_CFG: + ret = wlan_ret_rx_pkt_coalesce_cfg(pmpriv, resp, pioctl_buf); + break; +#endif + case HOST_CMD_APCMD_OPER_CTRL: + ret = wlan_uap_ret_oper_ctrl(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_INDEPENDENT_RESET_CFG: + ret = wlan_ret_ind_rst_cfg(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_802_11_PS_INACTIVITY_TIMEOUT: + break; + case HostCmd_CMD_GET_TSF: + ret = wlan_ret_get_tsf(pmpriv, resp, pioctl_buf); + break; + + case HostCmd_CMD_HOST_CLOCK_CFG: + ret = wlan_ret_host_clock_cfg(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_CHAN_REGION_CFG: + ret = wlan_ret_chan_region_cfg(pmpriv, resp, pioctl_buf); + break; +#if defined(SYSKT_MULTI) && defined(OOB_WAKEUP) || defined(SUSPEND_SDIO_PULL_DOWN) + case HostCmd_CMD_SDIO_PULL_CTRL: + break; +#endif + case HostCmd_CMD_BOOT_SLEEP: + ret = wlan_ret_boot_sleep(pmpriv, resp, pioctl_buf); + break; + default: + PRINTM(MERROR, "CMD_RESP: Unknown command response %#x\n", + resp->command); + if (pioctl_buf) + pioctl_buf->status_code = MLAN_ERROR_CMD_RESP_FAIL; + break; + } + LEAVE(); + return ret; +} + +/** + * @brief This function handles events generated by firmware + * + * @param priv A pointer to mlan_private structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_ops_uap_process_event(IN t_void *priv) +{ + pmlan_private pmpriv = (pmlan_private)priv; + pmlan_adapter pmadapter = pmpriv->adapter; + pmlan_callbacks pcb = &pmadapter->callbacks; + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u32 eventcause = pmadapter->event_cause; + pmlan_buffer pmbuf = pmadapter->pmlan_buffer_event; + t_u8 *event_buf = MNULL; + mlan_event *pevent = MNULL; + t_u8 sta_addr[MLAN_MAC_ADDR_LENGTH]; + sta_node *sta_ptr = MNULL; + t_u8 i = 0; + t_u8 channel = 0; + MrvlIEtypes_channel_band_t *pchan_info = MNULL; + chan_band_info *pchan_band_info = MNULL; + event_exceed_max_p2p_conn *event_excd_p2p = MNULL; + + ENTER(); + + /* Event length check */ + if (pmbuf && (pmbuf->data_len - sizeof(eventcause)) > MAX_EVENT_SIZE) { + pmbuf->status_code = MLAN_ERROR_PKT_SIZE_INVALID; + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + /* Allocate memory for event buffer */ + ret = pcb->moal_malloc(pmadapter->pmoal_handle, MAX_EVENT_SIZE, + MLAN_MEM_DEF, &event_buf); + if ((ret != MLAN_STATUS_SUCCESS) || !event_buf) { + PRINTM(MERROR, "Could not allocate buffer for event buf\n"); + if (pmbuf) + pmbuf->status_code = MLAN_ERROR_NO_MEM; + goto done; + } + pevent = (pmlan_event)event_buf; + memset(pmadapter, &pevent->event_id, 0, sizeof(pevent->event_id)); + + if (eventcause != EVENT_PS_SLEEP && eventcause != EVENT_PS_AWAKE && + pmbuf->data_len > sizeof(eventcause)) + DBG_HEXDUMP(MEVT_D, "EVENT", pmbuf->pbuf + pmbuf->data_offset, + pmbuf->data_len); + + switch (eventcause) { + case EVENT_MICRO_AP_BSS_START: + PRINTM(MEVENT, "EVENT: MICRO_AP_BSS_START\n"); + pmpriv->uap_bss_started = MTRUE; + memcpy(pmadapter, pmpriv->curr_addr, pmadapter->event_body + 2, + MLAN_MAC_ADDR_LENGTH); + pevent->event_id = MLAN_EVENT_ID_UAP_FW_BSS_START; + wlan_check_uap_capability(pmpriv, pmbuf); + wlan_coex_ampdu_rxwinsize(pmadapter); +#ifdef DRV_EMBEDDED_AUTHENTICATOR + if (IsAuthenticatorEnabled(pmpriv->psapriv)) + AuthenticatorKeyMgmtInit(pmpriv->psapriv, + pmpriv->curr_addr); +#endif + break; + case EVENT_MICRO_AP_BSS_ACTIVE: + PRINTM(MEVENT, "EVENT: MICRO_AP_BSS_ACTIVE\n"); + pmpriv->media_connected = MTRUE; + pmpriv->port_open = MTRUE; + pevent->event_id = MLAN_EVENT_ID_UAP_FW_BSS_ACTIVE; + break; + case EVENT_MICRO_AP_BSS_IDLE: + PRINTM(MEVENT, "EVENT: MICRO_AP_BSS_IDLE\n"); + pevent->event_id = MLAN_EVENT_ID_UAP_FW_BSS_IDLE; + pmpriv->media_connected = MFALSE; + wlan_clean_txrx(pmpriv); + wlan_notify_station_deauth(pmpriv); + wlan_delete_station_list(pmpriv); + pmpriv->port_open = MFALSE; + pmpriv->tx_pause = MFALSE; + break; + case EVENT_PS_AWAKE: + PRINTM(MINFO, "EVENT: AWAKE\n"); + PRINTM(MEVENT, "||"); + /* Handle unexpected PS AWAKE event */ + if (pmadapter->ps_state == PS_STATE_SLEEP_CFM) + break; + pmadapter->pm_wakeup_card_req = MFALSE; + pmadapter->pm_wakeup_fw_try = MFALSE; + pmadapter->ps_state = PS_STATE_AWAKE; + + break; + case EVENT_PS_SLEEP: + PRINTM(MINFO, "EVENT: SLEEP\n"); + PRINTM(MEVENT, "__"); + /* Handle unexpected PS SLEEP event */ + if (pmadapter->ps_state == PS_STATE_SLEEP_CFM) + break; + pmadapter->ps_state = PS_STATE_PRE_SLEEP; + wlan_check_ps_cond(pmadapter); + break; + case EVENT_MICRO_AP_STA_ASSOC: + wlan_process_sta_assoc_event(pmpriv, pevent, pmbuf); + memcpy(pmadapter, sta_addr, pmadapter->event_body + 2, + MLAN_MAC_ADDR_LENGTH); + sta_ptr = wlan_add_station_entry(pmpriv, sta_addr); + PRINTM(MMSG, "wlan: EVENT: MICRO_AP_STA_ASSOC " MACSTR "\n", + MAC2STR(sta_addr)); + if (!sta_ptr) + break; + if (pmpriv->is_11n_enabled +#ifdef DRV_EMBEDDED_AUTHENTICATOR + || IsAuthenticatorEnabled(pmpriv->psapriv) +#endif + ) { + wlan_check_sta_capability(pmpriv, pmbuf, sta_ptr); + for (i = 0; i < MAX_NUM_TID; i++) { + if (sta_ptr->is_11n_enabled) + sta_ptr->ampdu_sta[i] = + pmpriv->aggr_prio_tbl[i]. + ampdu_user; + else + sta_ptr->ampdu_sta[i] = + BA_STREAM_NOT_ALLOWED; + } + memset(pmadapter, sta_ptr->rx_seq, 0xff, + sizeof(sta_ptr->rx_seq)); + } + if (pmpriv->sec_info.wapi_enabled) + wlan_update_wapi_info_tlv(pmpriv, pmbuf); +#ifdef DRV_EMBEDDED_AUTHENTICATOR + /**enter authenticator*/ + if (IsAuthenticatorEnabled(pmpriv->psapriv)) + AuthenticatorSendEapolPacket(pmpriv->psapriv, + sta_ptr-> + cm_connectioninfo); +#endif + pevent->event_id = MLAN_EVENT_ID_DRV_PASSTHRU; + break; + case EVENT_MICRO_AP_STA_DEAUTH: + pevent->event_id = MLAN_EVENT_ID_UAP_FW_STA_DISCONNECT; + pevent->bss_index = pmpriv->bss_index; + pevent->event_len = pmbuf->data_len - 4; + /* skip event length field */ + memcpy(pmadapter, (t_u8 *)pevent->event_buf, + pmbuf->pbuf + pmbuf->data_offset + 4, pevent->event_len); + wlan_recv_event(pmpriv, pevent->event_id, pevent); + memcpy(pmadapter, sta_addr, pmadapter->event_body + 2, + MLAN_MAC_ADDR_LENGTH); + PRINTM(MMSG, "wlan: EVENT: MICRO_AP_STA_DEAUTH " MACSTR "\n", + MAC2STR(sta_addr)); + if (pmpriv->is_11n_enabled) { + wlan_cleanup_reorder_tbl(pmpriv, sta_addr); + wlan_11n_cleanup_txbastream_tbl(pmpriv, sta_addr); + } + wlan_wmm_delete_peer_ralist(pmpriv, sta_addr); + wlan_delete_station_entry(pmpriv, sta_addr); + pevent->event_id = MLAN_EVENT_ID_DRV_PASSTHRU; + break; + case EVENT_HS_ACT_REQ: + PRINTM(MEVENT, "EVENT: HS_ACT_REQ\n"); + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_802_11_HS_CFG_ENH, 0, + 0, MNULL, MNULL); + break; + case EVENT_ADDBA: + PRINTM(MEVENT, "EVENT: ADDBA Request\n"); + if (pmpriv->media_connected == MTRUE) + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_11N_ADDBA_RSP, + HostCmd_ACT_GEN_SET, 0, MNULL, + pmadapter->event_body); + else + PRINTM(MERROR, + "Ignore ADDBA Request event in BSS idle state\n"); + break; + case EVENT_DELBA: + PRINTM(MEVENT, "EVENT: DELBA Request\n"); + if (pmpriv->media_connected == MTRUE) + wlan_11n_delete_bastream(pmpriv, pmadapter->event_body); + else + PRINTM(MERROR, + "Ignore DELBA Request event in BSS idle state\n"); + break; + case EVENT_BA_STREAM_TIMEOUT: + PRINTM(MEVENT, "EVENT: BA Stream timeout\n"); + if (pmpriv->media_connected == MTRUE) + wlan_11n_ba_stream_timeout(pmpriv, + (HostCmd_DS_11N_BATIMEOUT *) + pmadapter->event_body); + else + PRINTM(MERROR, + "Ignore BA Stream timeout event in BSS idle state\n"); + break; + case EVENT_RXBA_SYNC: + PRINTM(MEVENT, "EVENT: RXBA_SYNC\n"); + wlan_11n_rxba_sync_event(pmpriv, pmadapter->event_body, + pmbuf->data_len - sizeof(eventcause)); + break; + case EVENT_AMSDU_AGGR_CTRL: + PRINTM(MEVENT, "EVENT: AMSDU_AGGR_CTRL %d\n", + *(t_u16 *)pmadapter->event_body); + pmadapter->tx_buf_size = + MIN(pmadapter->curr_tx_buf_size, + wlan_le16_to_cpu(*(t_u16 *)pmadapter->event_body)); + PRINTM(MEVENT, "tx_buf_size %d\n", pmadapter->tx_buf_size); + break; + case EVENT_TX_DATA_PAUSE: + PRINTM(MEVENT, "EVENT: TX_DATA_PAUSE\n"); + wlan_process_tx_pause_event(priv, pmbuf); + break; + case EVENT_RADAR_DETECTED: + PRINTM(MEVENT, "EVENT: Radar Detected\n"); + + /* Send as passthru first, this event can cause other events */ + memset(pmadapter, event_buf, 0x00, MAX_EVENT_SIZE); + pevent->bss_index = pmpriv->bss_index; + pevent->event_id = MLAN_EVENT_ID_DRV_PASSTHRU; + pevent->event_len = pmbuf->data_len; + memcpy(pmadapter, (t_u8 *)pevent->event_buf, + pmbuf->pbuf + pmbuf->data_offset, pevent->event_len); + wlan_recv_event(pmpriv, pevent->event_id, pevent); + pevent->event_id = 0; /* clear to avoid resending at end of fcn */ + + if (!pmpriv->intf_state_11h.is_11h_host) { + if (pmadapter->state_rdh.stage == RDH_OFF) { + pmadapter->state_rdh.stage = RDH_CHK_INTFS; + wlan_11h_radar_detected_handling(pmadapter, + pmpriv); + if (pmpriv->uap_host_based) + wlan_recv_event(priv, + MLAN_EVENT_ID_FW_RADAR_DETECTED, + MNULL); + } else { + PRINTM(MEVENT, + "Ignore Event Radar Detected - handling" + " already in progress.\n"); + } + } else { + +#ifdef DFS_TESTING_SUPPORT + if (pmpriv->adapter->dfs_test_params. + no_channel_change_on_radar || + pmpriv->adapter->dfs_test_params. + fixed_new_channel_on_radar) { + if (pmadapter->state_rdh.stage == RDH_OFF || + pmadapter->state_rdh.stage == + RDH_SET_CUSTOM_IE) { + pmadapter->state_rdh.stage = + RDH_CHK_INTFS; + wlan_11h_radar_detected_handling + (pmadapter, pmpriv); + } else + PRINTM(MEVENT, + "Ignore Event Radar Detected - handling already in progress.\n"); + } else { +#endif + pmpriv->intf_state_11h.tx_disabled = MTRUE; + wlan_recv_event(priv, + MLAN_EVENT_ID_FW_RADAR_DETECTED, + MNULL); + +#ifdef DFS_TESTING_SUPPORT + } +#endif + } + + break; + case EVENT_CHANNEL_REPORT_RDY: + PRINTM(MEVENT, "EVENT: Channel Report Ready\n"); + memset(pmadapter, event_buf, 0x00, MAX_EVENT_SIZE); + /* Setup event buffer */ + pevent->bss_index = pmpriv->bss_index; + pevent->event_id = MLAN_EVENT_ID_FW_CHANNEL_REPORT_RDY; + pevent->event_len = pmbuf->data_len - sizeof(eventcause); + /* Copy event data */ + memcpy(pmadapter, (t_u8 *)pevent->event_buf, + pmbuf->pbuf + pmbuf->data_offset + sizeof(eventcause), + pevent->event_len); + /* Handle / pass event data, and free buffer */ + ret = wlan_11h_handle_event_chanrpt_ready(pmpriv, pevent); + + if (pmpriv->intf_state_11h.is_11h_host) { + *((t_u8 *)pevent->event_buf) = + pmpriv->adapter->state_dfs.dfs_radar_found; + wlan_recv_event(pmpriv, + MLAN_EVENT_ID_FW_CHANNEL_REPORT_RDY, + pevent); + } else { + /* Send up this Event to unblock MOAL waitqueue */ + wlan_recv_event(pmpriv, MLAN_EVENT_ID_DRV_MEAS_REPORT, + MNULL); + } + pevent->event_id = MLAN_EVENT_ID_DRV_PASSTHRU; + break; + case EVENT_CHANNEL_SWITCH: + pchan_info = + (MrvlIEtypes_channel_band_t *)(pmadapter->event_body); + channel = pchan_info->channel; + PRINTM(MEVENT, "EVENT: CHANNEL_SWITCH new channel %d\n", + channel); + pmpriv->uap_channel = channel; + + if ((pmpriv->adapter->state_rdh.stage != RDH_OFF && + !pmpriv->intf_state_11h.is_11h_host) +#ifdef DFS_TESTING_SUPPORT + || pmpriv->adapter->dfs_test_params. + no_channel_change_on_radar || + pmpriv->adapter->dfs_test_params.fixed_new_channel_on_radar +#endif + ) { + /* Handle embedded DFS */ + if (pmpriv->adapter->state_rdh.stage == + RDH_SET_CUSTOM_IE) { + pmadapter->state_rdh.stage = + RDH_RESTART_TRAFFIC; + wlan_11h_radar_detected_handling(pmadapter, + pmpriv); + } + } else { + /* Handle Host-based DFS and non-DFS(normal uap) case */ + pmpriv->intf_state_11h.tx_disabled = MFALSE; + memset(pmadapter, event_buf, 0x00, MAX_EVENT_SIZE); + /* Setup event buffer */ + pevent->bss_index = pmpriv->bss_index; + pevent->event_id = + MLAN_EVENT_ID_FW_CHAN_SWITCH_COMPLETE; + pevent->event_len = sizeof(chan_band_info); + pchan_band_info = (chan_band_info *) pevent->event_buf; + /* Copy event data */ + memcpy(pmadapter, (t_u8 *)&pchan_band_info->bandcfg, + (t_u8 *)&pchan_info->bandcfg, + sizeof(pchan_info->bandcfg)); + pchan_band_info->channel = pchan_info->channel; + pchan_band_info->is_11n_enabled = + pmpriv->is_11n_enabled; + wlan_recv_event(pmpriv, + MLAN_EVENT_ID_FW_CHAN_SWITCH_COMPLETE, + pevent); + pevent->event_id = 0; + } + break; +#ifdef WIFI_DIRECT_SUPPORT + case EVENT_REMAIN_ON_CHANNEL_EXPIRED: + PRINTM(MEVENT, "EVENT: REMAIN_ON_CHANNEL_EXPIRED reason=%d\n", + *(t_u16 *)pmadapter->event_body); + wlan_recv_event(pmpriv, MLAN_EVENT_ID_DRV_FLUSH_RX_WORK, MNULL); + pevent->event_id = MLAN_EVENT_ID_FW_REMAIN_ON_CHAN_EXPIRED; + break; +#endif + case EVENT_MULTI_CHAN_INFO: + PRINTM(MEVENT, "EVENT: MULTI_CHAN_INFO\n"); + wlan_handle_event_multi_chan_info(pmpriv, pmbuf); + break; + + case EVENT_FW_DEBUG_INFO: + memset(pmadapter, event_buf, 0x00, MAX_EVENT_SIZE); + pevent->bss_index = pmpriv->bss_index; + pevent->event_id = MLAN_EVENT_ID_FW_DEBUG_INFO; + pevent->event_len = pmbuf->data_len - sizeof(eventcause); + memcpy(pmadapter, + (t_u8 *)pevent->event_buf, + pmbuf->pbuf + pmbuf->data_offset + sizeof(eventcause), + pevent->event_len); + PRINTM(MEVENT, "EVENT: FW Debug Info %s\n", + (t_u8 *)pevent->event_buf); + wlan_recv_event(pmpriv, pevent->event_id, pevent); + pevent->event_id = 0; /* clear to avoid resending at end of fcn */ + break; + case EVENT_TX_STATUS_REPORT: + PRINTM(MINFO, "EVENT: TX_STATUS\n"); + pevent->event_id = MLAN_EVENT_ID_FW_TX_STATUS; + break; + case EVENT_BT_COEX_WLAN_PARA_CHANGE: + PRINTM(MEVENT, "EVENT: BT coex wlan param update\n"); + wlan_bt_coex_wlan_param_update_event(pmpriv, pmbuf); + break; + case EVENT_EXCEED_MAX_P2P_CONN: + event_excd_p2p = + (event_exceed_max_p2p_conn *) (pmbuf->pbuf + + pmbuf->data_offset); + PRINTM(MEVENT, "EVENT: EXCEED MAX P2P CONNECTION\n"); + PRINTM(MEVENT, "REQUEST P2P MAC: " MACSTR "\n", + MAC2STR(event_excd_p2p->peer_mac_addr)); + pevent->event_id = MLAN_EVENT_ID_DRV_PASSTHRU; + break; + default: + pevent->event_id = MLAN_EVENT_ID_DRV_PASSTHRU; + break; + } + + if (pevent->event_id) { + pevent->bss_index = pmpriv->bss_index; + pevent->event_len = pmbuf->data_len; + memcpy(pmadapter, (t_u8 *)pevent->event_buf, + pmbuf->pbuf + pmbuf->data_offset, pevent->event_len); + wlan_recv_event(pmpriv, pevent->event_id, pevent); + } +done: + if (event_buf) + pcb->moal_mfree(pmadapter->pmoal_handle, event_buf); + LEAVE(); + return ret; +} + +/** + * @brief This function issues commands to initialize firmware + * + * @param priv A pointer to mlan_private structure + * @param first_bss flag for first BSS + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_PENDING or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_ops_uap_init_cmd(IN t_void *priv, IN t_u8 first_bss) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_private pmpriv = (pmlan_private)priv; + t_u16 last_cmd = 0; + + ENTER(); + if (!pmpriv) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + if (first_bss) { + if (wlan_adapter_init_cmd(pmpriv->adapter) == + MLAN_STATUS_FAILURE) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + } + + ret = wlan_prepare_cmd(pmpriv, HOST_CMD_APCMD_SYS_CONFIGURE, + HostCmd_ACT_GEN_GET, 0, MNULL, MNULL); + if (ret) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + last_cmd = HOST_CMD_APCMD_SYS_CONFIGURE; + /** set last_init_cmd */ + if (last_cmd) { + pmpriv->adapter->last_init_cmd = last_cmd; + ret = MLAN_STATUS_PENDING; + } +done: + LEAVE(); + return ret; +} diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_uap_ioctl.c b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_uap_ioctl.c new file mode 100644 index 000000000000..e662b1f411cc --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_uap_ioctl.c @@ -0,0 +1,2018 @@ +/** @file mlan_uap_ioctl.c + * + * @brief This file contains the handling of AP mode ioctls + * + * Copyright (C) 2009-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/******************************************************** +Change log: + 02/05/2009: initial version +********************************************************/ + +#include "mlan.h" +#include "mlan_util.h" +#include "mlan_fw.h" +#ifdef STA_SUPPORT +#include "mlan_join.h" +#endif +#include "mlan_main.h" +#include "mlan_uap.h" +#include "mlan_sdio.h" +#include "mlan_11n.h" +#include "mlan_fw.h" +#include "mlan_11h.h" + +/******************************************************** + Global Variables +********************************************************/ +extern t_u8 tos_to_tid_inv[]; + +/******************************************************** + Local Functions +********************************************************/ +/** + * @brief Stop BSS + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status +wlan_uap_bss_ioctl_stop(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_private pmpriv = pmadapter->priv[pioctl_req->bss_index]; + + ENTER(); + + ret = wlan_prepare_cmd(pmpriv, + HOST_CMD_APCMD_BSS_STOP, + HostCmd_ACT_GEN_SET, + 0, (t_void *)pioctl_req, MNULL); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +static t_bool +wlan_can_radar_det_skip(mlan_private *priv) +{ + mlan_private *priv_list[MLAN_MAX_BSS_NUM]; + mlan_private *pmpriv; + mlan_adapter *pmadapter = priv->adapter; + t_u8 pcount, i; + + /* In MBSS environment, if one of the BSS is already beaconing and DRCS + * is off then 11n_radar detection is not required for subsequent BSSes + * since they will follow the primary bss. + */ + if (!priv->adapter->mc_policy && + (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP)) { + memset(pmadapter, priv_list, 0x00, sizeof(priv_list)); + pcount = wlan_get_privs_by_cond(pmadapter, wlan_is_intf_active, + priv_list); + for (i = 0; i < pcount; i++) { + pmpriv = priv_list[i]; + if (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_UAP) + return MTRUE; + } + } + return MFALSE; +} + +/** + * @brief Callback to finish BSS IOCTL START + * Not to be called directly to initiate bss_start + * + * @param priv A pointer to mlan_private structure (cast from t_void*) + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + * @sa wlan_uap_bss_ioctl_start + */ +static mlan_status +wlan_uap_callback_bss_ioctl_start(IN t_void *priv) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = (mlan_private *)priv; + mlan_callbacks *pcb = (mlan_callbacks *)&pmpriv->adapter->callbacks; + wlan_uap_get_info_cb_t *puap_state_chan_cb = &pmpriv->uap_state_chan_cb; + t_u8 old_channel; + t_bool under_nop = MFALSE; + + ENTER(); + /* clear callback now that we're here */ + puap_state_chan_cb->get_chan_callback = MNULL; + + /* + * Check if the region and channel requires we check for radar. + */ + if ((puap_state_chan_cb->bandcfg.chanBand == BAND_5GHZ) && + !wlan_can_radar_det_skip(pmpriv) && + wlan_11h_radar_detect_required(pmpriv, + puap_state_chan_cb->channel)) { + /* If DFS repeater mode is on then before starting the uAP + * make sure that mlan0 is connected to some external AP + * for DFS channel operations. + */ + if (pmpriv->adapter->dfs_repeater) { + pmlan_private tmpriv = MNULL; + tmpriv = wlan_get_priv(pmpriv->adapter, + MLAN_BSS_ROLE_STA); + + if (tmpriv && !tmpriv->media_connected) { + PRINTM(MERROR, + "BSS start is blocked when DFS-repeater\n" + "mode is on and STA is not connected\n"); + pcb->moal_ioctl_complete(pmpriv->adapter-> + pmoal_handle, + puap_state_chan_cb-> + pioctl_req_curr, + MLAN_STATUS_FAILURE); + goto done; + } else { + /* STA is connected. + * Skip DFS check for bss_start since its a repeater + * mode + */ + goto prep_bss_start; + } + } + + /* first check if channel is under NOP */ + if (wlan_11h_is_channel_under_nop(pmpriv->adapter, + puap_state_chan_cb-> + channel)) { + /* recently we've seen radar on this channel */ + ret = MLAN_STATUS_FAILURE; + under_nop = MTRUE; + } + + /* Check cached radar check on the channel */ + if (ret == MLAN_STATUS_SUCCESS) + ret = wlan_11h_check_chan_report(pmpriv, + puap_state_chan_cb-> + channel); + + /* Found radar: try to switch to a non-dfs channel */ + if (ret != MLAN_STATUS_SUCCESS) { + old_channel = puap_state_chan_cb->channel; + ret = wlan_11h_switch_non_dfs_chan(pmpriv, + &puap_state_chan_cb-> + channel); + + if (ret == MLAN_STATUS_SUCCESS) { + + wlan_11h_update_bandcfg(&pmpriv-> + uap_state_chan_cb. + bandcfg, + puap_state_chan_cb-> + channel); + PRINTM(MCMD_D, + "NOP: uap band config:0x%x channel=%d\n", + pmpriv->uap_state_chan_cb.bandcfg, + puap_state_chan_cb->channel); + + ret = wlan_uap_set_channel(pmpriv, + pmpriv-> + uap_state_chan_cb. + bandcfg, + puap_state_chan_cb-> + channel); + if (ret == MLAN_STATUS_SUCCESS) { + if (under_nop) { + PRINTM(MMSG, + "Channel %d under NOP," + " switched to new channel %d successfully.\n", + old_channel, + puap_state_chan_cb-> + channel); + } else { + PRINTM(MMSG, + "Radar found on channel %d," + " switched to new channel %d successfully.\n", + old_channel, + puap_state_chan_cb-> + channel); + } + } else { + if (under_nop) { + PRINTM(MMSG, + "Channel %d under NOP," + " switch to new channel %d failed.\n", + old_channel, + puap_state_chan_cb-> + channel); + } else { + PRINTM(MMSG, + "Radar found on channel %d," + " switch to new channel %d failed.\n", + old_channel, + puap_state_chan_cb-> + channel); + } + pcb->moal_ioctl_complete(pmpriv-> + adapter-> + pmoal_handle, + puap_state_chan_cb-> + pioctl_req_curr, + MLAN_STATUS_FAILURE); + goto done; + } + } else { + if (under_nop) { + PRINTM(MMSG, + "Channel %d under NOP, no switch channel available.\n", + old_channel); + } else { + PRINTM(MMSG, + "Radar found on channel %d, no switch channel available.\n", + old_channel); + } + /* No command sent with the ioctl, need manually signal completion */ + pcb->moal_ioctl_complete(pmpriv->adapter-> + pmoal_handle, + puap_state_chan_cb-> + pioctl_req_curr, + MLAN_STATUS_FAILURE); + goto done; + } + } else { + PRINTM(MINFO, "No Radar found on channel %d\n", + puap_state_chan_cb->channel); + } + } + +prep_bss_start: + /* else okay to send command: not DFS channel or no radar */ + ret = wlan_prepare_cmd(pmpriv, + HOST_CMD_APCMD_BSS_START, + HostCmd_ACT_GEN_SET, + 0, + (t_void *)puap_state_chan_cb->pioctl_req_curr, + MNULL); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + +done: + puap_state_chan_cb->pioctl_req_curr = MNULL; /* prevent re-use */ + LEAVE(); + return ret; +} + +/** + * @brief Start BSS + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +/** + * @sa wlan_uap_callback_bss_ioctl_start + */ +static mlan_status +wlan_uap_bss_ioctl_start(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_private pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_bss *bss = MNULL; + + ENTER(); + + if (pmadapter->enable_net_mon == CHANNEL_SPEC_SNIFFER_MODE) { + PRINTM(MINFO, + "BSS start is blocked in Channel Specified Network Monitor mode...\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + bss = (mlan_ds_bss *)pioctl_req->pbuf; + pmpriv->uap_host_based = bss->param.host_based; + if (!pmpriv->intf_state_11h.is_11h_host && + pmpriv->intf_state_11h.is_11h_active) { + /* if FW supports ACS+DFS then sequence is different */ + + /* First check channel report, defer BSS_START CMD to callback. */ + /* store params, issue command to get UAP channel, whose CMD_RESP will + * callback remainder of bss_start handling */ + pmpriv->uap_state_chan_cb.pioctl_req_curr = pioctl_req; + pmpriv->uap_state_chan_cb.get_chan_callback = + wlan_uap_callback_bss_ioctl_start; + pmpriv->intf_state_11h.is_11h_host = MFALSE; + ret = wlan_uap_get_channel(pmpriv); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + } else { + ret = wlan_prepare_cmd(pmpriv, + HOST_CMD_APCMD_BSS_START, + HostCmd_ACT_GEN_SET, + 0, (t_void *)pioctl_req, MNULL); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + } + + LEAVE(); + return ret; +} + +/** + * @brief reset BSS + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status +wlan_uap_bss_ioctl_reset(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_private pmpriv = pmadapter->priv[pioctl_req->bss_index]; + t_u8 i = 0; + + ENTER(); + + /* + * Reset any uap private parameters here + */ + for (i = 0; i < pmadapter->max_mgmt_ie_index; i++) + memset(pmadapter, &pmpriv->mgmt_ie[i], 0, sizeof(custom_ie)); + pmpriv->add_ba_param.timeout = MLAN_DEFAULT_BLOCK_ACK_TIMEOUT; + pmpriv->add_ba_param.tx_win_size = MLAN_UAP_AMPDU_DEF_TXWINSIZE; + pmpriv->add_ba_param.rx_win_size = MLAN_UAP_AMPDU_DEF_RXWINSIZE; + pmpriv->user_rxwinsize = pmpriv->add_ba_param.rx_win_size; + + for (i = 0; i < MAX_NUM_TID; i++) { + pmpriv->aggr_prio_tbl[i].ampdu_user = tos_to_tid_inv[i]; + pmpriv->aggr_prio_tbl[i].amsdu = tos_to_tid_inv[i]; + pmpriv->addba_reject[i] = ADDBA_RSP_STATUS_ACCEPT; + } + pmpriv->aggr_prio_tbl[6].ampdu_user = + pmpriv->aggr_prio_tbl[7].ampdu_user = BA_STREAM_NOT_ALLOWED; + pmpriv->addba_reject[6] = + pmpriv->addba_reject[7] = ADDBA_RSP_STATUS_REJECT; + + /* hs_configured, hs_activated are reset by main loop */ + pmadapter->hs_cfg.conditions = HOST_SLEEP_DEF_COND; + pmadapter->hs_cfg.gpio = HOST_SLEEP_DEF_GPIO; + pmadapter->hs_cfg.gap = HOST_SLEEP_DEF_GAP; + + ret = wlan_prepare_cmd(pmpriv, + HOST_CMD_APCMD_SYS_RESET, + HostCmd_ACT_GEN_SET, + 0, (t_void *)pioctl_req, MNULL); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Set/Get MAC address + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status +wlan_uap_bss_ioctl_mac_address(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_bss *bss = MNULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u16 cmd_action = 0; + + ENTER(); + + bss = (mlan_ds_bss *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_SET) { + memcpy(pmadapter, pmpriv->curr_addr, &bss->param.mac_addr, + MLAN_MAC_ADDR_LENGTH); + cmd_action = HostCmd_ACT_GEN_SET; + } else + cmd_action = HostCmd_ACT_GEN_GET; + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, HOST_CMD_APCMD_SYS_CONFIGURE, + cmd_action, 0, (t_void *)pioctl_req, MNULL); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Set/Get wmm param + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status +wlan_uap_bss_ioctl_uap_wmm_param(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_bss *bss = MNULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u16 cmd_action = 0; + + ENTER(); + + bss = (mlan_ds_bss *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_SET) + cmd_action = HostCmd_ACT_GEN_SET; + else + cmd_action = HostCmd_ACT_GEN_GET; + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, HOST_CMD_APCMD_SYS_CONFIGURE, + cmd_action, 0, (t_void *)pioctl_req, MNULL); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Set/Get scan channels + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status +wlan_uap_bss_ioctl_uap_scan_channels(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_bss *bss = MNULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u16 cmd_action = 0; + + ENTER(); + + bss = (mlan_ds_bss *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_SET) + cmd_action = HostCmd_ACT_GEN_SET; + else + cmd_action = HostCmd_ACT_GEN_GET; + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, HOST_CMD_APCMD_SYS_CONFIGURE, + cmd_action, 0, (t_void *)pioctl_req, MNULL); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Set/Get UAP channel + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status +wlan_uap_bss_ioctl_uap_channel(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_bss *bss = MNULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u16 cmd_action = 0; + + ENTER(); + + bss = (mlan_ds_bss *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_SET) + cmd_action = HostCmd_ACT_GEN_SET; + else + cmd_action = HostCmd_ACT_GEN_GET; + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, HOST_CMD_APCMD_SYS_CONFIGURE, + cmd_action, 0, (t_void *)pioctl_req, MNULL); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Set/Get UAP operation control vaule + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status +wlan_uap_bss_ioctl_uap_oper_ctrl(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_bss *bss = MNULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u16 cmd_action = 0; + + ENTER(); + + if (pmadapter->fw_ver == HOST_API_VERSION_V15 + && pmadapter->fw_min_ver >= FW_MINOR_VERSION_1) { + bss = (mlan_ds_bss *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_SET) + cmd_action = HostCmd_ACT_GEN_SET; + else + cmd_action = HostCmd_ACT_GEN_GET; + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, HOST_CMD_APCMD_OPER_CTRL, + cmd_action, 0, (t_void *)pioctl_req, + (t_void *)pioctl_req->pbuf); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + } else { + PRINTM(MMSG, "FW don't support uap oper ctrl\n"); + } + LEAVE(); + return ret; +} + +/** + * @brief Get Uap statistics + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status +wlan_uap_get_stats(IN pmlan_adapter pmadapter, IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_private pmpriv = pmadapter->priv[pioctl_req->bss_index]; + + ENTER(); + + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_802_11_SNMP_MIB, + HostCmd_ACT_GEN_GET, + 0, (t_void *)pioctl_req, MNULL); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Get Uap MIB counters + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status +wlan_uap_get_stats_log(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + pmlan_private pmpriv = MNULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (pioctl_req != MNULL) { + pmpriv = pmadapter->priv[pioctl_req->bss_index]; + } else { + PRINTM(MERROR, "MLAN IOCTL information is not present\n"); + ret = MLAN_STATUS_FAILURE; + goto exit; + } + + /* Check information buffer length of MLAN IOCTL */ + if (pioctl_req->buf_len < sizeof(mlan_ds_get_stats)) { + PRINTM(MWARN, + "MLAN IOCTL information buffer length is too short.\n"); + pioctl_req->data_read_written = 0; + pioctl_req->buf_len_needed = sizeof(mlan_ds_get_stats); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_RESOURCE; + goto exit; + } + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_802_11_GET_LOG, + HostCmd_ACT_GEN_GET, + 0, (t_void *)pioctl_req, MNULL); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + +exit: + LEAVE(); + return ret; +} + +/** + * @brief Set/Get AP config + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status +wlan_uap_bss_ioctl_config(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u16 cmd_action = 0; + + ENTER(); + + if (pioctl_req->action == MLAN_ACT_SET) + cmd_action = HostCmd_ACT_GEN_SET; + else + cmd_action = HostCmd_ACT_GEN_GET; + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, HOST_CMD_APCMD_SYS_CONFIGURE, + cmd_action, 0, (t_void *)pioctl_req, MNULL); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief deauth sta + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status +wlan_uap_bss_ioctl_deauth_sta(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_private pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_bss *bss = MNULL; + + ENTER(); + + bss = (mlan_ds_bss *)pioctl_req->pbuf; + ret = wlan_prepare_cmd(pmpriv, + HOST_CMD_APCMD_STA_DEAUTH, + HostCmd_ACT_GEN_SET, + 0, + (t_void *)pioctl_req, + (t_void *)&bss->param.deauth_param); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Get station list + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status +wlan_uap_get_sta_list(IN pmlan_adapter pmadapter, IN pmlan_ioctl_req pioctl_req) +{ + pmlan_private pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, + HOST_CMD_APCMD_STA_LIST, + HostCmd_ACT_GEN_GET, + 0, (t_void *)pioctl_req, MNULL); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief soft_reset + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status +wlan_uap_misc_ioctl_soft_reset(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_SOFT_RESET, + HostCmd_ACT_GEN_SET, + 0, (t_void *)pioctl_req, MNULL); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Tx data pause + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status +wlan_uap_misc_ioctl_txdatapause(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_misc_cfg *pmisc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u16 cmd_action = 0; + + ENTER(); + + if (pioctl_req->action == MLAN_ACT_SET) + cmd_action = HostCmd_ACT_GEN_SET; + else + cmd_action = HostCmd_ACT_GEN_GET; + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_CFG_TX_DATA_PAUSE, + cmd_action, + 0, + (t_void *)pioctl_req, + &(pmisc->param.tx_datapause)); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Set/Get Power mode + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status +wlan_uap_pm_ioctl_mode(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_pm_cfg *pm = MNULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u16 cmd_action = 0; + t_u32 cmd_oid = 0; + + ENTER(); + + pm = (mlan_ds_pm_cfg *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_SET) { + if (pm->param.ps_mgmt.ps_mode == PS_MODE_INACTIVITY) { + cmd_action = EN_AUTO_PS; + cmd_oid = BITMAP_UAP_INACT_PS; + } else if (pm->param.ps_mgmt.ps_mode == PS_MODE_PERIODIC_DTIM) { + cmd_action = EN_AUTO_PS; + cmd_oid = BITMAP_UAP_DTIM_PS; + } else { + cmd_action = DIS_AUTO_PS; + cmd_oid = BITMAP_UAP_INACT_PS | BITMAP_UAP_DTIM_PS; + } + } else { + cmd_action = GET_PS; + cmd_oid = BITMAP_UAP_INACT_PS | BITMAP_UAP_DTIM_PS; + } + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_802_11_PS_MODE_ENH, + cmd_action, cmd_oid, (t_void *)pioctl_req, + (t_void *)&pm->param.ps_mgmt); + if ((ret == MLAN_STATUS_SUCCESS) && + (pioctl_req->action == MLAN_ACT_SET) && + (cmd_action == DIS_AUTO_PS)) { + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_802_11_PS_MODE_ENH, GET_PS, + 0, MNULL, MNULL); + } + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Set WAPI IE + * + * @param priv A pointer to mlan_private structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status +wlan_uap_set_wapi_ie(mlan_private *priv, pmlan_ioctl_req pioctl_req) +{ + mlan_ds_misc_cfg *misc = MNULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + if (misc->param.gen_ie.len) { + if (misc->param.gen_ie.len > sizeof(priv->wapi_ie)) { + PRINTM(MWARN, "failed to copy WAPI IE, too big\n"); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + LEAVE(); + return MLAN_STATUS_FAILURE; + } + memcpy(priv->adapter, priv->wapi_ie, misc->param.gen_ie.ie_data, + misc->param.gen_ie.len); + priv->wapi_ie_len = misc->param.gen_ie.len; + PRINTM(MIOCTL, "Set wapi_ie_len=%d IE=%#x\n", priv->wapi_ie_len, + priv->wapi_ie[0]); + DBG_HEXDUMP(MCMD_D, "wapi_ie", priv->wapi_ie, + priv->wapi_ie_len); + if (priv->wapi_ie[0] == WAPI_IE) + priv->sec_info.wapi_enabled = MTRUE; + } else { + memset(priv->adapter, priv->wapi_ie, 0, sizeof(priv->wapi_ie)); + priv->wapi_ie_len = misc->param.gen_ie.len; + PRINTM(MINFO, "Reset wapi_ie_len=%d IE=%#x\n", + priv->wapi_ie_len, priv->wapi_ie[0]); + priv->sec_info.wapi_enabled = MFALSE; + } + + /* Send request to firmware */ + ret = wlan_prepare_cmd(priv, HOST_CMD_APCMD_SYS_CONFIGURE, + HostCmd_ACT_GEN_SET, 0, (t_void *)pioctl_req, + MNULL); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Set generic IE + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status +wlan_uap_misc_ioctl_gen_ie(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_misc_cfg *misc = MNULL; + IEEEtypes_VendorHeader_t *pvendor_ie = MNULL; + + ENTER(); + + misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + + if ((misc->param.gen_ie.type == MLAN_IE_TYPE_GEN_IE) && + (pioctl_req->action == MLAN_ACT_SET)) { + if (misc->param.gen_ie.len) { + pvendor_ie = + (IEEEtypes_VendorHeader_t *)misc->param.gen_ie. + ie_data; + if (pvendor_ie->element_id == WAPI_IE) { + /* IE is a WAPI IE so call set_wapi function */ + ret = wlan_uap_set_wapi_ie(pmpriv, pioctl_req); + } + } else { + /* clear WAPI IE */ + ret = wlan_uap_set_wapi_ie(pmpriv, pioctl_req); + } + } + LEAVE(); + return ret; +} + +/** + * @brief Set/Get WAPI status + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success + */ +static mlan_status +wlan_uap_sec_ioctl_wapi_enable(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_sec_cfg *sec = MNULL; + ENTER(); + sec = (mlan_ds_sec_cfg *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_GET) { + if (pmpriv->wapi_ie_len) + sec->param.wapi_enabled = MTRUE; + else + sec->param.wapi_enabled = MFALSE; + } else { + if (sec->param.wapi_enabled == MFALSE) { + memset(pmpriv->adapter, pmpriv->wapi_ie, 0, + sizeof(pmpriv->wapi_ie)); + pmpriv->wapi_ie_len = 0; + PRINTM(MINFO, "Reset wapi_ie_len=%d IE=%#x\n", + pmpriv->wapi_ie_len, pmpriv->wapi_ie[0]); + pmpriv->sec_info.wapi_enabled = MFALSE; + } + } + pioctl_req->data_read_written = sizeof(t_u32) + MLAN_SUB_COMMAND_SIZE; + LEAVE(); + return ret; +} + +/** + * @brief Set encrypt key + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status +wlan_uap_sec_ioctl_set_encrypt_key(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_sec_cfg *sec = MNULL; + + ENTER(); + sec = (mlan_ds_sec_cfg *)pioctl_req->pbuf; + if (pioctl_req->action != MLAN_ACT_SET) { + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + LEAVE(); + return MLAN_STATUS_FAILURE; + } + if (!sec->param.encrypt_key.key_remove && + !sec->param.encrypt_key.key_len) { + PRINTM(MCMND, "Skip set key with key_len = 0\n"); + LEAVE(); + return ret; + } + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_802_11_KEY_MATERIAL, + HostCmd_ACT_GEN_SET, + KEY_INFO_ENABLED, + (t_void *)pioctl_req, &sec->param.encrypt_key); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + LEAVE(); + return ret; +} + +/** + * @brief Get BSS information + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success + */ +static mlan_status +wlan_uap_get_bss_info(IN pmlan_adapter pmadapter, IN pmlan_ioctl_req pioctl_req) +{ + pmlan_private pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_get_info *info; + + ENTER(); + + info = (mlan_ds_get_info *)pioctl_req->pbuf; + /* Connection status */ + info->param.bss_info.media_connected = pmpriv->media_connected; + + /* Radio status */ + info->param.bss_info.radio_on = pmadapter->radio_on; + + /* BSSID */ + memcpy(pmadapter, &info->param.bss_info.bssid, pmpriv->curr_addr, + MLAN_MAC_ADDR_LENGTH); + info->param.bss_info.scan_block = pmadapter->scan_block; + + info->param.bss_info.is_hs_configured = pmadapter->is_hs_configured; + pioctl_req->data_read_written = + sizeof(mlan_bss_info) + MLAN_SUB_COMMAND_SIZE; + + LEAVE(); + return ret; +} + +/** + * @brief Set Host Sleep configurations + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCES/MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status +wlan_uap_pm_ioctl_deepsleep(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_pm_cfg *pm = MNULL; + mlan_ds_auto_ds auto_ds; + t_u32 mode; + + ENTER(); + + if (pioctl_req->buf_len < sizeof(mlan_ds_pm_cfg)) { + PRINTM(MWARN, "MLAN bss IOCTL length is too short.\n"); + pioctl_req->data_read_written = 0; + pioctl_req->buf_len_needed = sizeof(mlan_ds_pm_cfg); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + LEAVE(); + return MLAN_STATUS_RESOURCE; + } + pm = (mlan_ds_pm_cfg *)pioctl_req->pbuf; + + if (pioctl_req->action == MLAN_ACT_GET) { + if (pmadapter->is_deep_sleep) { + pm->param.auto_deep_sleep.auto_ds = DEEP_SLEEP_ON; + pm->param.auto_deep_sleep.idletime = + pmadapter->idle_time; + } else + pm->param.auto_deep_sleep.auto_ds = DEEP_SLEEP_OFF; + } else { + if (pmadapter->is_deep_sleep && + pm->param.auto_deep_sleep.auto_ds == DEEP_SLEEP_ON) { + PRINTM(MMSG, "uAP already in deep sleep mode\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + if (((mlan_ds_pm_cfg *)pioctl_req->pbuf)->param.auto_deep_sleep. + auto_ds == DEEP_SLEEP_ON) { + auto_ds.auto_ds = DEEP_SLEEP_ON; + mode = EN_AUTO_PS; + PRINTM(MINFO, "Auto Deep Sleep: on\n"); + } else { + mode = DIS_AUTO_PS; + auto_ds.auto_ds = DEEP_SLEEP_OFF; + PRINTM(MINFO, "Auto Deep Sleep: off\n"); + } + if (((mlan_ds_pm_cfg *)pioctl_req->pbuf)->param.auto_deep_sleep. + idletime) + auto_ds.idletime = + ((mlan_ds_pm_cfg *)pioctl_req->pbuf)->param. + auto_deep_sleep.idletime; + else + auto_ds.idletime = pmadapter->idle_time; + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_802_11_PS_MODE_ENH, + (t_u16)mode, + BITMAP_AUTO_DS, + (t_void *)pioctl_req, &auto_ds); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + } + LEAVE(); + return ret; +} + +/** + * @brief Set SNMP MIB for 11D + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status +wlan_uap_snmp_mib_11d(IN pmlan_adapter pmadapter, IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_snmp_mib *snmp = MNULL; + state_11d_t flag; + + ENTER(); + + if (pioctl_req->buf_len < sizeof(mlan_ds_snmp_mib)) { + PRINTM(MWARN, "MLAN snmp_mib IOCTL length is too short.\n"); + pioctl_req->data_read_written = 0; + pioctl_req->buf_len_needed = sizeof(mlan_ds_snmp_mib); + LEAVE(); + return MLAN_STATUS_RESOURCE; + } + + if ((pioctl_req->action == MLAN_ACT_SET) && pmpriv->uap_bss_started) { + PRINTM(MIOCTL, + "11D setting cannot be changed while UAP bss is started.\n"); + pioctl_req->data_read_written = 0; + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + snmp = (mlan_ds_snmp_mib *)pioctl_req->pbuf; + flag = (snmp->param.oid_value) ? ENABLE_11D : DISABLE_11D; + + ret = wlan_11d_enable(pmpriv, (t_void *)pioctl_req, flag); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Callback to finish domain_info handling + * Not to be called directly to initiate domain_info setting. + * + * @param pmpriv A pointer to mlan_private structure (cast from t_void*) + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + * @sa wlan_uap_domain_info + */ +static mlan_status +wlan_uap_callback_domain_info(IN t_void *priv) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = (mlan_private *)priv; + wlan_uap_get_info_cb_t *puap_state_chan_cb = &pmpriv->uap_state_chan_cb; + mlan_ds_11d_cfg *cfg11d; + t_u8 band; + pmlan_adapter pmadapter = pmpriv->adapter; + pmlan_callbacks pcb = &pmadapter->callbacks; + + ENTER(); + /* clear callback now that we're here */ + puap_state_chan_cb->get_chan_callback = MNULL; + + if (!puap_state_chan_cb->pioctl_req_curr) { + PRINTM(MERROR, "pioctl_req_curr is null\n"); + LEAVE(); + return ret; + } + cfg11d = (mlan_ds_11d_cfg *)puap_state_chan_cb->pioctl_req_curr->pbuf; + band = (puap_state_chan_cb->bandcfg.chanBand == + BAND_5GHZ) ? BAND_A : BAND_B; + + ret = wlan_11d_handle_uap_domain_info(pmpriv, band, + cfg11d->param.domain_tlv, + puap_state_chan_cb-> + pioctl_req_curr); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + else { + puap_state_chan_cb->pioctl_req_curr->status_code = + MLAN_STATUS_FAILURE; + pcb->moal_ioctl_complete(pmadapter->pmoal_handle, + puap_state_chan_cb->pioctl_req_curr, + MLAN_STATUS_FAILURE); + } + + puap_state_chan_cb->pioctl_req_curr = MNULL; /* prevent re-use */ + LEAVE(); + return ret; +} + +/** + * @brief Set Domain Info for 11D + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + * @sa wlan_uap_callback_domain_info + */ +static mlan_status +wlan_uap_domain_info(IN pmlan_adapter pmadapter, IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + + ENTER(); + + if (pioctl_req->buf_len < sizeof(mlan_ds_11d_cfg)) { + PRINTM(MWARN, "MLAN 11d_cfg IOCTL length is too short.\n"); + pioctl_req->data_read_written = 0; + pioctl_req->buf_len_needed = sizeof(mlan_ds_11d_cfg); + LEAVE(); + return MLAN_STATUS_RESOURCE; + } + + if ((pioctl_req->action == MLAN_ACT_SET) && pmpriv->uap_bss_started) { + PRINTM(MIOCTL, + "Domain_info cannot be changed while UAP bss is started.\n"); + pioctl_req->data_read_written = 0; + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + /* store params, issue command to get UAP channel, whose CMD_RESP will + * callback remainder of domain_info handling */ + pmpriv->uap_state_chan_cb.pioctl_req_curr = pioctl_req; + pmpriv->uap_state_chan_cb.get_chan_callback = + wlan_uap_callback_domain_info; + + ret = wlan_uap_get_channel(pmpriv); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Callback to finish 11H channel check handling. + * Not to be called directly to initiate channel check. + * + * @param priv A pointer to mlan_private structure (cast from t_void*) + * + * @return MLAN_STATUS_SUCCESS/PENDING --success, otherwise fail + * @sa wlan_uap_11h_channel_check_req + */ +static mlan_status +wlan_uap_callback_11h_channel_check_req(IN t_void *priv) +{ + mlan_status ret = MLAN_STATUS_FAILURE; + mlan_private *pmpriv = (mlan_private *)priv; + mlan_callbacks *pcb = (mlan_callbacks *)&pmpriv->adapter->callbacks; + wlan_uap_get_info_cb_t *puap_state_chan_cb = &pmpriv->uap_state_chan_cb; + Band_Config_t *pband_cfg = &puap_state_chan_cb->bandcfg; + /* keep copy as local variable */ + pmlan_ioctl_req pioctl = puap_state_chan_cb->pioctl_req_curr; + + ENTER(); + /* clear callback now that we're here */ + puap_state_chan_cb->get_chan_callback = MNULL; + /* clear early to avoid race condition */ + puap_state_chan_cb->pioctl_req_curr = MNULL; + + /* + * Check if the region and channel requires a channel availability + * check. + */ + if ((puap_state_chan_cb->bandcfg.chanBand == BAND_5GHZ) && + !wlan_can_radar_det_skip(pmpriv) && + wlan_11h_radar_detect_required(pmpriv, puap_state_chan_cb->channel) + && !wlan_11h_is_channel_under_nop(pmpriv->adapter, + puap_state_chan_cb->channel)) { + + /* + * Radar detection is required for this channel, make sure + * 11h is activated in the firmware + */ + ret = wlan_11h_activate(pmpriv, MNULL, MTRUE); + ret = wlan_11h_config_master_radar_det(pmpriv, MTRUE); + ret = wlan_11h_check_update_radar_det_state(pmpriv); + + /* Check for radar on the channel */ + ret = wlan_11h_issue_radar_detect(pmpriv, + pioctl, + puap_state_chan_cb->channel, + *pband_cfg); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + } else { + /* No command sent with the ioctl, need manually signal completion */ + pcb->moal_ioctl_complete(pmpriv->adapter->pmoal_handle, + pioctl, MLAN_STATUS_COMPLETE); + } + + LEAVE(); + return ret; +} + +/** + * @brief 802.11h uap start channel check + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + * @sa wlan_uap_callback_11h_channel_check_req + */ +static mlan_status +wlan_uap_11h_channel_check_req(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_11h_cfg *p11h_cfg; + + ENTER(); + + if (pioctl_req->buf_len < sizeof(mlan_ds_11h_cfg)) { + PRINTM(MWARN, "MLAN 11h_cfg IOCTL length is too short.\n"); + pioctl_req->data_read_written = 0; + pioctl_req->buf_len_needed = sizeof(mlan_ds_11h_cfg); + LEAVE(); + return MLAN_STATUS_RESOURCE; + } + p11h_cfg = (mlan_ds_11h_cfg *)pioctl_req->pbuf; + pmpriv->intf_state_11h.is_11h_host = + p11h_cfg->param.chan_rpt_req.host_based; + + if (!pmpriv->intf_state_11h.is_11h_host) { + /* store params, issue command to get UAP channel, whose CMD_RESP will + * callback remainder of 11H channel check handling */ + pmpriv->uap_state_chan_cb.pioctl_req_curr = pioctl_req; + pmpriv->uap_state_chan_cb.get_chan_callback = + wlan_uap_callback_11h_channel_check_req; + + ret = wlan_uap_get_channel(pmpriv); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + } else { + if (!wlan_11h_is_active(pmpriv)) { + /* active 11h extention in Fw */ + ret = wlan_11h_activate(pmpriv, MNULL, MTRUE); + ret = wlan_11h_config_master_radar_det(pmpriv, MTRUE); + ret = wlan_11h_check_update_radar_det_state(pmpriv); + } + if (p11h_cfg->param.chan_rpt_req.millisec_dwell_time) { +#ifdef DFS_TESTING_SUPPORT + if (pmpriv->adapter->dfs_test_params. + user_cac_period_msec) { + PRINTM(MCMD_D, + "cfg80211 dfs_testing - user CAC period=%d (msec)\n", + pmpriv->adapter->dfs_test_params. + user_cac_period_msec); + p11h_cfg->param.chan_rpt_req. + millisec_dwell_time = + pmpriv->adapter->dfs_test_params. + user_cac_period_msec; + } +#endif + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_CHAN_REPORT_REQUEST, + HostCmd_ACT_GEN_SET, 0, + (t_void *)pioctl_req, + (t_void *)&p11h_cfg->param. + chan_rpt_req); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + } + } + + LEAVE(); + return ret; +} + +/** + * @brief Callback to finish 11H handling + * Not to be called directly to initiate 11H setting. + * + * @param pmpriv A pointer to mlan_private structure (cast from t_void*) + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + * @sa wlan_uap_snmp_mib_11h + */ +static mlan_status +wlan_uap_callback_snmp_mib_11h(IN t_void *priv) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = (mlan_private *)priv; + wlan_uap_get_info_cb_t *puap_state_chan_cb = &pmpriv->uap_state_chan_cb; + mlan_ds_snmp_mib *snmp; + t_bool enable_11h; + + ENTER(); + /* clear callback now that we're here */ + puap_state_chan_cb->get_chan_callback = MNULL; + + snmp = (mlan_ds_snmp_mib *)puap_state_chan_cb->pioctl_req_curr->pbuf; + enable_11h = (snmp->param.oid_value) ? MTRUE : MFALSE; + + if (enable_11h) { + if ((puap_state_chan_cb->bandcfg.chanBand == BAND_5GHZ) && + !wlan_can_radar_det_skip(pmpriv) && + wlan_11h_radar_detect_required(pmpriv, + puap_state_chan_cb-> + channel)) { + if (!wlan_11h_is_master_radar_det_active(pmpriv)) + wlan_11h_config_master_radar_det(pmpriv, MTRUE); + } + } + + ret = wlan_11h_activate(pmpriv, + (t_void *)puap_state_chan_cb->pioctl_req_curr, + enable_11h); + wlan_11h_check_update_radar_det_state(pmpriv); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + puap_state_chan_cb->pioctl_req_curr = MNULL; /* prevent re-use */ + LEAVE(); + return ret; +} + +/** + * @brief Set SNMP MIB for 11H + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + * @sa wlan_uap_callback_snmp_mib_11h + */ +static mlan_status +wlan_uap_snmp_mib_11h(IN pmlan_adapter pmadapter, IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_snmp_mib *snmp = MNULL; + t_bool enable; + + ENTER(); + + if (pioctl_req->buf_len < sizeof(mlan_ds_snmp_mib)) { + PRINTM(MWARN, "MLAN snmp_mib IOCTL length is too short.\n"); + pioctl_req->data_read_written = 0; + pioctl_req->buf_len_needed = sizeof(mlan_ds_snmp_mib); + LEAVE(); + return MLAN_STATUS_RESOURCE; + } + snmp = (mlan_ds_snmp_mib *)pioctl_req->pbuf; + enable = (snmp->param.oid_value) ? MTRUE : MFALSE; + + if (enable) { + /* first enable 11D if it is not enabled */ + if (!wlan_11d_is_enabled(pmpriv)) { + ret = wlan_11d_enable(pmpriv, MNULL, ENABLE_11D); + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, + "Failed to first enable 11D before enabling 11H.\n"); + LEAVE(); + return ret; + } + } + } + + /* store params, issue command to get UAP channel, whose CMD_RESP will + * callback remainder of 11H handling (and radar detect if DFS chan) */ + pmpriv->uap_state_chan_cb.pioctl_req_curr = pioctl_req; + pmpriv->uap_state_chan_cb.get_chan_callback = + wlan_uap_callback_snmp_mib_11h; + + ret = wlan_uap_get_channel(pmpriv); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/******************************************************** + Global Functions +********************************************************/ + +/** + * @brief Issue CMD to UAP firmware to get current channel + * + * @param pmpriv A pointer to mlan_private structure + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +mlan_status +wlan_uap_get_channel(IN pmlan_private pmpriv) +{ + MrvlIEtypes_channel_band_t tlv_chan_band; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + memset(pmpriv->adapter, &tlv_chan_band, 0, sizeof(tlv_chan_band)); + tlv_chan_band.header.type = TLV_TYPE_UAP_CHAN_BAND_CONFIG; + tlv_chan_band.header.len = sizeof(MrvlIEtypes_channel_band_t) + - sizeof(MrvlIEtypesHeader_t); + + ret = wlan_prepare_cmd(pmpriv, HOST_CMD_APCMD_SYS_CONFIGURE, + HostCmd_ACT_GEN_GET, 0, MNULL, &tlv_chan_band); + LEAVE(); + return ret; +} + +/** + * @brief Issue CMD to UAP firmware to set current channel + * + * @param pmpriv A pointer to mlan_private structure + * @param uap_band_cfg UAP band configuration + * @param channel New channel + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +mlan_status +wlan_uap_set_channel(IN pmlan_private pmpriv, + IN Band_Config_t uap_band_cfg, IN t_u8 channel) +{ + MrvlIEtypes_channel_band_t tlv_chan_band; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + memset(pmpriv->adapter, &tlv_chan_band, 0, sizeof(tlv_chan_band)); + tlv_chan_band.header.type = TLV_TYPE_UAP_CHAN_BAND_CONFIG; + tlv_chan_band.header.len = sizeof(MrvlIEtypes_channel_band_t) + - sizeof(MrvlIEtypesHeader_t); + tlv_chan_band.bandcfg = uap_band_cfg; + tlv_chan_band.channel = channel; + + ret = wlan_prepare_cmd(pmpriv, HOST_CMD_APCMD_SYS_CONFIGURE, + HostCmd_ACT_GEN_SET, 0, MNULL, &tlv_chan_band); + LEAVE(); + return ret; +} + +/** + * @brief Issue CMD to UAP firmware to get current beacon and dtim periods + * + * @param pmpriv A pointer to mlan_private structure + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +mlan_status +wlan_uap_get_beacon_dtim(IN pmlan_private pmpriv) +{ + t_u8 tlv_buffer[sizeof(MrvlIEtypes_beacon_period_t) + + sizeof(MrvlIEtypes_dtim_period_t)]; + MrvlIEtypes_beacon_period_t *ptlv_beacon_pd; + MrvlIEtypes_dtim_period_t *ptlv_dtim_pd; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + memset(pmpriv->adapter, &tlv_buffer, 0, sizeof(tlv_buffer)); + ptlv_beacon_pd = (MrvlIEtypes_beacon_period_t *)tlv_buffer; + ptlv_beacon_pd->header.type = TLV_TYPE_UAP_BEACON_PERIOD; + ptlv_beacon_pd->header.len = sizeof(MrvlIEtypes_beacon_period_t) + - sizeof(MrvlIEtypesHeader_t); + + ptlv_dtim_pd = (MrvlIEtypes_dtim_period_t *)(tlv_buffer + + + sizeof + (MrvlIEtypes_beacon_period_t)); + ptlv_dtim_pd->header.type = TLV_TYPE_UAP_DTIM_PERIOD; + ptlv_dtim_pd->header.len = sizeof(MrvlIEtypes_dtim_period_t) + - sizeof(MrvlIEtypesHeader_t); + + ret = wlan_prepare_cmd(pmpriv, HOST_CMD_APCMD_SYS_CONFIGURE, + HostCmd_ACT_GEN_GET, 0, MNULL, tlv_buffer); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get deauth control. + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +mlan_status +wlan_uap_snmp_mib_ctrl_deauth(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_snmp_mib *mib = (mlan_ds_snmp_mib *)pioctl_req->pbuf; + t_u16 cmd_action = 0; + + ENTER(); + + mib = (mlan_ds_snmp_mib *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_SET) { + cmd_action = HostCmd_ACT_GEN_SET; + } else { + cmd_action = HostCmd_ACT_GEN_GET; + } + + /* Send command to firmware */ + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_802_11_SNMP_MIB, + cmd_action, + StopDeauth_i, + (t_void *)pioctl_req, &mib->param.deauthctrl); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief MLAN uap ioctl handler + * + * @param adapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +mlan_status +wlan_ops_uap_ioctl(t_void *adapter, pmlan_ioctl_req pioctl_req) +{ + pmlan_adapter pmadapter = (pmlan_adapter)adapter; + mlan_status status = MLAN_STATUS_SUCCESS; + mlan_ds_bss *bss = MNULL; + mlan_ds_get_info *pget_info = MNULL; + mlan_ds_misc_cfg *misc = MNULL; + mlan_ds_sec_cfg *sec = MNULL; + mlan_ds_pm_cfg *pm = MNULL; + mlan_ds_11d_cfg *cfg11d = MNULL; + mlan_ds_snmp_mib *snmp = MNULL; + mlan_ds_11h_cfg *cfg11h = MNULL; + mlan_ds_radio_cfg *radiocfg = MNULL; + mlan_ds_rate *rate = MNULL; + mlan_ds_reg_mem *reg_mem = MNULL; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) + mlan_ds_scan *pscan; +#endif + + ENTER(); + + switch (pioctl_req->req_id) { + case MLAN_IOCTL_BSS: + bss = (mlan_ds_bss *)pioctl_req->pbuf; + if (bss->sub_command == MLAN_OID_BSS_MAC_ADDR) + status = wlan_uap_bss_ioctl_mac_address(pmadapter, + pioctl_req); + else if (bss->sub_command == MLAN_OID_BSS_STOP) + status = wlan_uap_bss_ioctl_stop(pmadapter, pioctl_req); + else if (bss->sub_command == MLAN_OID_BSS_START) + status = wlan_uap_bss_ioctl_start(pmadapter, + pioctl_req); + else if (bss->sub_command == MLAN_OID_UAP_BSS_CONFIG) + status = wlan_uap_bss_ioctl_config(pmadapter, + pioctl_req); + else if (bss->sub_command == MLAN_OID_UAP_DEAUTH_STA) + status = wlan_uap_bss_ioctl_deauth_sta(pmadapter, + pioctl_req); + else if (bss->sub_command == MLAN_OID_UAP_BSS_RESET) + status = wlan_uap_bss_ioctl_reset(pmadapter, + pioctl_req); +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) + else if (bss->sub_command == MLAN_OID_BSS_ROLE) { + util_enqueue_list_tail(pmadapter->pmoal_handle, + &pmadapter->ioctl_pending_q, + (pmlan_linked_list)pioctl_req, + pmadapter->callbacks. + moal_spin_lock, + pmadapter->callbacks. + moal_spin_unlock); + pmadapter->pending_ioctl = MTRUE; + status = MLAN_STATUS_PENDING; + } +#endif +#ifdef WIFI_DIRECT_SUPPORT + else if (bss->sub_command == MLAN_OID_WIFI_DIRECT_MODE) + status = wlan_bss_ioctl_wifi_direct_mode(pmadapter, + pioctl_req); +#endif + else if (bss->sub_command == MLAN_OID_BSS_REMOVE) + status = wlan_bss_ioctl_bss_remove(pmadapter, + pioctl_req); + else if (bss->sub_command == MLAN_OID_UAP_CFG_WMM_PARAM) + status = wlan_uap_bss_ioctl_uap_wmm_param(pmadapter, + pioctl_req); + else if (bss->sub_command == MLAN_OID_UAP_SCAN_CHANNELS) + status = wlan_uap_bss_ioctl_uap_scan_channels(pmadapter, + pioctl_req); + else if (bss->sub_command == MLAN_OID_UAP_CHANNEL) + status = wlan_uap_bss_ioctl_uap_channel(pmadapter, + pioctl_req); + else if (bss->sub_command == MLAN_OID_UAP_OPER_CTRL) + status = wlan_uap_bss_ioctl_uap_oper_ctrl(pmadapter, + pioctl_req); + break; +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) + case MLAN_IOCTL_SCAN: + pscan = (mlan_ds_scan *)pioctl_req->pbuf; + if ((pscan->sub_command == MLAN_OID_SCAN_NORMAL) && + (pioctl_req->action == MLAN_ACT_GET)) { + PRINTM(MIOCTL, "Get scan table in uap\n"); + pscan->param.scan_resp.pscan_table = + (t_u8 *)pmadapter->pscan_table; + pscan->param.scan_resp.num_in_scan_table = + pmadapter->num_in_scan_table; + pscan->param.scan_resp.age_in_secs = + pmadapter->age_in_secs; + pioctl_req->data_read_written = + sizeof(mlan_scan_resp) + MLAN_SUB_COMMAND_SIZE; + pscan->param.scan_resp.pchan_stats = + (t_u8 *)pmadapter->pchan_stats; + pscan->param.scan_resp.num_in_chan_stats = + pmadapter->num_in_chan_stats; + } + break; +#endif + case MLAN_IOCTL_GET_INFO: + pget_info = (mlan_ds_get_info *)pioctl_req->pbuf; + if (pget_info->sub_command == MLAN_OID_GET_VER_EXT) + status = wlan_get_info_ver_ext(pmadapter, pioctl_req); + else if (pget_info->sub_command == MLAN_OID_GET_DEBUG_INFO) + status = wlan_get_info_debug_info(pmadapter, + pioctl_req); + else if (pget_info->sub_command == MLAN_OID_GET_STATS) + status = wlan_uap_get_stats(pmadapter, pioctl_req); + else if (pget_info->sub_command == MLAN_OID_GET_UAP_STATS_LOG) + status = wlan_uap_get_stats_log(pmadapter, pioctl_req); + else if (pget_info->sub_command == MLAN_OID_UAP_STA_LIST) + status = wlan_uap_get_sta_list(pmadapter, pioctl_req); + else if (pget_info->sub_command == MLAN_OID_GET_BSS_INFO) + status = wlan_uap_get_bss_info(pmadapter, pioctl_req); + else if (pget_info->sub_command == MLAN_OID_GET_FW_INFO) { + pioctl_req->data_read_written = + sizeof(mlan_fw_info) + MLAN_SUB_COMMAND_SIZE; + memcpy(pmadapter, &pget_info->param.fw_info.mac_addr, + pmpriv->curr_addr, MLAN_MAC_ADDR_LENGTH); + pget_info->param.fw_info.fw_ver = + pmadapter->fw_release_number; + pget_info->param.fw_info.fw_bands = pmadapter->fw_bands; + pget_info->param.fw_info.ecsa_enable = + pmadapter->ecsa_enable; + pget_info->param.fw_info.getlog_enable = + pmadapter->getlog_enable; + pget_info->param.fw_info.hw_dev_mcs_support = + pmadapter->hw_dev_mcs_support; + pget_info->param.fw_info.hw_dot_11n_dev_cap = + pmadapter->hw_dot_11n_dev_cap; + pget_info->param.fw_info.usr_dev_mcs_support = + pmpriv->usr_dev_mcs_support; + pget_info->param.fw_info.region_code = + pmadapter->region_code; + pget_info->param.fw_info.fw_supplicant_support = + IS_FW_SUPPORT_SUPPLICANT(pmadapter) ? 0x01 : + 0x00; + pget_info->param.fw_info.antinfo = pmadapter->antinfo; + pget_info->param.fw_info.max_ap_assoc_sta = + pmadapter->max_sta_conn; + } + break; + case MLAN_IOCTL_MISC_CFG: + misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + if (misc->sub_command == MLAN_OID_MISC_INIT_SHUTDOWN) + status = wlan_misc_ioctl_init_shutdown(pmadapter, + pioctl_req); + if (misc->sub_command == MLAN_OID_MISC_SOFT_RESET) + status = wlan_uap_misc_ioctl_soft_reset(pmadapter, + pioctl_req); + if (misc->sub_command == MLAN_OID_MISC_HOST_CMD) + status = wlan_misc_ioctl_host_cmd(pmadapter, + pioctl_req); + if (misc->sub_command == MLAN_OID_MISC_REGION) + status = wlan_misc_ioctl_region(pmadapter, pioctl_req); + if (misc->sub_command == MLAN_OID_MISC_GEN_IE) + status = wlan_uap_misc_ioctl_gen_ie(pmadapter, + pioctl_req); + if (misc->sub_command == MLAN_OID_MISC_CUSTOM_IE) + status = wlan_misc_ioctl_custom_ie_list(pmadapter, + pioctl_req, + MTRUE); + if (misc->sub_command == MLAN_OID_MISC_TX_DATAPAUSE) + status = wlan_uap_misc_ioctl_txdatapause(pmadapter, + pioctl_req); + if (misc->sub_command == MLAN_OID_MISC_RX_MGMT_IND) + status = wlan_reg_rx_mgmt_ind(pmadapter, pioctl_req); +#ifdef DEBUG_LEVEL1 + if (misc->sub_command == MLAN_OID_MISC_DRVDBG) + status = wlan_set_drvdbg(pmadapter, pioctl_req); +#endif + + if (misc->sub_command == MLAN_OID_MISC_TXCONTROL) + status = wlan_misc_ioctl_txcontrol(pmadapter, + pioctl_req); + if (misc->sub_command == MLAN_OID_MISC_MAC_CONTROL) + status = wlan_misc_ioctl_mac_control(pmadapter, + pioctl_req); + if (misc->sub_command == MLAN_OID_MISC_MULTI_CHAN_CFG) + status = wlan_misc_ioctl_multi_chan_config(pmadapter, + pioctl_req); + if (misc->sub_command == MLAN_OID_MISC_MULTI_CHAN_POLICY) + status = wlan_misc_ioctl_multi_chan_policy(pmadapter, + pioctl_req); + if (misc->sub_command == MLAN_OID_MISC_DRCS_CFG) + status = wlan_misc_ioctl_drcs_config(pmadapter, + pioctl_req); +#ifdef RX_PACKET_COALESCE + if (misc->sub_command == MLAN_OID_MISC_RX_PACKET_COALESCE) + status = wlan_misc_ioctl_rx_pkt_coalesce_config + (pmadapter, pioctl_req); +#endif +#ifdef WIFI_DIRECT_SUPPORT + if (misc->sub_command == MLAN_OID_MISC_WIFI_DIRECT_CONFIG) + status = wlan_misc_p2p_config(pmadapter, pioctl_req); +#endif + + if (misc->sub_command == MLAN_OID_MISC_DFS_REAPTER_MODE) { + mlan_ds_misc_cfg *misc_cfg = MNULL; + + misc_cfg = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + misc_cfg->param.dfs_repeater.mode = + pmadapter->dfs_repeater; + pioctl_req->data_read_written = + sizeof(mlan_ds_misc_dfs_repeater); + + status = MLAN_STATUS_SUCCESS; + } + if (misc->sub_command == MLAN_OID_MISC_IND_RST_CFG) + status = wlan_misc_ioctl_ind_rst_cfg(pmadapter, + pioctl_req); + if (misc->sub_command == MLAN_OID_MISC_GET_TSF) + status = wlan_misc_ioctl_get_tsf(pmadapter, pioctl_req); + if (misc->sub_command == MLAN_OID_MISC_GET_CHAN_REGION_CFG) + status = wlan_misc_chan_reg_cfg(pmadapter, pioctl_req); + if (misc->sub_command == MLAN_OID_MISC_OPER_CLASS_CHECK) + status = wlan_misc_ioctl_operclass_validation(pmadapter, + pioctl_req); + if (misc->sub_command == MLAN_OID_MISC_OPER_CLASS) + status = wlan_misc_ioctl_oper_class(pmadapter, + pioctl_req); + if (misc->sub_command == MLAN_OID_MISC_PER_PKT_CFG) + status = wlan_misc_per_pkt_cfg(pmadapter, pioctl_req); + if (misc->sub_command == MLAN_OID_MISC_NET_MONITOR) + status = wlan_misc_ioctl_net_monitor(pmadapter, + pioctl_req); + if (misc->sub_command == MLAN_OID_MISC_FW_DUMP_EVENT) + status = wlan_misc_ioctl_fw_dump_event(pmadapter, + pioctl_req); + if (misc->sub_command == MLAN_OID_MISC_ROBUSTCOEX) + status = wlan_misc_robustcoex(pmadapter, pioctl_req); + if (misc->sub_command == MLAN_OID_MISC_GET_CORRELATED_TIME) + status = wlan_misc_get_correlated_time(pmadapter, + pioctl_req); + if (misc->sub_command == MLAN_OID_MISC_CFP_INFO) + status = wlan_get_cfpinfo(pmadapter, pioctl_req); + if (misc->sub_command == MLAN_OID_MISC_BOOT_SLEEP) + status = wlan_misc_bootsleep(pmadapter, pioctl_req); + break; + case MLAN_IOCTL_PM_CFG: + pm = (mlan_ds_pm_cfg *)pioctl_req->pbuf; + if (pm->sub_command == MLAN_OID_PM_CFG_PS_MODE) + status = wlan_uap_pm_ioctl_mode(pmadapter, pioctl_req); + if (pm->sub_command == MLAN_OID_PM_CFG_DEEP_SLEEP) + status = wlan_uap_pm_ioctl_deepsleep(pmadapter, + pioctl_req); + if (pm->sub_command == MLAN_OID_PM_CFG_HS_CFG) + status = wlan_pm_ioctl_hscfg(pmadapter, pioctl_req); + if (pm->sub_command == MLAN_OID_PM_HS_WAKEUP_REASON) + status = wlan_get_hs_wakeup_reason(pmadapter, + pioctl_req); + if (pm->sub_command == MLAN_OID_PM_MGMT_FILTER) + status = wlan_config_mgmt_filter(pmadapter, pioctl_req); + if (pm->sub_command == MLAN_OID_PM_INFO) + status = wlan_get_pm_info(pmadapter, pioctl_req); + if (pm->sub_command == MLAN_OID_PM_CFG_FW_WAKEUP_METHOD) + status = wlan_fw_wakeup_method(pmadapter, pioctl_req); + break; + case MLAN_IOCTL_SNMP_MIB: + snmp = (mlan_ds_snmp_mib *)pioctl_req->pbuf; + if (snmp->sub_command == MLAN_OID_SNMP_MIB_CTRL_DEAUTH) + status = wlan_uap_snmp_mib_ctrl_deauth(pmadapter, + pioctl_req); + if (snmp->sub_command == MLAN_OID_SNMP_MIB_DOT11D) + status = wlan_uap_snmp_mib_11d(pmadapter, pioctl_req); + if (snmp->sub_command == MLAN_OID_SNMP_MIB_DOT11H) + status = wlan_uap_snmp_mib_11h(pmadapter, pioctl_req); + break; + case MLAN_IOCTL_SEC_CFG: + sec = (mlan_ds_sec_cfg *)pioctl_req->pbuf; + if (sec->sub_command == MLAN_OID_SEC_CFG_ENCRYPT_KEY) + status = wlan_uap_sec_ioctl_set_encrypt_key(pmadapter, + pioctl_req); + if (sec->sub_command == MLAN_OID_SEC_CFG_WAPI_ENABLED) + status = wlan_uap_sec_ioctl_wapi_enable(pmadapter, + pioctl_req); + break; + case MLAN_IOCTL_11N_CFG: + status = wlan_11n_cfg_ioctl(pmadapter, pioctl_req); + break; + case MLAN_IOCTL_11D_CFG: + cfg11d = (mlan_ds_11d_cfg *)pioctl_req->pbuf; + if (cfg11d->sub_command == MLAN_OID_11D_DOMAIN_INFO) + status = wlan_uap_domain_info(pmadapter, pioctl_req); + break; + case MLAN_IOCTL_11H_CFG: + cfg11h = (mlan_ds_11h_cfg *)pioctl_req->pbuf; + if (cfg11h->sub_command == MLAN_OID_11H_CHANNEL_CHECK) + status = wlan_uap_11h_channel_check_req(pmadapter, + pioctl_req); +#if defined(DFS_TESTING_SUPPORT) + if (cfg11h->sub_command == MLAN_OID_11H_DFS_TESTING) + status = wlan_11h_ioctl_dfs_testing(pmadapter, + pioctl_req); + if (cfg11h->sub_command == MLAN_OID_11H_CHAN_NOP_INFO) + status = wlan_11h_ioctl_get_channel_nop_info(pmadapter, + pioctl_req); +#endif + if (cfg11h->sub_command == MLAN_OID_11H_CHAN_REPORT_REQUEST) + status = wlan_11h_ioctl_dfs_cancel_chan_report(pmpriv, + pioctl_req); + if (cfg11h->sub_command == MLAN_OID_11H_CHAN_SWITCH_COUNT) + status = wlan_11h_ioctl_chan_switch_count(pmadapter, + pioctl_req); + break; + case MLAN_IOCTL_RADIO_CFG: + radiocfg = (mlan_ds_radio_cfg *)pioctl_req->pbuf; + if (radiocfg->sub_command == MLAN_OID_RADIO_CTRL) + status = wlan_radio_ioctl_radio_ctl(pmadapter, + pioctl_req); + if (radiocfg->sub_command == MLAN_OID_REMAIN_CHAN_CFG) + status = wlan_radio_ioctl_remain_chan_cfg(pmadapter, + pioctl_req); + break; + case MLAN_IOCTL_RATE: + rate = (mlan_ds_rate *)pioctl_req->pbuf; + if (rate->sub_command == MLAN_OID_RATE_CFG) + status = wlan_rate_ioctl_cfg(pmadapter, pioctl_req); + else if (rate->sub_command == MLAN_OID_GET_DATA_RATE) + status = wlan_rate_ioctl_get_data_rate(pmadapter, + pioctl_req); + break; + + case MLAN_IOCTL_REG_MEM: + reg_mem = (mlan_ds_reg_mem *)pioctl_req->pbuf; + if (reg_mem->sub_command == MLAN_OID_REG_RW) + status = wlan_reg_mem_ioctl_reg_rw(pmadapter, + pioctl_req); + else if (reg_mem->sub_command == MLAN_OID_EEPROM_RD) + status = wlan_reg_mem_ioctl_read_eeprom(pmadapter, + pioctl_req); + else if (reg_mem->sub_command == MLAN_OID_MEM_RW) + status = wlan_reg_mem_ioctl_mem_rw(pmadapter, + pioctl_req); + break; + case MLAN_IOCTL_WMM_CFG: + status = wlan_wmm_cfg_ioctl(pmadapter, pioctl_req); + break; + default: + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + break; + } + LEAVE(); + return status; +} diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_uap_txrx.c b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_uap_txrx.c new file mode 100644 index 000000000000..5eaa53f5fdce --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_uap_txrx.c @@ -0,0 +1,756 @@ +/** @file mlan_uap_txrx.c + * + * @brief This file contains AP mode transmit and receive functions + * + * Copyright (C) 2009-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/******************************************************** +Change log: + 02/05/2009: initial version +********************************************************/ + +#include "mlan.h" +#include "mlan_util.h" +#include "mlan_fw.h" +#ifdef STA_SUPPORT +#include "mlan_join.h" +#endif +#include "mlan_main.h" +#include "mlan_uap.h" +#include "mlan_sdio.h" +#include "mlan_wmm.h" +#include "mlan_11n_aggr.h" +#include "mlan_11n_rxreorder.h" +#ifdef DRV_EMBEDDED_AUTHENTICATOR +#include "authenticator_api.h" +#endif + +/******************************************************** + Local Functions +********************************************************/ + +/** + * @brief This function processes received packet and forwards it + * to kernel/upper layer + * + * @param pmadapter A pointer to mlan_adapter + * @param pmbuf A pointer to mlan_buffer which includes the received packet + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status +wlan_upload_uap_rx_packet(pmlan_adapter pmadapter, pmlan_buffer pmbuf) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; +#ifdef DEBUG_LEVEL1 + pmlan_private priv = pmadapter->priv[pmbuf->bss_index]; +#endif + UapRxPD *prx_pd; + ENTER(); + prx_pd = (UapRxPD *)(pmbuf->pbuf + pmbuf->data_offset); + + /* Chop off RxPD */ + pmbuf->data_len -= prx_pd->rx_pkt_offset; + pmbuf->data_offset += prx_pd->rx_pkt_offset; + pmbuf->pparent = MNULL; + + DBG_HEXDUMP(MDAT_D, "uAP RxPD", (t_u8 *)prx_pd, + MIN(sizeof(UapRxPD), MAX_DATA_DUMP_LEN)); + DBG_HEXDUMP(MDAT_D, "uAP Rx Payload", + ((t_u8 *)prx_pd + prx_pd->rx_pkt_offset), + MIN(prx_pd->rx_pkt_length, MAX_DATA_DUMP_LEN)); + + pmadapter->callbacks.moal_get_system_time(pmadapter->pmoal_handle, + &pmbuf->out_ts_sec, + &pmbuf->out_ts_usec); + PRINTM_NETINTF(MDATA, priv); + PRINTM(MDATA, "%lu.%06lu : Data => kernel seq_num=%d tid=%d\n", + pmbuf->out_ts_sec, pmbuf->out_ts_usec, prx_pd->seq_num, + prx_pd->priority); + ret = pmadapter->callbacks.moal_recv_packet(pmadapter->pmoal_handle, + pmbuf); + if (ret == MLAN_STATUS_FAILURE) { + PRINTM(MERROR, + "uAP Rx Error: moal_recv_packet returned error\n"); + pmbuf->status_code = MLAN_ERROR_PKT_INVALID; + } + + if (ret != MLAN_STATUS_PENDING) + wlan_free_mlan_buffer(pmadapter, pmbuf); + LEAVE(); + + return ret; + +} + +/** + * @brief This function will check if unicast packet need be dropped + * + * @param priv A pointer to mlan_private + * @param mac mac address to find in station list table + * + * @return MLAN_STATUS_FAILURE -- drop packet, otherwise forward to network stack + */ +static mlan_status +wlan_check_unicast_packet(mlan_private *priv, t_u8 *mac) +{ + int j; + sta_node *sta_ptr = MNULL; + pmlan_adapter pmadapter = priv->adapter; + pmlan_private pmpriv = MNULL; + t_u8 pkt_type = 0; + mlan_status ret = MLAN_STATUS_SUCCESS; + ENTER(); + for (j = 0; j < MLAN_MAX_BSS_NUM; ++j) { + pmpriv = pmadapter->priv[j]; + if (pmpriv) { + if (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_STA) + continue; + sta_ptr = wlan_get_station_entry(pmpriv, mac); + if (sta_ptr) { + if (pmpriv == priv) + pkt_type = PKT_INTRA_UCAST; + else + pkt_type = PKT_INTER_UCAST; + break; + } + } + } + if ((pkt_type == PKT_INTRA_UCAST) && + (priv->pkt_fwd & PKT_FWD_INTRA_UCAST)) { + PRINTM(MDATA, "Drop INTRA_UCAST packet\n"); + ret = MLAN_STATUS_FAILURE; + } else if ((pkt_type == PKT_INTER_UCAST) && + (priv->pkt_fwd & PKT_FWD_INTER_UCAST)) { + PRINTM(MDATA, "Drop INTER_UCAST packet\n"); + ret = MLAN_STATUS_FAILURE; + } + LEAVE(); + return ret; +} + +/******************************************************** + Global Functions +********************************************************/ +/** + * @brief This function fill the txpd for tx packet + * + * @param priv A pointer to mlan_private structure + * @param pmbuf A pointer to the mlan_buffer for process + * + * @return headptr or MNULL + */ +t_void * +wlan_ops_uap_process_txpd(IN t_void *priv, IN pmlan_buffer pmbuf) +{ + pmlan_private pmpriv = (pmlan_private)priv; + UapTxPD *plocal_tx_pd; + t_u8 *head_ptr = MNULL; + t_u32 pkt_type; + t_u32 tx_control; + t_u8 dst_mac[MLAN_MAC_ADDR_LENGTH]; + + ENTER(); + + if (!pmbuf->data_len) { + PRINTM(MERROR, "uAP Tx Error: Invalid packet length: %d\n", + pmbuf->data_len); + pmbuf->status_code = MLAN_ERROR_PKT_SIZE_INVALID; + goto done; + } + if (pmbuf->buf_type == MLAN_BUF_TYPE_RAW_DATA) { + memcpy(pmpriv->adapter, &pkt_type, + pmbuf->pbuf + pmbuf->data_offset, sizeof(pkt_type)); + memcpy(pmpriv->adapter, &tx_control, + pmbuf->pbuf + pmbuf->data_offset + sizeof(pkt_type), + sizeof(tx_control)); + pmbuf->data_offset += sizeof(pkt_type) + sizeof(tx_control); + pmbuf->data_len -= sizeof(pkt_type) + sizeof(tx_control); + } + if (pmbuf->data_offset < (sizeof(UapTxPD) + pmpriv->intf_hr_len + + DMA_ALIGNMENT)) { + PRINTM(MERROR, + "not enough space for UapTxPD: headroom=%d pkt_len=%d, required=%d\n", + pmbuf->data_offset, pmbuf->data_len, + sizeof(UapTxPD) + pmpriv->intf_hr_len + DMA_ALIGNMENT); + DBG_HEXDUMP(MDAT_D, "drop pkt", + pmbuf->pbuf + pmbuf->data_offset, pmbuf->data_len); + pmbuf->status_code = MLAN_ERROR_PKT_SIZE_INVALID; + goto done; + } + + /* head_ptr should be aligned */ + head_ptr = + pmbuf->pbuf + pmbuf->data_offset - sizeof(UapTxPD) - + pmpriv->intf_hr_len; + head_ptr = (t_u8 *)((t_ptr)head_ptr & ~((t_ptr)(DMA_ALIGNMENT - 1))); + + plocal_tx_pd = (UapTxPD *)(head_ptr + pmpriv->intf_hr_len); + memset(pmpriv->adapter, plocal_tx_pd, 0, sizeof(UapTxPD)); + + /* Set the BSS number to TxPD */ + plocal_tx_pd->bss_num = GET_BSS_NUM(pmpriv); + plocal_tx_pd->bss_type = pmpriv->bss_type; + + plocal_tx_pd->tx_pkt_length = (t_u16)pmbuf->data_len; + + plocal_tx_pd->priority = (t_u8)pmbuf->priority; + plocal_tx_pd->pkt_delay_2ms = + wlan_wmm_compute_driver_packet_delay(pmpriv, pmbuf); + + if (plocal_tx_pd->priority < + NELEMENTS(pmpriv->wmm.user_pri_pkt_tx_ctrl)) + /* + * Set the priority specific tx_control field, setting of 0 will + * cause the default value to be used later in this function + */ + plocal_tx_pd->tx_control + = + pmpriv->wmm.user_pri_pkt_tx_ctrl[plocal_tx_pd-> + priority]; + + if (pmbuf->flags & MLAN_BUF_FLAG_TX_STATUS) { + plocal_tx_pd->tx_control_1 |= pmbuf->tx_seq_num << 8; + plocal_tx_pd->flags |= MRVDRV_TxPD_FLAGS_TX_PACKET_STATUS; + } + + /* Offset of actual data */ + plocal_tx_pd->tx_pkt_offset = + (t_u16)((t_ptr)pmbuf->pbuf + pmbuf->data_offset - + (t_ptr)plocal_tx_pd); + + if (!plocal_tx_pd->tx_control) { + /* TxCtrl set by user or default */ + plocal_tx_pd->tx_control = pmpriv->pkt_tx_ctrl; + } + + if (pmbuf->buf_type == MLAN_BUF_TYPE_RAW_DATA) { + plocal_tx_pd->tx_pkt_type = (t_u16)pkt_type; + plocal_tx_pd->tx_control = tx_control; + } + if (pmbuf->flags & MLAN_BUF_FLAG_TX_CTRL) { + if (pmbuf->u.tx_info.data_rate) { + memcpy(pmpriv->adapter, dst_mac, + pmbuf->pbuf + pmbuf->data_offset, + sizeof(dst_mac)); + plocal_tx_pd->tx_control |= + (wlan_ieee_rateid_to_mrvl_rateid + (pmpriv, pmbuf->u.tx_info.data_rate, + dst_mac) << 16); + plocal_tx_pd->tx_control |= TXPD_TXRATE_ENABLE; + } + plocal_tx_pd->tx_control_1 |= pmbuf->u.tx_info.channel << 21; + if (pmbuf->u.tx_info.bw) { + plocal_tx_pd->tx_control_1 |= pmbuf->u.tx_info.bw << 16; + plocal_tx_pd->tx_control_1 |= TXPD_BW_ENABLE; + } + if (pmbuf->u.tx_info.tx_power.tp.hostctl) + plocal_tx_pd->tx_control |= + pmbuf->u.tx_info.tx_power.val; + if (pmbuf->u.tx_info.retry_limit) { + plocal_tx_pd->tx_control |= + pmbuf->u.tx_info.retry_limit << 8; + plocal_tx_pd->tx_control |= TXPD_RETRY_ENABLE; + } + } + + uap_endian_convert_TxPD(plocal_tx_pd); + + /* Adjust the data offset and length to include TxPD in pmbuf */ + pmbuf->data_len += pmbuf->data_offset; + pmbuf->data_offset = (t_u32)((t_ptr)head_ptr - (t_ptr)pmbuf->pbuf); + pmbuf->data_len -= pmbuf->data_offset; + +done: + LEAVE(); + return head_ptr; +} + +/** + * @brief This function processes received packet and forwards it + * to kernel/upper layer + * + * @param adapter A pointer to mlan_adapter + * @param pmbuf A pointer to mlan_buffer which includes the received packet + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_ops_uap_process_rx_packet(IN t_void *adapter, IN pmlan_buffer pmbuf) +{ + pmlan_adapter pmadapter = (pmlan_adapter)adapter; + mlan_status ret = MLAN_STATUS_SUCCESS; + UapRxPD *prx_pd; + wlan_mgmt_pkt *puap_pkt_hdr = MNULL; + + RxPacketHdr_t *prx_pkt; + pmlan_private priv = pmadapter->priv[pmbuf->bss_index]; + t_u8 ta[MLAN_MAC_ADDR_LENGTH]; + t_u16 rx_pkt_type = 0; + sta_node *sta_ptr = MNULL; +#ifdef DRV_EMBEDDED_AUTHENTICATOR + t_u8 eapol_type[2] = { 0x88, 0x8e }; +#endif + t_u8 adj_rx_rate = 0; + t_u8 antenna = 0; + rxpd_extra_info *pextra_info = MNULL; + + ENTER(); + + prx_pd = (UapRxPD *)(pmbuf->pbuf + pmbuf->data_offset); + /* Endian conversion */ + uap_endian_convert_RxPD(prx_pd); + priv->rxpd_rate = prx_pd->rx_rate; + if (prx_pd->flags & RXPD_FLAG_EXTRA_HEADER) { + pextra_info = + (rxpd_extra_info *) ((t_u8 *)prx_pd + sizeof(*prx_pd)); + endian_convert_RxPD_extra_header(pextra_info); + } + + priv->rxpd_rate_info = prx_pd->rate_info; + + if (priv->bss_type == MLAN_BSS_TYPE_UAP) { + antenna = wlan_adjust_antenna(priv, (RxPD *)prx_pd); + adj_rx_rate = + wlan_adjust_data_rate(priv, priv->rxpd_rate, + priv->rxpd_rate_info); + pmadapter->callbacks.moal_hist_data_add(pmadapter->pmoal_handle, + pmbuf->bss_index, + adj_rx_rate, + prx_pd->snr, prx_pd->nf, + antenna); + } + + if (priv->rx_pkt_info) { + pmbuf->u.rx_info.data_rate = + wlan_index_to_data_rate(priv->adapter, prx_pd->rx_rate, + prx_pd->rate_info); + pmbuf->u.rx_info.channel = + (prx_pd->rx_info & RXPD_CHAN_MASK) >> 5; + pmbuf->u.rx_info.antenna = prx_pd->antenna; + pmbuf->u.rx_info.rssi = prx_pd->snr - prx_pd->nf; + } + + rx_pkt_type = prx_pd->rx_pkt_type; + prx_pkt = (RxPacketHdr_t *)((t_u8 *)prx_pd + prx_pd->rx_pkt_offset); + + PRINTM(MINFO, + "RX Data: data_len - prx_pd->rx_pkt_offset = %d - %d = %d\n", + pmbuf->data_len, prx_pd->rx_pkt_offset, + pmbuf->data_len - prx_pd->rx_pkt_offset); + + if ((prx_pd->rx_pkt_offset + prx_pd->rx_pkt_length) != + (t_u16)pmbuf->data_len) { + PRINTM(MERROR, + "Wrong rx packet: len=%d,rx_pkt_offset=%d," + " rx_pkt_length=%d\n", pmbuf->data_len, + prx_pd->rx_pkt_offset, prx_pd->rx_pkt_length); + pmbuf->status_code = MLAN_ERROR_PKT_SIZE_INVALID; + ret = MLAN_STATUS_FAILURE; + wlan_free_mlan_buffer(pmadapter, pmbuf); + goto done; + } + pmbuf->data_len = prx_pd->rx_pkt_offset + prx_pd->rx_pkt_length; + + if (pmadapter->priv[pmbuf->bss_index]->mgmt_frame_passthru_mask && + prx_pd->rx_pkt_type == PKT_TYPE_MGMT_FRAME) { + /* Check if this is mgmt packet and needs to + * forwarded to app as an event + */ + puap_pkt_hdr = + (wlan_mgmt_pkt *)((t_u8 *)prx_pd + + prx_pd->rx_pkt_offset); + puap_pkt_hdr->frm_len = wlan_le16_to_cpu(puap_pkt_hdr->frm_len); + if ((puap_pkt_hdr->wlan_header. + frm_ctl & IEEE80211_FC_MGMT_FRAME_TYPE_MASK) == 0) + wlan_process_802dot11_mgmt_pkt(pmadapter-> + priv[pmbuf->bss_index], + (t_u8 *)&puap_pkt_hdr-> + wlan_header, + puap_pkt_hdr->frm_len + + sizeof(wlan_mgmt_pkt) - + sizeof(puap_pkt_hdr-> + frm_len), + (RxPD *)prx_pd); + wlan_free_mlan_buffer(pmadapter, pmbuf); + goto done; + } +#ifdef DRV_EMBEDDED_AUTHENTICATOR + /**process eapol packet for uap*/ + if (IsAuthenticatorEnabled(priv->psapriv) && + (!memcmp(pmadapter, &prx_pkt->eth803_hdr.h803_len, + eapol_type, sizeof(eapol_type)))) { + ret = AuthenticatorProcessEapolPacket(priv->psapriv, + ((t_u8 *)prx_pd + + prx_pd->rx_pkt_offset), + prx_pd->rx_pkt_length); + if (ret == MLAN_STATUS_SUCCESS) { + wlan_free_mlan_buffer(pmadapter, pmbuf); + goto done; + } + } +#endif + + pmbuf->priority = prx_pd->priority; + memcpy(pmadapter, ta, prx_pkt->eth803_hdr.src_addr, + MLAN_MAC_ADDR_LENGTH); + if ((rx_pkt_type != PKT_TYPE_BAR) && (prx_pd->priority < MAX_NUM_TID)) { + sta_ptr = wlan_get_station_entry(priv, ta); + if (sta_ptr) + sta_ptr->rx_seq[prx_pd->priority] = prx_pd->seq_num; + } + /* check if UAP enable 11n */ + if (!priv->is_11n_enabled || + (!wlan_11n_get_rxreorder_tbl + ((mlan_private *)priv, prx_pd->priority, ta) + && (prx_pd->rx_pkt_type != PKT_TYPE_AMSDU) + )) { + if (priv->pkt_fwd) + wlan_process_uap_rx_packet(priv, pmbuf); + else + wlan_upload_uap_rx_packet(pmadapter, pmbuf); + goto done; + } + /* Reorder and send to OS */ + ret = mlan_11n_rxreorder_pkt(priv, prx_pd->seq_num, + prx_pd->priority, ta, + (t_u8)prx_pd->rx_pkt_type, (void *)pmbuf); + if (ret || (rx_pkt_type == PKT_TYPE_BAR)) { + wlan_free_mlan_buffer(pmadapter, pmbuf); + } +done: + LEAVE(); + return ret; +} + +/** + * @brief This function processes received packet and forwards it + * to kernel/upper layer or send back to firmware + * + * @param priv A pointer to mlan_private + * @param pmbuf A pointer to mlan_buffer which includes the received packet + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_uap_recv_packet(IN mlan_private *priv, IN pmlan_buffer pmbuf) +{ + pmlan_adapter pmadapter = priv->adapter; + mlan_status ret = MLAN_STATUS_SUCCESS; + RxPacketHdr_t *prx_pkt; + pmlan_buffer newbuf = MNULL; + + ENTER(); + + prx_pkt = (RxPacketHdr_t *)((t_u8 *)pmbuf->pbuf + pmbuf->data_offset); + + DBG_HEXDUMP(MDAT_D, "uap_recv_packet", pmbuf->pbuf + pmbuf->data_offset, + MIN(pmbuf->data_len, MAX_DATA_DUMP_LEN)); + + PRINTM(MDATA, "AMSDU dest " MACSTR "\n", + MAC2STR(prx_pkt->eth803_hdr.dest_addr)); + + /* don't do packet forwarding in disconnected state */ + if ((priv->media_connected == MFALSE) || + (pmbuf->data_len > MV_ETH_FRAME_LEN)) + goto upload; + + if (prx_pkt->eth803_hdr.dest_addr[0] & 0x01) { + if (!(priv->pkt_fwd & PKT_FWD_INTRA_BCAST)) { + /* Multicast pkt */ + newbuf = wlan_alloc_mlan_buffer(pmadapter, + MLAN_TX_DATA_BUF_SIZE_2K, + 0, MOAL_MALLOC_BUFFER); + if (newbuf) { + newbuf->bss_index = pmbuf->bss_index; + newbuf->buf_type = pmbuf->buf_type; + newbuf->priority = pmbuf->priority; + newbuf->in_ts_sec = pmbuf->in_ts_sec; + newbuf->in_ts_usec = pmbuf->in_ts_usec; + newbuf->data_offset = + (sizeof(UapTxPD) + priv->intf_hr_len + + DMA_ALIGNMENT); + util_scalar_increment(pmadapter->pmoal_handle, + &pmadapter-> + pending_bridge_pkts, + pmadapter->callbacks. + moal_spin_lock, + pmadapter->callbacks. + moal_spin_unlock); + + newbuf->flags |= MLAN_BUF_FLAG_BRIDGE_BUF; + + /* copy the data */ + memcpy(pmadapter, + (t_u8 *)newbuf->pbuf + + newbuf->data_offset, + pmbuf->pbuf + pmbuf->data_offset, + pmbuf->data_len); + newbuf->data_len = pmbuf->data_len; + wlan_wmm_add_buf_txqueue(pmadapter, newbuf); + if (util_scalar_read(pmadapter->pmoal_handle, + &pmadapter-> + pending_bridge_pkts, + pmadapter->callbacks. + moal_spin_lock, + pmadapter->callbacks. + moal_spin_unlock) > + RX_HIGH_THRESHOLD) + wlan_drop_tx_pkts(priv); + wlan_recv_event(priv, + MLAN_EVENT_ID_DRV_DEFER_HANDLING, + MNULL); + } + } + } else { + if ((!(priv->pkt_fwd & PKT_FWD_INTRA_UCAST)) && + (wlan_get_station_entry + (priv, prx_pkt->eth803_hdr.dest_addr))) { + /* Intra BSS packet */ + newbuf = wlan_alloc_mlan_buffer(pmadapter, + MLAN_TX_DATA_BUF_SIZE_2K, + 0, MOAL_MALLOC_BUFFER); + if (newbuf) { + newbuf->bss_index = pmbuf->bss_index; + newbuf->buf_type = pmbuf->buf_type; + newbuf->priority = pmbuf->priority; + newbuf->in_ts_sec = pmbuf->in_ts_sec; + newbuf->in_ts_usec = pmbuf->in_ts_usec; + newbuf->data_offset = + (sizeof(UapTxPD) + priv->intf_hr_len + + DMA_ALIGNMENT); + util_scalar_increment(pmadapter->pmoal_handle, + &pmadapter-> + pending_bridge_pkts, + pmadapter->callbacks. + moal_spin_lock, + pmadapter->callbacks. + moal_spin_unlock); + newbuf->flags |= MLAN_BUF_FLAG_BRIDGE_BUF; + + /* copy the data */ + memcpy(pmadapter, + (t_u8 *)newbuf->pbuf + + newbuf->data_offset, + pmbuf->pbuf + pmbuf->data_offset, + pmbuf->data_len); + newbuf->data_len = pmbuf->data_len; + wlan_wmm_add_buf_txqueue(pmadapter, newbuf); + if (util_scalar_read(pmadapter->pmoal_handle, + &pmadapter-> + pending_bridge_pkts, + pmadapter->callbacks. + moal_spin_lock, + pmadapter->callbacks. + moal_spin_unlock) > + RX_HIGH_THRESHOLD) + wlan_drop_tx_pkts(priv); + wlan_recv_event(priv, + MLAN_EVENT_ID_DRV_DEFER_HANDLING, + MNULL); + } + goto done; + } else if (MLAN_STATUS_FAILURE == + wlan_check_unicast_packet(priv, + prx_pkt->eth803_hdr. + dest_addr)) { + /* drop packet */ + PRINTM(MDATA, "Drop AMSDU dest " MACSTR "\n", + MAC2STR(prx_pkt->eth803_hdr.dest_addr)); + goto done; + } + } +upload: + /** send packet to moal */ + ret = pmadapter->callbacks.moal_recv_packet(pmadapter->pmoal_handle, + pmbuf); +done: + LEAVE(); + return ret; +} + +/** + * @brief This function processes received packet and forwards it + * to kernel/upper layer or send back to firmware + * + * @param priv A pointer to mlan_private + * @param pmbuf A pointer to mlan_buffer which includes the received packet + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +wlan_process_uap_rx_packet(IN mlan_private *priv, IN pmlan_buffer pmbuf) +{ + pmlan_adapter pmadapter = priv->adapter; + mlan_status ret = MLAN_STATUS_SUCCESS; + UapRxPD *prx_pd; + RxPacketHdr_t *prx_pkt; + pmlan_buffer newbuf = MNULL; + + ENTER(); + + prx_pd = (UapRxPD *)(pmbuf->pbuf + pmbuf->data_offset); + prx_pkt = (RxPacketHdr_t *)((t_u8 *)prx_pd + prx_pd->rx_pkt_offset); + + DBG_HEXDUMP(MDAT_D, "uAP RxPD", prx_pd, + MIN(sizeof(UapRxPD), MAX_DATA_DUMP_LEN)); + DBG_HEXDUMP(MDAT_D, "uAP Rx Payload", + ((t_u8 *)prx_pd + prx_pd->rx_pkt_offset), + MIN(prx_pd->rx_pkt_length, MAX_DATA_DUMP_LEN)); + + PRINTM(MINFO, + "RX Data: data_len - prx_pd->rx_pkt_offset = %d - %d = %d\n", + pmbuf->data_len, prx_pd->rx_pkt_offset, + pmbuf->data_len - prx_pd->rx_pkt_offset); + PRINTM(MDATA, "Rx dest " MACSTR "\n", + MAC2STR(prx_pkt->eth803_hdr.dest_addr)); + + if (pmadapter->enable_net_mon) { + pmbuf->flags |= MLAN_BUF_FLAG_NET_MONITOR; + goto upload; + } + + /* don't do packet forwarding in disconnected state */ + /* don't do packet forwarding when packet > 1514 */ + if ((priv->media_connected == MFALSE) || + ((pmbuf->data_len - prx_pd->rx_pkt_offset) > MV_ETH_FRAME_LEN)) + goto upload; + + if (prx_pkt->eth803_hdr.dest_addr[0] & 0x01) { + if (!(priv->pkt_fwd & PKT_FWD_INTRA_BCAST)) { + /* Multicast pkt */ + newbuf = wlan_alloc_mlan_buffer(pmadapter, + MLAN_TX_DATA_BUF_SIZE_2K, + 0, MOAL_MALLOC_BUFFER); + if (newbuf) { + newbuf->bss_index = pmbuf->bss_index; + newbuf->buf_type = pmbuf->buf_type; + newbuf->priority = pmbuf->priority; + newbuf->in_ts_sec = pmbuf->in_ts_sec; + newbuf->in_ts_usec = pmbuf->in_ts_usec; + newbuf->data_offset = + (sizeof(UapTxPD) + priv->intf_hr_len + + DMA_ALIGNMENT); + util_scalar_increment(pmadapter->pmoal_handle, + &pmadapter-> + pending_bridge_pkts, + pmadapter->callbacks. + moal_spin_lock, + pmadapter->callbacks. + moal_spin_unlock); + newbuf->flags |= MLAN_BUF_FLAG_BRIDGE_BUF; + + /* copy the data, skip rxpd */ + memcpy(pmadapter, + (t_u8 *)newbuf->pbuf + + newbuf->data_offset, + pmbuf->pbuf + pmbuf->data_offset + + prx_pd->rx_pkt_offset, + pmbuf->data_len - prx_pd->rx_pkt_offset); + newbuf->data_len = + pmbuf->data_len - prx_pd->rx_pkt_offset; + wlan_wmm_add_buf_txqueue(pmadapter, newbuf); + if (util_scalar_read(pmadapter->pmoal_handle, + &pmadapter-> + pending_bridge_pkts, + pmadapter->callbacks. + moal_spin_lock, + pmadapter->callbacks. + moal_spin_unlock) > + RX_HIGH_THRESHOLD) + wlan_drop_tx_pkts(priv); + wlan_recv_event(priv, + MLAN_EVENT_ID_DRV_DEFER_HANDLING, + MNULL); + } + } + } else { + if ((!(priv->pkt_fwd & PKT_FWD_INTRA_UCAST)) && + (wlan_get_station_entry + (priv, prx_pkt->eth803_hdr.dest_addr))) { + /* Forwarding Intra-BSS packet */ + pmbuf->data_len -= prx_pd->rx_pkt_offset; + pmbuf->data_offset += prx_pd->rx_pkt_offset; + pmbuf->flags |= MLAN_BUF_FLAG_BRIDGE_BUF; + util_scalar_increment(pmadapter->pmoal_handle, + &pmadapter->pending_bridge_pkts, + pmadapter->callbacks. + moal_spin_lock, + pmadapter->callbacks. + moal_spin_unlock); + wlan_wmm_add_buf_txqueue(pmadapter, pmbuf); + if (util_scalar_read(pmadapter->pmoal_handle, + &pmadapter->pending_bridge_pkts, + pmadapter->callbacks. + moal_spin_lock, + pmadapter->callbacks. + moal_spin_unlock) > + RX_HIGH_THRESHOLD) + wlan_drop_tx_pkts(priv); + wlan_recv_event(priv, MLAN_EVENT_ID_DRV_DEFER_HANDLING, + MNULL); + goto done; + } else if (MLAN_STATUS_FAILURE == + wlan_check_unicast_packet(priv, + prx_pkt->eth803_hdr. + dest_addr)) { + PRINTM(MDATA, "Drop Pkts: Rx dest " MACSTR "\n", + MAC2STR(prx_pkt->eth803_hdr.dest_addr)); + pmbuf->status_code = MLAN_ERROR_PKT_INVALID; + wlan_free_mlan_buffer(pmadapter, pmbuf); + goto done; + } + } + +upload: + /* Chop off RxPD */ + pmbuf->data_len -= prx_pd->rx_pkt_offset; + pmbuf->data_offset += prx_pd->rx_pkt_offset; + pmbuf->pparent = MNULL; + + pmadapter->callbacks.moal_get_system_time(pmadapter->pmoal_handle, + &pmbuf->out_ts_sec, + &pmbuf->out_ts_usec); + PRINTM_NETINTF(MDATA, priv); + PRINTM(MDATA, "%lu.%06lu : Data => kernel seq_num=%d tid=%d\n", + pmbuf->out_ts_sec, pmbuf->out_ts_usec, prx_pd->seq_num, + prx_pd->priority); + if (pmbuf->flags & MLAN_BUF_FLAG_NET_MONITOR) { + //Use some rxpd space to save rxpd info for radiotap header + //We should insure radiotap_info is not bigger than RxPD + wlan_rxpdinfo_to_radiotapinfo(priv, (RxPD *)prx_pd, + (radiotap_info *) (pmbuf->pbuf + + pmbuf-> + data_offset - + sizeof + (radiotap_info))); + } + + ret = pmadapter->callbacks.moal_recv_packet(pmadapter->pmoal_handle, + pmbuf); + if (ret == MLAN_STATUS_FAILURE) { + PRINTM(MERROR, + "uAP Rx Error: moal_recv_packet returned error\n"); + pmbuf->status_code = MLAN_ERROR_PKT_INVALID; + } + + if (ret != MLAN_STATUS_PENDING) + wlan_free_mlan_buffer(pmadapter, pmbuf); +done: + LEAVE(); + return ret; +} diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_util.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_util.h new file mode 100644 index 000000000000..9b1914c590be --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_util.h @@ -0,0 +1,537 @@ +/** @file mlan_util.h + * + * @brief This file contains wrappers for linked-list, + * spinlock and timer defines. + * + * Copyright (C) 2008-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 10/28/2008: initial version +******************************************************/ + +#ifndef _MLAN_UTIL_H_ +#define _MLAN_UTIL_H_ + +/** Circular doubly linked list */ +typedef struct _mlan_linked_list { + /** Pointer to previous node */ + struct _mlan_linked_list *pprev; + /** Pointer to next node */ + struct _mlan_linked_list *pnext; +} mlan_linked_list, *pmlan_linked_list; + +/** List head */ +typedef struct _mlan_list_head { + /** Pointer to previous node */ + struct _mlan_linked_list *pprev; + /** Pointer to next node */ + struct _mlan_linked_list *pnext; + /** Pointer to lock */ + t_void *plock; +} mlan_list_head, *pmlan_list_head; + +/** + * @brief This function initializes a list without locking + * + * @param phead List head + * + * @return N/A + */ +static INLINE t_void +util_init_list(pmlan_linked_list phead) +{ + /* Both next and prev point to self */ + phead->pprev = phead->pnext = (pmlan_linked_list)phead; +} + +/** + * @brief This function initializes a list + * + * @param phead List head + * @param lock_required A flag for spinlock requirement + * @param moal_init_lock A pointer to init lock handler + * + * @return N/A + */ +static INLINE t_void +util_init_list_head(t_void *pmoal_handle, + pmlan_list_head phead, + t_u8 lock_required, + mlan_status (*moal_init_lock) (t_void *handle, + t_void **pplock)) +{ + /* Both next and prev point to self */ + util_init_list((pmlan_linked_list)phead); + if (lock_required) + moal_init_lock(pmoal_handle, &phead->plock); + else + phead->plock = 0; +} + +/** + * @brief This function frees a list + * + * @param phead List head + * @param moal_free_lock A pointer to free lock handler + * + * @return N/A + */ +static INLINE t_void +util_free_list_head(t_void *pmoal_handle, + pmlan_list_head phead, + mlan_status (*moal_free_lock) (t_void *handle, + t_void *plock)) +{ + phead->pprev = phead->pnext = 0; + if (phead->plock) + moal_free_lock(pmoal_handle, phead->plock); +} + +/** + * @brief This function peeks into a list + * + * @param phead List head + * @param moal_spin_lock A pointer to spin lock handler + * @param moal_spin_unlock A pointer to spin unlock handler + * + * @return List node + */ +static INLINE pmlan_linked_list +util_peek_list(t_void *pmoal_handle, + pmlan_list_head phead, + mlan_status (*moal_spin_lock) (t_void *handle, t_void *plock), + mlan_status (*moal_spin_unlock) (t_void *handle, t_void *plock)) +{ + pmlan_linked_list pnode = 0; + + if (moal_spin_lock) + moal_spin_lock(pmoal_handle, phead->plock); + if (phead->pnext != (pmlan_linked_list)phead) + pnode = phead->pnext; + if (moal_spin_unlock) + moal_spin_unlock(pmoal_handle, phead->plock); + return pnode; +} + +/** + * @brief This function queues a node at the list tail + * + * @param phead List head + * @param pnode List node to queue + * @param moal_spin_lock A pointer to spin lock handler + * @param moal_spin_unlock A pointer to spin unlock handler + * + * @return N/A + */ +static INLINE t_void +util_enqueue_list_tail(t_void *pmoal_handle, + pmlan_list_head phead, + pmlan_linked_list pnode, + mlan_status (*moal_spin_lock) (t_void *handle, + t_void *plock), + mlan_status (*moal_spin_unlock) (t_void *handle, + t_void *plock)) +{ + pmlan_linked_list pold_last; + + if (moal_spin_lock) + moal_spin_lock(pmoal_handle, phead->plock); + pold_last = phead->pprev; + pnode->pprev = pold_last; + pnode->pnext = (pmlan_linked_list)phead; + + phead->pprev = pold_last->pnext = pnode; + if (moal_spin_unlock) + moal_spin_unlock(pmoal_handle, phead->plock); +} + +/** + * @brief This function adds a node at the list head + * + * @param phead List head + * @param pnode List node to add + * @param moal_spin_lock A pointer to spin lock handler + * @param moal_spin_unlock A pointer to spin unlock handler + * + * @return N/A + */ +static INLINE t_void +util_enqueue_list_head(t_void *pmoal_handle, + pmlan_list_head phead, + pmlan_linked_list pnode, + mlan_status (*moal_spin_lock) (t_void *handle, + t_void *plock), + mlan_status (*moal_spin_unlock) (t_void *handle, + t_void *plock)) +{ + pmlan_linked_list pold_first; + + if (moal_spin_lock) + moal_spin_lock(pmoal_handle, phead->plock); + pold_first = phead->pnext; + pnode->pprev = (pmlan_linked_list)phead; + pnode->pnext = pold_first; + + phead->pnext = pold_first->pprev = pnode; + if (moal_spin_unlock) + moal_spin_unlock(pmoal_handle, phead->plock); +} + +/** + * @brief This function removes a node from the list + * + * @param phead List head + * @param pnode List node to remove + * @param moal_spin_lock A pointer to spin lock handler + * @param moal_spin_unlock A pointer to spin unlock handler + * + * @return N/A + */ +static INLINE t_void +util_unlink_list(t_void *pmoal_handle, + pmlan_list_head phead, + pmlan_linked_list pnode, + mlan_status (*moal_spin_lock) (t_void *handle, t_void *plock), + mlan_status (*moal_spin_unlock) (t_void *handle, + t_void *plock)) +{ + pmlan_linked_list pmy_prev; + pmlan_linked_list pmy_next; + + if (moal_spin_lock) + moal_spin_lock(pmoal_handle, phead->plock); + pmy_prev = pnode->pprev; + pmy_next = pnode->pnext; + pmy_next->pprev = pmy_prev; + pmy_prev->pnext = pmy_next; + + pnode->pnext = pnode->pprev = 0; + if (moal_spin_unlock) + moal_spin_unlock(pmoal_handle, phead->plock); +} + +/** + * @brief This function dequeues a node from the list + * + * @param phead List head + * @param moal_spin_lock A pointer to spin lock handler + * @param moal_spin_unlock A pointer to spin unlock handler + * + * @return List node + */ +static INLINE pmlan_linked_list +util_dequeue_list(t_void *pmoal_handle, + pmlan_list_head phead, + mlan_status (*moal_spin_lock) (t_void *handle, t_void *plock), + mlan_status (*moal_spin_unlock) (t_void *handle, + t_void *plock)) +{ + pmlan_linked_list pnode; + + if (moal_spin_lock) + moal_spin_lock(pmoal_handle, phead->plock); + pnode = phead->pnext; + if (pnode && (pnode != (pmlan_linked_list)phead)) + util_unlink_list(pmoal_handle, phead, pnode, 0, 0); + else + pnode = 0; + if (moal_spin_unlock) + moal_spin_unlock(pmoal_handle, phead->plock); + return pnode; +} + +/** Access controlled scalar variable */ +typedef struct _mlan_scalar { + /** Value */ + t_s32 value; + /** Pointer to lock */ + t_void *plock; + /** Control flags */ + t_u32 flags; +} mlan_scalar, *pmlan_scalar; + +/** Flag to scalar lock acquired */ +#define MLAN_SCALAR_FLAG_UNIQUE_LOCK MBIT(16) + +/** scalar conditional value list */ +typedef enum _MLAN_SCALAR_CONDITIONAL { + MLAN_SCALAR_COND_EQUAL, + MLAN_SCALAR_COND_NOT_EQUAL, + MLAN_SCALAR_COND_GREATER_THAN, + MLAN_SCALAR_COND_GREATER_OR_EQUAL, + MLAN_SCALAR_COND_LESS_THAN, + MLAN_SCALAR_COND_LESS_OR_EQUAL +} MLAN_SCALAR_CONDITIONAL; + +/** + * @brief This function initializes a scalar + * + * @param pscalar Pointer to scalar + * @param val Initial scalar value + * @param plock_to_use A new lock is created if NULL, else lock to use + * @param moal_init_lock A pointer to init lock handler + * + * @return N/A + */ +static INLINE t_void +util_scalar_init(t_void *pmoal_handle, + pmlan_scalar pscalar, + t_s32 val, + t_void *plock_to_use, + mlan_status (*moal_init_lock) (t_void *handle, + t_void **pplock)) +{ + pscalar->value = val; + pscalar->flags = 0; + if (plock_to_use) { + pscalar->flags &= ~MLAN_SCALAR_FLAG_UNIQUE_LOCK; + pscalar->plock = plock_to_use; + } else { + pscalar->flags |= MLAN_SCALAR_FLAG_UNIQUE_LOCK; + moal_init_lock(pmoal_handle, &pscalar->plock); + } +} + +/** + * @brief This function frees a scalar + * + * @param pscalar Pointer to scalar + * @param moal_free_lock A pointer to free lock handler + * + * @return N/A + */ +static INLINE t_void +util_scalar_free(t_void *pmoal_handle, + pmlan_scalar pscalar, + mlan_status (*moal_free_lock) (t_void *handle, t_void *plock)) +{ + if (pscalar->flags & MLAN_SCALAR_FLAG_UNIQUE_LOCK) + moal_free_lock(pmoal_handle, pscalar->plock); +} + +/** + * @brief This function reads value from scalar + * + * @param pscalar Pointer to scalar + * @param moal_spin_lock A pointer to spin lock handler + * @param moal_spin_unlock A pointer to spin unlock handler + * + * @return Stored value + */ +static INLINE t_s32 +util_scalar_read(t_void *pmoal_handle, + pmlan_scalar pscalar, + mlan_status (*moal_spin_lock) (t_void *handle, t_void *plock), + mlan_status (*moal_spin_unlock) (t_void *handle, + t_void *plock)) +{ + t_s32 val; + + if (moal_spin_lock) + moal_spin_lock(pmoal_handle, pscalar->plock); + val = pscalar->value; + if (moal_spin_unlock) + moal_spin_unlock(pmoal_handle, pscalar->plock); + + return val; +} + +/** + * @brief This function writes value to scalar + * + * @param pscalar Pointer to scalar + * @param val Value to write + * @param moal_spin_lock A pointer to spin lock handler + * @param moal_spin_unlock A pointer to spin unlock handler + * + * @return N/A + */ +static INLINE t_void +util_scalar_write(t_void *pmoal_handle, + pmlan_scalar pscalar, + t_s32 val, + mlan_status (*moal_spin_lock) (t_void *handle, t_void *plock), + mlan_status (*moal_spin_unlock) (t_void *handle, + t_void *plock)) +{ + if (moal_spin_lock) + moal_spin_lock(pmoal_handle, pscalar->plock); + pscalar->value = val; + if (moal_spin_unlock) + moal_spin_unlock(pmoal_handle, pscalar->plock); +} + +/** + * @brief This function increments the value in scalar + * + * @param pscalar Pointer to scalar + * @param moal_spin_lock A pointer to spin lock handler + * @param moal_spin_unlock A pointer to spin unlock handler + * + * @return N/A + */ +static INLINE t_void +util_scalar_increment(t_void *pmoal_handle, + pmlan_scalar pscalar, + mlan_status (*moal_spin_lock) (t_void *handle, + t_void *plock), + mlan_status (*moal_spin_unlock) (t_void *handle, + t_void *plock)) +{ + if (moal_spin_lock) + moal_spin_lock(pmoal_handle, pscalar->plock); + pscalar->value++; + if (moal_spin_unlock) + moal_spin_unlock(pmoal_handle, pscalar->plock); +} + +/** + * @brief This function decrements the value in scalar + * + * @param pscalar Pointer to scalar + * @param moal_spin_lock A pointer to spin lock handler + * @param moal_spin_unlock A pointer to spin unlock handler + * + * @return N/A + */ +static INLINE t_void +util_scalar_decrement(t_void *pmoal_handle, + pmlan_scalar pscalar, + mlan_status (*moal_spin_lock) (t_void *handle, + t_void *plock), + mlan_status (*moal_spin_unlock) (t_void *handle, + t_void *plock)) +{ + if (moal_spin_lock) + moal_spin_lock(pmoal_handle, pscalar->plock); + pscalar->value--; + if (moal_spin_unlock) + moal_spin_unlock(pmoal_handle, pscalar->plock); +} + +/** + * @brief This function adds an offset to the value in scalar, + * and returns the new value + * + * @param pscalar Pointer to scalar + * @param offset Offset value (can be negative) + * @param moal_spin_lock A pointer to spin lock handler + * @param moal_spin_unlock A pointer to spin unlock handler + * + * @return Value after offset + */ +static INLINE t_s32 +util_scalar_offset(t_void *pmoal_handle, + pmlan_scalar pscalar, + t_s32 offset, + mlan_status (*moal_spin_lock) (t_void *handle, + t_void *plock), + mlan_status (*moal_spin_unlock) (t_void *handle, + t_void *plock)) +{ + t_s32 newval; + + if (moal_spin_lock) + moal_spin_lock(pmoal_handle, pscalar->plock); + newval = (pscalar->value += offset); + if (moal_spin_unlock) + moal_spin_unlock(pmoal_handle, pscalar->plock); + + return newval; +} + +/** + * @brief This function writes the value to the scalar + * if existing value compared with other value is true. + * + * @param pscalar Pointer to scalar + * @param condition Condition to check + * @param val_compare Value to compare against current value + * ((A X B), where B = val_compare) + * @param val_to_set Value to set if comparison is true + * @param moal_spin_lock A pointer to spin lock handler + * @param moal_spin_unlock A pointer to spin unlock handler + * + * @return Comparison result (MTRUE or MFALSE) + */ +static INLINE t_u8 +util_scalar_conditional_write(t_void *pmoal_handle, + pmlan_scalar pscalar, + MLAN_SCALAR_CONDITIONAL condition, + t_s32 val_compare, + t_s32 val_to_set, + mlan_status (*moal_spin_lock) (t_void *handle, + t_void *plock), + mlan_status (*moal_spin_unlock) (t_void *handle, + t_void *plock)) +{ + t_u8 update; + if (moal_spin_lock) + moal_spin_lock(pmoal_handle, pscalar->plock); + + switch (condition) { + case MLAN_SCALAR_COND_EQUAL: + update = (pscalar->value == val_compare); + break; + case MLAN_SCALAR_COND_NOT_EQUAL: + update = (pscalar->value != val_compare); + break; + case MLAN_SCALAR_COND_GREATER_THAN: + update = (pscalar->value > val_compare); + break; + case MLAN_SCALAR_COND_GREATER_OR_EQUAL: + update = (pscalar->value >= val_compare); + break; + case MLAN_SCALAR_COND_LESS_THAN: + update = (pscalar->value < val_compare); + break; + case MLAN_SCALAR_COND_LESS_OR_EQUAL: + update = (pscalar->value <= val_compare); + break; + default: + update = MFALSE; + break; + } + if (update) + pscalar->value = val_to_set; + + if (moal_spin_unlock) + moal_spin_unlock(pmoal_handle, pscalar->plock); + return (update) ? MTRUE : MFALSE; +} + +/** + * @brief This function counts the bits of unsigned int number + * + * @param num number + * @return number of bits + */ +static t_u32 INLINE +bitcount(t_u32 num) +{ + t_u32 count = 0; + static t_u32 nibblebits[] = { + 0, 1, 1, 2, 1, 2, 2, 3, + 1, 2, 2, 3, 2, 3, 3, 4 + }; + for (; num != 0; num >>= 4) + count += nibblebits[num & 0x0f]; + return count; +} + +#endif /* !_MLAN_UTIL_H_ */ diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_wmm.c b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_wmm.c new file mode 100644 index 000000000000..0c7bedf80033 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_wmm.c @@ -0,0 +1,3752 @@ +/** @file mlan_wmm.c + * + * @brief This file contains functions for WMM. + * + * Copyright (C) 2008-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/******************************************************** +Change log: + 10/24/2008: initial version +********************************************************/ + +#include "mlan.h" +#ifdef STA_SUPPORT +#include "mlan_join.h" +#endif +#include "mlan_util.h" +#include "mlan_fw.h" +#include "mlan_main.h" +#include "mlan_wmm.h" +#include "mlan_11n.h" +#include "mlan_sdio.h" + +/******************************************************** + Local Variables +********************************************************/ + +/** Maximum value FW can accept for driver delay in packet transmission */ +#define DRV_PKT_DELAY_TO_FW_MAX 512 + +/* + * Upper and Lower threshold for packet queuing in the driver + + * - When the number of packets queued reaches the upper limit, + * the driver will stop the net queue in the app/kernel space. + + * - When the number of packets drops beneath the lower limit after + * having reached the upper limit, the driver will restart the net + * queue. + */ + +/** Lower threshold for packet queuing in the driver. + * When the number of packets drops beneath the lower limit after having + * reached the upper limit, the driver will restart the net queue. + */ +#define WMM_QUEUED_PACKET_LOWER_LIMIT 180 + +/** Upper threshold for packet queuing in the driver. + * When the number of packets queued reaches the upper limit, the driver + * will stop the net queue in the app/kernel space. + */ +#define WMM_QUEUED_PACKET_UPPER_LIMIT 200 + +/** Offset for TOS field in the IP header */ +#define IPTOS_OFFSET 5 + +/** WMM information IE */ +static const t_u8 wmm_info_ie[] = { WMM_IE, 0x07, + 0x00, 0x50, 0xf2, 0x02, + 0x00, 0x01, 0x00 +}; + +/** Type enumeration of WMM AC_QUEUES */ +typedef MLAN_PACK_START enum _wmm_ac_e { + AC_BE, + AC_BK, + AC_VI, + AC_VO +} MLAN_PACK_END wmm_ac_e; + +/** + * AC Priorities go from AC_BK to AC_VO. The ACI enumeration for AC_BK (1) + * is higher than the enumeration for AC_BE (0); hence the needed + * mapping conversion for wmm AC to priority Queue Index + */ +static const t_u8 wmm_aci_to_qidx_map[] = { WMM_AC_BE, + WMM_AC_BK, + WMM_AC_VI, + WMM_AC_VO +}; + +/** + * This table will be used to store the tid values based on ACs. + * It is initialized to default values per TID. + */ +t_u8 tos_to_tid[] = { + /* TID DSCP_P2 DSCP_P1 DSCP_P0 WMM_AC */ + 0x01, /* 0 1 0 AC_BK */ + 0x02, /* 0 0 0 AC_BK */ + 0x00, /* 0 0 1 AC_BE */ + 0x03, /* 0 1 1 AC_BE */ + 0x04, /* 1 0 0 AC_VI */ + 0x05, /* 1 0 1 AC_VI */ + 0x06, /* 1 1 0 AC_VO */ + 0x07 /* 1 1 1 AC_VO */ +}; + +/** + * This table inverses the tos_to_tid operation to get a priority + * which is in sequential order, and can be compared. + * Use this to compare the priority of two different TIDs. + */ +t_u8 tos_to_tid_inv[] = { 0x02, /* from tos_to_tid[2] = 0 */ + 0x00, /* from tos_to_tid[0] = 1 */ + 0x01, /* from tos_to_tid[1] = 2 */ + 0x03, + 0x04, + 0x05, + 0x06, + 0x07 +}; + +/** + * This table will provide the tid value for given ac. This table does not + * change and will be used to copy back the default values to tos_to_tid in + * case of disconnect. + */ +const t_u8 ac_to_tid[4][2] = { {1, 2}, {0, 3}, {4, 5}, {6, 7} }; + +/* Map of TOS UP values to WMM AC */ +static const mlan_wmm_ac_e tos_to_ac[] = { WMM_AC_BE, + WMM_AC_BK, + WMM_AC_BK, + WMM_AC_BE, + WMM_AC_VI, + WMM_AC_VI, + WMM_AC_VO, + WMM_AC_VO +}; + +raListTbl *wlan_wmm_get_ralist_node(pmlan_private priv, t_u8 tid, + t_u8 *ra_addr); + +/******************************************************** + Local Functions +********************************************************/ +#ifdef DEBUG_LEVEL2 +/** + * @brief Debug print function to display the priority parameters for a WMM AC + * + * @param pac_param Pointer to the AC parameters to display + * + * @return N/A + */ +static void +wlan_wmm_ac_debug_print(const IEEEtypes_WmmAcParameters_t *pac_param) +{ + const char *ac_str[] = { "BK", "BE", "VI", "VO" }; + + ENTER(); + + PRINTM(MINFO, "WMM AC_%s: ACI=%d, ACM=%d, Aifsn=%d, " + "EcwMin=%d, EcwMax=%d, TxopLimit=%d\n", + ac_str[wmm_aci_to_qidx_map[pac_param->aci_aifsn.aci]], + pac_param->aci_aifsn.aci, pac_param->aci_aifsn.acm, + pac_param->aci_aifsn.aifsn, pac_param->ecw.ecw_min, + pac_param->ecw.ecw_max, + wlan_le16_to_cpu(pac_param->tx_op_limit)); + + LEAVE(); +} + +/** Print the WMM AC for debug purpose */ +#define PRINTM_AC(pac_param) wlan_wmm_ac_debug_print(pac_param) +#else +/** Print the WMM AC for debug purpose */ +#define PRINTM_AC(pac_param) +#endif + +/** + * @brief Allocate route address + * + * @param pmadapter Pointer to the mlan_adapter structure + * @param ra Pointer to the route address + * + * @return ra_list + */ +static +raListTbl * +wlan_wmm_allocate_ralist_node(pmlan_adapter pmadapter, t_u8 *ra) +{ + raListTbl *ra_list = MNULL; + + ENTER(); + + if (pmadapter->callbacks. + moal_malloc(pmadapter->pmoal_handle, sizeof(raListTbl), + MLAN_MEM_DEF, (t_u8 **)&ra_list)) { + PRINTM(MERROR, "Fail to allocate ra_list\n"); + goto done; + } + util_init_list((pmlan_linked_list)ra_list); + util_init_list_head((t_void *)pmadapter->pmoal_handle, + &ra_list->buf_head, MFALSE, + pmadapter->callbacks.moal_init_lock); + + memcpy(pmadapter, ra_list->ra, ra, MLAN_MAC_ADDR_LENGTH); + + ra_list->del_ba_count = 0; + ra_list->total_pkts = 0; + ra_list->tx_pause = 0; + PRINTM(MINFO, "RAList: Allocating buffers for TID %p\n", ra_list); +done: + LEAVE(); + return ra_list; +} + +/** + * @brief Add packet to TDLS pending TX queue + * + * @param priv A pointer to mlan_private + * @param pmbuf Pointer to the mlan_buffer data struct + * + * @return N/A + */ +static t_void +wlan_add_buf_tdls_txqueue(pmlan_private priv, pmlan_buffer pmbuf) +{ + mlan_adapter *pmadapter = priv->adapter; + ENTER(); + util_enqueue_list_tail(pmadapter->pmoal_handle, &priv->tdls_pending_txq, + (pmlan_linked_list)pmbuf, + pmadapter->callbacks.moal_spin_lock, + pmadapter->callbacks.moal_spin_unlock); + LEAVE(); +} + +/** + * @brief Clean up the tdls pending TX queue + * + * @param priv A pointer to mlan_private + * + * @return N/A + */ +static t_void +wlan_cleanup_tdls_txq(pmlan_private priv) +{ + pmlan_buffer pmbuf; + mlan_adapter *pmadapter = priv->adapter; + ENTER(); + + pmadapter->callbacks.moal_spin_lock(pmadapter->pmoal_handle, + priv->tdls_pending_txq.plock); + while ((pmbuf = + (pmlan_buffer)util_peek_list(pmadapter->pmoal_handle, + &priv->tdls_pending_txq, MNULL, + MNULL))) { + util_unlink_list(pmadapter->pmoal_handle, + &priv->tdls_pending_txq, + (pmlan_linked_list)pmbuf, MNULL, MNULL); + wlan_write_data_complete(pmadapter, pmbuf, MLAN_STATUS_FAILURE); + } + pmadapter->callbacks.moal_spin_unlock(pmadapter->pmoal_handle, + priv->tdls_pending_txq.plock); + LEAVE(); +} + +/** + * @brief Map ACs to TID + * + * @param priv Pointer to the mlan_private driver data struct + * @param queue_priority Queue_priority structure + * + * @return N/A + */ +static void +wlan_wmm_queue_priorities_tid(pmlan_private priv, t_u8 queue_priority[]) +{ + int i; + + ENTER(); + + for (i = 0; i < 4; ++i) { + tos_to_tid[7 - (i * 2)] = ac_to_tid[queue_priority[i]][1]; + tos_to_tid[6 - (i * 2)] = ac_to_tid[queue_priority[i]][0]; + } + + for (i = 0; i < MAX_NUM_TID; i++) + tos_to_tid_inv[tos_to_tid[i]] = (t_u8)i; + + /* in case priorities have changed, force highest priority so + * next packet will check from top to re-establish the highest + */ + util_scalar_write(priv->adapter->pmoal_handle, + &priv->wmm.highest_queued_prio, + HIGH_PRIO_TID, + priv->adapter->callbacks.moal_spin_lock, + priv->adapter->callbacks.moal_spin_unlock); + + LEAVE(); +} + +/** + * @brief Evaluate whether or not an AC is to be downgraded + * + * @param priv Pointer to the mlan_private driver data struct + * @param eval_ac AC to evaluate for downgrading + * + * @return WMM AC The eval_ac traffic is to be sent on. + */ +static mlan_wmm_ac_e +wlan_wmm_eval_downgrade_ac(pmlan_private priv, mlan_wmm_ac_e eval_ac) +{ + int down_ac; + mlan_wmm_ac_e ret_ac; + WmmAcStatus_t *pac_status; + + ENTER(); + + pac_status = &priv->wmm.ac_status[eval_ac]; + + if (pac_status->disabled == MFALSE) { + LEAVE(); + /* Okay to use this AC, its enabled */ + return eval_ac; + } + + /* Setup a default return value of the lowest priority */ + ret_ac = WMM_AC_BK; + + /* + * Find the highest AC that is enabled and does not require admission + * control. The spec disallows downgrading to an AC, which is enabled + * due to a completed admission control. Unadmitted traffic is not + * to be sent on an AC with admitted traffic. + */ + for (down_ac = WMM_AC_BK; down_ac < eval_ac; down_ac++) { + pac_status = &priv->wmm.ac_status[down_ac]; + + if ((pac_status->disabled == MFALSE) + && (pac_status->flow_required == MFALSE)) + /* AC is enabled and does not require admission control */ + ret_ac = (mlan_wmm_ac_e)down_ac; + } + + LEAVE(); + return ret_ac; +} + +/** + * @brief Convert the IP TOS field to an WMM AC Queue assignment + * + * @param pmadapter A pointer to mlan_adapter structure + * @param tos IP TOS field + * + * @return WMM AC Queue mapping of the IP TOS field + */ +static mlan_wmm_ac_e INLINE +wlan_wmm_convert_tos_to_ac(pmlan_adapter pmadapter, t_u32 tos) +{ + ENTER(); + + if (tos >= NELEMENTS(tos_to_ac)) { + LEAVE(); + return WMM_AC_BE; + } + + LEAVE(); + return tos_to_ac[tos]; +} + +/** + * @brief Evaluate a given TID and downgrade it to a lower TID if the + * WMM Parameter IE received from the AP indicates that the AP + * is disabled (due to call admission control (ACM bit). Mapping + * of TID to AC is taken care internally + * + * @param priv Pointer to the mlan_private data struct + * @param tid tid to evaluate for downgrading + * + * @return Same tid as input if downgrading not required or + * the tid the traffic for the given tid should be downgraded to + */ +static t_u8 INLINE +wlan_wmm_downgrade_tid(pmlan_private priv, t_u32 tid) +{ + mlan_wmm_ac_e ac_down; + pmlan_adapter pmadapter = priv->adapter; + + ENTER(); + + ac_down = + priv->wmm. + ac_down_graded_vals[wlan_wmm_convert_tos_to_ac(pmadapter, tid)]; + LEAVE(); + /* + * Send the index to tid array, picking from the array will be + * taken care by dequeuing function + */ + if (tid == 1 || tid == 2) + return ac_to_tid[ac_down][(tid + 1) % 2]; + else if (tid >= MAX_NUM_TID) + return ac_to_tid[ac_down][0]; + else + return ac_to_tid[ac_down][tid % 2]; +} + +/** + * @brief Delete packets in RA node + * + * @param priv Pointer to the mlan_private driver data struct + * @param ra_list Pointer to raListTbl + * + * @return N/A + */ +static INLINE void +wlan_wmm_del_pkts_in_ralist_node(pmlan_private priv, raListTbl *ra_list) +{ + pmlan_buffer pmbuf; + pmlan_adapter pmadapter = priv->adapter; + + ENTER(); + while ((pmbuf = + (pmlan_buffer)util_peek_list(pmadapter->pmoal_handle, + &ra_list->buf_head, MNULL, + MNULL))) { + util_unlink_list(pmadapter->pmoal_handle, &ra_list->buf_head, + (pmlan_linked_list)pmbuf, MNULL, MNULL); + wlan_write_data_complete(pmadapter, pmbuf, MLAN_STATUS_FAILURE); + } + util_free_list_head((t_void *)pmadapter->pmoal_handle, + &ra_list->buf_head, + pmadapter->callbacks.moal_free_lock); + + LEAVE(); +} + +/** + * @brief Delete packets in RA list + * + * @param priv Pointer to the mlan_private driver data struct + * @param ra_list_head ra list header + * + * @return N/A + */ +static INLINE void +wlan_wmm_del_pkts_in_ralist(pmlan_private priv, mlan_list_head *ra_list_head) +{ + raListTbl *ra_list; + + ENTER(); + + ra_list = + (raListTbl *)util_peek_list(priv->adapter->pmoal_handle, + ra_list_head, MNULL, MNULL); + + while (ra_list && ra_list != (raListTbl *)ra_list_head) { + wlan_wmm_del_pkts_in_ralist_node(priv, ra_list); + + ra_list = ra_list->pnext; + } + + LEAVE(); +} + +/** + * @brief Clean up the wmm queue + * + * @param priv Pointer to the mlan_private driver data struct + * + * @return N/A + */ +static void +wlan_wmm_cleanup_queues(pmlan_private priv) +{ + int i; + + ENTER(); + + for (i = 0; i < MAX_NUM_TID; i++) { + wlan_wmm_del_pkts_in_ralist(priv, + &priv->wmm.tid_tbl_ptr[i].ra_list); + priv->wmm.pkts_queued[i] = 0; + priv->wmm.pkts_paused[i] = 0; + } + util_scalar_write(priv->adapter->pmoal_handle, + &priv->wmm.tx_pkts_queued, 0, MNULL, MNULL); + util_scalar_write(priv->adapter->pmoal_handle, + &priv->wmm.highest_queued_prio, HIGH_PRIO_TID, MNULL, + MNULL); + + LEAVE(); +} + +/** + * @brief Delete all route address from RA list + * + * @param priv Pointer to the mlan_private driver data struct + * + * @return N/A + */ +static void +wlan_wmm_delete_all_ralist(pmlan_private priv) +{ + raListTbl *ra_list; + int i; + pmlan_adapter pmadapter = priv->adapter; + + ENTER(); + + for (i = 0; i < MAX_NUM_TID; ++i) { + PRINTM(MINFO, "RAList: Freeing buffers for TID %d\n", i); + while ((ra_list = + (raListTbl *)util_peek_list(pmadapter->pmoal_handle, + &priv->wmm.tid_tbl_ptr[i]. + ra_list, MNULL, MNULL))) { + util_unlink_list(pmadapter->pmoal_handle, + &priv->wmm.tid_tbl_ptr[i].ra_list, + (pmlan_linked_list)ra_list, MNULL, + MNULL); + + pmadapter->callbacks.moal_mfree(pmadapter->pmoal_handle, + (t_u8 *)ra_list); + } + + util_init_list((pmlan_linked_list) + &priv->wmm.tid_tbl_ptr[i].ra_list); + priv->wmm.tid_tbl_ptr[i].ra_list_curr = MNULL; + } + + LEAVE(); +} + +/** + * @brief Get queue RA pointer + * + * @param priv Pointer to the mlan_private driver data struct + * @param tid TID + * @param ra_addr Pointer to the route address + * + * @return ra_list + */ +static raListTbl * +wlan_wmm_get_queue_raptr(pmlan_private priv, t_u8 tid, t_u8 *ra_addr) +{ + raListTbl *ra_list; +#if defined(UAP_SUPPORT) + t_u8 bcast_addr[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; +#endif + + ENTER(); + ra_list = wlan_wmm_get_ralist_node(priv, tid, ra_addr); + if (ra_list) { + LEAVE(); + return ra_list; + } +#if defined(UAP_SUPPORT) + if ((GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) && + (0 != + memcmp(priv->adapter, ra_addr, bcast_addr, sizeof(bcast_addr)))) { + if (MNULL == wlan_get_station_entry(priv, ra_addr)) { + PRINTM(MDATA, "Drop packets to unknown station\n"); + LEAVE(); + return MNULL; + } + } +#endif + wlan_ralist_add(priv, ra_addr); + + ra_list = wlan_wmm_get_ralist_node(priv, tid, ra_addr); + LEAVE(); + return ra_list; +} + +#ifdef STA_SUPPORT +/** + * @brief Sends wmmac host event + * + * @param priv Pointer to the mlan_private driver data struct + * @param type_str Type of host event + * @param src_addr Pointer to the source Address + * @param tid TID + * @param up User priority + * @param status Status code or Reason code + * + * @return N/A + */ +static void +wlan_send_wmmac_host_event(pmlan_private priv, + char *type_str, + t_u8 *src_addr, t_u8 tid, t_u8 up, t_u8 status) +{ + t_u8 event_buf[100]; + mlan_event *pevent; + t_u8 *pout_buf; + + ENTER(); + + /* Format one of the following two output strings: + ** - TSPEC:ADDTS_RSP:[]:TID=X:UP=Y + ** - TSPEC:DELTS_RX:[]:TID=X:UP=Y + */ + pevent = (mlan_event *)event_buf; + pout_buf = pevent->event_buf; + + memcpy(priv->adapter, pout_buf, (t_u8 *)"TSPEC:", 6); + pout_buf += 6; + + memcpy(priv->adapter, pout_buf, (t_u8 *)type_str, + wlan_strlen(type_str)); + pout_buf += wlan_strlen(type_str); + + *pout_buf++ = ':'; + *pout_buf++ = '['; + + if (status >= 100) { + *pout_buf++ = (status / 100) + '0'; + status = (status % 100); + } + + if (status >= 10) { + *pout_buf++ = (status / 10) + '0'; + status = (status % 10); + } + + *pout_buf++ = status + '0'; + + memcpy(priv->adapter, pout_buf, (t_u8 *)"]:TID", 5); + pout_buf += 5; + *pout_buf++ = tid + '0'; + + memcpy(priv->adapter, pout_buf, (t_u8 *)":UP", 3); + pout_buf += 3; + *pout_buf++ = up + '0'; + + *pout_buf = '\0'; + + pevent->bss_index = priv->bss_index; + pevent->event_id = MLAN_EVENT_ID_DRV_REPORT_STRING; + pevent->event_len = wlan_strlen((const char *)(pevent->event_buf)); + + wlan_recv_event(priv, MLAN_EVENT_ID_DRV_REPORT_STRING, pevent); + LEAVE(); +} +#endif /* STA_SUPPORT */ + +/** + * @brief This function gets the highest priority list pointer + * + * @param pmadapter A pointer to mlan_adapter + * @param priv A pointer to mlan_private + * @param tid A pointer to return tid + * + * @return raListTbl + */ +static raListTbl * +wlan_wmm_get_highest_priolist_ptr(pmlan_adapter pmadapter, + pmlan_private *priv, int *tid) +{ + pmlan_private priv_tmp; + raListTbl *ptr, *head; + mlan_bssprio_node *bssprio_node, *bssprio_head; + tid_tbl_t *tid_ptr; + int i, j; + int next_prio = 0; + int next_tid = 0; + ENTER(); + + PRINTM(MDAT_D, "POP\n"); + for (j = pmadapter->priv_num - 1; j >= 0; --j) { + if (!(util_peek_list(pmadapter->pmoal_handle, + &pmadapter->bssprio_tbl[j].bssprio_head, + MNULL, MNULL))) + continue; + + if (pmadapter->bssprio_tbl[j].bssprio_cur == + (mlan_bssprio_node *) + &pmadapter->bssprio_tbl[j].bssprio_head) { + pmadapter->bssprio_tbl[j].bssprio_cur = + pmadapter->bssprio_tbl[j].bssprio_cur->pnext; + } + + bssprio_head + = bssprio_node = pmadapter->bssprio_tbl[j].bssprio_cur; + + do { + priv_tmp = bssprio_node->priv; + + if ((priv_tmp->port_ctrl_mode == MTRUE) + && (priv_tmp->port_open == MFALSE)) { + PRINTM(MINFO, "get_highest_prio_ptr(): " + "PORT_CLOSED Ignore pkts from BSS%d\n", + priv_tmp->bss_index); + /* Ignore data pkts from a BSS if port is closed */ + goto next_intf; + } + if (priv_tmp->tx_pause == MTRUE) { + PRINTM(MINFO, "get_highest_prio_ptr(): " + "TX PASUE Ignore pkts from BSS%d\n", + priv_tmp->bss_index); + /* Ignore data pkts from a BSS if tx pause */ + goto next_intf; + } + + pmadapter->callbacks.moal_spin_lock(pmadapter-> + pmoal_handle, + priv_tmp->wmm. + ra_list_spinlock); + + for (i = util_scalar_read(pmadapter->pmoal_handle, + &priv_tmp->wmm. + highest_queued_prio, MNULL, + MNULL); i >= LOW_PRIO_TID; + --i) { + + tid_ptr = + &(priv_tmp)->wmm. + tid_tbl_ptr[tos_to_tid[i]]; + if (!util_peek_list + (pmadapter->pmoal_handle, &tid_ptr->ra_list, + MNULL, MNULL)) + continue; + + /* + * Always choose the next ra we transmitted + * last time, this way we pick the ra's in + * round robin fashion. + */ + head = ptr = tid_ptr->ra_list_curr->pnext; + if (ptr == (raListTbl *)&tid_ptr->ra_list) + head = ptr = ptr->pnext; + + do { + if (!ptr->tx_pause && + util_peek_list(pmadapter-> + pmoal_handle, + &ptr->buf_head, + MNULL, MNULL)) { + + /* Because WMM only support BK/BE/VI/VO, we have 8 tid + * We should balance the traffic of the same AC */ + if (i % 2) + next_prio = i - 1; + else + next_prio = i + 1; + next_tid = + tos_to_tid[next_prio]; + if (priv_tmp->wmm. + pkts_queued[next_tid] && + (priv_tmp->wmm. + pkts_queued[next_tid] > + priv_tmp->wmm. + pkts_paused[next_tid])) + util_scalar_write + (pmadapter-> + pmoal_handle, + &priv_tmp->wmm. + highest_queued_prio, + next_prio, + MNULL, MNULL); + else + /* if highest_queued_prio > i, set it to i */ + util_scalar_conditional_write + (pmadapter-> + pmoal_handle, + &priv_tmp->wmm. + highest_queued_prio, + MLAN_SCALAR_COND_GREATER_THAN, + i, i, MNULL, + MNULL); + *priv = priv_tmp; + *tid = tos_to_tid[i]; + /* hold priv->ra_list_spinlock to maintain ptr */ + PRINTM(MDAT_D, + "get highest prio ptr %p, tid %d\n", + ptr, *tid); + LEAVE(); + return ptr; + } + + ptr = ptr->pnext; + if (ptr == + (raListTbl *)&tid_ptr->ra_list) + ptr = ptr->pnext; + } while (ptr != head); + } + + /* If priv still has packets queued, reset to HIGH_PRIO_TID */ + if (util_scalar_read(pmadapter->pmoal_handle, + &priv_tmp->wmm.tx_pkts_queued, + MNULL, MNULL)) + util_scalar_write(pmadapter->pmoal_handle, + &priv_tmp->wmm. + highest_queued_prio, + HIGH_PRIO_TID, MNULL, MNULL); + else + /* No packet at any TID for this priv. Mark as such to skip + * checking TIDs for this priv (until pkt is added). */ + util_scalar_write(pmadapter->pmoal_handle, + &priv_tmp->wmm. + highest_queued_prio, + NO_PKT_PRIO_TID, MNULL, + MNULL); + + pmadapter->callbacks.moal_spin_unlock(pmadapter-> + pmoal_handle, + priv_tmp->wmm. + ra_list_spinlock); + +next_intf: + bssprio_node = bssprio_node->pnext; + if (bssprio_node == (mlan_bssprio_node *) + &pmadapter->bssprio_tbl[j].bssprio_head) + bssprio_node = bssprio_node->pnext; + pmadapter->bssprio_tbl[j].bssprio_cur = bssprio_node; + } while (bssprio_node != bssprio_head); + } + + LEAVE(); + return MNULL; +} + +/** + * @brief This function gets the number of packets in the Tx queue + * + * @param priv A pointer to mlan_private + * @param ptr A pointer to RA list table + * @param max_buf_size Maximum buffer size + * + * @return Packet count + */ +static int +wlan_num_pkts_in_txq(mlan_private *priv, raListTbl *ptr, int max_buf_size) +{ + int count = 0, total_size = 0; + pmlan_buffer pmbuf; + + ENTER(); + + for (pmbuf = (pmlan_buffer)ptr->buf_head.pnext; + pmbuf != (pmlan_buffer)(&ptr->buf_head); pmbuf = pmbuf->pnext) { + + total_size += pmbuf->data_len; + if (total_size < max_buf_size) + ++count; + else + break; + } + + LEAVE(); + return count; +} + +/** + * @brief This function sends a single packet + * + * @param priv A pointer to mlan_private + * @param ptr A pointer to RA list table + * @param ptrindex ptr's TID index + * + * @return N/A + */ +static void INLINE +wlan_send_single_packet(pmlan_private priv, raListTbl *ptr, int ptrindex) +{ + pmlan_buffer pmbuf; + pmlan_buffer pmbuf_next; + mlan_tx_param tx_param; + pmlan_adapter pmadapter = priv->adapter; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + pmbuf = (pmlan_buffer)util_dequeue_list(pmadapter->pmoal_handle, + &ptr->buf_head, MNULL, MNULL); + if (pmbuf) { + PRINTM(MINFO, "Dequeuing the packet %p %p\n", ptr, pmbuf); + priv->wmm.pkts_queued[ptrindex]--; + util_scalar_decrement(pmadapter->pmoal_handle, + &priv->wmm.tx_pkts_queued, MNULL, MNULL); + ptr->total_pkts--; + pmbuf_next = + (pmlan_buffer)util_peek_list(pmadapter->pmoal_handle, + &ptr->buf_head, MNULL, + MNULL); + pmadapter->callbacks.moal_spin_unlock(pmadapter->pmoal_handle, + priv->wmm. + ra_list_spinlock); + + tx_param.next_pkt_len = ((pmbuf_next) + ? pmbuf_next->data_len + + sizeof(TxPD) : 0); + status = wlan_process_tx(priv, pmbuf, &tx_param); + + if (status == MLAN_STATUS_RESOURCE) { + /** Queue the packet back at the head */ + PRINTM(MDAT_D, "Queuing pkt back to raList %p %p\n", + ptr, pmbuf); + pmadapter->callbacks.moal_spin_lock(pmadapter-> + pmoal_handle, + priv->wmm. + ra_list_spinlock); + + if (!wlan_is_ralist_valid(priv, ptr, ptrindex)) { + pmadapter->callbacks. + moal_spin_unlock(pmadapter-> + pmoal_handle, + priv->wmm. + ra_list_spinlock); + wlan_write_data_complete(pmadapter, pmbuf, + MLAN_STATUS_FAILURE); + LEAVE(); + return; + } + priv->wmm.pkts_queued[ptrindex]++; + util_scalar_increment(pmadapter->pmoal_handle, + &priv->wmm.tx_pkts_queued, MNULL, + MNULL); + util_enqueue_list_head(pmadapter->pmoal_handle, + &ptr->buf_head, + (pmlan_linked_list)pmbuf, MNULL, + MNULL); + + ptr->total_pkts++; + pmbuf->flags |= MLAN_BUF_FLAG_REQUEUED_PKT; + pmadapter->callbacks.moal_spin_unlock(pmadapter-> + pmoal_handle, + priv->wmm. + ra_list_spinlock); + } else { + pmadapter->callbacks.moal_spin_lock(pmadapter-> + pmoal_handle, + priv->wmm. + ra_list_spinlock); + if (wlan_is_ralist_valid(priv, ptr, ptrindex)) { + priv->wmm.packets_out[ptrindex]++; + priv->wmm.tid_tbl_ptr[ptrindex].ra_list_curr = + ptr; + } + pmadapter->bssprio_tbl[priv->bss_priority].bssprio_cur = + pmadapter->bssprio_tbl[priv->bss_priority]. + bssprio_cur->pnext; + pmadapter->callbacks.moal_spin_unlock(pmadapter-> + pmoal_handle, + priv->wmm. + ra_list_spinlock); + } + } else { + pmadapter->callbacks.moal_spin_unlock(pmadapter->pmoal_handle, + priv->wmm. + ra_list_spinlock); + PRINTM(MINFO, "Nothing to send\n"); + } + + LEAVE(); +} + +/** + * @brief This function checks if this mlan_buffer is already processed. + * + * @param priv A pointer to mlan_private + * @param ptr A pointer to RA list table + * + * @return MTRUE or MFALSE + */ +static int INLINE +wlan_is_ptr_processed(mlan_private *priv, raListTbl *ptr) +{ + pmlan_buffer pmbuf; + + pmbuf = (pmlan_buffer)util_peek_list(priv->adapter->pmoal_handle, + &ptr->buf_head, MNULL, MNULL); + if (pmbuf && (pmbuf->flags & MLAN_BUF_FLAG_REQUEUED_PKT)) + return MTRUE; + + return MFALSE; +} + +/** + * @brief This function sends a single packet that has been processed + * + * @param priv A pointer to mlan_private + * @param ptr A pointer to RA list table + * @param ptrindex ptr's TID index + * + * @return N/A + */ +static void INLINE +wlan_send_processed_packet(pmlan_private priv, raListTbl *ptr, int ptrindex) +{ + pmlan_buffer pmbuf_next = MNULL; + mlan_tx_param tx_param; + pmlan_buffer pmbuf; + pmlan_adapter pmadapter = priv->adapter; + mlan_status ret = MLAN_STATUS_FAILURE; + + pmbuf = (pmlan_buffer)util_dequeue_list(pmadapter->pmoal_handle, + &ptr->buf_head, MNULL, MNULL); + if (pmbuf) { + pmbuf_next = + (pmlan_buffer)util_peek_list(pmadapter->pmoal_handle, + &ptr->buf_head, MNULL, + MNULL); + pmadapter->callbacks.moal_spin_unlock(pmadapter->pmoal_handle, + priv->wmm. + ra_list_spinlock); + tx_param.next_pkt_len = + ((pmbuf_next) ? pmbuf_next->data_len + + sizeof(TxPD) : 0); + ret = wlan_sdio_host_to_card(pmadapter, MLAN_TYPE_DATA, pmbuf, + &tx_param); + switch (ret) { + case MLAN_STATUS_RESOURCE: + PRINTM(MINFO, "MLAN_STATUS_RESOURCE is returned\n"); + pmadapter->callbacks.moal_spin_lock(pmadapter-> + pmoal_handle, + priv->wmm. + ra_list_spinlock); + + if (!wlan_is_ralist_valid(priv, ptr, ptrindex)) { + pmadapter->callbacks. + moal_spin_unlock(pmadapter-> + pmoal_handle, + priv->wmm. + ra_list_spinlock); + wlan_write_data_complete(pmadapter, pmbuf, + MLAN_STATUS_FAILURE); + LEAVE(); + return; + } + util_enqueue_list_head(pmadapter->pmoal_handle, + &ptr->buf_head, + (pmlan_linked_list)pmbuf, + MNULL, MNULL); + + pmbuf->flags |= MLAN_BUF_FLAG_REQUEUED_PKT; + pmadapter->callbacks.moal_spin_unlock(pmadapter-> + pmoal_handle, + priv->wmm. + ra_list_spinlock); + break; + case MLAN_STATUS_FAILURE: + pmadapter->data_sent = MFALSE; + PRINTM(MERROR, "Error: Failed to write data\n"); + pmadapter->dbg.num_tx_host_to_card_failure++; + pmbuf->status_code = MLAN_ERROR_DATA_TX_FAIL; + wlan_write_data_complete(pmadapter, pmbuf, ret); + break; + case MLAN_STATUS_PENDING: + break; + case MLAN_STATUS_SUCCESS: + DBG_HEXDUMP(MDAT_D, "Tx", + pmbuf->pbuf + pmbuf->data_offset, + MIN(pmbuf->data_len + sizeof(TxPD), + MAX_DATA_DUMP_LEN)); + wlan_write_data_complete(pmadapter, pmbuf, ret); + break; + default: + break; + } + if (ret != MLAN_STATUS_RESOURCE) { + pmadapter->callbacks.moal_spin_lock(pmadapter-> + pmoal_handle, + priv->wmm. + ra_list_spinlock); + if (wlan_is_ralist_valid(priv, ptr, ptrindex)) { + priv->wmm.packets_out[ptrindex]++; + priv->wmm.tid_tbl_ptr[ptrindex].ra_list_curr = + ptr; + ptr->total_pkts--; + } + pmadapter->bssprio_tbl[priv->bss_priority].bssprio_cur = + pmadapter->bssprio_tbl[priv->bss_priority]. + bssprio_cur->pnext; + priv->wmm.pkts_queued[ptrindex]--; + util_scalar_decrement(pmadapter->pmoal_handle, + &priv->wmm.tx_pkts_queued, + MNULL, MNULL); + pmadapter->callbacks.moal_spin_unlock(pmadapter-> + pmoal_handle, + priv->wmm. + ra_list_spinlock); + } + } else { + pmadapter->callbacks.moal_spin_unlock(pmadapter->pmoal_handle, + priv->wmm. + ra_list_spinlock); + } +} + +/** + * @brief This function dequeues a packet + * + * @param pmadapter A pointer to mlan_adapter + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static int +wlan_dequeue_tx_packet(pmlan_adapter pmadapter) +{ + raListTbl *ptr; + pmlan_private priv = MNULL; + int ptrindex = 0; + t_u8 ra[MLAN_MAC_ADDR_LENGTH]; + int tid_del = 0; + int tid = 0; + + ENTER(); + + ptr = wlan_wmm_get_highest_priolist_ptr(pmadapter, &priv, &ptrindex); + if (!ptr) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + /* Note:- Spinlock is locked in wlan_wmm_get_highest_priolist_ptr + * when it returns a pointer (for the priv it returns), + * and is unlocked in wlan_send_processed_packet, + * wlan_send_single_packet or wlan_11n_aggregate_pkt. + * The spinlock would be required for some parts of both of function. + * But, the the bulk of these function will execute w/o spinlock. + * Unlocking the spinlock inside these function will help us avoid + * taking the spinlock again, check to see if the ptr is still + * valid and then proceed. This is done purely to increase + * execution time. */ + + /* Note:- Also, anybody adding code which does not get into + * wlan_send_processed_packet, wlan_send_single_packet, or + * wlan_11n_aggregate_pkt should make sure ra_list_spinlock + * is freed. Otherwise there would be a lock up. */ + + tid = wlan_get_tid(priv->adapter, ptr); + if (tid >= MAX_NUM_TID) + tid = wlan_wmm_downgrade_tid(priv, tid); + + if (wlan_is_ptr_processed(priv, ptr)) { + wlan_send_processed_packet(priv, ptr, ptrindex); + LEAVE(); + return MLAN_STATUS_SUCCESS; + } + + if (!ptr->is_11n_enabled || + (ptr->ba_status || ptr->del_ba_count >= DEL_BA_THRESHOLD) +#ifdef STA_SUPPORT + || priv->wps.session_enable +#endif /* STA_SUPPORT */ + ) { + if (ptr->is_11n_enabled && ptr->ba_status + && ptr->amsdu_in_ampdu + && wlan_is_amsdu_allowed(priv, ptr, tid) + && (wlan_num_pkts_in_txq(priv, ptr, pmadapter->tx_buf_size) + >= MIN_NUM_AMSDU)) { + wlan_11n_aggregate_pkt(priv, ptr, priv->intf_hr_len, + ptrindex); + } else + wlan_send_single_packet(priv, ptr, ptrindex); + } else { + if (wlan_is_ampdu_allowed(priv, ptr, tid) && + (ptr->packet_count > ptr->ba_packet_threshold)) { + if (wlan_is_bastream_avail(priv)) { + PRINTM(MINFO, + "BA setup threshold %d reached. tid=%d\n", + ptr->packet_count, tid); + if (!wlan_11n_get_txbastream_tbl + (priv, tid, ptr->ra, MFALSE)) { + wlan_11n_create_txbastream_tbl(priv, + ptr->ra, + tid, + BA_STREAM_SETUP_INPROGRESS); + wlan_send_addba(priv, tid, ptr->ra); + } + } else if (wlan_find_stream_to_delete(priv, ptr, + tid, &tid_del, + ra)) { + PRINTM(MDAT_D, "tid_del=%d tid=%d\n", tid_del, + tid); + if (!wlan_11n_get_txbastream_tbl + (priv, tid, ptr->ra, MFALSE)) { + wlan_11n_create_txbastream_tbl(priv, + ptr->ra, + tid, + BA_STREAM_SETUP_INPROGRESS); + wlan_send_delba(priv, MNULL, tid_del, + ra, 1); + } + } + } + if (wlan_is_amsdu_allowed(priv, ptr, tid) && + (wlan_num_pkts_in_txq(priv, ptr, + pmadapter->tx_buf_size) >= + MIN_NUM_AMSDU)) { + wlan_11n_aggregate_pkt(priv, ptr, priv->intf_hr_len, + ptrindex); + } else { + wlan_send_single_packet(priv, ptr, ptrindex); + } + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief update tx_pause flag in ra_list + * + * @param priv A pointer to mlan_private + * @param mac peer mac address + * @param tx_pause tx_pause flag (0/1) + * + * @return N/A + */ +t_void +wlan_update_ralist_tx_pause(pmlan_private priv, t_u8 *mac, t_u8 tx_pause) +{ + raListTbl *ra_list; + int i; + pmlan_adapter pmadapter = priv->adapter; + t_u32 pkt_cnt = 0; + t_u32 tx_pkts_queued = 0; + ENTER(); + + pmadapter->callbacks.moal_spin_lock(pmadapter->pmoal_handle, + priv->wmm.ra_list_spinlock); + for (i = 0; i < MAX_NUM_TID; ++i) { + ra_list = wlan_wmm_get_ralist_node(priv, i, mac); + if (ra_list && ra_list->tx_pause != tx_pause) { + pkt_cnt += ra_list->total_pkts; + ra_list->tx_pause = tx_pause; + if (tx_pause) + priv->wmm.pkts_paused[i] += ra_list->total_pkts; + else + priv->wmm.pkts_paused[i] -= ra_list->total_pkts; + + } + } + if (pkt_cnt) { + tx_pkts_queued = util_scalar_read(pmadapter->pmoal_handle, + &priv->wmm.tx_pkts_queued, + MNULL, MNULL); + if (tx_pause) + tx_pkts_queued -= pkt_cnt; + else + tx_pkts_queued += pkt_cnt; + util_scalar_write(priv->adapter->pmoal_handle, + &priv->wmm.tx_pkts_queued, tx_pkts_queued, + MNULL, MNULL); + util_scalar_write(priv->adapter->pmoal_handle, + &priv->wmm.highest_queued_prio, HIGH_PRIO_TID, + MNULL, MNULL); + } + pmadapter->callbacks.moal_spin_unlock(pmadapter->pmoal_handle, + priv->wmm.ra_list_spinlock); + LEAVE(); +} + +#ifdef STA_SUPPORT +/** + * @brief update tx_pause flag in none tdls ra_list + * + * @param priv A pointer to mlan_private + * @param mac peer mac address + * @param tx_pause tx_pause flag (0/1) + * + * @return N/A + */ +t_void +wlan_update_non_tdls_ralist(mlan_private *priv, t_u8 *mac, t_u8 tx_pause) +{ + raListTbl *ra_list; + int i; + pmlan_adapter pmadapter = priv->adapter; + t_u32 pkt_cnt = 0; + t_u32 tx_pkts_queued = 0; + ENTER(); + + pmadapter->callbacks.moal_spin_lock(pmadapter->pmoal_handle, + priv->wmm.ra_list_spinlock); + for (i = 0; i < MAX_NUM_TID; ++i) { + ra_list = + (raListTbl *)util_peek_list(priv->adapter->pmoal_handle, + &priv->wmm.tid_tbl_ptr[i]. + ra_list, MNULL, MNULL); + while (ra_list && + (ra_list != + (raListTbl *)&priv->wmm.tid_tbl_ptr[i].ra_list)) { + if (memcmp + (priv->adapter, ra_list->ra, mac, + MLAN_MAC_ADDR_LENGTH) && + ra_list->tx_pause != tx_pause) { + pkt_cnt += ra_list->total_pkts; + ra_list->tx_pause = tx_pause; + if (tx_pause) + priv->wmm.pkts_paused[i] += + ra_list->total_pkts; + else + priv->wmm.pkts_paused[i] -= + ra_list->total_pkts; + } + ra_list = ra_list->pnext; + } + } + if (pkt_cnt) { + tx_pkts_queued = util_scalar_read(pmadapter->pmoal_handle, + &priv->wmm.tx_pkts_queued, + MNULL, MNULL); + if (tx_pause) + tx_pkts_queued -= pkt_cnt; + else + tx_pkts_queued += pkt_cnt; + util_scalar_write(priv->adapter->pmoal_handle, + &priv->wmm.tx_pkts_queued, tx_pkts_queued, + MNULL, MNULL); + util_scalar_write(priv->adapter->pmoal_handle, + &priv->wmm.highest_queued_prio, HIGH_PRIO_TID, + MNULL, MNULL); + } + pmadapter->callbacks.moal_spin_unlock(pmadapter->pmoal_handle, + priv->wmm.ra_list_spinlock); + LEAVE(); + return; +} + +/** + * @brief find tdls buffer from ralist + * + * @param priv A pointer to mlan_private + * @param ralist A pointer to ralistTbl + * @param mac TDLS peer mac address + * + * @return pmlan_buffer or MNULL + */ +static pmlan_buffer +wlan_find_tdls_packets(mlan_private *priv, raListTbl *ra_list, t_u8 *mac) +{ + pmlan_buffer pmbuf = MNULL; + mlan_adapter *pmadapter = priv->adapter; + t_u8 ra[MLAN_MAC_ADDR_LENGTH]; + ENTER(); + pmbuf = (pmlan_buffer)util_peek_list(priv->adapter->pmoal_handle, + &ra_list->buf_head, MNULL, MNULL); + if (!pmbuf) { + LEAVE(); + return MNULL; + } + while (pmbuf != (pmlan_buffer)&ra_list->buf_head) { + memcpy(pmadapter, ra, pmbuf->pbuf + pmbuf->data_offset, + MLAN_MAC_ADDR_LENGTH); + if (!memcmp(priv->adapter, ra, mac, MLAN_MAC_ADDR_LENGTH)) { + LEAVE(); + return pmbuf; + } + pmbuf = pmbuf->pnext; + } + LEAVE(); + return MNULL; +} + +/** + * @brief find tdls buffer from tdls pending queue + * + * @param priv A pointer to mlan_private + * @param mac TDLS peer mac address + * + * @return pmlan_buffer or MNULL + */ +static pmlan_buffer +wlan_find_packets_tdls_txq(mlan_private *priv, t_u8 *mac) +{ + pmlan_buffer pmbuf = MNULL; + mlan_adapter *pmadapter = priv->adapter; + t_u8 ra[MLAN_MAC_ADDR_LENGTH]; + ENTER(); + pmbuf = (pmlan_buffer)util_peek_list(priv->adapter->pmoal_handle, + &priv->tdls_pending_txq, + MNULL, MNULL); + if (!pmbuf) { + LEAVE(); + return MNULL; + } + while (pmbuf != (pmlan_buffer)&priv->tdls_pending_txq) { + memcpy(pmadapter, ra, pmbuf->pbuf + pmbuf->data_offset, + MLAN_MAC_ADDR_LENGTH); + if (!memcmp(priv->adapter, ra, mac, MLAN_MAC_ADDR_LENGTH)) { + LEAVE(); + return pmbuf; + } + pmbuf = pmbuf->pnext; + } + LEAVE(); + return MNULL; +} + +/** + * @brief Remove TDLS ralist and move packets to AP's ralist + * + * @param priv A pointer to mlan_private + * @param mac TDLS peer mac address + * + * @return N/A + */ +static t_void +wlan_wmm_delete_tdls_ralist(pmlan_private priv, t_u8 *mac) +{ + raListTbl *ra_list; + raListTbl *ra_list_ap = MNULL; + int i; + pmlan_adapter pmadapter = priv->adapter; + pmlan_buffer pmbuf; + ENTER(); + + for (i = 0; i < MAX_NUM_TID; ++i) { + ra_list = wlan_wmm_get_ralist_node(priv, i, mac); + if (ra_list) { + PRINTM(MDATA, "delete TDLS ralist %p\n", ra_list); + ra_list_ap = + (raListTbl *)util_peek_list(pmadapter-> + pmoal_handle, + &priv->wmm. + tid_tbl_ptr[i]. + ra_list, MNULL, + MNULL); + while ((pmbuf = + (pmlan_buffer)util_peek_list(pmadapter-> + pmoal_handle, + &ra_list->buf_head, + MNULL, MNULL))) { + util_unlink_list(pmadapter->pmoal_handle, + &ra_list->buf_head, + (pmlan_linked_list)pmbuf, + MNULL, MNULL); + util_enqueue_list_tail(pmadapter->pmoal_handle, + &ra_list_ap->buf_head, + (pmlan_linked_list)pmbuf, + MNULL, MNULL); + ra_list_ap->total_pkts++; + ra_list_ap->packet_count++; + } + util_free_list_head((t_void *)pmadapter->pmoal_handle, + &ra_list->buf_head, + pmadapter->callbacks. + moal_free_lock); + + util_unlink_list(pmadapter->pmoal_handle, + &priv->wmm.tid_tbl_ptr[i].ra_list, + (pmlan_linked_list)ra_list, MNULL, + MNULL); + pmadapter->callbacks.moal_mfree(pmadapter->pmoal_handle, + (t_u8 *)ra_list); + if (priv->wmm.tid_tbl_ptr[i].ra_list_curr == ra_list) + priv->wmm.tid_tbl_ptr[i].ra_list_curr = + ra_list_ap; + } + } + + LEAVE(); + +} +#endif /* STA_SUPPORT */ +/******************************************************** + Global Functions +********************************************************/ + +/** + * @brief Get the threshold value for BA setup using system time. + * + * @param pmadapter Pointer to the mlan_adapter structure + * + * @return threshold value. + */ +t_u8 +wlan_get_random_ba_threshold(pmlan_adapter pmadapter) +{ + t_u32 sec, usec; + t_u8 ba_threshold = 0; + + ENTER(); + + /* setup ba_packet_threshold here random number between + [BA_SETUP_PACKET_OFFSET, BA_SETUP_PACKET_OFFSET+BA_SETUP_MAX_PACKET_THRESHOLD-1] */ + +#define BA_SETUP_MAX_PACKET_THRESHOLD 16 +#define BA_SETUP_PACKET_OFFSET 16 + + pmadapter->callbacks.moal_get_system_time(pmadapter->pmoal_handle, &sec, + &usec); + sec = (sec & 0xFFFF) + (sec >> 16); + usec = (usec & 0xFFFF) + (usec >> 16); + + ba_threshold = + (((sec << 16) + usec) % BA_SETUP_MAX_PACKET_THRESHOLD) + + BA_SETUP_PACKET_OFFSET; + PRINTM(MINFO, "setup BA after %d packets\n", ba_threshold); + + LEAVE(); + return ba_threshold; +} + +/** + * @brief This function cleans Tx/Rx queues + * + * @param priv A pointer to mlan_private + * + * @return N/A + */ +t_void +wlan_clean_txrx(pmlan_private priv) +{ + mlan_adapter *pmadapter = priv->adapter; + t_u8 i = 0; + + ENTER(); + + wlan_cleanup_bypass_txq(priv); + + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) { + wlan_cleanup_tdls_txq(priv); + } + wlan_11n_cleanup_reorder_tbl(priv); + wlan_11n_deleteall_txbastream_tbl(priv); + pmadapter->callbacks.moal_spin_lock(pmadapter->pmoal_handle, + priv->wmm.ra_list_spinlock); + wlan_wmm_cleanup_queues(priv); + wlan_wmm_delete_all_ralist(priv); + memcpy(pmadapter, tos_to_tid, ac_to_tid, sizeof(tos_to_tid)); + for (i = 0; i < MAX_NUM_TID; i++) + tos_to_tid_inv[tos_to_tid[i]] = (t_u8)i; +#if defined(UAP_SUPPORT) + priv->num_drop_pkts = 0; +#endif +#ifdef SDIO_MULTI_PORT_TX_AGGR + memset(pmadapter, pmadapter->mpa_tx_count, 0, + sizeof(pmadapter->mpa_tx_count)); + pmadapter->mpa_sent_no_ports = 0; + pmadapter->mpa_sent_last_pkt = 0; +#endif +#ifdef SDIO_MULTI_PORT_RX_AGGR + memset(pmadapter, pmadapter->mpa_rx_count, 0, + sizeof(pmadapter->mpa_rx_count)); +#endif + pmadapter->callbacks.moal_spin_unlock(pmadapter->pmoal_handle, + priv->wmm.ra_list_spinlock); + + LEAVE(); +} + +/** + * @brief Set the WMM queue priorities to their default values + * + * @param priv Pointer to the mlan_private driver data struct + * + * @return N/A + */ +void +wlan_wmm_default_queue_priorities(pmlan_private priv) +{ + ENTER(); + + /* Default queue priorities: VO->VI->BE->BK */ + priv->wmm.queue_priority[0] = WMM_AC_VO; + priv->wmm.queue_priority[1] = WMM_AC_VI; + priv->wmm.queue_priority[2] = WMM_AC_BE; + priv->wmm.queue_priority[3] = WMM_AC_BK; + + LEAVE(); +} + +/** + * @brief Initialize WMM priority queues + * + * @param priv Pointer to the mlan_private driver data struct + * @param pwmm_ie Pointer to the IEEEtypes_WmmParameter_t data struct + * + * @return N/A + */ +void +wlan_wmm_setup_queue_priorities(pmlan_private priv, + IEEEtypes_WmmParameter_t *pwmm_ie) +{ + t_u16 cw_min, avg_back_off, tmp[4]; + t_u32 i, j, num_ac; + t_u8 ac_idx; + + ENTER(); + + if (!pwmm_ie || priv->wmm_enabled == MFALSE) { + /* WMM is not enabled, just set the defaults and return */ + wlan_wmm_default_queue_priorities(priv); + LEAVE(); + return; + } + memset(priv->adapter, tmp, 0, sizeof(tmp)); + + HEXDUMP("WMM: setup_queue_priorities: param IE", + (t_u8 *)pwmm_ie, sizeof(IEEEtypes_WmmParameter_t)); + + PRINTM(MINFO, "WMM Parameter IE: version=%d, " + "qos_info Parameter Set Count=%d, Reserved=%#x\n", + pwmm_ie->vend_hdr.version, pwmm_ie->qos_info.para_set_count, + pwmm_ie->reserved); + + for (num_ac = 0; num_ac < NELEMENTS(pwmm_ie->ac_params); num_ac++) { + cw_min = (1 << pwmm_ie->ac_params[num_ac].ecw.ecw_min) - 1; + avg_back_off + = + (cw_min >> 1) + + pwmm_ie->ac_params[num_ac].aci_aifsn.aifsn; + + ac_idx = wmm_aci_to_qidx_map[pwmm_ie->ac_params[num_ac]. + aci_aifsn.aci]; + priv->wmm.queue_priority[ac_idx] = ac_idx; + tmp[ac_idx] = avg_back_off; + + PRINTM(MCMND, "WMM: CWmax=%d CWmin=%d Avg Back-off=%d\n", + (1 << pwmm_ie->ac_params[num_ac].ecw.ecw_max) - 1, + cw_min, avg_back_off); + PRINTM_AC(&pwmm_ie->ac_params[num_ac]); + } + + HEXDUMP("WMM: avg_back_off", (t_u8 *)tmp, sizeof(tmp)); + HEXDUMP("WMM: queue_priority", priv->wmm.queue_priority, + sizeof(priv->wmm.queue_priority)); + + /* Bubble sort */ + for (i = 0; i < num_ac; i++) { + for (j = 1; j < num_ac - i; j++) { + if (tmp[j - 1] > tmp[j]) { + SWAP_U16(tmp[j - 1], tmp[j]); + SWAP_U8(priv->wmm.queue_priority[j - 1], + priv->wmm.queue_priority[j]); + } else if (tmp[j - 1] == tmp[j]) { + if (priv->wmm.queue_priority[j - 1] + < priv->wmm.queue_priority[j]) { + SWAP_U8(priv->wmm.queue_priority[j - 1], + priv->wmm.queue_priority[j]); + } + } + } + } + + wlan_wmm_queue_priorities_tid(priv, priv->wmm.queue_priority); + + HEXDUMP("WMM: avg_back_off, sort", (t_u8 *)tmp, sizeof(tmp)); + DBG_HEXDUMP(MCMD_D, "WMM: queue_priority, sort", + priv->wmm.queue_priority, sizeof(priv->wmm.queue_priority)); + LEAVE(); +} + +/** + * @brief Downgrade WMM priority queue + * + * @param priv Pointer to the mlan_private driver data struct + * + * @return N/A + */ +void +wlan_wmm_setup_ac_downgrade(pmlan_private priv) +{ + int ac_val; + + ENTER(); + + PRINTM(MINFO, "WMM: AC Priorities: BK(0), BE(1), VI(2), VO(3)\n"); + + if (priv->wmm_enabled == MFALSE) { + /* WMM is not enabled, default priorities */ + for (ac_val = WMM_AC_BK; ac_val <= WMM_AC_VO; ac_val++) { + priv->wmm.ac_down_graded_vals[ac_val] = + (mlan_wmm_ac_e)ac_val; + } + } else { + for (ac_val = WMM_AC_BK; ac_val <= WMM_AC_VO; ac_val++) { + priv->wmm.ac_down_graded_vals[ac_val] + = wlan_wmm_eval_downgrade_ac(priv, + (mlan_wmm_ac_e) + ac_val); + PRINTM(MINFO, "WMM: AC PRIO %d maps to %d\n", ac_val, + priv->wmm.ac_down_graded_vals[ac_val]); + } + } + + LEAVE(); +} + +/** + * @brief Allocate and add a RA list for all TIDs with the given RA + * + * @param priv Pointer to the mlan_private driver data struct + * @param ra Address of the receiver STA (AP in case of infra) + * + * @return N/A + */ +void +wlan_ralist_add(mlan_private *priv, t_u8 *ra) +{ + int i; + raListTbl *ra_list; + pmlan_adapter pmadapter = priv->adapter; + tdlsStatus_e status; + + ENTER(); + + for (i = 0; i < MAX_NUM_TID; ++i) { + ra_list = wlan_wmm_allocate_ralist_node(pmadapter, ra); + PRINTM(MINFO, "Creating RA List %p for tid %d\n", ra_list, i); + if (!ra_list) + break; + ra_list->max_amsdu = 0; + ra_list->ba_status = BA_STREAM_NOT_SETUP; + ra_list->amsdu_in_ampdu = MFALSE; + if (queuing_ra_based(priv)) { + ra_list->is_11n_enabled = wlan_is_11n_enabled(priv, ra); + if (ra_list->is_11n_enabled) + ra_list->max_amsdu = + get_station_max_amsdu_size(priv, ra); + ra_list->tx_pause = wlan_is_tx_pause(priv, ra); + } else { + ra_list->is_tdls_link = MFALSE; + ra_list->tx_pause = MFALSE; + status = wlan_get_tdls_link_status(priv, ra); + if (MTRUE == wlan_is_tdls_link_setup(status)) { + ra_list->is_11n_enabled = + is_station_11n_enabled(priv, ra); + if (ra_list->is_11n_enabled) + ra_list->max_amsdu = + get_station_max_amsdu_size(priv, + ra); + ra_list->is_tdls_link = MTRUE; + } else { + ra_list->is_11n_enabled = IS_11N_ENABLED(priv); + if (ra_list->is_11n_enabled) + ra_list->max_amsdu = priv->max_amsdu; + } + } + + PRINTM_NETINTF(MDATA, priv); + PRINTM(MDATA, "ralist %p: is_11n_enabled=%d max_amsdu=%d\n", + ra_list, ra_list->is_11n_enabled, ra_list->max_amsdu); + + if (ra_list->is_11n_enabled) { + ra_list->packet_count = 0; + ra_list->ba_packet_threshold = + wlan_get_random_ba_threshold(pmadapter); + } + + util_enqueue_list_tail(pmadapter->pmoal_handle, + &priv->wmm.tid_tbl_ptr[i].ra_list, + (pmlan_linked_list)ra_list, MNULL, + MNULL); + + if (!priv->wmm.tid_tbl_ptr[i].ra_list_curr) + priv->wmm.tid_tbl_ptr[i].ra_list_curr = ra_list; + } + + LEAVE(); +} + +/** + * @brief Initialize the WMM parameter. + * + * @param pmadapter Pointer to the mlan_adapter data structure + * + * @return N/A + */ +t_void +wlan_init_wmm_param(pmlan_adapter pmadapter) +{ + /* Reuse the same structure of WmmAcParameters_t for configuration purpose here. + * the definition of acm bit is changed to ucm (user configuration mode) + * FW will take the setting of aifsn,ecw_max,ecw_min, tx_op_limit + * only when ucm is set to 1. othewise the default setting/behavoir in + * firmware will be used. + */ + pmadapter->ac_params[AC_BE].aci_aifsn.acm = 0; + pmadapter->ac_params[AC_BE].aci_aifsn.aci = AC_BE; + pmadapter->ac_params[AC_BE].aci_aifsn.aifsn = 3; + pmadapter->ac_params[AC_BE].ecw.ecw_max = 10; + pmadapter->ac_params[AC_BE].ecw.ecw_min = 4; + pmadapter->ac_params[AC_BE].tx_op_limit = 0; + + pmadapter->ac_params[AC_BK].aci_aifsn.acm = 0; + pmadapter->ac_params[AC_BK].aci_aifsn.aci = AC_BK; + pmadapter->ac_params[AC_BK].aci_aifsn.aifsn = 7; + pmadapter->ac_params[AC_BK].ecw.ecw_max = 10; + pmadapter->ac_params[AC_BK].ecw.ecw_min = 4; + pmadapter->ac_params[AC_BK].tx_op_limit = 0; + + pmadapter->ac_params[AC_VI].aci_aifsn.acm = 0; + pmadapter->ac_params[AC_VI].aci_aifsn.aci = AC_VI; + pmadapter->ac_params[AC_VI].aci_aifsn.aifsn = 2; + pmadapter->ac_params[AC_VI].ecw.ecw_max = 4; + pmadapter->ac_params[AC_VI].ecw.ecw_min = 3; + pmadapter->ac_params[AC_VI].tx_op_limit = 188; + + pmadapter->ac_params[AC_VO].aci_aifsn.acm = 0; + pmadapter->ac_params[AC_VO].aci_aifsn.aci = AC_VO; + pmadapter->ac_params[AC_VO].aci_aifsn.aifsn = 2; + pmadapter->ac_params[AC_VO].ecw.ecw_max = 3; + pmadapter->ac_params[AC_VO].ecw.ecw_min = 2; + pmadapter->ac_params[AC_VO].tx_op_limit = 102; + +} + +/** + * @brief Initialize the WMM state information and the WMM data path queues. + * + * @param pmadapter Pointer to the mlan_adapter data structure + * + * @return N/A + */ +t_void +wlan_wmm_init(pmlan_adapter pmadapter) +{ + int i, j; + pmlan_private priv; + + ENTER(); + + for (j = 0; j < pmadapter->priv_num; ++j) { + priv = pmadapter->priv[j]; + if (priv) { + for (i = 0; i < MAX_NUM_TID; ++i) { + priv->aggr_prio_tbl[i].amsdu = + tos_to_tid_inv[i]; + priv->aggr_prio_tbl[i].ampdu_ap = + priv->aggr_prio_tbl[i].ampdu_user = + tos_to_tid_inv[i]; + priv->ibss_ampdu[i] = + priv->aggr_prio_tbl[i].ampdu_user; + priv->wmm.pkts_queued[i] = 0; + priv->wmm.pkts_paused[i] = 0; + priv->wmm.tid_tbl_ptr[i].ra_list_curr = MNULL; + } + priv->wmm.drv_pkt_delay_max = WMM_DRV_DELAY_MAX; + + priv->aggr_prio_tbl[6].amsdu = BA_STREAM_NOT_ALLOWED; + priv->aggr_prio_tbl[7].amsdu = BA_STREAM_NOT_ALLOWED; + priv->aggr_prio_tbl[6].ampdu_ap + = priv->aggr_prio_tbl[6].ampdu_user = + BA_STREAM_NOT_ALLOWED; + priv->ibss_ampdu[6] = BA_STREAM_NOT_ALLOWED; + + priv->aggr_prio_tbl[7].ampdu_ap + = priv->aggr_prio_tbl[7].ampdu_user = + BA_STREAM_NOT_ALLOWED; + priv->ibss_ampdu[7] = BA_STREAM_NOT_ALLOWED; + + priv->add_ba_param.timeout = + MLAN_DEFAULT_BLOCK_ACK_TIMEOUT; +#ifdef STA_SUPPORT + if (priv->bss_type == MLAN_BSS_TYPE_STA) { + priv->add_ba_param.tx_win_size = + MLAN_STA_AMPDU_DEF_TXWINSIZE; + priv->add_ba_param.rx_win_size = + MLAN_STA_AMPDU_DEF_RXWINSIZE; + } +#endif +#ifdef WIFI_DIRECT_SUPPORT + if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT) { + priv->add_ba_param.tx_win_size = + MLAN_WFD_AMPDU_DEF_TXRXWINSIZE; + priv->add_ba_param.rx_win_size = + MLAN_WFD_AMPDU_DEF_TXRXWINSIZE; + } +#endif + if (priv->bss_type == MLAN_BSS_TYPE_NAN) { + priv->add_ba_param.tx_win_size = + MLAN_NAN_AMPDU_DEF_TXRXWINSIZE; + priv->add_ba_param.rx_win_size = + MLAN_NAN_AMPDU_DEF_TXRXWINSIZE; + } +#ifdef UAP_SUPPORT + if (priv->bss_type == MLAN_BSS_TYPE_UAP) { + priv->add_ba_param.tx_win_size = + MLAN_UAP_AMPDU_DEF_TXWINSIZE; + priv->add_ba_param.rx_win_size = + MLAN_UAP_AMPDU_DEF_RXWINSIZE; + } +#endif + priv->user_rxwinsize = priv->add_ba_param.rx_win_size; + priv->add_ba_param.tx_amsdu = MTRUE; + priv->add_ba_param.rx_amsdu = MTRUE; + memset(priv->adapter, priv->rx_seq, 0xff, + sizeof(priv->rx_seq)); + wlan_wmm_default_queue_priorities(priv); + } + } + + LEAVE(); +} + +/** + * @brief Setup the queue priorities and downgrade any queues as required + * by the WMM info. Setups default values if WMM is not active + * for this association. + * + * @param priv Pointer to the mlan_private driver data struct + * + * @return N/A + */ +void +wlan_wmm_setup_queues(pmlan_private priv) +{ + ENTER(); + wlan_wmm_setup_queue_priorities(priv, MNULL); + wlan_wmm_setup_ac_downgrade(priv); + LEAVE(); +} + +#ifdef STA_SUPPORT +/** + * @brief Send a command to firmware to retrieve the current WMM status + * + * @param priv Pointer to the mlan_private driver data struct + * + * @return MLAN_STATUS_SUCCESS; MLAN_STATUS_FAILURE + */ +mlan_status +wlan_cmd_wmm_status_change(pmlan_private priv) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + ret = wlan_prepare_cmd(priv, HostCmd_CMD_WMM_GET_STATUS, 0, 0, 0, + MNULL); + LEAVE(); + return ret; +} +#endif + +/** + * @brief Check if wmm TX queue is empty + * + * @param pmadapter Pointer to the mlan_adapter driver data struct + * + * @return MFALSE if not empty; MTRUE if empty + */ +int +wlan_wmm_lists_empty(pmlan_adapter pmadapter) +{ + int j; + pmlan_private priv; + + ENTER(); + + for (j = 0; j < pmadapter->priv_num; ++j) { + priv = pmadapter->priv[j]; + if (priv) { + if ((priv->port_ctrl_mode == MTRUE) && + (priv->port_open == MFALSE)) { + PRINTM(MINFO, + "wmm_lists_empty: PORT_CLOSED Ignore pkts from BSS%d\n", + j); + continue; + } + if (priv->tx_pause) + continue; + + if (util_scalar_read(pmadapter->pmoal_handle, + &priv->wmm.tx_pkts_queued, + pmadapter->callbacks. + moal_spin_lock, + pmadapter->callbacks. + moal_spin_unlock)) { + LEAVE(); + return MFALSE; + } + } + } + + LEAVE(); + return MTRUE; +} + +/** + * @brief Get ralist node + * + * @param priv Pointer to the mlan_private driver data struct + * @param tid TID + * @param ra_addr Pointer to the route address + * + * @return ra_list or MNULL + */ +raListTbl * +wlan_wmm_get_ralist_node(pmlan_private priv, t_u8 tid, t_u8 *ra_addr) +{ + raListTbl *ra_list; + ENTER(); + ra_list = + (raListTbl *)util_peek_list(priv->adapter->pmoal_handle, + &priv->wmm.tid_tbl_ptr[tid].ra_list, + MNULL, MNULL); + while (ra_list && (ra_list != (raListTbl *) + &priv->wmm.tid_tbl_ptr[tid].ra_list)) { + if (!memcmp + (priv->adapter, ra_list->ra, ra_addr, + MLAN_MAC_ADDR_LENGTH)) { + LEAVE(); + return ra_list; + } + ra_list = ra_list->pnext; + } + LEAVE(); + return MNULL; +} + +/** + * @brief Check if RA list is valid or not + * + * @param priv Pointer to the mlan_private driver data struct + * @param ra_list Pointer to raListTbl + * @param ptrindex TID pointer index + * + * @return MTRUE- valid. MFALSE- invalid. + */ +int +wlan_is_ralist_valid(mlan_private *priv, raListTbl *ra_list, int ptrindex) +{ + raListTbl *rlist; + + ENTER(); + + rlist = (raListTbl *)util_peek_list(priv->adapter->pmoal_handle, + &priv->wmm.tid_tbl_ptr[ptrindex]. + ra_list, MNULL, MNULL); + + while (rlist && (rlist != (raListTbl *) + &priv->wmm.tid_tbl_ptr[ptrindex].ra_list)) { + if (rlist == ra_list) { + LEAVE(); + return MTRUE; + } + + rlist = rlist->pnext; + } + LEAVE(); + return MFALSE; +} + +/** + * @brief Update an existing raList with a new RA and 11n capability + * + * @param priv Pointer to the mlan_private driver data struct + * @param old_ra Old receiver address + * @param new_ra New receiver address + * + * @return integer count of updated nodes + */ +int +wlan_ralist_update(mlan_private *priv, t_u8 *old_ra, t_u8 *new_ra) +{ + t_u8 tid; + int update_count; + raListTbl *ra_list; + + ENTER(); + + update_count = 0; + + for (tid = 0; tid < MAX_NUM_TID; ++tid) { + + ra_list = wlan_wmm_get_ralist_node(priv, tid, old_ra); + + if (ra_list) { + update_count++; + + if (queuing_ra_based(priv)) + ra_list->is_11n_enabled = + wlan_is_11n_enabled(priv, new_ra); + else + ra_list->is_11n_enabled = IS_11N_ENABLED(priv); + ra_list->packet_count = 0; + ra_list->ba_packet_threshold = + wlan_get_random_ba_threshold(priv->adapter); + ra_list->amsdu_in_ampdu = MFALSE; + ra_list->ba_status = BA_STREAM_NOT_SETUP; + PRINTM(MINFO, + "ralist_update: %p, %d, " MACSTR "-->" MACSTR + "\n", ra_list, ra_list->is_11n_enabled, + MAC2STR(ra_list->ra), MAC2STR(new_ra)); + + memcpy(priv->adapter, ra_list->ra, new_ra, + MLAN_MAC_ADDR_LENGTH); + } + } + + LEAVE(); + return update_count; +} + +/** + * @brief Add packet to WMM queue + * + * @param pmadapter Pointer to the mlan_adapter driver data struct + * @param pmbuf Pointer to the mlan_buffer data struct + * + * @return N/A + */ +t_void +wlan_wmm_add_buf_txqueue(pmlan_adapter pmadapter, pmlan_buffer pmbuf) +{ + pmlan_private priv = pmadapter->priv[pmbuf->bss_index]; + t_u32 tid; + raListTbl *ra_list; + t_u8 ra[MLAN_MAC_ADDR_LENGTH], tid_down; + tdlsStatus_e status; +#if defined(UAP_SUPPORT) + sta_node *sta_ptr = MNULL; +#endif + + ENTER(); + + pmbuf->buf_type = MLAN_BUF_TYPE_DATA; + if (!priv->media_connected) { + PRINTM_NETINTF(MWARN, priv); + PRINTM(MWARN, "Drop packet %p in disconnect state\n", pmbuf); + wlan_write_data_complete(pmadapter, pmbuf, MLAN_STATUS_FAILURE); + LEAVE(); + return; + } + tid = pmbuf->priority; + pmadapter->callbacks.moal_spin_lock(pmadapter->pmoal_handle, + priv->wmm.ra_list_spinlock); + tid_down = wlan_wmm_downgrade_tid(priv, tid); + + /* In case of infra as we have already created the list during association + we just don't have to call get_queue_raptr, we will have only 1 raptr + for a tid in case of infra */ + if (!queuing_ra_based(priv)) { + memcpy(pmadapter, ra, pmbuf->pbuf + pmbuf->data_offset, + MLAN_MAC_ADDR_LENGTH); + status = wlan_get_tdls_link_status(priv, ra); + if (MTRUE == wlan_is_tdls_link_setup(status)) { + ra_list = wlan_wmm_get_queue_raptr(priv, tid_down, ra); + pmbuf->flags |= MLAN_BUF_FLAG_TDLS; + } else if (status == TDLS_SETUP_INPROGRESS) { + wlan_add_buf_tdls_txqueue(priv, pmbuf); + pmadapter->callbacks.moal_spin_unlock(pmadapter-> + pmoal_handle, + priv->wmm. + ra_list_spinlock); + LEAVE(); + return; + } else + ra_list = + (raListTbl *)util_peek_list(pmadapter-> + pmoal_handle, + &priv->wmm. + tid_tbl_ptr + [tid_down].ra_list, + MNULL, MNULL); + } else { + memcpy(pmadapter, ra, pmbuf->pbuf + pmbuf->data_offset, + MLAN_MAC_ADDR_LENGTH); + /** put multicast/broadcast packet in the same ralist */ + if (ra[0] & 0x01) + memset(pmadapter, ra, 0xff, sizeof(ra)); +#if defined(UAP_SUPPORT) + else if (priv->bss_type == MLAN_BSS_TYPE_UAP) { + sta_ptr = wlan_get_station_entry(priv, ra); + if (sta_ptr) { + if (!sta_ptr->is_wmm_enabled) { + tid_down = + wlan_wmm_downgrade_tid(priv, + 0xff); + } + } + } +#endif + ra_list = wlan_wmm_get_queue_raptr(priv, tid_down, ra); + } + + if (!ra_list) { + PRINTM_NETINTF(MWARN, priv); + PRINTM(MWARN, + "Drop packet %p, ra_list=%p, media_connected=%d\n", + pmbuf, ra_list, priv->media_connected); + pmadapter->callbacks.moal_spin_unlock(pmadapter->pmoal_handle, + priv->wmm. + ra_list_spinlock); + wlan_write_data_complete(pmadapter, pmbuf, MLAN_STATUS_FAILURE); + LEAVE(); + return; + } + + PRINTM_NETINTF(MDATA, priv); + PRINTM(MDATA, + "Adding pkt %p (priority=%d, tid_down=%d) to ra_list %p\n", + pmbuf, pmbuf->priority, tid_down, ra_list); + util_enqueue_list_tail(pmadapter->pmoal_handle, &ra_list->buf_head, + (pmlan_linked_list)pmbuf, MNULL, MNULL); + + ra_list->total_pkts++; + ra_list->packet_count++; + + priv->wmm.pkts_queued[tid_down]++; + if (ra_list->tx_pause) { + priv->wmm.pkts_paused[tid_down]++; + } else { + util_scalar_increment(pmadapter->pmoal_handle, + &priv->wmm.tx_pkts_queued, MNULL, MNULL); + /* if highest_queued_prio < prio(tid_down), set it to prio(tid_down) */ + util_scalar_conditional_write(pmadapter->pmoal_handle, + &priv->wmm.highest_queued_prio, + MLAN_SCALAR_COND_LESS_THAN, + tos_to_tid_inv[tid_down], + tos_to_tid_inv[tid_down], + MNULL, MNULL); + } + /* Record the current time the packet was queued; used to determine + * the amount of time the packet was queued in the driver before it + * was sent to the firmware. The delay is then sent along with the + * packet to the firmware for aggregate delay calculation for stats + * and MSDU lifetime expiry. + */ + pmadapter->callbacks.moal_get_system_time(pmadapter->pmoal_handle, + &pmbuf->in_ts_sec, + &pmbuf->in_ts_usec); + pmadapter->callbacks.moal_spin_unlock(pmadapter->pmoal_handle, + priv->wmm.ra_list_spinlock); + + LEAVE(); +} + +#ifdef STA_SUPPORT +/** + * @brief Process the GET_WMM_STATUS command response from firmware + * + * The GET_WMM_STATUS response may contain multiple TLVs for: + * - AC Queue status TLVs + * - Current WMM Parameter IE TLV + * - Admission Control action frame TLVs + * + * This function parses the TLVs and then calls further functions + * to process any changes in the queue prioritize or state. + * + * @param priv Pointer to the mlan_private driver data struct + * @param ptlv Pointer to the tlv block returned in the response. + * @param resp_len Length of TLV block + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_ret_wmm_get_status(pmlan_private priv, t_u8 *ptlv, int resp_len) +{ + t_u8 *pcurrent = ptlv; + t_u32 tlv_len; + t_u8 send_wmm_event; + MrvlIEtypes_Data_t *ptlv_hdr; + MrvlIEtypes_WmmQueueStatus_t *ptlv_wmm_q_status; + IEEEtypes_WmmParameter_t *pwmm_param_ie = MNULL; + WmmAcStatus_t *pac_status; + + MrvlIETypes_ActionFrame_t *ptlv_action; + IEEEtypes_Action_WMM_AddTsRsp_t *padd_ts_rsp; + IEEEtypes_Action_WMM_DelTs_t *pdel_ts; + + ENTER(); + + send_wmm_event = MFALSE; + + PRINTM(MINFO, "WMM: WMM_GET_STATUS cmdresp received: %d\n", resp_len); + HEXDUMP("CMD_RESP: WMM_GET_STATUS", pcurrent, resp_len); + + while (resp_len >= sizeof(ptlv_hdr->header)) { + ptlv_hdr = (MrvlIEtypes_Data_t *)pcurrent; + tlv_len = wlan_le16_to_cpu(ptlv_hdr->header.len); + if ((tlv_len + sizeof(ptlv_hdr->header)) > resp_len) { + PRINTM(MERROR, + "WMM get status: Error in processing TLV buffer\n"); + resp_len = 0; + continue; + } + + switch (wlan_le16_to_cpu(ptlv_hdr->header.type)) { + case TLV_TYPE_WMMQSTATUS: + ptlv_wmm_q_status = + (MrvlIEtypes_WmmQueueStatus_t *)ptlv_hdr; + PRINTM(MEVENT, "WMM_STATUS: QSTATUS TLV: %d\n", + ptlv_wmm_q_status->queue_index); + + PRINTM(MINFO, + "CMD_RESP: WMM_GET_STATUS: QSTATUS TLV: %d, %d, %d\n", + ptlv_wmm_q_status->queue_index, + ptlv_wmm_q_status->flow_required, + ptlv_wmm_q_status->disabled); + + pac_status = + &priv->wmm.ac_status[ptlv_wmm_q_status-> + queue_index]; + pac_status->disabled = ptlv_wmm_q_status->disabled; + pac_status->flow_required = + ptlv_wmm_q_status->flow_required; + pac_status->flow_created = + ptlv_wmm_q_status->flow_created; + break; + + case TLV_TYPE_VENDOR_SPECIFIC_IE: /* WMM_IE */ + /* + * Point the regular IEEE IE 2 bytes into the Marvell IE + * and setup the IEEE IE type and length byte fields + */ + + PRINTM(MEVENT, "WMM STATUS: WMM IE\n"); + + HEXDUMP("WMM: WMM TLV:", (t_u8 *)ptlv_hdr, tlv_len + 4); + + pwmm_param_ie = + (IEEEtypes_WmmParameter_t *)(pcurrent + 2); + pwmm_param_ie->vend_hdr.len = (t_u8)tlv_len; + pwmm_param_ie->vend_hdr.element_id = WMM_IE; + + PRINTM(MINFO, + "CMD_RESP: WMM_GET_STATUS: WMM Parameter Set: %d\n", + pwmm_param_ie->qos_info.para_set_count); + + memcpy(priv->adapter, + (t_u8 *)&priv->curr_bss_params.bss_descriptor. + wmm_ie, pwmm_param_ie, + MIN(sizeof(IEEEtypes_WmmParameter_t), + (pwmm_param_ie->vend_hdr.len + 2))); + send_wmm_event = MTRUE; + break; + + case TLV_TYPE_IEEE_ACTION_FRAME: + PRINTM(MEVENT, "WMM_STATUS: IEEE Action Frame\n"); + ptlv_action = (MrvlIETypes_ActionFrame_t *)pcurrent; + + ptlv_action->actionFrame.wmmAc.tspecAct.category = + wlan_le32_to_cpu(ptlv_action->actionFrame.wmmAc. + tspecAct.category); + if (ptlv_action->actionFrame.wmmAc.tspecAct.category == + IEEE_MGMT_ACTION_CATEGORY_WMM_TSPEC) { + + ptlv_action->actionFrame.wmmAc.tspecAct.action = + wlan_le32_to_cpu(ptlv_action-> + actionFrame.wmmAc. + tspecAct.action); + switch (ptlv_action->actionFrame.wmmAc.tspecAct. + action) { + case TSPEC_ACTION_CODE_ADDTS_RSP: + padd_ts_rsp = + &ptlv_action->actionFrame.wmmAc. + addTsRsp; + wlan_send_wmmac_host_event(priv, + "ADDTS_RSP", + ptlv_action-> + srcAddr, + padd_ts_rsp-> + tspecIE. + TspecBody. + TSInfo.TID, + padd_ts_rsp-> + tspecIE. + TspecBody. + TSInfo. + UserPri, + padd_ts_rsp-> + statusCode); + break; + + case TSPEC_ACTION_CODE_DELTS: + pdel_ts = + &ptlv_action->actionFrame.wmmAc. + delTs; + wlan_send_wmmac_host_event(priv, + "DELTS_RX", + ptlv_action-> + srcAddr, + pdel_ts-> + tspecIE. + TspecBody. + TSInfo.TID, + pdel_ts-> + tspecIE. + TspecBody. + TSInfo. + UserPri, + pdel_ts-> + reasonCode); + break; + + case TSPEC_ACTION_CODE_ADDTS_REQ: + default: + break; + } + } + break; + + default: + break; + } + + pcurrent += (tlv_len + sizeof(ptlv_hdr->header)); + resp_len -= (tlv_len + sizeof(ptlv_hdr->header)); + } + + wlan_wmm_setup_queue_priorities(priv, pwmm_param_ie); + wlan_wmm_setup_ac_downgrade(priv); + + if (send_wmm_event) { + wlan_recv_event(priv, MLAN_EVENT_ID_FW_WMM_CONFIG_CHANGE, + MNULL); + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Call back from the command module to allow insertion of a WMM TLV + * + * If the BSS we are associating to supports WMM, add the required WMM + * Information IE to the association request command buffer in the form + * of a Marvell extended IEEE IE. + * + * @param priv Pointer to the mlan_private driver data struct + * @param ppassoc_buf Output parameter: Pointer to the TLV output buffer, + * modified on return to point after the appended WMM TLV + * @param pwmm_ie Pointer to the WMM IE for the BSS we are joining + * @param pht_cap Pointer to the HT IE for the BSS we are joining + * + * @return Length of data appended to the association tlv buffer + */ +t_u32 +wlan_wmm_process_association_req(pmlan_private priv, + t_u8 **ppassoc_buf, + IEEEtypes_WmmParameter_t *pwmm_ie, + IEEEtypes_HTCap_t *pht_cap) +{ + MrvlIEtypes_WmmParamSet_t *pwmm_tlv; + t_u32 ret_len = 0; + + ENTER(); + + /* Null checks */ + if (!ppassoc_buf) { + LEAVE(); + return 0; + } + if (!(*ppassoc_buf)) { + LEAVE(); + return 0; + } + + if (!pwmm_ie) { + LEAVE(); + return 0; + } + + PRINTM(MINFO, "WMM: process assoc req: bss->wmmIe=0x%x\n", + pwmm_ie->vend_hdr.element_id); + + if ((priv->wmm_required + || (pht_cap && (pht_cap->ieee_hdr.element_id == HT_CAPABILITY) + && (priv->config_bands & BAND_GN + || priv->config_bands & BAND_AN)) + ) + && pwmm_ie->vend_hdr.element_id == WMM_IE) { + pwmm_tlv = (MrvlIEtypes_WmmParamSet_t *)*ppassoc_buf; + pwmm_tlv->header.type = (t_u16)wmm_info_ie[0]; + pwmm_tlv->header.type = wlan_cpu_to_le16(pwmm_tlv->header.type); + pwmm_tlv->header.len = (t_u16)wmm_info_ie[1]; + memcpy(priv->adapter, pwmm_tlv->wmm_ie, &wmm_info_ie[2], + pwmm_tlv->header.len); + if (pwmm_ie->qos_info.qos_uapsd) + memcpy(priv->adapter, + (t_u8 *)(pwmm_tlv->wmm_ie + + pwmm_tlv->header.len - + sizeof(priv->wmm_qosinfo)), + &priv->wmm_qosinfo, sizeof(priv->wmm_qosinfo)); + + ret_len = sizeof(pwmm_tlv->header) + pwmm_tlv->header.len; + pwmm_tlv->header.len = wlan_cpu_to_le16(pwmm_tlv->header.len); + + HEXDUMP("ASSOC_CMD: WMM IE", (t_u8 *)pwmm_tlv, ret_len); + *ppassoc_buf += ret_len; + } + + LEAVE(); + return ret_len; +} +#endif /* STA_SUPPORT */ + +/** + * @brief Compute the time delay in the driver queues for a given packet. + * + * When the packet is received at the OS/Driver interface, the current + * time is set in the packet structure. The difference between the present + * time and that received time is computed in this function and limited + * based on pre-compiled limits in the driver. + * + * @param priv Ptr to the mlan_private driver data struct + * @param pmbuf Ptr to the mlan_buffer which has been previously timestamped + * + * @return Time delay of the packet in 2ms units after having limit applied + */ +t_u8 +wlan_wmm_compute_driver_packet_delay(pmlan_private priv, + const pmlan_buffer pmbuf) +{ + t_u8 ret_val = 0; + t_u32 out_ts_sec, out_ts_usec; + t_s32 queue_delay; + + ENTER(); + + priv->adapter->callbacks.moal_get_system_time(priv->adapter-> + pmoal_handle, &out_ts_sec, + &out_ts_usec); + + queue_delay = (t_s32)(out_ts_sec - pmbuf->in_ts_sec) * 1000; + queue_delay += (t_s32)(out_ts_usec - pmbuf->in_ts_usec) / 1000; + + /* + * Queue delay is passed as a uint8 in units of 2ms (ms shifted + * by 1). Min value (other than 0) is therefore 2ms, max is 510ms. + * + * Pass max value if queue_delay is beyond the uint8 range + */ + ret_val = (t_u8)(MIN(queue_delay, priv->wmm.drv_pkt_delay_max) >> 1); + + PRINTM(MINFO, "WMM: Pkt Delay: %d ms, %d ms sent to FW\n", + queue_delay, ret_val); + + LEAVE(); + return ret_val; +} + +/** + * @brief Transmit the highest priority packet awaiting in the WMM Queues + * + * @param pmadapter Pointer to the mlan_adapter driver data struct + * + * @return N/A + */ +void +wlan_wmm_process_tx(pmlan_adapter pmadapter) +{ + ENTER(); + + do { + if (wlan_dequeue_tx_packet(pmadapter)) + break; + if (pmadapter->sdio_ireg & UP_LD_CMD_PORT_HOST_INT_STATUS) { +#ifdef SDIO_MULTI_PORT_TX_AGGR + wlan_send_mp_aggr_buf(pmadapter); +#endif + break; + } + + /* Check if busy */ + } while (!pmadapter->data_sent && !pmadapter->tx_lock_flag + && !wlan_wmm_lists_empty(pmadapter)); + + LEAVE(); + return; +} + +/** + * @brief select wmm queue + * + * @param pmpriv A pointer to mlan_private structure + * @param tid TID 0-7 + * + * @return wmm_queue priority (0-3) + */ +t_u8 +wlan_wmm_select_queue(mlan_private *pmpriv, t_u8 tid) +{ + pmlan_adapter pmadapter = pmpriv->adapter; + t_u8 i; + mlan_wmm_ac_e ac_down = + pmpriv->wmm. + ac_down_graded_vals[wlan_wmm_convert_tos_to_ac(pmadapter, tid)]; + + ENTER(); + + for (i = 0; i < 4; i++) { + if (pmpriv->wmm.queue_priority[i] == ac_down) { + LEAVE(); + return i; + } + } + LEAVE(); + return 0; +} + +/** + * @brief Delete tx packets in RA list + * + * @param priv Pointer to the mlan_private driver data struct + * @param ra_list_head ra list header + * @param tid tid + * + * @return N/A + */ +static INLINE t_u8 +wlan_del_tx_pkts_in_ralist(pmlan_private priv, + mlan_list_head *ra_list_head, int tid) +{ + raListTbl *ra_list = MNULL; + pmlan_adapter pmadapter = priv->adapter; + pmlan_buffer pmbuf = MNULL; + t_u8 ret = MFALSE; + ENTER(); + ra_list = + (raListTbl *)util_peek_list(priv->adapter->pmoal_handle, + ra_list_head, MNULL, MNULL); + while (ra_list && ra_list != (raListTbl *)ra_list_head) { + if (ra_list->total_pkts && (ra_list->tx_pause || + (ra_list->total_pkts > + RX_LOW_THRESHOLD))) { + pmbuf = (pmlan_buffer)util_dequeue_list(pmadapter-> + pmoal_handle, + &ra_list-> + buf_head, MNULL, + MNULL); + if (pmbuf) { + PRINTM(MDATA, + "Drop pkts: tid=%d tx_pause=%d pkts=%d " + MACSTR "\n", tid, ra_list->tx_pause, + ra_list->total_pkts, + MAC2STR(ra_list->ra)); + wlan_write_data_complete(pmadapter, pmbuf, + MLAN_STATUS_FAILURE); + priv->wmm.pkts_queued[tid]--; + priv->num_drop_pkts++; + ra_list->total_pkts--; + if (ra_list->tx_pause) + priv->wmm.pkts_paused[tid]--; + else + util_scalar_decrement(pmadapter-> + pmoal_handle, + &priv->wmm. + tx_pkts_queued, + MNULL, MNULL); + ret = MTRUE; + break; + } + } + ra_list = ra_list->pnext; + } + + LEAVE(); + return ret; +} + +/** + * @brief Drop tx pkts + * + * @param priv Pointer to the mlan_private driver data struct + * + * @return N/A + */ +t_void +wlan_drop_tx_pkts(pmlan_private priv) +{ + int j; + static int i; + pmlan_adapter pmadapter = priv->adapter; + pmadapter->callbacks.moal_spin_lock(pmadapter->pmoal_handle, + priv->wmm.ra_list_spinlock); + for (j = 0; j < MAX_NUM_TID; j++, i++) { + if (i == MAX_NUM_TID) + i = 0; + if (wlan_del_tx_pkts_in_ralist + (priv, &priv->wmm.tid_tbl_ptr[i].ra_list, i)) { + i++; + break; + } + } + pmadapter->callbacks.moal_spin_unlock(pmadapter->pmoal_handle, + priv->wmm.ra_list_spinlock); + return; +} + +/** + * @brief Remove peer ralist + * + * @param priv A pointer to mlan_private + * @param mac peer mac address + * + * @return N/A + */ +t_void +wlan_wmm_delete_peer_ralist(pmlan_private priv, t_u8 *mac) +{ + raListTbl *ra_list; + int i; + pmlan_adapter pmadapter = priv->adapter; + t_u32 pkt_cnt = 0; + t_u32 tx_pkts_queued = 0; + + ENTER(); + pmadapter->callbacks.moal_spin_lock(pmadapter->pmoal_handle, + priv->wmm.ra_list_spinlock); + for (i = 0; i < MAX_NUM_TID; ++i) { + ra_list = wlan_wmm_get_ralist_node(priv, i, mac); + if (ra_list) { + PRINTM(MINFO, "delete sta ralist %p\n", ra_list); + priv->wmm.pkts_queued[i] -= ra_list->total_pkts; + if (ra_list->tx_pause) + priv->wmm.pkts_paused[i] -= ra_list->total_pkts; + else + pkt_cnt += ra_list->total_pkts; + wlan_wmm_del_pkts_in_ralist_node(priv, ra_list); + + util_unlink_list(pmadapter->pmoal_handle, + &priv->wmm.tid_tbl_ptr[i].ra_list, + (pmlan_linked_list)ra_list, MNULL, + MNULL); + pmadapter->callbacks.moal_mfree(pmadapter->pmoal_handle, + (t_u8 *)ra_list); + if (priv->wmm.tid_tbl_ptr[i].ra_list_curr == ra_list) + priv->wmm.tid_tbl_ptr[i].ra_list_curr = + (raListTbl *)&priv->wmm.tid_tbl_ptr[i]. + ra_list; + } + } + if (pkt_cnt) { + tx_pkts_queued = util_scalar_read(pmadapter->pmoal_handle, + &priv->wmm.tx_pkts_queued, + MNULL, MNULL); + tx_pkts_queued -= pkt_cnt; + util_scalar_write(priv->adapter->pmoal_handle, + &priv->wmm.tx_pkts_queued, tx_pkts_queued, + MNULL, MNULL); + util_scalar_write(priv->adapter->pmoal_handle, + &priv->wmm.highest_queued_prio, HIGH_PRIO_TID, + MNULL, MNULL); + } + pmadapter->callbacks.moal_spin_unlock(pmadapter->pmoal_handle, + priv->wmm.ra_list_spinlock); + LEAVE(); +} + +#ifdef STA_SUPPORT +/** + * @brief Hold TDLS packets to tdls pending queue + * + * @param priv A pointer to mlan_private + * @param mac station mac address + * + * @return N/A + */ +t_void +wlan_hold_tdls_packets(pmlan_private priv, t_u8 *mac) +{ + pmlan_buffer pmbuf; + mlan_adapter *pmadapter = priv->adapter; + raListTbl *ra_list = MNULL; + t_u8 i; + + ENTER(); + pmadapter->callbacks.moal_spin_lock(pmadapter->pmoal_handle, + priv->wmm.ra_list_spinlock); + PRINTM(MDATA, "wlan_hold_tdls_packets: " MACSTR "\n", MAC2STR(mac)); + for (i = 0; i < MAX_NUM_TID; ++i) { + ra_list = (raListTbl *)util_peek_list(pmadapter->pmoal_handle, + &priv->wmm.tid_tbl_ptr[i]. + ra_list, MNULL, MNULL); + if (ra_list) { + while ((pmbuf = + wlan_find_tdls_packets(priv, ra_list, mac))) { + util_unlink_list(pmadapter->pmoal_handle, + &ra_list->buf_head, + (pmlan_linked_list)pmbuf, + MNULL, MNULL); + ra_list->total_pkts--; + priv->wmm.pkts_queued[i]--; + util_scalar_decrement(pmadapter->pmoal_handle, + &priv->wmm.tx_pkts_queued, + MNULL, MNULL); + ra_list->packet_count--; + wlan_add_buf_tdls_txqueue(priv, pmbuf); + PRINTM(MDATA, "hold tdls packet=%p\n", pmbuf); + } + } + } + pmadapter->callbacks.moal_spin_unlock(pmadapter->pmoal_handle, + priv->wmm.ra_list_spinlock); + LEAVE(); +} + +/** + * @brief move TDLS packets back to ralist + * + * @param priv A pointer to mlan_private + * @param mac TDLS peer mac address + * @param status tdlsStatus + * + * @return pmlan_buffer or MNULL + */ +t_void +wlan_restore_tdls_packets(pmlan_private priv, t_u8 *mac, tdlsStatus_e status) +{ + pmlan_buffer pmbuf; + mlan_adapter *pmadapter = priv->adapter; + raListTbl *ra_list = MNULL; + t_u32 tid; + t_u32 tid_down; + + ENTER(); + PRINTM(MDATA, "wlan_restore_tdls_packets: " MACSTR " status=%d\n", + MAC2STR(mac), status); + + pmadapter->callbacks.moal_spin_lock(pmadapter->pmoal_handle, + priv->wmm.ra_list_spinlock); + + while ((pmbuf = wlan_find_packets_tdls_txq(priv, mac))) { + util_unlink_list(pmadapter->pmoal_handle, + &priv->tdls_pending_txq, + (pmlan_linked_list)pmbuf, MNULL, MNULL); + tid = pmbuf->priority; + tid_down = wlan_wmm_downgrade_tid(priv, tid); + if (status == TDLS_SETUP_COMPLETE) { + ra_list = wlan_wmm_get_queue_raptr(priv, tid_down, mac); + pmbuf->flags |= MLAN_BUF_FLAG_TDLS; + } else { + ra_list = + (raListTbl *)util_peek_list(pmadapter-> + pmoal_handle, + &priv->wmm. + tid_tbl_ptr + [tid_down].ra_list, + MNULL, MNULL); + pmbuf->flags &= ~MLAN_BUF_FLAG_TDLS; + } + if (!ra_list) { + PRINTM_NETINTF(MWARN, priv); + PRINTM(MWARN, + "Drop packet %p, ra_list=%p media_connected=%d\n", + pmbuf, ra_list, priv->media_connected); + wlan_write_data_complete(pmadapter, pmbuf, + MLAN_STATUS_FAILURE); + continue; + } + PRINTM_NETINTF(MDATA, priv); + PRINTM(MDATA, + "ADD TDLS pkt %p (priority=%d) back to ra_list %p\n", + pmbuf, pmbuf->priority, ra_list); + util_enqueue_list_tail(pmadapter->pmoal_handle, + &ra_list->buf_head, + (pmlan_linked_list)pmbuf, MNULL, MNULL); + ra_list->total_pkts++; + ra_list->packet_count++; + priv->wmm.pkts_queued[tid_down]++; + util_scalar_increment(pmadapter->pmoal_handle, + &priv->wmm.tx_pkts_queued, MNULL, MNULL); + util_scalar_conditional_write(pmadapter->pmoal_handle, + &priv->wmm.highest_queued_prio, + MLAN_SCALAR_COND_LESS_THAN, + tos_to_tid_inv[tid_down], + tos_to_tid_inv[tid_down], MNULL, + MNULL); + } + if (status != TDLS_SETUP_COMPLETE) + wlan_wmm_delete_tdls_ralist(priv, mac); + pmadapter->callbacks.moal_spin_unlock(pmadapter->pmoal_handle, + priv->wmm.ra_list_spinlock); + LEAVE(); +} + +/** + * @brief This function prepares the command of ADDTS + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param pdata_buf A pointer to data buffer + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_cmd_wmm_addts_req(IN pmlan_private pmpriv, + OUT HostCmd_DS_COMMAND *cmd, IN t_void *pdata_buf) +{ + mlan_ds_wmm_addts *paddts = (mlan_ds_wmm_addts *)pdata_buf; + HostCmd_DS_WMM_ADDTS_REQ *pcmd_addts = &cmd->params.add_ts; + + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_WMM_ADDTS_REQ); + cmd->size = wlan_cpu_to_le16(sizeof(pcmd_addts->dialog_token) + + sizeof(pcmd_addts->timeout_ms) + + sizeof(pcmd_addts->command_result) + + sizeof(pcmd_addts->ieee_status_code) + + paddts->ie_data_len + S_DS_GEN); + cmd->result = 0; + + pcmd_addts->timeout_ms = wlan_cpu_to_le32(paddts->timeout); + pcmd_addts->dialog_token = paddts->dialog_tok; + memcpy(pmpriv->adapter, + pcmd_addts->tspec_data, + paddts->ie_data, MIN(WMM_TSPEC_SIZE, paddts->ie_data_len)); + + LEAVE(); + + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of ADDTS + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_ret_wmm_addts_req(IN pmlan_private pmpriv, + const IN HostCmd_DS_COMMAND *resp, + OUT mlan_ioctl_req *pioctl_buf) +{ + mlan_ds_wmm_cfg *pwmm = MNULL; + mlan_ds_wmm_addts *paddts = MNULL; + const HostCmd_DS_WMM_ADDTS_REQ *presp_addts = &resp->params.add_ts; + + ENTER(); + + if (pioctl_buf) { + pwmm = (mlan_ds_wmm_cfg *)pioctl_buf->pbuf; + paddts = (mlan_ds_wmm_addts *)&pwmm->param.addts; + paddts->result = wlan_le32_to_cpu(presp_addts->command_result); + paddts->dialog_tok = presp_addts->dialog_token; + paddts->status_code = (t_u32)presp_addts->ieee_status_code; + + if (paddts->result == MLAN_CMD_RESULT_SUCCESS) { + /* The tspecData field is potentially variable in size due to + * extra IEs that may have been in the ADDTS response action + * frame. Calculate the data length from the firmware command + * response. + */ + paddts->ie_data_len + = (t_u8)(resp->size + - sizeof(presp_addts->command_result) + - sizeof(presp_addts->timeout_ms) + - sizeof(presp_addts->dialog_token) + - sizeof(presp_addts->ieee_status_code) + - S_DS_GEN); + + /* Copy the TSPEC data include any extra IEs after the TSPEC */ + memcpy(pmpriv->adapter, + paddts->ie_data, + presp_addts->tspec_data, paddts->ie_data_len); + } else { + paddts->ie_data_len = 0; + } + PRINTM(MINFO, "TSPEC: ADDTS ret = %d,%d sz=%d\n", + paddts->result, paddts->status_code, + paddts->ie_data_len); + + HEXDUMP("TSPEC: ADDTS data", + paddts->ie_data, paddts->ie_data_len); + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares the command of DELTS + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param pdata_buf A pointer to data buffer + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_cmd_wmm_delts_req(IN pmlan_private pmpriv, + OUT HostCmd_DS_COMMAND *cmd, IN t_void *pdata_buf) +{ + mlan_ds_wmm_delts *pdelts = (mlan_ds_wmm_delts *)pdata_buf; + HostCmd_DS_WMM_DELTS_REQ *pcmd_delts = &cmd->params.del_ts; + + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_WMM_DELTS_REQ); + cmd->size = wlan_cpu_to_le16(sizeof(pcmd_delts->dialog_token) + + sizeof(pcmd_delts->command_result) + + sizeof(pcmd_delts->ieee_reason_code) + + pdelts->ie_data_len + S_DS_GEN); + cmd->result = 0; + pcmd_delts->ieee_reason_code = (t_u8)pdelts->status_code; + memcpy(pmpriv->adapter, + pcmd_delts->tspec_data, + pdelts->ie_data, MIN(WMM_TSPEC_SIZE, pdelts->ie_data_len)); + + LEAVE(); + + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of DELTS + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_ret_wmm_delts_req(IN pmlan_private pmpriv, + const IN HostCmd_DS_COMMAND *resp, + OUT mlan_ioctl_req *pioctl_buf) +{ + mlan_ds_wmm_cfg *pwmm; + IEEEtypes_WMM_TSPEC_t *ptspec_ie; + const HostCmd_DS_WMM_DELTS_REQ *presp_delts = &resp->params.del_ts; + + ENTER(); + + if (pioctl_buf) { + pwmm = (mlan_ds_wmm_cfg *)pioctl_buf->pbuf; + pwmm->param.delts.result = + wlan_le32_to_cpu(presp_delts->command_result); + + PRINTM(MINFO, "TSPEC: DELTS result = %d\n", + presp_delts->command_result); + + if (pwmm->param.delts.result == 0) { + ptspec_ie = + (IEEEtypes_WMM_TSPEC_t *)presp_delts-> + tspec_data; + wlan_send_wmmac_host_event(pmpriv, "DELTS_TX", MNULL, + ptspec_ie->TspecBody.TSInfo. + TID, + ptspec_ie->TspecBody.TSInfo. + UserPri, + presp_delts-> + ieee_reason_code); + + } + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares the command of WMM_QUEUE_STATS + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param pdata_buf A pointer to data buffer + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_cmd_wmm_queue_stats(IN pmlan_private pmpriv, + OUT HostCmd_DS_COMMAND *cmd, IN t_void *pdata_buf) +{ + mlan_ds_wmm_queue_stats *pqstats = (mlan_ds_wmm_queue_stats *)pdata_buf; + HostCmd_DS_WMM_QUEUE_STATS *pcmd_qstats = &cmd->params.queue_stats; + t_u8 id; + + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_WMM_QUEUE_STATS); + cmd->size = wlan_cpu_to_le16(sizeof(HostCmd_DS_WMM_QUEUE_STATS) + + S_DS_GEN); + cmd->result = 0; + + pcmd_qstats->action = pqstats->action; + pcmd_qstats->select_is_userpri = 1; + pcmd_qstats->select_bin = pqstats->user_priority; + pcmd_qstats->pkt_count = wlan_cpu_to_le16(pqstats->pkt_count); + pcmd_qstats->pkt_loss = wlan_cpu_to_le16(pqstats->pkt_loss); + pcmd_qstats->avg_queue_delay = + wlan_cpu_to_le32(pqstats->avg_queue_delay); + pcmd_qstats->avg_tx_delay = wlan_cpu_to_le32(pqstats->avg_tx_delay); + pcmd_qstats->used_time = wlan_cpu_to_le16(pqstats->used_time); + pcmd_qstats->policed_time = wlan_cpu_to_le16(pqstats->policed_time); + for (id = 0; id < MLAN_WMM_STATS_PKTS_HIST_BINS; id++) { + pcmd_qstats->delay_histogram[id] = + wlan_cpu_to_le16(pqstats->delay_histogram[id]); + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of WMM_QUEUE_STATS + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_ret_wmm_queue_stats(IN pmlan_private pmpriv, + const IN HostCmd_DS_COMMAND *resp, + OUT mlan_ioctl_req *pioctl_buf) +{ + mlan_ds_wmm_cfg *pwmm = MNULL; + mlan_ds_wmm_queue_stats *pqstats = MNULL; + const HostCmd_DS_WMM_QUEUE_STATS *presp_qstats = + &resp->params.queue_stats; + t_u8 id; + + ENTER(); + + if (pioctl_buf) { + pwmm = (mlan_ds_wmm_cfg *)pioctl_buf->pbuf; + pqstats = (mlan_ds_wmm_queue_stats *)&pwmm->param.q_stats; + + pqstats->action = presp_qstats->action; + pqstats->user_priority = presp_qstats->select_bin; + pqstats->pkt_count = wlan_le16_to_cpu(presp_qstats->pkt_count); + pqstats->pkt_loss = wlan_le16_to_cpu(presp_qstats->pkt_loss); + pqstats->avg_queue_delay + = wlan_le32_to_cpu(presp_qstats->avg_queue_delay); + pqstats->avg_tx_delay + = wlan_le32_to_cpu(presp_qstats->avg_tx_delay); + pqstats->used_time = wlan_le16_to_cpu(presp_qstats->used_time); + pqstats->policed_time + = wlan_le16_to_cpu(presp_qstats->policed_time); + for (id = 0; id < MLAN_WMM_STATS_PKTS_HIST_BINS; id++) { + pqstats->delay_histogram[id] + = wlan_le16_to_cpu(presp_qstats-> + delay_histogram[id]); + } + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares the command of WMM_TS_STATUS + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param pdata_buf A pointer to data buffer + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_cmd_wmm_ts_status(IN pmlan_private pmpriv, + OUT HostCmd_DS_COMMAND *cmd, IN t_void *pdata_buf) +{ + mlan_ds_wmm_ts_status *pts_status = (mlan_ds_wmm_ts_status *)pdata_buf; + HostCmd_DS_WMM_TS_STATUS *pcmd_ts_status = &cmd->params.ts_status; + + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_WMM_TS_STATUS); + cmd->size = wlan_cpu_to_le16(sizeof(HostCmd_DS_WMM_TS_STATUS) + + S_DS_GEN); + cmd->result = 0; + + memcpy(pmpriv->adapter, (t_void *)pcmd_ts_status, (t_void *)pts_status, + sizeof(HostCmd_DS_WMM_TS_STATUS)); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of WMM_TS_STATUS + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_ret_wmm_ts_status(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + OUT mlan_ioctl_req *pioctl_buf) +{ + mlan_ds_wmm_cfg *pwmm = MNULL; + HostCmd_DS_WMM_TS_STATUS *presp_ts_status = &resp->params.ts_status; + + ENTER(); + + if (pioctl_buf) { + pwmm = (mlan_ds_wmm_cfg *)pioctl_buf->pbuf; + presp_ts_status->medium_time + = wlan_le16_to_cpu(presp_ts_status->medium_time); + memcpy(pmpriv->adapter, + (t_void *)&pwmm->param.ts_status, + (t_void *)presp_ts_status, + sizeof(mlan_ds_wmm_ts_status)); + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Set/Get WMM status + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success + */ +static mlan_status +wlan_wmm_ioctl_enable(IN pmlan_adapter pmadapter, IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_wmm_cfg *wmm = MNULL; + ENTER(); + wmm = (mlan_ds_wmm_cfg *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_GET) + wmm->param.wmm_enable = (t_u32)pmpriv->wmm_required; + else + pmpriv->wmm_required = (t_u8)wmm->param.wmm_enable; + pioctl_req->data_read_written = sizeof(t_u32) + MLAN_SUB_COMMAND_SIZE; + LEAVE(); + return ret; +} + +/** + * @brief Set/Get WMM QoS configuration + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success + */ +static mlan_status +wlan_wmm_ioctl_qos(IN pmlan_adapter pmadapter, IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_wmm_cfg *wmm = MNULL; + + ENTER(); + + wmm = (mlan_ds_wmm_cfg *)pioctl_req->pbuf; + + if (pioctl_req->action == MLAN_ACT_GET) + wmm->param.qos_cfg = pmpriv->wmm_qosinfo; + else { + pmpriv->wmm_qosinfo = wmm->param.qos_cfg; + pmpriv->saved_wmm_qosinfo = wmm->param.qos_cfg; + } + + pioctl_req->data_read_written = sizeof(t_u8) + MLAN_SUB_COMMAND_SIZE; + + LEAVE(); + return ret; +} + +/** + * @brief Request for add a TSPEC + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status +wlan_wmm_ioctl_addts_req(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_private pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_wmm_cfg *cfg = MNULL; + + ENTER(); + cfg = (mlan_ds_wmm_cfg *)pioctl_req->pbuf; + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_WMM_ADDTS_REQ, + 0, 0, (t_void *)pioctl_req, + (t_void *)&cfg->param.addts); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Request for delete a TSPEC + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status +wlan_wmm_ioctl_delts_req(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_private pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_wmm_cfg *cfg = MNULL; + + ENTER(); + cfg = (mlan_ds_wmm_cfg *)pioctl_req->pbuf; + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_WMM_DELTS_REQ, + 0, 0, (t_void *)pioctl_req, + (t_void *)&cfg->param.delts); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief To get and start/stop queue stats on a WMM AC + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status +wlan_wmm_ioctl_queue_stats(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_private pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_wmm_cfg *cfg = MNULL; + + ENTER(); + cfg = (mlan_ds_wmm_cfg *)pioctl_req->pbuf; + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_WMM_QUEUE_STATS, + 0, 0, (t_void *)pioctl_req, + (t_void *)&cfg->param.q_stats); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Get the status of the WMM AC queues + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success + */ +static mlan_status +wlan_wmm_ioctl_queue_status(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_private pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_wmm_cfg *cfg = MNULL; + mlan_ds_wmm_queue_status *pqstatus = MNULL; + WmmAcStatus_t *pac_status = MNULL; + mlan_wmm_ac_e ac_idx; + + ENTER(); + + cfg = (mlan_ds_wmm_cfg *)pioctl_req->pbuf; + pqstatus = (mlan_ds_wmm_queue_status *)&cfg->param.q_status; + + for (ac_idx = WMM_AC_BK; ac_idx <= WMM_AC_VO; ac_idx++) { + pac_status = &pmpriv->wmm.ac_status[ac_idx]; + + /* Firmware status */ + pqstatus->ac_status[ac_idx].flow_required = + pac_status->flow_required; + pqstatus->ac_status[ac_idx].flow_created = + pac_status->flow_created; + pqstatus->ac_status[ac_idx].disabled = pac_status->disabled; + + /* ACM bit reflected in firmware status (redundant) */ + pqstatus->ac_status[ac_idx].wmm_acm = pac_status->flow_required; + } + + LEAVE(); + return ret; +} + +/** + * @brief Get the status of the WMM Traffic Streams + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status +wlan_wmm_ioctl_ts_status(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_private pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_wmm_cfg *cfg = MNULL; + + ENTER(); + + cfg = (mlan_ds_wmm_cfg *)pioctl_req->pbuf; + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_WMM_TS_STATUS, + 0, 0, (t_void *)pioctl_req, + (t_void *)&cfg->param.ts_status); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} +#endif /* STA_SUPPORT */ + +/** + * @brief This function prepares the command of WMM_PARAM_CONFIG + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action cmd action. + * @param pdata_buf A pointer to data buffer + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_cmd_wmm_param_config(IN pmlan_private pmpriv, + OUT HostCmd_DS_COMMAND *cmd, + IN t_u8 cmd_action, IN t_void *pdata_buf) +{ + wmm_ac_parameters_t *ac_params = (wmm_ac_parameters_t *)pdata_buf; + HostCmd_DS_WMM_PARAM_CONFIG *pcmd_cfg = &cmd->params.param_config; + t_u8 i = 0; + + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_WMM_PARAM_CONFIG); + cmd->size = + wlan_cpu_to_le16(sizeof(HostCmd_DS_WMM_PARAM_CONFIG) + + S_DS_GEN); + cmd->result = 0; + + pcmd_cfg->action = cmd_action; + if (cmd_action == HostCmd_ACT_GEN_SET) { + memcpy(pmpriv->adapter, pcmd_cfg->ac_params, ac_params, + sizeof(wmm_ac_parameters_t) * MAX_AC_QUEUES); + for (i = 0; i < MAX_AC_QUEUES; i++) { + pcmd_cfg->ac_params[i].tx_op_limit = + wlan_cpu_to_le16(pcmd_cfg->ac_params[i]. + tx_op_limit); + } + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of WMM_PARAM_CONFIG + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_ret_wmm_param_config(IN pmlan_private pmpriv, + const IN HostCmd_DS_COMMAND *resp, + OUT mlan_ioctl_req *pioctl_buf) +{ + mlan_ds_wmm_cfg *pwmm = MNULL; + HostCmd_DS_WMM_PARAM_CONFIG *pcfg = + (HostCmd_DS_WMM_PARAM_CONFIG *) & resp->params.param_config; + t_u8 i; + + ENTER(); + + if (pioctl_buf) { + pwmm = (mlan_ds_wmm_cfg *)pioctl_buf->pbuf; + for (i = 0; i < MAX_AC_QUEUES; i++) { + pcfg->ac_params[i].tx_op_limit = + wlan_le16_to_cpu(pcfg->ac_params[i]. + tx_op_limit); + } + memcpy(pmpriv->adapter, pwmm->param.ac_params, pcfg->ac_params, + sizeof(wmm_ac_parameters_t) * MAX_AC_QUEUES); + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares the command of WMM_QUEUE_CONFIG + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param pdata_buf A pointer to data buffer + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_cmd_wmm_queue_config(IN pmlan_private pmpriv, + OUT HostCmd_DS_COMMAND *cmd, IN t_void *pdata_buf) +{ + mlan_ds_wmm_queue_config *pqcfg = (mlan_ds_wmm_queue_config *)pdata_buf; + HostCmd_DS_WMM_QUEUE_CONFIG *pcmd_qcfg = &cmd->params.queue_config; + + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_WMM_QUEUE_CONFIG); + cmd->size = wlan_cpu_to_le16(sizeof(pcmd_qcfg->action) + + sizeof(pcmd_qcfg->access_category) + + sizeof(pcmd_qcfg->msdu_lifetime_expiry) + + S_DS_GEN); + cmd->result = 0; + + pcmd_qcfg->action = pqcfg->action; + pcmd_qcfg->access_category = pqcfg->access_category; + pcmd_qcfg->msdu_lifetime_expiry = + wlan_cpu_to_le16(pqcfg->msdu_lifetime_expiry); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of WMM_QUEUE_CONFIG + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +wlan_ret_wmm_queue_config(IN pmlan_private pmpriv, + const IN HostCmd_DS_COMMAND *resp, + OUT mlan_ioctl_req *pioctl_buf) +{ + mlan_ds_wmm_cfg *pwmm = MNULL; + const HostCmd_DS_WMM_QUEUE_CONFIG *presp_qcfg = + &resp->params.queue_config; + + ENTER(); + + if (pioctl_buf) { + pwmm = (mlan_ds_wmm_cfg *)pioctl_buf->pbuf; + pwmm->param.q_cfg.action = wlan_le32_to_cpu(presp_qcfg->action); + pwmm->param.q_cfg.access_category = + wlan_le32_to_cpu(presp_qcfg->access_category); + pwmm->param.q_cfg.msdu_lifetime_expiry = + wlan_le16_to_cpu(presp_qcfg->msdu_lifetime_expiry); + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Set/Get a specified AC Queue's parameters + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status +wlan_wmm_ioctl_queue_config(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_private pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_wmm_cfg *cfg = MNULL; + + ENTER(); + cfg = (mlan_ds_wmm_cfg *)pioctl_req->pbuf; + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_WMM_QUEUE_CONFIG, + 0, 0, (t_void *)pioctl_req, + (t_void *)&cfg->param.q_cfg); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief WMM configuration handler + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +mlan_status +wlan_wmm_cfg_ioctl(IN pmlan_adapter pmadapter, IN pmlan_ioctl_req pioctl_req) +{ + mlan_status status = MLAN_STATUS_SUCCESS; + mlan_ds_wmm_cfg *wmm = MNULL; + + ENTER(); + + if (pioctl_req->buf_len < sizeof(mlan_ds_wmm_cfg)) { + PRINTM(MWARN, "MLAN bss IOCTL length is too short.\n"); + pioctl_req->data_read_written = 0; + pioctl_req->buf_len_needed = sizeof(mlan_ds_wmm_cfg); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + LEAVE(); + return MLAN_STATUS_RESOURCE; + } + wmm = (mlan_ds_wmm_cfg *)pioctl_req->pbuf; + switch (wmm->sub_command) { +#ifdef STA_SUPPORT + case MLAN_OID_WMM_CFG_ENABLE: + status = wlan_wmm_ioctl_enable(pmadapter, pioctl_req); + break; + case MLAN_OID_WMM_CFG_QOS: + status = wlan_wmm_ioctl_qos(pmadapter, pioctl_req); + break; + case MLAN_OID_WMM_CFG_ADDTS: + status = wlan_wmm_ioctl_addts_req(pmadapter, pioctl_req); + break; + case MLAN_OID_WMM_CFG_DELTS: + status = wlan_wmm_ioctl_delts_req(pmadapter, pioctl_req); + break; + case MLAN_OID_WMM_CFG_QUEUE_STATS: + status = wlan_wmm_ioctl_queue_stats(pmadapter, pioctl_req); + break; + case MLAN_OID_WMM_CFG_QUEUE_STATUS: + status = wlan_wmm_ioctl_queue_status(pmadapter, pioctl_req); + break; + case MLAN_OID_WMM_CFG_TS_STATUS: + status = wlan_wmm_ioctl_ts_status(pmadapter, pioctl_req); + break; +#endif + case MLAN_OID_WMM_CFG_QUEUE_CONFIG: + status = wlan_wmm_ioctl_queue_config(pmadapter, pioctl_req); + break; + default: + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + status = MLAN_STATUS_FAILURE; + break; + } + LEAVE(); + return status; +} + +/** + * @brief Get ralist info + * + * @param priv A pointer to mlan_private structure + * @param buf A pointer to ralist_info structure + * @return number of ralist entry + * + */ +int +wlan_get_ralist_info(mlan_private *priv, ralist_info *buf) +{ + ralist_info *plist = buf; + mlan_list_head *ra_list_head = MNULL; + raListTbl *ra_list; + int i; + int count = 0; + for (i = 0; i < MAX_NUM_TID; i++) { + ra_list_head = &priv->wmm.tid_tbl_ptr[i].ra_list; + ra_list = + (raListTbl *)util_peek_list(priv->adapter->pmoal_handle, + ra_list_head, MNULL, MNULL); + while (ra_list && ra_list != (raListTbl *)ra_list_head) { + if (ra_list->total_pkts) { + plist->total_pkts = ra_list->total_pkts; + plist->tid = i; + plist->tx_pause = ra_list->tx_pause; + memcpy(priv->adapter, plist->ra, ra_list->ra, + MLAN_MAC_ADDR_LENGTH); + plist++; + count++; + if (count >= MLAN_MAX_RALIST_NUM) + break; + } + ra_list = ra_list->pnext; + } + } + LEAVE(); + return count; +} + +/** + * @brief dump ralist info + * + * @param priv A pointer to mlan_private structure + * + * @return N/A + * + */ +void +wlan_dump_ralist(mlan_private *priv) +{ + mlan_list_head *ra_list_head = MNULL; + raListTbl *ra_list; + mlan_adapter *pmadapter = priv->adapter; + int i; + t_u32 tx_pkts_queued; + + tx_pkts_queued = + util_scalar_read(pmadapter->pmoal_handle, + &priv->wmm.tx_pkts_queued, MNULL, MNULL); + PRINTM(MERROR, "bss_index = %d, tx_pkts_queued = %d\n", priv->bss_index, + tx_pkts_queued); + if (!tx_pkts_queued) + return; + for (i = 0; i < MAX_NUM_TID; i++) { + ra_list_head = &priv->wmm.tid_tbl_ptr[i].ra_list; + ra_list = + (raListTbl *)util_peek_list(priv->adapter->pmoal_handle, + ra_list_head, MNULL, MNULL); + while (ra_list && ra_list != (raListTbl *)ra_list_head) { + if (ra_list->total_pkts) { + PRINTM(MERROR, + "ralist ra: %02x:%02x:%02x:%02x:%02x:%02x tid=%d pkts=%d pause=%d\n", + ra_list->ra[0], ra_list->ra[1], + ra_list->ra[2], ra_list->ra[3], + ra_list->ra[4], ra_list->ra[5], i, + ra_list->total_pkts, ra_list->tx_pause); + } + ra_list = ra_list->pnext; + } + } + return; +} + +/** + * @brief get tid down + * + * @param priv A pointer to mlan_private structure + * @param tid tid + * + * @return tid_down + * + */ +int +wlan_get_wmm_tid_down(mlan_private *priv, int tid) +{ + return wlan_wmm_downgrade_tid(priv, tid); +} diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_wmm.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_wmm.h new file mode 100644 index 000000000000..14f5636d4240 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlan/mlan_wmm.h @@ -0,0 +1,242 @@ +/** @file mlan_wmm.h + * + * @brief This file contains related macros, enum, and struct + * of wmm functionalities + * + * Copyright (C) 2008-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/**************************************************** +Change log: + 10/24/2008: initial version +****************************************************/ + +#ifndef _MLAN_WMM_H_ +#define _MLAN_WMM_H_ + +/** + * @brief This function gets the TID + * + * @param pmadapter A pointer to mlan_adapter structure + * @param ptr A pointer to RA list table + * + * @return TID + */ +static INLINE int +wlan_get_tid(pmlan_adapter pmadapter, raListTbl *ptr) +{ + pmlan_buffer mbuf; + + ENTER(); + mbuf = (pmlan_buffer)util_peek_list(pmadapter->pmoal_handle, + &ptr->buf_head, MNULL, MNULL); + LEAVE(); + + return mbuf->priority; +} + +/** + * @brief This function gets the length of a list + * + * @param pmadapter A pointer to mlan_adapter structure + * @param head A pointer to mlan_list_head + * + * @return Length of list + */ +static INLINE int +wlan_wmm_list_len(pmlan_adapter pmadapter, pmlan_list_head head) +{ + pmlan_linked_list pos; + int count = 0; + + ENTER(); + + pos = head->pnext; + + while (pos != (pmlan_linked_list)head) { + ++count; + pos = pos->pnext; + } + + LEAVE(); + return count; +} + +/** + * @brief This function requests a ralist lock + * + * @param priv A pointer to mlan_private structure + * + * @return N/A + */ +static INLINE t_void +wlan_request_ralist_lock(IN mlan_private *priv) +{ + mlan_adapter *pmadapter = priv->adapter; + mlan_callbacks *pcb = (mlan_callbacks *)&pmadapter->callbacks; + + ENTER(); + + /* Call MOAL spin lock callback function */ + pcb->moal_spin_lock(pmadapter->pmoal_handle, + priv->wmm.ra_list_spinlock); + + LEAVE(); + return; +} + +/** + * @brief This function releases a lock on ralist + * + * @param priv A pointer to mlan_private structure + * + * @return N/A + */ +static INLINE t_void +wlan_release_ralist_lock(IN mlan_private *priv) +{ + mlan_adapter *pmadapter = priv->adapter; + mlan_callbacks *pcb = (mlan_callbacks *)&pmadapter->callbacks; + + ENTER(); + + /* Call MOAL spin unlock callback function */ + pcb->moal_spin_unlock(pmadapter->pmoal_handle, + priv->wmm.ra_list_spinlock); + + LEAVE(); + return; +} + +/** Add buffer to WMM Tx queue */ +void wlan_wmm_add_buf_txqueue(pmlan_adapter pmadapter, pmlan_buffer pmbuf); +/** Add to RA list */ +void wlan_ralist_add(mlan_private *priv, t_u8 *ra); +/** Update the RA list */ +int wlan_ralist_update(mlan_private *priv, t_u8 *old_ra, t_u8 *new_ra); + +/** WMM status change command handler */ +mlan_status wlan_cmd_wmm_status_change(pmlan_private priv); +/** Check if WMM lists are empty */ +int wlan_wmm_lists_empty(pmlan_adapter pmadapter); +/** Process WMM transmission */ +t_void wlan_wmm_process_tx(pmlan_adapter pmadapter); +/** Test to see if the ralist ptr is valid */ +int wlan_is_ralist_valid(mlan_private *priv, raListTbl *ra_list, int tid); + +raListTbl *wlan_wmm_get_ralist_node(pmlan_private priv, t_u8 tid, + t_u8 *ra_addr); +t_u8 wlan_get_random_ba_threshold(pmlan_adapter pmadapter); + +/** Compute driver packet delay */ +t_u8 wlan_wmm_compute_driver_packet_delay(pmlan_private priv, + const pmlan_buffer pmbuf); +/** Initialize WMM */ +t_void wlan_wmm_init(pmlan_adapter pmadapter); +/** Initialize WMM paramter */ +t_void wlan_init_wmm_param(pmlan_adapter pmadapter); +/** Setup WMM queues */ +extern void wlan_wmm_setup_queues(pmlan_private priv); +/* Setup default queues */ +void wlan_wmm_default_queue_priorities(pmlan_private priv); +/* process wmm_param_config command */ +mlan_status wlan_cmd_wmm_param_config(IN pmlan_private pmpriv, + OUT HostCmd_DS_COMMAND *cmd, + IN t_u8 cmd_action, IN t_void *pdata_buf); + +/* process wmm_param_config command response */ +mlan_status wlan_ret_wmm_param_config(IN pmlan_private pmpriv, + const IN HostCmd_DS_COMMAND *resp, + OUT mlan_ioctl_req *pioctl_buf); + +#ifdef STA_SUPPORT +/** Process WMM association request */ +extern t_u32 wlan_wmm_process_association_req(pmlan_private priv, + t_u8 **ppAssocBuf, + IEEEtypes_WmmParameter_t *pWmmIE, + IEEEtypes_HTCap_t *pHTCap); +#endif /* STA_SUPPORT */ + +/** setup wmm queue priorities */ +void wlan_wmm_setup_queue_priorities(pmlan_private priv, + IEEEtypes_WmmParameter_t *wmm_ie); + +/* Get tid_down from tid */ +int wlan_get_wmm_tid_down(mlan_private *priv, int tid); +/** Downgrade WMM priority queue */ +void wlan_wmm_setup_ac_downgrade(pmlan_private priv); +/** select WMM queue */ +t_u8 wlan_wmm_select_queue(mlan_private *pmpriv, t_u8 tid); +t_void wlan_wmm_delete_peer_ralist(pmlan_private priv, t_u8 *mac); + +#ifdef STA_SUPPORT +/* + * Functions used in the cmd handling routine + */ +/** WMM ADDTS request command handler */ +extern mlan_status wlan_cmd_wmm_addts_req(IN pmlan_private pmpriv, + OUT HostCmd_DS_COMMAND *cmd, + IN t_void *pdata_buf); +/** WMM DELTS request command handler */ +extern mlan_status wlan_cmd_wmm_delts_req(IN pmlan_private pmpriv, + OUT HostCmd_DS_COMMAND *cmd, + IN t_void *pdata_buf); +/** WMM QUEUE_STATS command handler */ +extern mlan_status wlan_cmd_wmm_queue_stats(IN pmlan_private pmpriv, + OUT HostCmd_DS_COMMAND *cmd, + IN t_void *pdata_buf); +/** WMM TS_STATUS command handler */ +extern mlan_status wlan_cmd_wmm_ts_status(IN pmlan_private pmpriv, + OUT HostCmd_DS_COMMAND *cmd, + IN t_void *pdata_buf); + +/* + * Functions used in the cmdresp handling routine + */ +/** WMM get status command response handler */ +extern mlan_status wlan_ret_wmm_get_status(IN pmlan_private priv, + IN t_u8 *ptlv, IN int resp_len); +/** WMM ADDTS request command response handler */ +extern mlan_status wlan_ret_wmm_addts_req(IN pmlan_private pmpriv, + const IN HostCmd_DS_COMMAND *resp, + OUT mlan_ioctl_req *pioctl_buf); +/** WMM DELTS request command response handler */ +extern mlan_status wlan_ret_wmm_delts_req(IN pmlan_private pmpriv, + const IN HostCmd_DS_COMMAND *resp, + OUT mlan_ioctl_req *pioctl_buf); +/** WMM QUEUE_STATS command response handler */ +extern mlan_status wlan_ret_wmm_queue_stats(IN pmlan_private pmpriv, + const IN HostCmd_DS_COMMAND *resp, + OUT mlan_ioctl_req *pioctl_buf); +/** WMM TS_STATUS command response handler */ +extern mlan_status wlan_ret_wmm_ts_status(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *resp, + OUT mlan_ioctl_req *pioctl_buf); +#endif /* STA_SUPPORT */ + +/** WMM QUEUE_CONFIG command handler */ +extern mlan_status wlan_cmd_wmm_queue_config(IN pmlan_private pmpriv, + OUT HostCmd_DS_COMMAND *cmd, + IN t_void *pdata_buf); + +/** WMM QUEUE_CONFIG command response handler */ +extern mlan_status wlan_ret_wmm_queue_config(IN pmlan_private pmpriv, + const IN HostCmd_DS_COMMAND *resp, + OUT mlan_ioctl_req *pioctl_buf); + +mlan_status wlan_wmm_cfg_ioctl(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req); +#endif /* !_MLAN_WMM_H_ */ diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/mlan.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/mlan.h new file mode 100644 index 000000000000..05d71a3bcd36 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/mlan.h @@ -0,0 +1,35 @@ +/** @file mlan.h + * + * @brief This file declares all APIs that will be called from MOAL module. + * It also defines the data structures used for APIs between MLAN and MOAL. + * + * Copyright (C) 2008-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 10/13/2008: initial version + 11/07/2008: split mlan.h into mlan_decl.h & mlan_ioctl.h +******************************************************/ + +#ifndef _MLAN_H_ +#define _MLAN_H_ + +#include "mlan_decl.h" +#include "mlan_ioctl.h" +#include "mlan_ieee.h" + +#endif /* !_MLAN_H_ */ diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/mlan_decl.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/mlan_decl.h new file mode 100644 index 000000000000..57bc23e81df6 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/mlan_decl.h @@ -0,0 +1,1441 @@ +/** @file mlan_decl.h + * + * @brief This file declares the generic data structures and APIs. + * + * Copyright (C) 2008-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 11/07/2008: initial version +******************************************************/ + +#ifndef _MLAN_DECL_H_ +#define _MLAN_DECL_H_ + +/** MLAN release version */ +#define MLAN_RELEASE_VERSION "C506" + +/** Re-define generic data types for MLAN/MOAL */ +/** Signed char (1-byte) */ +typedef signed char t_s8; +/** Unsigned char (1-byte) */ +typedef unsigned char t_u8; +/** Signed short (2-bytes) */ +typedef short t_s16; +/** Unsigned short (2-bytes) */ +typedef unsigned short t_u16; +/** Signed long (4-bytes) */ +typedef int t_s32; +/** Unsigned long (4-bytes) */ +typedef unsigned int t_u32; +/** Signed long long 8-bytes) */ +typedef long long t_s64; +/** Unsigned long long 8-bytes) */ +typedef unsigned long long t_u64; +/** Void pointer (4-bytes) */ +typedef void t_void; +/** Size type */ +typedef t_u32 t_size; +/** Boolean type */ +typedef t_u8 t_bool; + +#ifdef MLAN_64BIT +/** Pointer type (64-bit) */ +typedef t_u64 t_ptr; +/** Signed value (64-bit) */ +typedef t_s64 t_sval; +#else +/** Pointer type (32-bit) */ +typedef t_u32 t_ptr; +/** Signed value (32-bit) */ +typedef t_s32 t_sval; +#endif + +/** Constants below */ + +#ifdef __GNUC__ +/** Structure packing begins */ +#define MLAN_PACK_START +/** Structure packeing end */ +#define MLAN_PACK_END __attribute__((packed)) +#else /* !__GNUC__ */ +#ifdef PRAGMA_PACK +/** Structure packing begins */ +#define MLAN_PACK_START +/** Structure packeing end */ +#define MLAN_PACK_END +#else /* !PRAGMA_PACK */ +/** Structure packing begins */ +#define MLAN_PACK_START __packed +/** Structure packing end */ +#define MLAN_PACK_END +#endif /* PRAGMA_PACK */ +#endif /* __GNUC__ */ + +#ifndef INLINE +#ifdef __GNUC__ +/** inline directive */ +#define INLINE inline +#else +/** inline directive */ +#define INLINE __inline +#endif +#endif + +/** MLAN TRUE */ +#define MTRUE (1) +/** MLAN FALSE */ +#define MFALSE (0) + +#define CHANNEL_SPEC_SNIFFER_MODE 1 + +#ifndef MACSTR +/** MAC address security format */ +#define MACSTR "%02x:XX:XX:XX:%02x:%02x" +#endif + +#ifndef MAC2STR +/** MAC address security print arguments */ +#define MAC2STR(a) (a)[0], (a)[4], (a)[5] +#endif + +#ifndef FULL_MACSTR +#define FULL_MACSTR "%02x:%02x:%02x:%02x:%02x:%02x" +#endif +#ifndef FULL_MAC2STR +#define FULL_MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] +#endif + +/** Macros for Data Alignment : size */ +#define ALIGN_SZ(p, a) \ + (((p) + ((a) - 1)) & ~((a) - 1)) + +/** Macros for Data Alignment : address */ +#define ALIGN_ADDR(p, a) \ + ((((t_ptr)(p)) + (((t_ptr)(a)) - 1)) & ~(((t_ptr)(a)) - 1)) + +/** Return the byte offset of a field in the given structure */ +#define MLAN_FIELD_OFFSET(type, field) ((t_u32)(t_ptr)&(((type *)0)->field)) +/** Return aligned offset */ +#define OFFSET_ALIGN_ADDR(p, a) (t_u32)(ALIGN_ADDR(p, a) - (t_ptr)p) + +/** Maximum BSS numbers */ +#define MLAN_MAX_BSS_NUM (16) + +/** NET IP alignment */ +#define MLAN_NET_IP_ALIGN 2 + +/** DMA alignment */ +/* SDIO3.0 Inrevium Adapter require 32 bit DMA alignment */ +#define DMA_ALIGNMENT 32 + +/** max size of TxPD */ +#define MAX_TXPD_SIZE 32 + +/** Minimum data header length */ +#define MLAN_MIN_DATA_HEADER_LEN (DMA_ALIGNMENT+MAX_TXPD_SIZE) + +/** rx data header length */ +#define MLAN_RX_HEADER_LEN MLAN_MIN_DATA_HEADER_LEN + +/** This is current limit on Maximum Tx AMPDU allowed */ +#define MLAN_MAX_TX_BASTREAM_SUPPORTED 16 +#define MLAN_MAX_TX_BASTREAM_DEFAULT 2 +/** This is current limit on Maximum Rx AMPDU allowed */ +#define MLAN_MAX_RX_BASTREAM_SUPPORTED 16 + +#ifdef STA_SUPPORT +/** Default Win size attached during ADDBA request */ +#define MLAN_STA_AMPDU_DEF_TXWINSIZE 64 +/** Default Win size attached during ADDBA response */ +#define MLAN_STA_AMPDU_DEF_RXWINSIZE 64 +/** RX winsize for COEX */ +#define MLAN_STA_COEX_AMPDU_DEF_RXWINSIZE 16 +#endif /* STA_SUPPORT */ +#ifdef UAP_SUPPORT +/** Default Win size attached during ADDBA request */ +#define MLAN_UAP_AMPDU_DEF_TXWINSIZE 48 +/** Default Win size attached during ADDBA response */ +#define MLAN_UAP_AMPDU_DEF_RXWINSIZE 32 +/** RX winsize for COEX */ +#define MLAN_UAP_COEX_AMPDU_DEF_RXWINSIZE 16 +#endif /* UAP_SUPPORT */ + +#ifdef WIFI_DIRECT_SUPPORT +/** WFD use the same window size for tx/rx */ +#define MLAN_WFD_AMPDU_DEF_TXRXWINSIZE 64 +/** RX winsize for COEX */ +#define MLAN_WFD_COEX_AMPDU_DEF_RXWINSIZE 16 +#endif + +/** NAN use the same window size for tx/rx */ +#define MLAN_NAN_AMPDU_DEF_TXRXWINSIZE 16 +/** RX winsize for COEX */ +#define MLAN_NAN_COEX_AMPDU_DEF_RXWINSIZE 16 + +/** Block ack timeout value */ +#define MLAN_DEFAULT_BLOCK_ACK_TIMEOUT 0xffff +/** Maximum Tx Win size configured for ADDBA request [10 bits] */ +#define MLAN_AMPDU_MAX_TXWINSIZE 0x3ff +/** Maximum Rx Win size configured for ADDBA request [10 bits] */ +#define MLAN_AMPDU_MAX_RXWINSIZE 0x3ff + +/** Rate index for HR/DSSS 0 */ +#define MLAN_RATE_INDEX_HRDSSS0 0 +/** Rate index for HR/DSSS 3 */ +#define MLAN_RATE_INDEX_HRDSSS3 3 +/** Rate index for OFDM 0 */ +#define MLAN_RATE_INDEX_OFDM0 4 +/** Rate index for OFDM 7 */ +#define MLAN_RATE_INDEX_OFDM7 11 +/** Rate index for MCS 0 */ +#define MLAN_RATE_INDEX_MCS0 0 +/** Rate index for MCS 7 */ +#define MLAN_RATE_INDEX_MCS7 7 +/** Rate index for MCS 9 */ +#define MLAN_RATE_INDEX_MCS9 9 +/** Rate index for MCS 32 */ +#define MLAN_RATE_INDEX_MCS32 32 +/** Rate index for MCS 127 */ +#define MLAN_RATE_INDEX_MCS127 127 + +/** Rate bitmap for OFDM 0 */ +#define MLAN_RATE_BITMAP_OFDM0 16 +/** Rate bitmap for OFDM 7 */ +#define MLAN_RATE_BITMAP_OFDM7 23 +/** Rate bitmap for MCS 0 */ +#define MLAN_RATE_BITMAP_MCS0 32 +/** Rate bitmap for MCS 127 */ +#define MLAN_RATE_BITMAP_MCS127 159 + +/** Size of rx data buffer */ +#define MLAN_RX_DATA_BUF_SIZE (4 * 1024) +/** Size of rx command buffer */ +#define MLAN_RX_CMD_BUF_SIZE (2 * 1024) + +#define MLAN_USB_RX_DATA_BUF_SIZE MLAN_RX_DATA_BUF_SIZE + +/** MLAN MAC Address Length */ +#define MLAN_MAC_ADDR_LENGTH (6) +/** MLAN 802.11 MAC Address */ +typedef t_u8 mlan_802_11_mac_addr[MLAN_MAC_ADDR_LENGTH]; + +/** MLAN Maximum SSID Length */ +#define MLAN_MAX_SSID_LENGTH (32) + +/** RTS/FRAG related defines */ +/** Minimum RTS value */ +#define MLAN_RTS_MIN_VALUE (0) +/** Maximum RTS value */ +#define MLAN_RTS_MAX_VALUE (2347) +/** Minimum FRAG value */ +#define MLAN_FRAG_MIN_VALUE (256) +/** Maximum FRAG value */ +#define MLAN_FRAG_MAX_VALUE (2346) + +/** Minimum tx retry count */ +#define MLAN_TX_RETRY_MIN (0) +/** Maximum tx retry count */ +#define MLAN_TX_RETRY_MAX (14) + +/** max Wmm AC queues */ +#define MAX_AC_QUEUES 4 + +/** define SDIO block size for data Tx/Rx */ +/* We support up to 480-byte block size due to FW buffer limitation. */ +#define MLAN_SDIO_BLOCK_SIZE 256 + +/** define SDIO block size for firmware download */ +#define MLAN_SDIO_BLOCK_SIZE_FW_DNLD MLAN_SDIO_BLOCK_SIZE + +/** define allocated buffer size */ +#define ALLOC_BUF_SIZE (4 * 1024) +/** SDIO MP aggr pkt limit */ +#define SDIO_MP_AGGR_DEF_PKT_LIMIT (16) + +/** SDIO IO Port mask */ +#define MLAN_SDIO_IO_PORT_MASK 0xfffff +/** SDIO Block/Byte mode mask */ +#define MLAN_SDIO_BYTE_MODE_MASK 0x80000000 + +/** Max retry number of IO write */ +#define MAX_WRITE_IOMEM_RETRY 2 + +/** IN parameter */ +#define IN +/** OUT parameter */ +#define OUT + +/** BIT value */ +#define MBIT(x) (((t_u32)1) << (x)) + +/** Buffer flag for requeued packet */ +#define MLAN_BUF_FLAG_REQUEUED_PKT MBIT(0) +/** Buffer flag for transmit buf from moal */ +#define MLAN_BUF_FLAG_MOAL_TX_BUF MBIT(1) +/** Buffer flag for malloc mlan_buffer */ +#define MLAN_BUF_FLAG_MALLOC_BUF MBIT(2) + +/** Buffer flag for bridge packet */ +#define MLAN_BUF_FLAG_BRIDGE_BUF MBIT(3) + +/** Buffer flag for TDLS */ +#define MLAN_BUF_FLAG_TDLS MBIT(8) + +/** Buffer flag for TCP_ACK */ +#define MLAN_BUF_FLAG_TCP_ACK MBIT(9) + +/** Buffer flag for TX_STATUS */ +#define MLAN_BUF_FLAG_TX_STATUS MBIT(10) + +/** Buffer flag for NET_MONITOR */ +#define MLAN_BUF_FLAG_NET_MONITOR MBIT(11) + +/** Buffer flag for NULL data packet */ +#define MLAN_BUF_FLAG_NULL_PKT MBIT(12) + +#define MLAN_BUF_FLAG_TX_CTRL MBIT(14) + +#ifdef DEBUG_LEVEL1 +/** Debug level bit definition */ +#define MMSG MBIT(0) +#define MFATAL MBIT(1) +#define MERROR MBIT(2) +#define MDATA MBIT(3) +#define MCMND MBIT(4) +#define MEVENT MBIT(5) +#define MINTR MBIT(6) +#define MIOCTL MBIT(7) + +#define MMPA_D MBIT(15) +#define MDAT_D MBIT(16) +#define MCMD_D MBIT(17) +#define MEVT_D MBIT(18) +#define MFW_D MBIT(19) +#define MIF_D MBIT(20) + +#define MENTRY MBIT(28) +#define MWARN MBIT(29) +#define MINFO MBIT(30) +#define MHEX_DUMP MBIT(31) +#endif /* DEBUG_LEVEL1 */ + +/** Memory allocation type: DMA */ +#define MLAN_MEM_DMA MBIT(0) + +/** Default memory allocation flag */ +#define MLAN_MEM_DEF 0 + +/** mlan_status */ +typedef enum _mlan_status { + MLAN_STATUS_FAILURE = 0xffffffff, + MLAN_STATUS_SUCCESS = 0, + MLAN_STATUS_PENDING, + MLAN_STATUS_RESOURCE, + MLAN_STATUS_COMPLETE, +} mlan_status; + +/** mlan_error_code */ +typedef enum _mlan_error_code { + /** No error */ + MLAN_ERROR_NO_ERROR = 0, + /** Firmware/device errors below (MSB=0) */ + MLAN_ERROR_FW_NOT_READY = 0x00000001, + MLAN_ERROR_FW_BUSY = 0x00000002, + MLAN_ERROR_FW_CMDRESP = 0x00000003, + MLAN_ERROR_DATA_TX_FAIL = 0x00000004, + MLAN_ERROR_DATA_RX_FAIL = 0x00000005, + /** Driver errors below (MSB=1) */ + MLAN_ERROR_PKT_SIZE_INVALID = 0x80000001, + MLAN_ERROR_PKT_TIMEOUT = 0x80000002, + MLAN_ERROR_PKT_INVALID = 0x80000003, + MLAN_ERROR_CMD_INVALID = 0x80000004, + MLAN_ERROR_CMD_TIMEOUT = 0x80000005, + MLAN_ERROR_CMD_DNLD_FAIL = 0x80000006, + MLAN_ERROR_CMD_CANCEL = 0x80000007, + MLAN_ERROR_CMD_RESP_FAIL = 0x80000008, + MLAN_ERROR_CMD_ASSOC_FAIL = 0x80000009, + MLAN_ERROR_CMD_SCAN_FAIL = 0x8000000A, + MLAN_ERROR_IOCTL_INVALID = 0x8000000B, + MLAN_ERROR_IOCTL_FAIL = 0x8000000C, + MLAN_ERROR_EVENT_UNKNOWN = 0x8000000D, + MLAN_ERROR_INVALID_PARAMETER = 0x8000000E, + MLAN_ERROR_NO_MEM = 0x8000000F, + /** More to add */ +} mlan_error_code; + +/** mlan_buf_type */ +typedef enum _mlan_buf_type { + MLAN_BUF_TYPE_CMD = 1, + MLAN_BUF_TYPE_DATA, + MLAN_BUF_TYPE_EVENT, + MLAN_BUF_TYPE_RAW_DATA, + MLAN_BUF_TYPE_SPA_DATA, +} mlan_buf_type; + +/** MLAN BSS type */ +typedef enum _mlan_bss_type { + MLAN_BSS_TYPE_STA = 0, + MLAN_BSS_TYPE_UAP = 1, +#ifdef WIFI_DIRECT_SUPPORT + MLAN_BSS_TYPE_WIFIDIRECT = 2, +#endif + MLAN_BSS_TYPE_NAN = 4, + MLAN_BSS_TYPE_ANY = 0xff, +} mlan_bss_type; + +/** MLAN BSS role */ +typedef enum _mlan_bss_role { + MLAN_BSS_ROLE_STA = 0, + MLAN_BSS_ROLE_UAP = 1, + MLAN_BSS_ROLE_ANY = 0xff, +} mlan_bss_role; + +/** BSS role bit mask */ +#define BSS_ROLE_BIT_MASK MBIT(0) + +/** Get BSS role */ +#define GET_BSS_ROLE(priv) ((priv)->bss_role & BSS_ROLE_BIT_MASK) + +/** mlan_data_frame_type */ +typedef enum _mlan_data_frame_type { + MLAN_DATA_FRAME_TYPE_ETH_II = 0, + MLAN_DATA_FRAME_TYPE_802_11, +} mlan_data_frame_type; + +/** mlan_event_id */ +typedef enum _mlan_event_id { + /* Event generated by firmware (MSB=0) */ + MLAN_EVENT_ID_FW_UNKNOWN = 0x00000001, + MLAN_EVENT_ID_FW_ADHOC_LINK_SENSED = 0x00000002, + MLAN_EVENT_ID_FW_ADHOC_LINK_LOST = 0x00000003, + MLAN_EVENT_ID_FW_DISCONNECTED = 0x00000004, + MLAN_EVENT_ID_FW_MIC_ERR_UNI = 0x00000005, + MLAN_EVENT_ID_FW_MIC_ERR_MUL = 0x00000006, + MLAN_EVENT_ID_FW_BCN_RSSI_LOW = 0x00000007, + MLAN_EVENT_ID_FW_BCN_RSSI_HIGH = 0x00000008, + MLAN_EVENT_ID_FW_BCN_SNR_LOW = 0x00000009, + MLAN_EVENT_ID_FW_BCN_SNR_HIGH = 0x0000000A, + MLAN_EVENT_ID_FW_MAX_FAIL = 0x0000000B, + MLAN_EVENT_ID_FW_DATA_RSSI_LOW = 0x0000000C, + MLAN_EVENT_ID_FW_DATA_RSSI_HIGH = 0x0000000D, + MLAN_EVENT_ID_FW_DATA_SNR_LOW = 0x0000000E, + MLAN_EVENT_ID_FW_DATA_SNR_HIGH = 0x0000000F, + MLAN_EVENT_ID_FW_LINK_QUALITY = 0x00000010, + MLAN_EVENT_ID_FW_PORT_RELEASE = 0x00000011, + MLAN_EVENT_ID_FW_PRE_BCN_LOST = 0x00000012, + MLAN_EVENT_ID_FW_DEBUG_INFO = 0x00000013, + MLAN_EVENT_ID_FW_WMM_CONFIG_CHANGE = 0x0000001A, + MLAN_EVENT_ID_FW_HS_WAKEUP = 0x0000001B, + MLAN_EVENT_ID_FW_BG_SCAN = 0x0000001D, + MLAN_EVENT_ID_FW_BG_SCAN_STOPPED = 0x0000001E, + MLAN_EVENT_ID_FW_WEP_ICV_ERR = 0x00000020, + MLAN_EVENT_ID_FW_STOP_TX = 0x00000021, + MLAN_EVENT_ID_FW_START_TX = 0x00000022, + MLAN_EVENT_ID_FW_CHANNEL_SWITCH_ANN = 0x00000023, + MLAN_EVENT_ID_FW_RADAR_DETECTED = 0x00000024, + MLAN_EVENT_ID_FW_CHANNEL_REPORT_RDY = 0x00000025, + MLAN_EVENT_ID_FW_BW_CHANGED = 0x00000026, + MLAN_EVENT_ID_FW_REMAIN_ON_CHAN_EXPIRED = 0x0000002B, +#ifdef UAP_SUPPORT + MLAN_EVENT_ID_UAP_FW_BSS_START = 0x0000002C, + MLAN_EVENT_ID_UAP_FW_BSS_ACTIVE = 0x0000002D, + MLAN_EVENT_ID_UAP_FW_BSS_IDLE = 0x0000002E, + MLAN_EVENT_ID_UAP_FW_STA_CONNECT = 0x00000030, + MLAN_EVENT_ID_UAP_FW_STA_DISCONNECT = 0x00000031, +#endif + + MLAN_EVENT_ID_FW_DUMP_INFO = 0x00000033, + + MLAN_EVENT_ID_FW_TX_STATUS = 0x00000034, + MLAN_EVENT_ID_FW_CHAN_SWITCH_COMPLETE = 0x00000036, + /* Event generated by MLAN driver (MSB=1) */ + MLAN_EVENT_ID_DRV_CONNECTED = 0x80000001, + MLAN_EVENT_ID_DRV_DEFER_HANDLING = 0x80000002, + MLAN_EVENT_ID_DRV_HS_ACTIVATED = 0x80000003, + MLAN_EVENT_ID_DRV_HS_DEACTIVATED = 0x80000004, + MLAN_EVENT_ID_DRV_MGMT_FRAME = 0x80000005, + MLAN_EVENT_ID_DRV_OBSS_SCAN_PARAM = 0x80000006, + MLAN_EVENT_ID_DRV_PASSTHRU = 0x80000007, + MLAN_EVENT_ID_DRV_SCAN_REPORT = 0x80000009, + MLAN_EVENT_ID_DRV_MEAS_REPORT = 0x8000000A, + MLAN_EVENT_ID_DRV_ASSOC_FAILURE_REPORT = 0x8000000B, + MLAN_EVENT_ID_DRV_REPORT_STRING = 0x8000000F, + MLAN_EVENT_ID_DRV_DBG_DUMP = 0x80000012, + MLAN_EVENT_ID_DRV_BGSCAN_RESULT = 0x80000013, + MLAN_EVENT_ID_DRV_FLUSH_RX_WORK = 0x80000015, + MLAN_EVENT_ID_DRV_DEFER_RX_WORK = 0x80000016, + MLAN_EVENT_ID_DRV_TDLS_TEARDOWN_REQ = 0x80000017, + MLAN_EVENT_ID_DRV_FT_RESPONSE = 0x80000018, + MLAN_EVENT_ID_DRV_FLUSH_MAIN_WORK = 0x80000019, +#ifdef UAP_SUPPORT + MLAN_EVENT_ID_DRV_UAP_CHAN_INFO = 0x80000020, +#endif + MLAN_EVENT_ID_FW_ROAM_OFFLOAD_RESULT = 0x80000023, + MLAN_EVENT_ID_NAN_STARTED = 0x80000024, +} mlan_event_id; + +/** Data Structures */ +/** mlan_image data structure */ +typedef struct _mlan_fw_image { + /** Helper image buffer pointer */ + t_u8 *phelper_buf; + /** Helper image length */ + t_u32 helper_len; + /** Firmware image buffer pointer */ + t_u8 *pfw_buf; + /** Firmware image length */ + t_u32 fw_len; + /** Firmware reload flag */ + t_u8 fw_reload; +} mlan_fw_image, *pmlan_fw_image; + +#define OID_TYPE_CAL 0x2 +#define OID_TYPE_DPD 0xa + +/** Custom data structure */ +typedef struct _mlan_init_param { + /** DPD data buffer pointer */ + t_u8 *pdpd_data_buf; + /** DPD data length */ + t_u32 dpd_data_len; + /** region txpowerlimit cfg data buffer pointer */ + t_u8 *ptxpwr_data_buf; + /** region txpowerlimit cfg data length */ + t_u32 txpwr_data_len; + /** Cal data buffer pointer */ + t_u8 *pcal_data_buf; + /** Cal data length */ + t_u32 cal_data_len; + /** Other custom data */ +} mlan_init_param, *pmlan_init_param; + +/** channel band */ +enum { + BAND_2GHZ = 0, + BAND_5GHZ = 1, + BAND_4GHZ = 2, +}; + +/** channel offset */ +enum { + SEC_CHAN_NONE = 0, + SEC_CHAN_ABOVE = 1, + SEC_CHAN_5MHZ = 2, + SEC_CHAN_BELOW = 3 +}; + +/** channel bandwidth */ +enum { + CHAN_BW_20MHZ = 0, + CHAN_BW_10MHZ, + CHAN_BW_40MHZ, +}; + +/** scan mode */ +enum { + SCAN_MODE_MANUAL = 0, + SCAN_MODE_ACS, + SCAN_MODE_USER, +}; + +/** Band_Config_t */ +typedef MLAN_PACK_START struct _Band_Config_t { +#ifdef BIG_ENDIAN_SUPPORT + /** Channel Selection Mode - (00)=manual, (01)=ACS, (02)=user*/ + t_u8 scanMode:2; + /** Secondary Channel Offset - (00)=None, (01)=Above, (11)=Below */ + t_u8 chan2Offset:2; + /** Channel Width - (00)=20MHz, (10)=40MHz, (11)=80MHz */ + t_u8 chanWidth:2; + /** Band Info - (00)=2.4GHz, (01)=5GHz */ + t_u8 chanBand:2; +#else + /** Band Info - (00)=2.4GHz, (01)=5GHz */ + t_u8 chanBand:2; + /** Channel Width - (00)=20MHz, (10)=40MHz, (11)=80MHz */ + t_u8 chanWidth:2; + /** Secondary Channel Offset - (00)=None, (01)=Above, (11)=Below */ + t_u8 chan2Offset:2; + /** Channel Selection Mode - (00)=manual, (01)=ACS, (02)=Adoption mode*/ + t_u8 scanMode:2; +#endif +} MLAN_PACK_END Band_Config_t; + +/** channel_band_t */ +typedef MLAN_PACK_START struct _chan_band_info { + /** Band Configuration */ + Band_Config_t bandcfg; + /** channel */ + t_u8 channel; + /** 11n flag */ + t_u8 is_11n_enabled; + /** center channel */ + t_u8 center_chan; +} MLAN_PACK_END chan_band_info; + +/** mlan_event data structure */ +typedef struct _mlan_event { + /** BSS index number for multiple BSS support */ + t_u32 bss_index; + /** Event ID */ + mlan_event_id event_id; + /** Event length */ + t_u32 event_len; + /** Event buffer */ + t_u8 event_buf[0]; +} mlan_event, *pmlan_event; + +/** mlan_ioctl_req data structure */ +typedef struct _mlan_ioctl_req { + /** Pointer to previous mlan_ioctl_req */ + struct _mlan_ioctl_req *pprev; + /** Pointer to next mlan_ioctl_req */ + struct _mlan_ioctl_req *pnext; + /** Status code from firmware/driver */ + t_u32 status_code; + /** BSS index number for multiple BSS support */ + t_u32 bss_index; + /** Request id */ + t_u32 req_id; + /** Action: set or get */ + t_u32 action; + /** Pointer to buffer */ + t_u8 *pbuf; + /** Length of buffer */ + t_u32 buf_len; + /** Length of the data read/written in buffer */ + t_u32 data_read_written; + /** Length of buffer needed */ + t_u32 buf_len_needed; + /** Reserved for MOAL module */ + t_ptr reserved_1; +} mlan_ioctl_req, *pmlan_ioctl_req; + +/** mix rate information structure */ +typedef MLAN_PACK_START struct _mix_rate_info { + /** bit0: LGI: gi=0, SGI: gi= 1 */ + /** bit1-2: 20M: bw=0, 40M: bw=1, 80M: bw=2, 160M: bw=3 */ + /** bit3-4: LG: format=0, HT: format=1, VHT: format=2 */ + /** bit5: LDPC: 0-not support, 1-support */ + /** bit6-7:reserved */ + t_u8 rate_info; + /** MCS index */ + t_u8 mcs_index; + /** bitrate, in 500Kbps */ + t_u16 bitrate; +} MLAN_PACK_END mix_rate_info, *pmix_rate_info; + +/** rxpd extra information structure */ +typedef MLAN_PACK_START struct _rxpd_extra_info { + /** flags */ + t_u8 flags; + /** channel.flags */ + t_u16 channel_flags; + /** mcs.known */ + t_u8 mcs_known; + /** mcs.flags */ + t_u8 mcs_flags; +} MLAN_PACK_END rxpd_extra_info, *prxpd_extra_info; + +/** rdaio tap information structure */ +typedef MLAN_PACK_START struct _radiotap_info { + /** Rate Info */ + mix_rate_info rate_info; + /** SNR */ + t_s8 snr; + /** Noise Floor */ + t_s8 nf; + /** band config */ + t_u8 band_config; + /** chan number */ + t_u8 chan_num; + /** antenna */ + t_u8 antenna; + /** extra rxpd info from FW */ + rxpd_extra_info extra_info; +} MLAN_PACK_END radiotap_info, *pradiotap_info; + +/** txpower structure */ +typedef MLAN_PACK_START struct { +#ifdef BIG_ENDIAN_SUPPORT + /** Host tx power ctrl: + 0x0: use fw setting for TX power + 0x1: value specified in bit[6] and bit[5:0] are valid */ + t_u8 hostctl:1; + /** Sign of the power specified in bit[5:0] */ + t_u8 sign:1; + /** Power to be used for transmission(in dBm) */ + t_u8 abs_val:6; +#else + /** Power to be used for transmission(in dBm) */ + t_u8 abs_val:6; + /** Sign of the power specified in bit[5:0] */ + t_u8 sign:1; + /** Host tx power ctrl: + 0x0: use fw setting for TX power + 0x1: value specified in bit[6] and bit[5:0] are valid */ + t_u8 hostctl:1; +#endif +} MLAN_PACK_END tx_power_t; +/* pkt_txctrl */ +typedef MLAN_PACK_START struct _pkt_txctrl { + /**Data rate in unit of 0.5Mbps */ + t_u16 data_rate; + /*Channel number to transmit the frame */ + t_u8 channel; + /** Bandwidth to transmit the frame*/ + t_u8 bw; + /** Power to be used for transmission*/ + union { + tx_power_t tp; + t_u8 val; + } tx_power; + /** Retry time of tx transmission*/ + t_u8 retry_limit; +} MLAN_PACK_END pkt_txctrl, *ppkt_txctrl; + +/** pkt_rxinfo */ +typedef MLAN_PACK_START struct _pkt_rxinfo { + /** Data rate of received paccket*/ + t_u16 data_rate; + /** Channel on which packet was received*/ + t_u8 channel; + /** Rx antenna*/ + t_u8 antenna; + /** Rx Rssi*/ + t_u8 rssi; +} MLAN_PACK_END pkt_rxinfo, *ppkt_rxinfo; + +/** mlan_buffer data structure */ +typedef struct _mlan_buffer { + /** Pointer to previous mlan_buffer */ + struct _mlan_buffer *pprev; + /** Pointer to next mlan_buffer */ + struct _mlan_buffer *pnext; + /** Status code from firmware/driver */ + t_u32 status_code; + /** Flags for this buffer */ + t_u32 flags; + /** BSS index number for multiple BSS support */ + t_u32 bss_index; + /** Buffer descriptor, e.g. skb in Linux */ + t_void *pdesc; + /** Pointer to buffer */ + t_u8 *pbuf; + /** Offset to data */ + t_u32 data_offset; + /** Data length */ + t_u32 data_len; + /** Buffer type: data, cmd, event etc. */ + mlan_buf_type buf_type; + + /** Fields below are valid for data packet only */ + /** QoS priority */ + t_u32 priority; + /** Time stamp when packet is received (seconds) */ + t_u32 in_ts_sec; + /** Time stamp when packet is received (micro seconds) */ + t_u32 in_ts_usec; + /** Time stamp when packet is processed (seconds) */ + t_u32 out_ts_sec; + /** Time stamp when packet is processed (micro seconds) */ + t_u32 out_ts_usec; + /** tx_seq_num */ + t_u32 tx_seq_num; + + /** Fields below are valid for MLAN module only */ + /** Pointer to parent mlan_buffer */ + struct _mlan_buffer *pparent; + /** Use count for this buffer */ + t_u32 use_count; + union { + pkt_txctrl tx_info; + pkt_rxinfo rx_info; + } u; +} mlan_buffer, *pmlan_buffer; + +/** mlan_fw_info data structure */ +typedef struct _mlan_hw_info { + /** Firmware capabilities */ + t_u32 fw_cap; +} mlan_hw_info, *pmlan_hw_info; + +/** mlan_bss_attr data structure */ +typedef struct _mlan_bss_attr { + /** BSS type */ + t_u32 bss_type; + /** Data frame type: Ethernet II, 802.11, etc. */ + t_u32 frame_type; + /** The BSS is active (non-0) or not (0). */ + t_u32 active; + /** BSS Priority */ + t_u32 bss_priority; + /** BSS number */ + t_u32 bss_num; + /** The BSS is virtual */ + t_u32 bss_virtual; +} mlan_bss_attr, *pmlan_bss_attr; + +/** bss tbl data structure */ +typedef struct _mlan_bss_tbl { + /** BSS Attributes */ + mlan_bss_attr bss_attr[MLAN_MAX_BSS_NUM]; +} mlan_bss_tbl, *pmlan_bss_tbl; + +#ifdef PRAGMA_PACK +#pragma pack(push, 1) +#endif + +/** Type enumeration for the command result */ +typedef MLAN_PACK_START enum _mlan_cmd_result_e { + MLAN_CMD_RESULT_SUCCESS = 0, + MLAN_CMD_RESULT_FAILURE = 1, + MLAN_CMD_RESULT_TIMEOUT = 2, + MLAN_CMD_RESULT_INVALID_DATA = 3 +} MLAN_PACK_END mlan_cmd_result_e; + +/** Type enumeration of WMM AC_QUEUES */ +typedef MLAN_PACK_START enum _mlan_wmm_ac_e { + WMM_AC_BK, + WMM_AC_BE, + WMM_AC_VI, + WMM_AC_VO +} MLAN_PACK_END mlan_wmm_ac_e; + +/** Type enumeration for the action field in the Queue Config command */ +typedef MLAN_PACK_START enum _mlan_wmm_queue_config_action_e { + MLAN_WMM_QUEUE_CONFIG_ACTION_GET = 0, + MLAN_WMM_QUEUE_CONFIG_ACTION_SET = 1, + MLAN_WMM_QUEUE_CONFIG_ACTION_DEFAULT = 2, + MLAN_WMM_QUEUE_CONFIG_ACTION_MAX +} MLAN_PACK_END mlan_wmm_queue_config_action_e; + +/** Type enumeration for the action field in the queue stats command */ +typedef MLAN_PACK_START enum _mlan_wmm_queue_stats_action_e { + MLAN_WMM_STATS_ACTION_START = 0, + MLAN_WMM_STATS_ACTION_STOP = 1, + MLAN_WMM_STATS_ACTION_GET_CLR = 2, + MLAN_WMM_STATS_ACTION_SET_CFG = 3, /* Not currently used */ + MLAN_WMM_STATS_ACTION_GET_CFG = 4, /* Not currently used */ + MLAN_WMM_STATS_ACTION_MAX +} MLAN_PACK_END mlan_wmm_queue_stats_action_e; + +/** + * @brief IOCTL structure for a Traffic stream status. + * + */ +typedef MLAN_PACK_START struct { + /** TSID: Range: 0->7 */ + t_u8 tid; + /** TSID specified is valid */ + t_u8 valid; + /** AC TSID is active on */ + t_u8 access_category; + /** UP specified for the TSID */ + t_u8 user_priority; + /** Power save mode for TSID: 0 (legacy), 1 (UAPSD) */ + t_u8 psb; + /** Upstream(0), Downlink(1), Bidirectional(3) */ + t_u8 flow_dir; + /** Medium time granted for the TSID */ + t_u16 medium_time; +} MLAN_PACK_END wlan_ioctl_wmm_ts_status_t, +/** Type definition of mlan_ds_wmm_ts_status for MLAN_OID_WMM_CFG_TS_STATUS */ +mlan_ds_wmm_ts_status, *pmlan_ds_wmm_ts_status; + +/** Max Ie length */ +#define MAX_IE_SIZE 256 + +/** custom IE */ +typedef MLAN_PACK_START struct _custom_ie { + /** IE Index */ + t_u16 ie_index; + /** Mgmt Subtype Mask */ + t_u16 mgmt_subtype_mask; + /** IE Length */ + t_u16 ie_length; + /** IE buffer */ + t_u8 ie_buffer[MAX_IE_SIZE]; +} MLAN_PACK_END custom_ie; + +/** Max IE index to FW */ +#define MAX_MGMT_IE_INDEX_TO_FW 4 +/** Max IE index per BSS */ +#define MAX_MGMT_IE_INDEX 16 + +/** custom IE info */ +typedef MLAN_PACK_START struct _custom_ie_info { + /** size of buffer */ + t_u16 buf_size; + /** no of buffers of buf_size */ + t_u16 buf_count; +} MLAN_PACK_END custom_ie_info; + +/** TLV buffer : Max Mgmt IE */ +typedef MLAN_PACK_START struct _tlvbuf_max_mgmt_ie { + /** Type */ + t_u16 type; + /** Length */ + t_u16 len; + /** No of tuples */ + t_u16 count; + /** custom IE info tuples */ + custom_ie_info info[MAX_MGMT_IE_INDEX]; +} MLAN_PACK_END tlvbuf_max_mgmt_ie; + +/** TLV buffer : custom IE */ +typedef MLAN_PACK_START struct _tlvbuf_custom_ie { + /** Type */ + t_u16 type; + /** Length */ + t_u16 len; + /** IE data */ + custom_ie ie_data_list[MAX_MGMT_IE_INDEX_TO_FW]; + /** Max mgmt IE TLV */ + tlvbuf_max_mgmt_ie max_mgmt_ie; +} MLAN_PACK_END mlan_ds_misc_custom_ie; + +/** Max TDLS config data length */ +#define MAX_TDLS_DATA_LEN 1024 + +/** Action commands for TDLS enable/disable */ +#define WLAN_TDLS_CONFIG 0x00 +/** Action commands for TDLS configuration :Set */ +#define WLAN_TDLS_SET_INFO 0x01 +/** Action commands for TDLS configuration :Discovery Request */ +#define WLAN_TDLS_DISCOVERY_REQ 0x02 +/** Action commands for TDLS configuration :Setup Request */ +#define WLAN_TDLS_SETUP_REQ 0x03 +/** Action commands for TDLS configuration :Tear down Request */ +#define WLAN_TDLS_TEAR_DOWN_REQ 0x04 +/** Action ID for TDLS power mode */ +#define WLAN_TDLS_POWER_MODE 0x05 +/**Action ID for init TDLS Channel Switch*/ +#define WLAN_TDLS_INIT_CHAN_SWITCH 0x06 +/** Action ID for stop TDLS Channel Switch */ +#define WLAN_TDLS_STOP_CHAN_SWITCH 0x07 +/** Action ID for configure CS related parameters */ +#define WLAN_TDLS_CS_PARAMS 0x08 +/** Action ID for Disable CS */ +#define WLAN_TDLS_CS_DISABLE 0x09 +/** Action ID for TDLS link status */ +#define WLAN_TDLS_LINK_STATUS 0x0A +/** Action ID for Host TDLS config uapsd and CS */ +#define WLAN_HOST_TDLS_CONFIG 0x0D +/** Action ID for TDLS CS immediate return */ +#define WLAN_TDLS_DEBUG_CS_RET_IM 0xFFF7 +/** Action ID for TDLS Stop RX */ +#define WLAN_TDLS_DEBUG_STOP_RX 0xFFF8 +/** Action ID for TDLS Allow weak security for links establish */ +#define WLAN_TDLS_DEBUG_ALLOW_WEAK_SECURITY 0xFFF9 +/** Action ID for TDLS Ignore key lifetime expiry */ +#define WLAN_TDLS_DEBUG_IGNORE_KEY_EXPIRY 0xFFFA +/** Action ID for TDLS Higher/Lower mac Test */ +#define WLAN_TDLS_DEBUG_HIGHER_LOWER_MAC 0xFFFB +/** Action ID for TDLS Prohibited Test */ +#define WLAN_TDLS_DEBUG_SETUP_PROHIBITED 0xFFFC +/** Action ID for TDLS Existing link Test */ +#define WLAN_TDLS_DEBUG_SETUP_SAME_LINK 0xFFFD +/** Action ID for TDLS Fail Setup Confirm */ +#define WLAN_TDLS_DEBUG_FAIL_SETUP_CONFIRM 0xFFFE +/** Action commands for TDLS debug: Wrong BSS Request */ +#define WLAN_TDLS_DEBUG_WRONG_BSS 0xFFFF + +/** tdls each link rate information */ +typedef MLAN_PACK_START struct _tdls_link_rate_info { + /** Tx Data Rate */ + t_u8 tx_data_rate; + /** Tx Rate HT info*/ + t_u8 tx_rate_htinfo; +} MLAN_PACK_END tdls_link_rate_info; + +/** tdls each link status */ +typedef MLAN_PACK_START struct _tdls_each_link_status { + /** peer mac Address */ + t_u8 peer_mac[MLAN_MAC_ADDR_LENGTH]; + /** Link Flags */ + t_u8 link_flags; + /** Traffic Status */ + t_u8 traffic_status; + /** Tx Failure Count */ + t_u8 tx_fail_count; + /** Channel Number */ + t_u32 active_channel; + /** Last Data RSSI in dBm */ + t_s16 data_rssi_last; + /** Last Data NF in dBm */ + t_s16 data_nf_last; + /** AVG DATA RSSI in dBm */ + t_s16 data_rssi_avg; + /** AVG DATA NF in dBm */ + t_s16 data_nf_avg; + union { + /** tdls rate info */ + tdls_link_rate_info rate_info; + /** tdls link final rate*/ + t_u16 final_data_rate; + } u; + /** Security Method */ + t_u8 security_method; + /** Key Lifetime in milliseconds */ + t_u32 key_lifetime; + /** Key Length */ + t_u8 key_length; + /** actual key */ + t_u8 key[0]; +} MLAN_PACK_END tdls_each_link_status; + +/** TDLS configuration data */ +typedef MLAN_PACK_START struct _tdls_all_config { + union { + /** TDLS state enable disable */ + MLAN_PACK_START struct _tdls_config { + /** enable or disable */ + t_u16 enable; + } MLAN_PACK_END tdls_config; + /** Host tdls config */ + MLAN_PACK_START struct _host_tdls_cfg { + /** support uapsd */ + t_u8 uapsd_support; + /** channel_switch */ + t_u8 cs_support; + /** TLV length */ + t_u16 tlv_len; + /** tdls info */ + t_u8 tlv_buffer[0]; + } MLAN_PACK_END host_tdls_cfg; + /** TDLS set info */ + MLAN_PACK_START struct _tdls_set_data { + /** (tlv + capInfo) length */ + t_u16 tlv_length; + /** Cap Info */ + t_u16 cap_info; + /** TLV buffer */ + t_u8 tlv_buffer[0]; + } MLAN_PACK_END tdls_set; + + /** TDLS discovery and others having mac argument */ + MLAN_PACK_START struct _tdls_discovery_data { + /** peer mac Address */ + t_u8 peer_mac_addr[MLAN_MAC_ADDR_LENGTH]; + } MLAN_PACK_END tdls_discovery, tdls_stop_chan_switch, + tdls_link_status_req; + + /** TDLS discovery Response */ + MLAN_PACK_START struct _tdls_discovery_resp { + /** payload length */ + t_u16 payload_len; + /** peer mac Address */ + t_u8 peer_mac_addr[MLAN_MAC_ADDR_LENGTH]; + /** RSSI */ + t_s8 rssi; + /** Cap Info */ + t_u16 cap_info; + /** TLV buffer */ + t_u8 tlv_buffer[0]; + } MLAN_PACK_END tdls_discovery_resp; + + /** TDLS setup request */ + MLAN_PACK_START struct _tdls_setup_data { + /** peer mac Address */ + t_u8 peer_mac_addr[MLAN_MAC_ADDR_LENGTH]; + /** timeout value in milliseconds */ + t_u32 setup_timeout; + /** key lifetime in milliseconds */ + t_u32 key_lifetime; + } MLAN_PACK_END tdls_setup; + + /** TDLS tear down info */ + MLAN_PACK_START struct _tdls_tear_down_data { + /** peer mac Address */ + t_u8 peer_mac_addr[MLAN_MAC_ADDR_LENGTH]; + /** reason code */ + t_u16 reason_code; + } MLAN_PACK_END tdls_tear_down, tdls_cmd_resp; + + /** TDLS power mode info */ + MLAN_PACK_START struct _tdls_power_mode_data { + /** peer mac Address */ + t_u8 peer_mac_addr[MLAN_MAC_ADDR_LENGTH]; + /** Power Mode */ + t_u16 power_mode; + } MLAN_PACK_END tdls_power_mode; + + /** TDLS channel switch info */ + MLAN_PACK_START struct _tdls_chan_switch { + /** peer mac Address */ + t_u8 peer_mac_addr[MLAN_MAC_ADDR_LENGTH]; + /** Channel Switch primary channel no */ + t_u8 primary_channel; + /** Channel Switch secondary channel offset */ + t_u8 secondary_channel_offset; + /** Channel Switch Band */ + t_u8 band; + /** Channel Switch time in milliseconds */ + t_u16 switch_time; + /** Channel Switch timeout in milliseconds */ + t_u16 switch_timeout; + /** Channel Regulatory class*/ + t_u8 regulatory_class; + /** peridicity flag*/ + t_u8 periodicity; + } MLAN_PACK_END tdls_chan_switch; + + /** TDLS channel switch paramters */ + MLAN_PACK_START struct _tdls_cs_params { + /** unit time, multiples of 10ms */ + t_u8 unit_time; + /** threshold for other link */ + t_u8 threshold_otherlink; + /** threshold for direct link */ + t_u8 threshold_directlink; + } MLAN_PACK_END tdls_cs_params; + + /** tdls disable channel switch */ + MLAN_PACK_START struct _tdls_disable_cs { + /** Data*/ + t_u16 data; + } MLAN_PACK_END tdls_disable_cs; + /** TDLS debug data */ + MLAN_PACK_START struct _tdls_debug_data { + /** debug data */ + t_u16 debug_data; + } MLAN_PACK_END tdls_debug_data; + + /** TDLS link status Response */ + MLAN_PACK_START struct _tdls_link_status_resp { + /** payload length */ + t_u16 payload_len; + /** number of links */ + t_u8 active_links; + /** structure for link status */ + tdls_each_link_status link_stats[1]; + } MLAN_PACK_END tdls_link_status_resp; + + } u; +} MLAN_PACK_END tdls_all_config; + +/** TDLS configuration buffer */ +typedef MLAN_PACK_START struct _buf_tdls_config { + /** TDLS Action */ + t_u16 tdls_action; + /** TDLS data */ + t_u8 tdls_data[MAX_TDLS_DATA_LEN]; +} MLAN_PACK_END mlan_ds_misc_tdls_config; + +/** Event structure for tear down */ +typedef struct _tdls_tear_down_event { + /** Peer mac address */ + t_u8 peer_mac_addr[MLAN_MAC_ADDR_LENGTH]; + /** Reason code */ + t_u16 reason_code; +} tdls_tear_down_event; + +#ifdef PRAGMA_PACK +#pragma pack(pop) +#endif + +/** mlan_callbacks data structure */ +typedef struct _mlan_callbacks { + /** moal_get_fw_data */ + mlan_status (*moal_get_fw_data) (IN t_void *pmoal_handle, + IN t_u32 offset, + IN t_u32 len, OUT t_u8 *pbuf); + /** moal_get_hw_spec_complete */ + mlan_status (*moal_get_hw_spec_complete) (IN t_void *pmoal_handle, + IN mlan_status status, + IN mlan_hw_info * phw, + IN pmlan_bss_tbl ptbl); + /** moal_init_fw_complete */ + mlan_status (*moal_init_fw_complete) (IN t_void *pmoal_handle, + IN mlan_status status); + /** moal_shutdown_fw_complete */ + mlan_status (*moal_shutdown_fw_complete) (IN t_void *pmoal_handle, + IN mlan_status status); + /** moal_send_packet_complete */ + mlan_status (*moal_send_packet_complete) (IN t_void *pmoal_handle, + IN pmlan_buffer pmbuf, + IN mlan_status status); + /** moal_recv_complete */ + mlan_status (*moal_recv_complete) (IN t_void *pmoal_handle, + IN pmlan_buffer pmbuf, + IN t_u32 port, + IN mlan_status status); + /** moal_recv_packet */ + mlan_status (*moal_recv_packet) (IN t_void *pmoal_handle, + IN pmlan_buffer pmbuf); + /** moal_recv_event */ + mlan_status (*moal_recv_event) (IN t_void *pmoal_handle, + IN pmlan_event pmevent); + /** moal_ioctl_complete */ + mlan_status (*moal_ioctl_complete) (IN t_void *pmoal_handle, + IN pmlan_ioctl_req pioctl_req, + IN mlan_status status); + + /** moal_alloc_mlan_buffer */ + mlan_status (*moal_alloc_mlan_buffer) (IN t_void *pmoal_handle, + IN t_u32 size, + OUT pmlan_buffer *pmbuf); + /** moal_free_mlan_buffer */ + mlan_status (*moal_free_mlan_buffer) (IN t_void *pmoal_handle, + IN pmlan_buffer pmbuf); + + /** moal_write_reg */ + mlan_status (*moal_write_reg) (IN t_void *pmoal_handle, + IN t_u32 reg, IN t_u32 data); + /** moal_read_reg */ + mlan_status (*moal_read_reg) (IN t_void *pmoal_handle, + IN t_u32 reg, OUT t_u32 *data); + /** moal_write_data_sync */ + mlan_status (*moal_write_data_sync) (IN t_void *pmoal_handle, + IN pmlan_buffer pmbuf, + IN t_u32 port, IN t_u32 timeout); + /** moal_read_data_sync */ + mlan_status (*moal_read_data_sync) (IN t_void *pmoal_handle, + IN OUT pmlan_buffer pmbuf, + IN t_u32 port, IN t_u32 timeout); + /** moal_malloc */ + mlan_status (*moal_malloc) (IN t_void *pmoal_handle, + IN t_u32 size, + IN t_u32 flag, OUT t_u8 **ppbuf); + /** moal_mfree */ + mlan_status (*moal_mfree) (IN t_void *pmoal_handle, IN t_u8 *pbuf); + /** moal_vmalloc */ + mlan_status (*moal_vmalloc) (IN t_void *pmoal_handle, + IN t_u32 size, OUT t_u8 **ppbuf); + /** moal_vfree */ + mlan_status (*moal_vfree) (IN t_void *pmoal_handle, IN t_u8 *pbuf); + /** moal_memset */ + t_void *(*moal_memset) (IN t_void *pmoal_handle, + IN t_void *pmem, IN t_u8 byte, IN t_u32 num); + /** moal_memcpy */ + t_void *(*moal_memcpy) (IN t_void *pmoal_handle, + IN t_void *pdest, + IN const t_void *psrc, IN t_u32 num); + /** moal_memmove */ + t_void *(*moal_memmove) (IN t_void *pmoal_handle, + IN t_void *pdest, + IN const t_void *psrc, IN t_u32 num); + /** moal_memcmp */ + t_s32 (*moal_memcmp) (IN t_void *pmoal_handle, + IN const t_void *pmem1, + IN const t_void *pmem2, IN t_u32 num); + /** moal_udelay */ + t_void (*moal_udelay) (IN t_void *pmoal_handle, IN t_u32 udelay); + /** moal_get_system_time */ + mlan_status (*moal_get_system_time) (IN t_void *pmoal_handle, + OUT t_u32 *psec, OUT t_u32 *pusec); + /** moal_init_timer*/ + mlan_status (*moal_init_timer) (IN t_void *pmoal_handle, + OUT t_void **pptimer, + IN t_void (*callback) (t_void + *pcontext), + IN t_void *pcontext); + /** moal_free_timer */ + mlan_status (*moal_free_timer) (IN t_void *pmoal_handle, + IN t_void *ptimer); + /** moal_start_timer*/ + mlan_status (*moal_start_timer) (IN t_void *pmoal_handle, + IN t_void *ptimer, + IN t_u8 periodic, IN t_u32 msec); + /** moal_stop_timer*/ + mlan_status (*moal_stop_timer) (IN t_void *pmoal_handle, + IN t_void *ptimer); + /** moal_init_lock */ + mlan_status (*moal_init_lock) (IN t_void *pmoal_handle, + OUT t_void **pplock); + /** moal_free_lock */ + mlan_status (*moal_free_lock) (IN t_void *pmoal_handle, + IN t_void *plock); + /** moal_spin_lock */ + mlan_status (*moal_spin_lock) (IN t_void *pmoal_handle, + IN t_void *plock); + /** moal_spin_unlock */ + mlan_status (*moal_spin_unlock) (IN t_void *pmoal_handle, + IN t_void *plock); + /** moal_print */ + t_void (*moal_print) (IN t_void *pmoal_handle, + IN t_u32 level, IN char *pformat, IN ... + ); + /** moal_print_netintf */ + t_void (*moal_print_netintf) (IN t_void *pmoal_handle, + IN t_u32 bss_index, IN t_u32 level); + /** moal_assert */ + t_void (*moal_assert) (IN t_void *pmoal_handle, IN t_u32 cond); + /** moal_hist_data_add */ + t_void (*moal_hist_data_add) (IN t_void *pmoal_handle, + IN t_u32 bss_index, + IN t_u8 rx_rate, + IN t_s8 snr, + IN t_s8 nflr, IN t_u8 antenna); + t_void (*moal_updata_peer_signal) (IN t_void *pmoal_handle, + IN t_u32 bss_index, + IN t_u8 *peer_addr, + IN t_s8 snr, IN t_s8 nflr); + mlan_status (*moal_get_host_time_ns) (OUT t_u64 *time); + t_u32 (*moal_do_div) (IN t_u64 num, IN t_u32 base); +} mlan_callbacks, *pmlan_callbacks; + +/** Parameter unchanged, use MLAN default setting */ +#define ROBUSTCOEX_GPIO_UNCHANGED 0 +/** Parameter enabled, override MLAN default setting */ +#define ROBUSTCOEX_GPIO_CFG 1 + +/** Interrupt Mode SDIO */ +#define INT_MODE_SDIO 0 +/** Interrupt Mode GPIO */ +#define INT_MODE_GPIO 1 +/** New mode: GPIO-1 as a duplicated signal of interrupt as appear of SDIO_DAT1 */ +#define GPIO_INT_NEW_MODE 255 + +/** Parameter unchanged, use MLAN default setting */ +#define MLAN_INIT_PARA_UNCHANGED 0 +/** Parameter enabled, override MLAN default setting */ +#define MLAN_INIT_PARA_ENABLED 1 +/** Parameter disabled, override MLAN default setting */ +#define MLAN_INIT_PARA_DISABLED 2 + +/** mlan_device data structure */ +typedef struct _mlan_device { + /** MOAL Handle */ + t_void *pmoal_handle; + /** BSS Attributes */ + mlan_bss_attr bss_attr[MLAN_MAX_BSS_NUM]; + /** Callbacks */ + mlan_callbacks callbacks; +#ifdef MFG_CMD_SUPPORT + /** MFG mode */ + t_u32 mfg_mode; +#endif + /** SDIO interrupt mode (0: INT_MODE_SDIO, 1: INT_MODE_GPIO) */ + t_u32 int_mode; + /** GPIO interrupt pin number */ + t_u32 gpio_pin; +#ifdef DEBUG_LEVEL1 + /** Driver debug bit masks */ + t_u32 drvdbg; +#endif + /** allocate fixed buffer size for scan beacon buffer*/ + t_u32 fixed_beacon_buffer; +#ifdef SDIO_MULTI_PORT_TX_AGGR + /** SDIO MPA Tx */ + t_u32 mpa_tx_cfg; +#endif +#ifdef SDIO_MULTI_PORT_RX_AGGR + /** SDIO MPA Rx */ + t_u32 mpa_rx_cfg; +#endif + /** SDIO Single port rx aggr */ + t_u8 sdio_rx_aggr_enable; +#if defined(SDIO_MULTI_PORT_TX_AGGR) || defined(SDIO_MULTI_PORT_RX_AGGR) + /* see blk_queue_max_segment_size */ + t_u32 max_seg_size; + /* see blk_queue_max_segments */ + t_u16 max_segs; +#endif + /** Auto deep sleep */ + t_u32 auto_ds; + /** IEEE PS mode */ + t_u32 ps_mode; + /** Max Tx buffer size */ + t_u32 max_tx_buf; +#if defined(STA_SUPPORT) + /** 802.11d configuration */ + t_u32 cfg_11d; +#endif + /** enable/disable rx work */ + t_u8 rx_work; + /** dev cap mask */ + t_u32 dev_cap_mask; + /** oob independent reset */ + t_u32 indrstcfg; + /** dtim interval */ + t_u32 multi_dtim; + /** IEEE ps inactivity timeout value */ + t_u32 inact_tmo; + /** Host sleep wakeup interval */ + t_u32 hs_wake_interval; + /** GPIO to indicate wakeup source */ + t_u8 indication_gpio; + /** channel time and mode for DRCS*/ + t_u32 drcs_chantime_mode; + t_bool fw_region; +} mlan_device, *pmlan_device; + +/** MLAN API function prototype */ +#define MLAN_API + +/** Registration */ +MLAN_API mlan_status mlan_register(IN pmlan_device pmdevice, + OUT t_void **ppmlan_adapter); + +/** Un-registration */ +MLAN_API mlan_status mlan_unregister(IN t_void *pmlan_adapter + ); + +/** Firmware Downloading */ +MLAN_API mlan_status mlan_dnld_fw(IN t_void *pmlan_adapter, + IN pmlan_fw_image pmfw); + +/** Custom data pass API */ +MLAN_API mlan_status mlan_set_init_param(IN t_void *pmlan_adapter, + IN pmlan_init_param pparam); + +/** Firmware Initialization */ +MLAN_API mlan_status mlan_init_fw(IN t_void *pmlan_adapter + ); + +/** Firmware Shutdown */ +MLAN_API mlan_status mlan_shutdown_fw(IN t_void *pmlan_adapter + ); + +/** Main Process */ +MLAN_API mlan_status mlan_main_process(IN t_void *pmlan_adapter + ); + +/** Rx process */ +mlan_status mlan_rx_process(IN t_void *pmlan_adapter, IN t_u8 *rx_pkts); + +/** Packet Transmission */ +MLAN_API mlan_status mlan_send_packet(IN t_void *pmlan_adapter, + IN pmlan_buffer pmbuf); + +/** Packet Reception complete callback */ +MLAN_API mlan_status mlan_recv_packet_complete(IN t_void *pmlan_adapter, + IN pmlan_buffer pmbuf, + IN mlan_status status); + +/** interrupt handler */ +MLAN_API mlan_status mlan_interrupt(IN t_void *pmlan_adapter); + +#if defined(SYSKT) +/** GPIO IRQ callback function */ +MLAN_API t_void mlan_hs_callback(IN t_void *pctx); +#endif /* SYSKT_MULTI || SYSKT */ + +MLAN_API t_void mlan_pm_wakeup_card(IN t_void *pmlan_adapter); + +MLAN_API t_u8 mlan_is_main_process_running(IN t_void *adapter); + +/** mlan ioctl */ +MLAN_API mlan_status mlan_ioctl(IN t_void *pmlan_adapter, + IN pmlan_ioctl_req pioctl_req); +/** mlan select wmm queue */ +MLAN_API t_u8 mlan_select_wmm_queue(IN t_void *pmlan_adapter, + IN t_u8 bss_num, IN t_u8 tid); +#endif /* !_MLAN_DECL_H_ */ diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/mlan_ieee.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/mlan_ieee.h new file mode 100644 index 000000000000..3d75e1e5758c --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/mlan_ieee.h @@ -0,0 +1,1583 @@ +/** @file mlan_ieee.h + * + * @brief This file contains IEEE information element related + * definitions used in MLAN and MOAL module. + * + * Copyright (C) 2008-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 11/03/2008: initial version +******************************************************/ + +#ifndef _MLAN_IEEE_H_ +#define _MLAN_IEEE_H_ + +/** FIX IES size in beacon buffer */ +#define WLAN_802_11_FIXED_IE_SIZE 12 +/** WLAN supported rates */ +#define WLAN_SUPPORTED_RATES 14 + +/** WLAN supported rates extension*/ +#define WLAN_SUPPORTED_RATES_EXT 32 + +/** Enumeration definition*/ +/** WLAN_802_11_NETWORK_TYPE */ +typedef enum _WLAN_802_11_NETWORK_TYPE { + Wlan802_11FH, + Wlan802_11DS, + /* Defined as upper bound */ + Wlan802_11NetworkTypeMax +} WLAN_802_11_NETWORK_TYPE; + +#ifdef BIG_ENDIAN_SUPPORT +/** Frame control: Type Mgmt frame */ +#define IEEE80211_FC_MGMT_FRAME_TYPE_MASK 0x3000 +/** Frame control: SubType Mgmt frame */ +#define IEEE80211_GET_FC_MGMT_FRAME_SUBTYPE(fc) (((fc) & 0xF000) >> 12) +#else +/** Frame control: Type Mgmt frame */ +#define IEEE80211_FC_MGMT_FRAME_TYPE_MASK 0x000C +/** Frame control: SubType Mgmt frame */ +#define IEEE80211_GET_FC_MGMT_FRAME_SUBTYPE(fc) (((fc) & 0x00F0) >> 4) +#endif + +#ifdef PRAGMA_PACK +#pragma pack(push, 1) +#endif + +/* Reason codes */ +#define IEEE_80211_REASONCODE_UNSPECIFIED 1 + +/** IEEE Type definitions */ +typedef MLAN_PACK_START enum _IEEEtypes_ElementId_e { + SSID = 0, + SUPPORTED_RATES = 1, + + FH_PARAM_SET = 2, + DS_PARAM_SET = 3, + CF_PARAM_SET = 4, + + IBSS_PARAM_SET = 6, + + COUNTRY_INFO = 7, + + POWER_CONSTRAINT = 32, + POWER_CAPABILITY = 33, + TPC_REQUEST = 34, + TPC_REPORT = 35, + CHANNEL_SWITCH_ANN = 37, + EXTEND_CHANNEL_SWITCH_ANN = 60, + QUIET = 40, + IBSS_DFS = 41, + SUPPORTED_CHANNELS = 36, + REGULATORY_CLASS = 59, + HT_CAPABILITY = 45, + QOS_INFO = 46, + HT_OPERATION = 61, + BSSCO_2040 = 72, + OVERLAPBSSSCANPARAM = 74, + EXT_CAPABILITY = 127, + LINK_ID = 101, + /*IEEE802.11r */ + MOBILITY_DOMAIN = 54, + FAST_BSS_TRANSITION = 55, + TIMEOUT_INTERVAL = 56, + RIC = 57, + QOS_MAPPING = 110, + + ERP_INFO = 42, + + EXTENDED_SUPPORTED_RATES = 50, + + VENDOR_SPECIFIC_221 = 221, + WMM_IE = VENDOR_SPECIFIC_221, + + WPS_IE = VENDOR_SPECIFIC_221, + + WPA_IE = VENDOR_SPECIFIC_221, + RSN_IE = 48, + VS_IE = VENDOR_SPECIFIC_221, + WAPI_IE = 68, +} MLAN_PACK_END IEEEtypes_ElementId_e; + +/** IEEE IE header */ +typedef MLAN_PACK_START struct _IEEEtypes_Header_t { + /** Element ID */ + t_u8 element_id; + /** Length */ + t_u8 len; +} MLAN_PACK_END IEEEtypes_Header_t, *pIEEEtypes_Header_t; + +/** Vendor specific IE header */ +typedef MLAN_PACK_START struct _IEEEtypes_VendorHeader_t { + /** Element ID */ + t_u8 element_id; + /** Length */ + t_u8 len; + /** OUI */ + t_u8 oui[3]; + /** OUI type */ + t_u8 oui_type; + /** OUI subtype */ + t_u8 oui_subtype; + /** Version */ + t_u8 version; +} MLAN_PACK_END IEEEtypes_VendorHeader_t, *pIEEEtypes_VendorHeader_t; + +/** Vendor specific IE */ +typedef MLAN_PACK_START struct _IEEEtypes_VendorSpecific_t { + /** Vendor specific IE header */ + IEEEtypes_VendorHeader_t vend_hdr; + /** IE Max - size of previous fields */ + t_u8 data[IEEE_MAX_IE_SIZE - sizeof(IEEEtypes_VendorHeader_t)]; +} MLAN_PACK_END IEEEtypes_VendorSpecific_t, *pIEEEtypes_VendorSpecific_t; + +/** IEEE IE */ +typedef MLAN_PACK_START struct _IEEEtypes_Generic_t { + /** Generic IE header */ + IEEEtypes_Header_t ieee_hdr; + /** IE Max - size of previous fields */ + t_u8 data[IEEE_MAX_IE_SIZE - sizeof(IEEEtypes_Header_t)]; +} MLAN_PACK_END IEEEtypes_Generic_t, *pIEEEtypes_Generic_t; + +/**ft capability policy*/ +typedef MLAN_PACK_START struct _IEEEtypes_FtCapPolicy_t { +#ifdef BIG_ENDIAN_SUPPORT + /** Reserved */ + t_u8 reserved:6; + /** RIC support */ + t_u8 ric:1; + /** FT over the DS capable */ + t_u8 ft_over_ds:1; +#else + /** FT over the DS capable */ + t_u8 ft_over_ds:1; + /** RIC support */ + t_u8 ric:1; + /** Reserved */ + t_u8 reserved:6; +#endif +} MLAN_PACK_END IEEEtypes_FtCapPolicy_t; + +/** Mobility domain IE */ +typedef MLAN_PACK_START struct _IEEEtypes_MobilityDomain_t { + /** Generic IE header */ + IEEEtypes_Header_t ieee_hdr; + /** Mobility Domain ID */ + t_u16 mdid; + /** FT Capability policy */ + t_u8 ft_cap; +} MLAN_PACK_END IEEEtypes_MobilityDomain_t; + +/**FT MIC Control*/ +typedef MLAN_PACK_START struct _IEEEtypes_FT_MICControl_t { + /** reserved */ + t_u8 reserved; + /** element count */ + t_u8 element_count; +} MLAN_PACK_END IEEEtypes_FT_MICControl_t; + +/** FTIE MIC LEN */ +#define FTIE_MIC_LEN 16 + +/**FT IE*/ +typedef MLAN_PACK_START struct _IEEEtypes_FastBssTransElement_t { + /** Generic IE header */ + IEEEtypes_Header_t ieee_hdr; + /** mic control */ + IEEEtypes_FT_MICControl_t mic_control; + /** mic */ + t_u8 mic[FTIE_MIC_LEN]; + /** ANonce */ + t_u8 a_nonce[32]; + /** SNonce */ + t_u8 s_nonce[32]; + /** sub element */ + t_u8 sub_element[1]; +} MLAN_PACK_END IEEEtypes_FastBssTransElement_t; + +/** auth frame body*/ +typedef MLAN_PACK_START struct { + /** auth alg */ + t_u16 auth_alg; + /** auth transaction */ + t_u16 auth_transaction; + /** status code */ + t_u16 status_code; + /** variable */ + t_u8 variable[0]; +} MLAN_PACK_END IEEEtypes_Auth_framebody; + +/*Category for FT*/ +#define FT_CATEGORY 6 +/** FT ACTION request */ +#define FT_ACTION_REQUEST 1 +/** FT ACTION response */ +#define FT_ACTION_RESPONSE 2 + +/*FT response and FT ack*/ +typedef MLAN_PACK_START struct { + /** category */ + t_u8 category; + /** action */ + t_u8 action; + /** sta address */ + t_u8 sta_addr[MLAN_MAC_ADDR_LENGTH]; + /** target ap address */ + t_u8 target_ap_addr[MLAN_MAC_ADDR_LENGTH]; + /** status code */ + t_u16 status_code; + /** varible */ + t_u8 variable[0]; +} MLAN_PACK_END IEEEtypes_Ft_action_response; + +/**FT request */ +typedef MLAN_PACK_START struct { + /** category */ + t_u8 category; + /** action */ + t_u8 action; + /** sta address */ + t_u8 sta_addr[MLAN_MAC_ADDR_LENGTH]; + /** target ap address */ + t_u8 target_ap_addr[MLAN_MAC_ADDR_LENGTH]; + /** varible */ + t_u8 variable[0]; +} MLAN_PACK_END IEEEtypes_Ft_action_request; + +/*Mgmt frame*/ +typedef MLAN_PACK_START struct { + /** frame control */ + t_u16 frame_control; + /** duration */ + t_u16 duration; + /** dest address */ + t_u8 da[MLAN_MAC_ADDR_LENGTH]; + /** source address */ + t_u8 sa[MLAN_MAC_ADDR_LENGTH]; + /** bssid */ + t_u8 bssid[MLAN_MAC_ADDR_LENGTH]; + /** seq control */ + t_u16 seq_ctrl; + /** address 4 */ + t_u8 addr4[MLAN_MAC_ADDR_LENGTH]; + union { + IEEEtypes_Auth_framebody auth; + IEEEtypes_Ft_action_response ft_resp; + IEEEtypes_Ft_action_request ft_req; + } u; +} MLAN_PACK_END IEEE80211_MGMT; + +/** TLV header */ +typedef MLAN_PACK_START struct _TLV_Generic_t { + /** Type */ + t_u16 type; + /** Length */ + t_u16 len; +} MLAN_PACK_END TLV_Generic_t, *pTLV_Generic_t; + +/** Capability information mask */ +#define CAPINFO_MASK \ +(~(MBIT(15) | MBIT(14) | MBIT(11) | MBIT(9))) + +/** Capability Bit Map*/ +#ifdef BIG_ENDIAN_SUPPORT +typedef MLAN_PACK_START struct _IEEEtypes_CapInfo_t { + t_u8 rsrvd1:2; + t_u8 dsss_ofdm:1; + t_u8 radio_measurement:1; + t_u8 rsvrd2:1; + t_u8 short_slot_time:1; + t_u8 rsrvd3:1; + t_u8 spectrum_mgmt:1; + t_u8 chan_agility:1; + t_u8 pbcc:1; + t_u8 short_preamble:1; + t_u8 privacy:1; + t_u8 cf_poll_rqst:1; + t_u8 cf_pollable:1; + t_u8 ibss:1; + t_u8 ess:1; +} MLAN_PACK_END IEEEtypes_CapInfo_t, *pIEEEtypes_CapInfo_t; +#else +typedef MLAN_PACK_START struct _IEEEtypes_CapInfo_t { + /** Capability Bit Map : ESS */ + t_u8 ess:1; + /** Capability Bit Map : IBSS */ + t_u8 ibss:1; + /** Capability Bit Map : CF pollable */ + t_u8 cf_pollable:1; + /** Capability Bit Map : CF poll request */ + t_u8 cf_poll_rqst:1; + /** Capability Bit Map : privacy */ + t_u8 privacy:1; + /** Capability Bit Map : Short preamble */ + t_u8 short_preamble:1; + /** Capability Bit Map : PBCC */ + t_u8 pbcc:1; + /** Capability Bit Map : Channel agility */ + t_u8 chan_agility:1; + /** Capability Bit Map : Spectrum management */ + t_u8 spectrum_mgmt:1; + /** Capability Bit Map : Reserved */ + t_u8 rsrvd3:1; + /** Capability Bit Map : Short slot time */ + t_u8 short_slot_time:1; + /** Capability Bit Map : APSD */ + t_u8 Apsd:1; + /** Capability Bit Map : Reserved */ + t_u8 rsvrd2:1; + /** Capability Bit Map : DSS OFDM */ + t_u8 dsss_ofdm:1; + /** Capability Bit Map : Reserved */ + t_u8 rsrvd1:2; +} MLAN_PACK_END IEEEtypes_CapInfo_t, *pIEEEtypes_CapInfo_t; +#endif /* BIG_ENDIAN_SUPPORT */ + +/** IEEEtypes_CfParamSet_t */ +typedef MLAN_PACK_START struct _IEEEtypes_CfParamSet_t { + /** CF peremeter : Element ID */ + t_u8 element_id; + /** CF peremeter : Length */ + t_u8 len; + /** CF peremeter : Count */ + t_u8 cfp_cnt; + /** CF peremeter : Period */ + t_u8 cfp_period; + /** CF peremeter : Maximum duration */ + t_u16 cfp_max_duration; + /** CF peremeter : Remaining duration */ + t_u16 cfp_duration_remaining; +} MLAN_PACK_END IEEEtypes_CfParamSet_t, *pIEEEtypes_CfParamSet_t; + +/** IEEEtypes_IbssParamSet_t */ +typedef MLAN_PACK_START struct _IEEEtypes_IbssParamSet_t { + /** Element ID */ + t_u8 element_id; + /** Length */ + t_u8 len; + /** ATIM window value in milliseconds */ + t_u16 atim_window; +} MLAN_PACK_END IEEEtypes_IbssParamSet_t, *pIEEEtypes_IbssParamSet_t; + +/** IEEEtypes_SsParamSet_t */ +typedef MLAN_PACK_START union _IEEEtypes_SsParamSet_t { + /** SS parameter : CF parameter set */ + IEEEtypes_CfParamSet_t cf_param_set; + /** SS parameter : IBSS parameter set */ + IEEEtypes_IbssParamSet_t ibss_param_set; +} MLAN_PACK_END IEEEtypes_SsParamSet_t, *pIEEEtypes_SsParamSet_t; + +/** IEEEtypes_FhParamSet_t */ +typedef MLAN_PACK_START struct _IEEEtypes_FhParamSet_t { + /** FH parameter : Element ID */ + t_u8 element_id; + /** FH parameter : Length */ + t_u8 len; + /** FH parameter : Dwell time in milliseconds */ + t_u16 dwell_time; + /** FH parameter : Hop set */ + t_u8 hop_set; + /** FH parameter : Hop pattern */ + t_u8 hop_pattern; + /** FH parameter : Hop index */ + t_u8 hop_index; +} MLAN_PACK_END IEEEtypes_FhParamSet_t, *pIEEEtypes_FhParamSet_t; + +/** IEEEtypes_DsParamSet_t */ +typedef MLAN_PACK_START struct _IEEEtypes_DsParamSet_t { + /** DS parameter : Element ID */ + t_u8 element_id; + /** DS parameter : Length */ + t_u8 len; + /** DS parameter : Current channel */ + t_u8 current_chan; +} MLAN_PACK_END IEEEtypes_DsParamSet_t, *pIEEEtypes_DsParamSet_t; + +/** IEEEtypes_PhyParamSet_t */ +typedef MLAN_PACK_START union _IEEEtypes_PhyParamSet_t { + /** FH parameter set */ + IEEEtypes_FhParamSet_t fh_param_set; + /** DS parameter set */ + IEEEtypes_DsParamSet_t ds_param_set; +} MLAN_PACK_END IEEEtypes_PhyParamSet_t, *pIEEEtypes_PhyParamSet_t; + +/** IEEEtypes_ERPInfo_t */ +typedef MLAN_PACK_START struct _IEEEtypes_ERPInfo_t { + /** Element ID */ + t_u8 element_id; + /** Length */ + t_u8 len; + /** ERP flags */ + t_u8 erp_flags; +} MLAN_PACK_END IEEEtypes_ERPInfo_t, *pIEEEtypes_ERPInfo_t; + +/** IEEEtypes_AId_t */ +typedef t_u16 IEEEtypes_AId_t; + +/** IEEEtypes_StatusCode_t */ +typedef t_u16 IEEEtypes_StatusCode_t; + +/** Fixed size in assoc_resp */ +#define ASSOC_RESP_FIXED_SIZE 6 +/** IEEEtypes_AssocRsp_t */ +typedef MLAN_PACK_START struct _IEEEtypes_AssocRsp_t { + /** Capability information */ + IEEEtypes_CapInfo_t capability; + /** Association response status code */ + IEEEtypes_StatusCode_t status_code; + /** Association ID */ + IEEEtypes_AId_t a_id; + /** IE data buffer */ + t_u8 ie_buffer[1]; +} MLAN_PACK_END IEEEtypes_AssocRsp_t, *pIEEEtypes_AssocRsp_t; + +/** 802.11 supported rates */ +typedef t_u8 WLAN_802_11_RATES[WLAN_SUPPORTED_RATES]; + +/** cipher TKIP */ +#define WPA_CIPHER_TKIP 2 +/** cipher AES */ +#define WPA_CIPHER_AES_CCM 4 +/** AKM: 8021x */ +#define RSN_AKM_8021X 1 +/** AKM: PSK */ +#define RSN_AKM_PSK 2 +/** AKM: PSK SHA256 */ +#define RSN_AKM_PSK_SHA256 6 +#if defined(STA_SUPPORT) +/** Pairwise Cipher Suite length */ +#define PAIRWISE_CIPHER_SUITE_LEN 4 +/** AKM Suite length */ +#define AKM_SUITE_LEN 4 +/** MFPC bit in RSN capability */ +#define MFPC_BIT 7 +/** MFPR bit in RSN capability */ +#define MFPR_BIT 6 +/** PMF ORing mask */ +#define PMF_MASK 0x00c0 +#endif + +/** wpa_suite_t */ +typedef MLAN_PACK_START struct _wpa_suite_t { + /** OUI */ + t_u8 oui[3]; + /** tyep */ + t_u8 type; +} MLAN_PACK_END wpa_suite, wpa_suite_mcast_t; + +/** wpa_suite_ucast_t */ +typedef MLAN_PACK_START struct { + /* count */ + t_u16 count; + /** wpa_suite list */ + wpa_suite list[1]; +} MLAN_PACK_END wpa_suite_ucast_t, wpa_suite_auth_key_mgmt_t; + +/** IEEEtypes_Rsn_t */ +typedef MLAN_PACK_START struct _IEEEtypes_Rsn_t { + /** Rsn : Element ID */ + t_u8 element_id; + /** Rsn : Length */ + t_u8 len; + /** Rsn : version */ + t_u16 version; + /** Rsn : group cipher */ + wpa_suite_mcast_t group_cipher; + /** Rsn : pairwise cipher */ + wpa_suite_ucast_t pairwise_cipher; +} MLAN_PACK_END IEEEtypes_Rsn_t, *pIEEEtypes_Rsn_t; + +/** IEEEtypes_Wpa_t */ +typedef MLAN_PACK_START struct _IEEEtypes_Wpa_t { + /** Wpa : Element ID */ + t_u8 element_id; + /** Wpa : Length */ + t_u8 len; + /** Wpa : oui */ + t_u8 oui[4]; + /** version */ + t_u16 version; + /** Wpa : group cipher */ + wpa_suite_mcast_t group_cipher; + /** Wpa : pairwise cipher */ + wpa_suite_ucast_t pairwise_cipher; +} MLAN_PACK_END IEEEtypes_Wpa_t, *pIEEEtypes_Wpa_t; + +/** Data structure of WMM QoS information */ +typedef MLAN_PACK_START struct _IEEEtypes_WmmQosInfo_t { +#ifdef BIG_ENDIAN_SUPPORT + /** QoS UAPSD */ + t_u8 qos_uapsd:1; + /** Reserved */ + t_u8 reserved:3; + /** Parameter set count */ + t_u8 para_set_count:4; +#else + /** Parameter set count */ + t_u8 para_set_count:4; + /** Reserved */ + t_u8 reserved:3; + /** QoS UAPSD */ + t_u8 qos_uapsd:1; +#endif /* BIG_ENDIAN_SUPPORT */ +} MLAN_PACK_END IEEEtypes_WmmQosInfo_t, *pIEEEtypes_WmmQosInfo_t; + +/** Data structure of WMM Aci/Aifsn */ +typedef MLAN_PACK_START struct _IEEEtypes_WmmAciAifsn_t { +#ifdef BIG_ENDIAN_SUPPORT + /** Reserved */ + t_u8 reserved:1; + /** Aci */ + t_u8 aci:2; + /** Acm */ + t_u8 acm:1; + /** Aifsn */ + t_u8 aifsn:4; +#else + /** Aifsn */ + t_u8 aifsn:4; + /** Acm */ + t_u8 acm:1; + /** Aci */ + t_u8 aci:2; + /** Reserved */ + t_u8 reserved:1; +#endif /* BIG_ENDIAN_SUPPORT */ +} MLAN_PACK_END IEEEtypes_WmmAciAifsn_t, *pIEEEtypes_WmmAciAifsn_t; + +/** Data structure of WMM ECW */ +typedef MLAN_PACK_START struct _IEEEtypes_WmmEcw_t { +#ifdef BIG_ENDIAN_SUPPORT + /** Maximum Ecw */ + t_u8 ecw_max:4; + /** Minimum Ecw */ + t_u8 ecw_min:4; +#else + /** Minimum Ecw */ + t_u8 ecw_min:4; + /** Maximum Ecw */ + t_u8 ecw_max:4; +#endif /* BIG_ENDIAN_SUPPORT */ +} MLAN_PACK_END IEEEtypes_WmmEcw_t, *pIEEEtypes_WmmEcw_t; + +/** Data structure of WMM AC parameters */ +typedef MLAN_PACK_START struct _IEEEtypes_WmmAcParameters_t { + IEEEtypes_WmmAciAifsn_t aci_aifsn; /**< AciAifSn */ + IEEEtypes_WmmEcw_t ecw; /**< Ecw */ + t_u16 tx_op_limit; /**< Tx op limit */ +} MLAN_PACK_END IEEEtypes_WmmAcParameters_t, *pIEEEtypes_WmmAcParameters_t; + +/** Data structure of WMM Info IE */ +typedef MLAN_PACK_START struct _IEEEtypes_WmmInfo_t { + + /** + * WMM Info IE - Vendor Specific Header: + * element_id [221/0xdd] + * Len [7] + * Oui [00:50:f2] + * OuiType [2] + * OuiSubType [0] + * Version [1] + */ + IEEEtypes_VendorHeader_t vend_hdr; + + /** QoS information */ + IEEEtypes_WmmQosInfo_t qos_info; + +} MLAN_PACK_END IEEEtypes_WmmInfo_t, *pIEEEtypes_WmmInfo_t; + +/** Data structure of WMM parameter IE */ +typedef MLAN_PACK_START struct _IEEEtypes_WmmParameter_t { + /** + * WMM Parameter IE - Vendor Specific Header: + * element_id [221/0xdd] + * Len [24] + * Oui [00:50:f2] + * OuiType [2] + * OuiSubType [1] + * Version [1] + */ + IEEEtypes_VendorHeader_t vend_hdr; + + /** QoS information */ + IEEEtypes_WmmQosInfo_t qos_info; + /** Reserved */ + t_u8 reserved; + + /** AC Parameters Record WMM_AC_BE, WMM_AC_BK, WMM_AC_VI, WMM_AC_VO */ + IEEEtypes_WmmAcParameters_t ac_params[MAX_AC_QUEUES]; +} MLAN_PACK_END IEEEtypes_WmmParameter_t, *pIEEEtypes_WmmParameter_t; + +/** Enumerator for TSPEC direction */ +typedef MLAN_PACK_START enum _IEEEtypes_WMM_TSPEC_TS_Info_Direction_e { + + TSPEC_DIR_UPLINK = 0, + TSPEC_DIR_DOWNLINK = 1, + /* 2 is a reserved value */ + TSPEC_DIR_BIDIRECT = 3, + +} MLAN_PACK_END IEEEtypes_WMM_TSPEC_TS_Info_Direction_e; + +/** Enumerator for TSPEC PSB */ +typedef MLAN_PACK_START enum _IEEEtypes_WMM_TSPEC_TS_Info_PSB_e { + + TSPEC_PSB_LEGACY = 0, + TSPEC_PSB_TRIG = 1, + +} MLAN_PACK_END IEEEtypes_WMM_TSPEC_TS_Info_PSB_e; + +/** Enumerator for TSPEC Ack Policy */ +typedef MLAN_PACK_START enum _IEEEtypes_WMM_TSPEC_TS_Info_AckPolicy_e { + + TSPEC_ACKPOLICY_NORMAL = 0, + TSPEC_ACKPOLICY_NOACK = 1, + /* 2 is reserved */ + TSPEC_ACKPOLICY_BLOCKACK = 3, + +} MLAN_PACK_END IEEEtypes_WMM_TSPEC_TS_Info_AckPolicy_e; + +/** Enumerator for TSPEC Trafffice type */ +typedef MLAN_PACK_START enum _IEEEtypes_WMM_TSPEC_TS_TRAFFIC_TYPE_e { + + TSPEC_TRAFFIC_APERIODIC = 0, + TSPEC_TRAFFIC_PERIODIC = 1, + +} MLAN_PACK_END IEEEtypes_WMM_TSPEC_TS_TRAFFIC_TYPE_e; + +/** Data structure of WMM TSPEC information */ +typedef MLAN_PACK_START struct { +#ifdef BIG_ENDIAN_SUPPORT + t_u8 Reserved17_23:7; /* ! Reserved */ + t_u8 Schedule:1; + IEEEtypes_WMM_TSPEC_TS_Info_AckPolicy_e AckPolicy:2; + t_u8 UserPri:3; /* ! 802.1d User Priority */ + IEEEtypes_WMM_TSPEC_TS_Info_PSB_e PowerSaveBehavior:1; /* ! Legacy/Trigg */ + t_u8 Aggregation:1; /* ! Reserved */ + t_u8 AccessPolicy2:1; /* ! */ + t_u8 AccessPolicy1:1; /* ! */ + IEEEtypes_WMM_TSPEC_TS_Info_Direction_e Direction:2; + t_u8 TID:4; /* ! Unique identifier */ + IEEEtypes_WMM_TSPEC_TS_TRAFFIC_TYPE_e TrafficType:1; +#else + IEEEtypes_WMM_TSPEC_TS_TRAFFIC_TYPE_e TrafficType:1; + t_u8 TID:4; /* ! Unique identifier */ + IEEEtypes_WMM_TSPEC_TS_Info_Direction_e Direction:2; + t_u8 AccessPolicy1:1; /* ! */ + t_u8 AccessPolicy2:1; /* ! */ + t_u8 Aggregation:1; /* ! Reserved */ + IEEEtypes_WMM_TSPEC_TS_Info_PSB_e PowerSaveBehavior:1; /* ! Legacy/Trigg */ + t_u8 UserPri:3; /* ! 802.1d User Priority */ + IEEEtypes_WMM_TSPEC_TS_Info_AckPolicy_e AckPolicy:2; + t_u8 Schedule:1; + t_u8 Reserved17_23:7; /* ! Reserved */ +#endif +} MLAN_PACK_END IEEEtypes_WMM_TSPEC_TS_Info_t; + +/** Data structure of WMM TSPEC Nominal Size */ +typedef MLAN_PACK_START struct { +#ifdef BIG_ENDIAN_SUPPORT + t_u16 Fixed:1; /* ! 1: Fixed size given in Size, 0: Var, size is nominal */ + t_u16 Size:15; /* ! Nominal size in octets */ +#else + t_u16 Size:15; /* ! Nominal size in octets */ + t_u16 Fixed:1; /* ! 1: Fixed size given in Size, 0: Var, size is nominal */ +#endif +} MLAN_PACK_END IEEEtypes_WMM_TSPEC_NomMSDUSize_t; + +/** Data structure of WMM TSPEC SBWA */ +typedef MLAN_PACK_START struct { +#ifdef BIG_ENDIAN_SUPPORT + t_u16 Whole:3; /* ! Whole portion */ + t_u16 Fractional:13; /* ! Fractional portion */ +#else + t_u16 Fractional:13; /* ! Fractional portion */ + t_u16 Whole:3; /* ! Whole portion */ +#endif +} MLAN_PACK_END IEEEtypes_WMM_TSPEC_SBWA; + +/** Data structure of WMM TSPEC Body */ +typedef MLAN_PACK_START struct { + + /** TS Information */ + IEEEtypes_WMM_TSPEC_TS_Info_t TSInfo; + /** NomMSDU size */ + IEEEtypes_WMM_TSPEC_NomMSDUSize_t NomMSDUSize; + /** MAximum MSDU size */ + t_u16 MaximumMSDUSize; + /** Minimum Service Interval */ + t_u32 MinServiceInterval; + /** Maximum Service Interval */ + t_u32 MaxServiceInterval; + /** Inactivity Interval */ + t_u32 InactivityInterval; + /** Suspension Interval */ + t_u32 SuspensionInterval; + /** Service Start Time */ + t_u32 ServiceStartTime; + /** Minimum Data Rate */ + t_u32 MinimumDataRate; + /** Mean Data Rate */ + t_u32 MeanDataRate; + /** Peak Data Rate */ + t_u32 PeakDataRate; + /** Maximum Burst Size */ + t_u32 MaxBurstSize; + /** Delay Bound */ + t_u32 DelayBound; + /** Minimum Phy Rate */ + t_u32 MinPHYRate; + /** Surplus BA Allowance */ + IEEEtypes_WMM_TSPEC_SBWA SurplusBWAllowance; + /** Medium Time */ + t_u16 MediumTime; +} MLAN_PACK_END IEEEtypes_WMM_TSPEC_Body_t; + +/** Data structure of WMM TSPEC all elements */ +typedef MLAN_PACK_START struct { + /** Element ID */ + t_u8 ElementId; + /** Length */ + t_u8 Len; + /** Oui Type */ + t_u8 OuiType[4]; /* 00:50:f2:02 */ + /** Ouisubtype */ + t_u8 OuiSubType; /* 01 */ + /** Version */ + t_u8 Version; + + /** TspecBody */ + IEEEtypes_WMM_TSPEC_Body_t TspecBody; + +} MLAN_PACK_END IEEEtypes_WMM_TSPEC_t; + +/** WMM Action Category values */ +typedef MLAN_PACK_START enum _IEEEtypes_ActionCategory_e { + + IEEE_MGMT_ACTION_CATEGORY_SPECTRUM_MGMT = 0, + IEEE_MGMT_ACTION_CATEGORY_QOS = 1, + IEEE_MGMT_ACTION_CATEGORY_DLS = 2, + IEEE_MGMT_ACTION_CATEGORY_BLOCK_ACK = 3, + IEEE_MGMT_ACTION_CATEGORY_PUBLIC = 4, + IEEE_MGMT_ACTION_CATEGORY_RADIO_RSRC = 5, + IEEE_MGMT_ACTION_CATEGORY_FAST_BSS_TRANS = 6, + IEEE_MGMT_ACTION_CATEGORY_HT = 7, + + IEEE_MGMT_ACTION_CATEGORY_WNM = 10, + IEEE_MGMT_ACTION_CATEGORY_UNPROTECT_WNM = 11, + + IEEE_MGMT_ACTION_CATEGORY_WMM_TSPEC = 17 +} MLAN_PACK_END IEEEtypes_ActionCategory_e; + +/** WMM TSPEC operations */ +typedef MLAN_PACK_START enum _IEEEtypes_WMM_Tspec_Action_e { + + TSPEC_ACTION_CODE_ADDTS_REQ = 0, + TSPEC_ACTION_CODE_ADDTS_RSP = 1, + TSPEC_ACTION_CODE_DELTS = 2, + +} MLAN_PACK_END IEEEtypes_WMM_Tspec_Action_e; + +/** WMM TSPEC Category Action Base */ +typedef MLAN_PACK_START struct { + + /** Category */ + IEEEtypes_ActionCategory_e category; + /** Action */ + IEEEtypes_WMM_Tspec_Action_e action; + /** Dialog Token */ + t_u8 dialogToken; + +} MLAN_PACK_END IEEEtypes_WMM_Tspec_Action_Base_Tspec_t; + +/** WMM TSPEC AddTS request structure */ +typedef MLAN_PACK_START struct { + + /** Tspec action */ + IEEEtypes_WMM_Tspec_Action_Base_Tspec_t tspecAct; + /** Status Code */ + t_u8 statusCode; + /** tspecIE */ + IEEEtypes_WMM_TSPEC_t tspecIE; + + /* Place holder for additional elements after the TSPEC */ + t_u8 subElem[256]; + +} MLAN_PACK_END IEEEtypes_Action_WMM_AddTsReq_t; + +/** WMM TSPEC AddTS response structure */ +typedef MLAN_PACK_START struct { + IEEEtypes_WMM_Tspec_Action_Base_Tspec_t tspecAct; + t_u8 statusCode; + IEEEtypes_WMM_TSPEC_t tspecIE; + + /* Place holder for additional elements after the TSPEC */ + t_u8 subElem[256]; + +} MLAN_PACK_END IEEEtypes_Action_WMM_AddTsRsp_t; + +/** WMM TSPEC DelTS structure */ +typedef MLAN_PACK_START struct { + /** tspec Action */ + IEEEtypes_WMM_Tspec_Action_Base_Tspec_t tspecAct; + /** Reason Code */ + t_u8 reasonCode; + /** tspecIE */ + IEEEtypes_WMM_TSPEC_t tspecIE; + +} MLAN_PACK_END IEEEtypes_Action_WMM_DelTs_t; + +/** union of WMM TSPEC structures */ +typedef MLAN_PACK_START union { + /** tspec Action */ + IEEEtypes_WMM_Tspec_Action_Base_Tspec_t tspecAct; + + /** add TS request */ + IEEEtypes_Action_WMM_AddTsReq_t addTsReq; + /** add TS response */ + IEEEtypes_Action_WMM_AddTsRsp_t addTsRsp; + /** Delete TS */ + IEEEtypes_Action_WMM_DelTs_t delTs; + +} MLAN_PACK_END IEEEtypes_Action_WMMAC_t; + +/** union of WMM TSPEC & Action category */ +typedef MLAN_PACK_START union { + /** Category */ + IEEEtypes_ActionCategory_e category; + + /** wmmAc */ + IEEEtypes_Action_WMMAC_t wmmAc; + +} MLAN_PACK_END IEEEtypes_ActionFrame_t; + +/** Data structure for subband set */ +typedef MLAN_PACK_START struct _IEEEtypes_SubbandSet_t { + /** First channel */ + t_u8 first_chan; + /** Number of channels */ + t_u8 no_of_chan; + /** Maximum Tx power in dBm */ + t_u8 max_tx_pwr; +} MLAN_PACK_END IEEEtypes_SubbandSet_t, *pIEEEtypes_SubbandSet_t; + +#ifdef STA_SUPPORT +/** Data structure for Country IE */ +typedef MLAN_PACK_START struct _IEEEtypes_CountryInfoSet_t { + /** Element ID */ + t_u8 element_id; + /** Length */ + t_u8 len; + /** Country code */ + t_u8 country_code[COUNTRY_CODE_LEN]; + /** Set of subbands */ + IEEEtypes_SubbandSet_t sub_band[1]; +} MLAN_PACK_END IEEEtypes_CountryInfoSet_t, *pIEEEtypes_CountryInfoSet_t; + +/** Data structure for Country IE full set */ +typedef MLAN_PACK_START struct _IEEEtypes_CountryInfoFullSet_t { + /** Element ID */ + t_u8 element_id; + /** Length */ + t_u8 len; + /** Country code */ + t_u8 country_code[COUNTRY_CODE_LEN]; + /** Set of subbands */ + IEEEtypes_SubbandSet_t sub_band[MRVDRV_MAX_SUBBAND_802_11D]; +} MLAN_PACK_END IEEEtypes_CountryInfoFullSet_t, + *pIEEEtypes_CountryInfoFullSet_t; + +#endif /* STA_SUPPORT */ + +/** Data structure for Link ID */ +typedef MLAN_PACK_START struct _IEEEtypes_LinkIDElement_t { + /** Element ID */ + t_u8 element_id; + /** Length */ + t_u8 len; + /** bssid */ + t_u8 bssid[MLAN_MAC_ADDR_LENGTH]; + /** initial sta address */ + t_u8 init_sta[MLAN_MAC_ADDR_LENGTH]; + /** respose sta address */ + t_u8 resp_sta[MLAN_MAC_ADDR_LENGTH]; +} MLAN_PACK_END IEEEtypes_LinkIDElement_t, *pIEEEtypes_LinkIDElement_t; + +/** HT Capabilities Data */ +typedef struct MLAN_PACK_START _HTCap_t { + /** HT Capabilities Info field */ + t_u16 ht_cap_info; + /** A-MPDU Parameters field */ + t_u8 ampdu_param; + /** Supported MCS Set field */ + t_u8 supported_mcs_set[16]; + /** HT Extended Capabilities field */ + t_u16 ht_ext_cap; + /** Transmit Beamforming Capabilities field */ + t_u32 tx_bf_cap; + /** Antenna Selection Capability field */ + t_u8 asel; +} MLAN_PACK_END HTCap_t, *pHTCap_t; + +/** HT Information Data */ +typedef struct MLAN_PACK_START _HTInfo_t { + /** Primary channel */ + t_u8 pri_chan; + /** Field 2 */ + t_u8 field2; + /** Field 3 */ + t_u16 field3; + /** Field 4 */ + t_u16 field4; + /** Bitmap indicating MCSs supported by all HT STAs in the BSS */ + t_u8 basic_mcs_set[16]; +} MLAN_PACK_END HTInfo_t, *pHTInfo_t; + +/** 20/40 BSS Coexistence Data */ +typedef struct MLAN_PACK_START _BSSCo2040_t { + /** 20/40 BSS Coexistence value */ + t_u8 bss_co_2040_value; +} MLAN_PACK_END BSSCo2040_t, *pBSSCo2040_t; + +#define MAX_DSCP_EXCEPTION_NUM 21 +/** DSCP Range */ +typedef struct MLAN_PACK_START _DSCP_Exception_t { + /* DSCP value 0 to 63 or ff */ + t_u8 dscp_value; + /* user priority 0-7 */ + t_u8 user_priority; +} MLAN_PACK_END DSCP_Exception_t, *pDSCP_Exception_t; + +/** DSCP Range */ +typedef struct MLAN_PACK_START _DSCP_Range_t { + /* DSCP low value */ + t_u8 dscp_low_value; + /* DSCP high value */ + t_u8 dscp_high_value; +} MLAN_PACK_END DSCP_Range_t, *pDSCP_Range_t; + +/** Overlapping BSS Scan Parameters Data */ +typedef struct MLAN_PACK_START _OverlapBSSScanParam_t { + /** OBSS Scan Passive Dwell in milliseconds */ + t_u16 obss_scan_passive_dwell; + /** OBSS Scan Active Dwell in milliseconds */ + t_u16 obss_scan_active_dwell; + /** BSS Channel Width Trigger Scan Interval in seconds */ + t_u16 bss_chan_width_trigger_scan_int; + /** OBSS Scan Passive Total Per Channel */ + t_u16 obss_scan_passive_total; + /** OBSS Scan Active Total Per Channel */ + t_u16 obss_scan_active_total; + /** BSS Width Channel Transition Delay Factor */ + t_u16 bss_width_chan_trans_delay; + /** OBSS Scan Activity Threshold */ + t_u16 obss_scan_active_threshold; +} MLAN_PACK_END OBSSScanParam_t, *pOBSSScanParam_t; + +/** HT Capabilities IE */ +typedef MLAN_PACK_START struct _IEEEtypes_HTCap_t { + /** Generic IE header */ + IEEEtypes_Header_t ieee_hdr; + /** HTCap struct */ + HTCap_t ht_cap; +} MLAN_PACK_END IEEEtypes_HTCap_t, *pIEEEtypes_HTCap_t; + +/** HT Information IE */ +typedef MLAN_PACK_START struct _IEEEtypes_HTInfo_t { + /** Generic IE header */ + IEEEtypes_Header_t ieee_hdr; + /** HTInfo struct */ + HTInfo_t ht_info; +} MLAN_PACK_END IEEEtypes_HTInfo_t, *pIEEEtypes_HTInfo_t; + +/** 20/40 BSS Coexistence IE */ +typedef MLAN_PACK_START struct _IEEEtypes_2040BSSCo_t { + /** Generic IE header */ + IEEEtypes_Header_t ieee_hdr; + /** BSSCo2040_t struct */ + BSSCo2040_t bss_co_2040; +} MLAN_PACK_END IEEEtypes_2040BSSCo_t, *pIEEEtypes_2040BSSCo_t; + +/** Extended Capabilities IE */ +typedef MLAN_PACK_START struct _IEEEtypes_ExtCap_t { + /** Generic IE header */ + IEEEtypes_Header_t ieee_hdr; + /** ExtCap_t struct */ + ExtCap_t ext_cap; +} MLAN_PACK_END IEEEtypes_ExtCap_t, *pIEEEtypes_ExtCap_t; + +/** Overlapping BSS Scan Parameters IE */ +typedef MLAN_PACK_START struct _IEEEtypes_OverlapBSSScanParam_t { + /** Generic IE header */ + IEEEtypes_Header_t ieee_hdr; + /** OBSSScanParam_t struct */ + OBSSScanParam_t obss_scan_param; +} MLAN_PACK_END IEEEtypes_OverlapBSSScanParam_t, + *pIEEEtypes_OverlapBSSScanParam_t; + +/** Maximum number of subbands in the IEEEtypes_SupportedChannels_t structure */ +#define WLAN_11H_MAX_SUBBANDS 5 + +/** Maximum number of DFS channels configured in IEEEtypes_IBSS_DFS_t */ +#define WLAN_11H_MAX_IBSS_DFS_CHANNELS 25 + +/** IEEE Power Constraint element (7.3.2.15) */ +typedef MLAN_PACK_START struct { + t_u8 element_id; /**< IEEE Element ID = 32 */ + t_u8 len; /**< Element length after id and len */ + t_u8 local_constraint; + /**< Local power constraint applied to 11d + chan info */ +} MLAN_PACK_END IEEEtypes_PowerConstraint_t; + +/** IEEE Power Capability element (7.3.2.16) */ +typedef MLAN_PACK_START struct { + t_u8 element_id; /**< IEEE Element ID = 33 */ + t_u8 len; /**< Element length after id and len */ + t_s8 min_tx_power_capability; + /**< Minimum Transmit power (dBm) */ + t_s8 max_tx_power_capability; + /**< Maximum Transmit power (dBm) */ +} MLAN_PACK_END IEEEtypes_PowerCapability_t; + +/** IEEE TPC Report element (7.3.2.18) */ +typedef MLAN_PACK_START struct { + t_u8 element_id;/**< IEEE Element ID = 35 */ + t_u8 len; /**< Element length after id and len */ + t_s8 tx_power; /**< Max power used to transmit the TPC Report frame (dBm) */ + t_s8 link_margin; + /**< Link margin when TPC Request received (dB) */ +} MLAN_PACK_END IEEEtypes_TPCReport_t; + +/* IEEE Supported Channel sub-band description (7.3.2.19) */ +/** + * Sub-band description used in the supported channels element. + */ +typedef MLAN_PACK_START struct { + t_u8 start_chan;/**< Starting channel in the subband */ + t_u8 num_chans; /**< Number of channels in the subband */ + +} MLAN_PACK_END IEEEtypes_SupportChan_Subband_t; + +/* IEEE Supported Channel element (7.3.2.19) */ +/** + * Sent in association requests. Details the sub-bands and number + * of channels supported in each subband + */ +typedef MLAN_PACK_START struct { + t_u8 element_id;/**< IEEE Element ID = 36 */ + t_u8 len; /**< Element length after id and len */ + + /** Configured sub-bands information in the element */ + IEEEtypes_SupportChan_Subband_t subband[WLAN_11H_MAX_SUBBANDS]; + +} MLAN_PACK_END IEEEtypes_SupportedChannels_t; + +/* IEEE Channel Switch Announcement Element (7.3.2.20) */ +/** + * Provided in beacons and probe responses. Used to advertise when + * and to which channel it is changing to. Only starting STAs in + * an IBSS and APs are allowed to originate a chan switch element. + */ +typedef MLAN_PACK_START struct { + t_u8 element_id; /**< IEEE Element ID = 37 */ + t_u8 len; /**< Element length after id and len */ + t_u8 chan_switch_mode; /**< STA should not transmit any frames if 1 */ + t_u8 new_channel_num; /**< Channel # that AP/IBSS is moving to */ + t_u8 chan_switch_count; /**< # of TBTTs before channel switch */ + +} MLAN_PACK_END IEEEtypes_ChanSwitchAnn_t; + +/* IEEE Quiet Period Element (7.3.2.23) */ +/** + * Provided in beacons and probe responses. Indicates times during + * which the STA should not be transmitting data. Only starting STAs in + * an IBSS and APs are allowed to originate a quiet element. + */ +typedef MLAN_PACK_START struct { + t_u8 element_id; /**< IEEE Element ID = 40 */ + t_u8 len; /**< Element length after id and len */ + t_u8 quiet_count; /**< Number of TBTTs until beacon with the quiet period */ + t_u8 quiet_period; /**< Regular quiet period, # of TBTTS between periods */ + t_u16 quiet_duration; + /**< Duration of the quiet period in TUs */ + t_u16 quiet_offset; /**< Offset in TUs from the TBTT for the quiet period */ + +} MLAN_PACK_END IEEEtypes_Quiet_t; + +/** +*** @brief Map octet of the basic measurement report (7.3.2.22.1) +**/ +typedef MLAN_PACK_START struct { +#ifdef BIG_ENDIAN_SUPPORT + /**< Reserved */ + t_u8 rsvd5_7:3; + /**< Channel is unmeasured */ + t_u8 unmeasured:1; + /**< Radar detected on channel */ + t_u8 radar:1; + /**< Unidentified signal found on channel */ + t_u8 unidentified_sig:1; + /**< OFDM preamble detected on channel */ + t_u8 ofdm_preamble:1; + /**< At least one valid MPDU received on channel */ + t_u8 bss:1; +#else + /**< At least one valid MPDU received on channel */ + t_u8 bss:1; + /**< OFDM preamble detected on channel */ + t_u8 ofdm_preamble:1; + /**< Unidentified signal found on channel */ + t_u8 unidentified_sig:1; + /**< Radar detected on channel */ + t_u8 radar:1; + /**< Channel is unmeasured */ + t_u8 unmeasured:1; + /**< Reserved */ + t_u8 rsvd5_7:3; +#endif /* BIG_ENDIAN_SUPPORT */ + +} MLAN_PACK_END MeasRptBasicMap_t; + +/* IEEE DFS Channel Map field (7.3.2.24) */ +/** + * Used to list supported channels and provide a octet "map" field which + * contains a basic measurement report for that channel in the + * IEEEtypes_IBSS_DFS_t element + */ +typedef MLAN_PACK_START struct { + t_u8 channel_number; /**< Channel number */ + MeasRptBasicMap_t rpt_map; + /**< Basic measurement report for the channel */ + +} MLAN_PACK_END IEEEtypes_ChannelMap_t; + +/* IEEE IBSS DFS Element (7.3.2.24) */ +/** + * IBSS DFS element included in ad hoc beacons and probe responses. + * Provides information regarding the IBSS DFS Owner as well as the + * originating STAs supported channels and basic measurement results. + */ +typedef MLAN_PACK_START struct { + t_u8 element_id; /**< IEEE Element ID = 41 */ + t_u8 len; /**< Element length after id and len */ + t_u8 dfs_owner[MLAN_MAC_ADDR_LENGTH]; + /**< DFS Owner STA Address */ + t_u8 dfs_recovery_interval; /**< DFS Recovery time in TBTTs */ + + /** Variable length map field, one Map entry for each supported channel */ + IEEEtypes_ChannelMap_t channel_map[WLAN_11H_MAX_IBSS_DFS_CHANNELS]; + +} MLAN_PACK_END IEEEtypes_IBSS_DFS_t; + +/* 802.11h BSS information kept for each BSSID received in scan results */ +/** + * IEEE BSS information needed from scan results for later processing in + * join commands + */ +typedef struct { + t_u8 sensed_11h; + /**< Capability bit set or 11h IE found in this BSS */ + + IEEEtypes_PowerConstraint_t power_constraint; + /**< Power Constraint IE */ + IEEEtypes_PowerCapability_t power_capability; + /**< Power Capability IE */ + IEEEtypes_TPCReport_t tpc_report; /**< TPC Report IE */ + IEEEtypes_ChanSwitchAnn_t chan_switch_ann;/**< Channel Switch Announcement IE */ + IEEEtypes_Quiet_t quiet; /**< Quiet IE */ + IEEEtypes_IBSS_DFS_t ibss_dfs; /**< IBSS DFS Element IE */ + +} wlan_11h_bss_info_t; + +/** Ethernet packet type for TDLS */ +#define MLAN_ETHER_PKT_TYPE_TDLS_ACTION (0x890D) + +/*802.11z TDLS action frame type and strcuct */ +typedef MLAN_PACK_START struct { + /*link indentifier ie =101 */ + t_u8 element_id; + /*len = 18 */ + t_u8 len; + /** bssid */ + t_u8 bssid[MLAN_MAC_ADDR_LENGTH]; + /** init sta mac address */ + t_u8 init_sta[MLAN_MAC_ADDR_LENGTH]; + /** resp sta mac address */ + t_u8 resp_sta[MLAN_MAC_ADDR_LENGTH]; +} MLAN_PACK_END IEEEtypes_tdls_linkie; + +/** action code for tdls setup request */ +#define TDLS_SETUP_REQUEST 0 +/** action code for tdls setup response */ +#define TDLS_SETUP_RESPONSE 1 +/** action code for tdls setup confirm */ +#define TDLS_SETUP_CONFIRM 2 +/** action code for tdls tear down */ +#define TDLS_TEARDOWN 3 +/** action code for tdls traffic indication */ +#define TDLS_PEER_TRAFFIC_INDICATION 4 +/** action code for tdls channel switch request */ +#define TDLS_CHANNEL_SWITCH_REQUEST 5 +/** action code for tdls channel switch response */ +#define TDLS_CHANNEL_SWITCH_RESPONSE 6 +/** action code for tdls psm request */ +#define TDLS_PEER_PSM_REQUEST 7 +/** action code for tdls psm response */ +#define TDLS_PEER_PSM_RESPONSE 8 +/** action code for tdls traffic response */ +#define TDLS_PEER_TRAFFIC_RESPONSE 9 +/** action code for tdls discovery request */ +#define TDLS_DISCOVERY_REQUEST 10 +/** action code for TDLS discovery response */ +#define TDLS_DISCOVERY_RESPONSE 14 +/** category public */ +#define CATEGORY_PUBLIC 4 + +/** action code for 20/40 BSS Coexsitence Management frame */ +#define BSS_20_40_COEX 0 + +#ifdef STA_SUPPORT +/** Macro for maximum size of scan response buffer */ +#define MAX_SCAN_RSP_BUF (16 * 1024) + +/** Maximum number of channels that can be sent in user scan config */ +#define WLAN_USER_SCAN_CHAN_MAX 50 + +/** Maximum length of SSID list */ +#define MRVDRV_MAX_SSID_LIST_LENGTH 10 + +/** Scan all the channels in specified band */ +#define BAND_SPECIFIED 0x80 + +/** + * IOCTL SSID List sub-structure sent in wlan_ioctl_user_scan_cfg + * + * Used to specify SSID specific filters as well as SSID pattern matching + * filters for scan result processing in firmware. + */ +typedef MLAN_PACK_START struct _wlan_user_scan_ssid { + /** SSID */ + t_u8 ssid[MLAN_MAX_SSID_LENGTH + 1]; + /** Maximum length of SSID */ + t_u8 max_len; +} MLAN_PACK_END wlan_user_scan_ssid; + +/** + * @brief IOCTL channel sub-structure sent in wlan_ioctl_user_scan_cfg + * + * Multiple instances of this structure are included in the IOCTL command + * to configure a instance of a scan on the specific channel. + */ +typedef MLAN_PACK_START struct _wlan_user_scan_chan { + /** Channel Number to scan */ + t_u8 chan_number; + /** Radio type: 'B/G' Band = 0, 'A' Band = 1 */ + t_u8 radio_type; + /** Scan type: Active = 1, Passive = 2 */ + t_u8 scan_type; + /** Reserved */ + t_u8 reserved; + /** Scan duration in milliseconds; if 0 default used */ + t_u32 scan_time; +} MLAN_PACK_END wlan_user_scan_chan; + +/** channel statictics */ +typedef MLAN_PACK_START struct _ChanStatistics_t { + /** channle number */ + t_u8 chan_num; + /** band info */ + Band_Config_t bandcfg; + /** flags */ + t_u8 flags; + /** noise */ + t_s8 noise; + /** total network */ + t_u16 total_networks; + /** scan duration */ + t_u16 cca_scan_duration; + /** busy duration */ + t_u16 cca_busy_duration; +} MLAN_PACK_END ChanStatistics_t; + +/** Enhance ext scan type defination */ +typedef enum _MLAN_EXT_SCAN_TYPE { + EXT_SCAN_DEFAULT, + EXT_SCAN_ENHANCE, + EXT_SCAN_CANCEL, +} MLAN_EXT_SCAN_TYPE; + +/** + * Input structure to configure an immediate scan cmd to firmware + * + * Specifies a number of parameters to be used in general for the scan + * as well as a channel list (wlan_user_scan_chan) for each scan period + * desired. + */ +typedef MLAN_PACK_START struct { + /** + * Flag set to keep the previous scan table intact + * + * If set, the scan results will accumulate, replacing any previous + * matched entries for a BSS with the new scan data + */ + t_u8 keep_previous_scan; + /** + * BSS mode to be sent in the firmware command + * + * Field can be used to restrict the types of networks returned in the + * scan. Valid settings are: + * + * - MLAN_SCAN_MODE_BSS (infrastructure) + * - MLAN_SCAN_MODE_IBSS (adhoc) + * - MLAN_SCAN_MODE_ANY (unrestricted, adhoc and infrastructure) + */ + t_u8 bss_mode; + /** + * Configure the number of probe requests for active chan scans + */ + t_u8 num_probes; + /** + * @brief ssid filter flag + */ + t_u8 ssid_filter; + /** + * @brief BSSID filter sent in the firmware command to limit the results + */ + t_u8 specific_bssid[MLAN_MAC_ADDR_LENGTH]; + /** + * SSID filter list used in the to limit the scan results + */ + wlan_user_scan_ssid ssid_list[MRVDRV_MAX_SSID_LIST_LENGTH]; + /** + * Variable number (fixed maximum) of channels to scan up + */ + wlan_user_scan_chan chan_list[WLAN_USER_SCAN_CHAN_MAX]; + /** scan channel gap */ + t_u16 scan_chan_gap; + /** scan type: 0 legacy, 1: enhance scan*/ + t_u8 ext_scan_type; + /** flag to filer only probe response */ + t_u8 proberesp_only; +} MLAN_PACK_END wlan_user_scan_cfg; + +/** Default scan interval in millisecond*/ +#define DEFAULT_BGSCAN_INTERVAL 30000 + +/** action get all, except pps/uapsd config */ +#define BG_SCAN_ACT_GET 0x0000 +/** action set all, except pps/uapsd config */ +#define BG_SCAN_ACT_SET 0x0001 +/** action get pps/uapsd config */ +#define BG_SCAN_ACT_GET_PPS_UAPSD 0x0100 +/** action set pps/uapsd config */ +#define BG_SCAN_ACT_SET_PPS_UAPSD 0x0101 +/** action set all */ +#define BG_SCAN_ACT_SET_ALL 0xff01 +/** ssid match */ +#define BG_SCAN_SSID_MATCH 0x0001 +/** ssid match and RSSI exceeded */ +#define BG_SCAN_SSID_RSSI_MATCH 0x0004 +/**wait for all channel scan to complete to report scan result*/ +#define BG_SCAN_WAIT_ALL_CHAN_DONE 0x80000000 +/** Maximum number of channels that can be sent in bg scan config */ +#define WLAN_BG_SCAN_CHAN_MAX 38 + +/** + * Input structure to configure bs scan cmd to firmware + */ +typedef MLAN_PACK_START struct { + /** action */ + t_u16 action; + /** enable/disable */ + t_u8 enable; + /** BSS type: + * MLAN_SCAN_MODE_BSS (infrastructure) + * MLAN_SCAN_MODE_IBSS (adhoc) + * MLAN_SCAN_MODE_ANY (unrestricted, adhoc and infrastructure) + */ + t_u8 bss_type; + /** number of channel scanned during each scan */ + t_u8 chan_per_scan; + /** interval between consecutive scan */ + t_u32 scan_interval; + /** bit 0: ssid match bit 1: ssid match and SNR exceeded + * bit 2: ssid match and RSSI exceeded + * bit 31: wait for all channel scan to complete to report scan result + */ + t_u32 report_condition; + /* Configure the number of probe requests for active chan scans */ + t_u8 num_probes; + /** RSSI threshold */ + t_u8 rssi_threshold; + /** SNR threshold */ + t_u8 snr_threshold; + /** repeat count */ + t_u16 repeat_count; + /** start later flag */ + t_u16 start_later; + /** SSID filter list used in the to limit the scan results */ + wlan_user_scan_ssid ssid_list[MRVDRV_MAX_SSID_LIST_LENGTH]; + /** Variable number (fixed maximum) of channels to scan up */ + wlan_user_scan_chan chan_list[WLAN_BG_SCAN_CHAN_MAX]; + /** scan channel gap */ + t_u16 scan_chan_gap; +} MLAN_PACK_END wlan_bgscan_cfg; +#endif /* STA_SUPPORT */ + +#ifdef PRAGMA_PACK +#pragma pack(pop) +#endif + +/** BSSDescriptor_t + * Structure used to store information for beacon/probe response + */ +typedef struct _BSSDescriptor_t { + /** MAC address */ + mlan_802_11_mac_addr mac_address; + + /** SSID */ + mlan_802_11_ssid ssid; + + /** WEP encryption requirement */ + t_u32 privacy; + + /** Receive signal strength in dBm */ + t_s32 rssi; + + /** Channel */ + t_u32 channel; + + /** Freq */ + t_u32 freq; + + /** Beacon period */ + t_u16 beacon_period; + + /** ATIM window */ + t_u32 atim_window; + + /** ERP flags */ + t_u8 erp_flags; + + /** Type of network in use */ + WLAN_802_11_NETWORK_TYPE network_type_use; + + /** Network infrastructure mode */ + t_u32 bss_mode; + + /** Network supported rates */ + WLAN_802_11_RATES supported_rates; + + /** Supported data rates */ + t_u8 data_rates[WLAN_SUPPORTED_RATES]; + + /** Current channel bandwidth + * 0 : 20MHZ + * 1 : 40MHZ + * 2 : 80MHZ + * 3 : 160MHZ + */ + t_u8 curr_bandwidth; + + /** Network band. + * BAND_B(0x01): 'b' band + * BAND_G(0x02): 'g' band + * BAND_A(0X04): 'a' band + */ + t_u16 bss_band; + + /** TSF timestamp from the current firmware TSF */ + t_u64 network_tsf; + + /** TSF value included in the beacon/probe response */ + t_u8 time_stamp[8]; + + /** PHY parameter set */ + IEEEtypes_PhyParamSet_t phy_param_set; + + /** SS parameter set */ + IEEEtypes_SsParamSet_t ss_param_set; + + /** Capability information */ + IEEEtypes_CapInfo_t cap_info; + + /** WMM IE */ + IEEEtypes_WmmParameter_t wmm_ie; + + /** 802.11h BSS information */ + wlan_11h_bss_info_t wlan_11h_bss_info; + + /** Indicate disabling 11n when associate with AP */ + t_u8 disable_11n; + /** 802.11n BSS information */ + /** HT Capabilities IE */ + IEEEtypes_HTCap_t *pht_cap; + /** HT Capabilities Offset */ + t_u16 ht_cap_offset; + /** HT Information IE */ + IEEEtypes_HTInfo_t *pht_info; + /** HT Information Offset */ + t_u16 ht_info_offset; + /** 20/40 BSS Coexistence IE */ + IEEEtypes_2040BSSCo_t *pbss_co_2040; + /** 20/40 BSS Coexistence Offset */ + t_u16 bss_co_2040_offset; + /** Extended Capabilities IE */ + IEEEtypes_ExtCap_t *pext_cap; + /** Extended Capabilities Offset */ + t_u16 ext_cap_offset; + /** Overlapping BSS Scan Parameters IE */ + IEEEtypes_OverlapBSSScanParam_t *poverlap_bss_scan_param; + /** Overlapping BSS Scan Parameters Offset */ + t_u16 overlap_bss_offset; + +#ifdef STA_SUPPORT + /** Country information set */ + IEEEtypes_CountryInfoFullSet_t country_info; +#endif /* STA_SUPPORT */ + + /** WPA IE */ + IEEEtypes_VendorSpecific_t *pwpa_ie; + /** WPA IE offset in the beacon buffer */ + t_u16 wpa_offset; + /** RSN IE */ + IEEEtypes_Generic_t *prsn_ie; + /** RSN IE offset in the beacon buffer */ + t_u16 rsn_offset; +#ifdef STA_SUPPORT + /** WAPI IE */ + IEEEtypes_Generic_t *pwapi_ie; + /** WAPI IE offset in the beacon buffer */ + t_u16 wapi_offset; +#endif + /* Mobility domain IE */ + IEEEtypes_MobilityDomain_t *pmd_ie; + /** Mobility domain IE offset in the beacon buffer */ + t_u16 md_offset; + + /** Pointer to the returned scan response */ + t_u8 *pbeacon_buf; + /** Length of the stored scan response */ + t_u32 beacon_buf_size; + /** Max allocated size for updated scan response */ + t_u32 beacon_buf_size_max; + +} BSSDescriptor_t, *pBSSDescriptor_t; + +#endif /* !_MLAN_IEEE_H_ */ diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/mlan_ioctl.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/mlan_ioctl.h new file mode 100644 index 000000000000..e5be9ac0776c --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/mlan_ioctl.h @@ -0,0 +1,4403 @@ +/** @file mlan_ioctl.h + * + * @brief This file declares the IOCTL data structures and APIs. + * + * Copyright (C) 2008-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +/****************************************************** +Change log: + 11/07/2008: initial version +******************************************************/ + +#ifndef _MLAN_IOCTL_H_ +#define _MLAN_IOCTL_H_ + +/** Enumeration for IOCTL request ID */ +enum _mlan_ioctl_req_id { + /* Scan Group */ + MLAN_IOCTL_SCAN = 0x00010000, + MLAN_OID_SCAN_NORMAL = 0x00010001, + MLAN_OID_SCAN_SPECIFIC_SSID = 0x00010002, + MLAN_OID_SCAN_USER_CONFIG = 0x00010003, + MLAN_OID_SCAN_CONFIG = 0x00010004, + MLAN_OID_SCAN_GET_CURRENT_BSS = 0x00010005, + MLAN_OID_SCAN_CANCEL = 0x00010006, + MLAN_OID_SCAN_TABLE_FLUSH = 0x0001000A, + MLAN_OID_SCAN_BGSCAN_CONFIG = 0x0001000B, + /* BSS Configuration Group */ + MLAN_IOCTL_BSS = 0x00020000, + MLAN_OID_BSS_START = 0x00020001, + MLAN_OID_BSS_STOP = 0x00020002, + MLAN_OID_BSS_MODE = 0x00020003, + MLAN_OID_BSS_CHANNEL = 0x00020004, + MLAN_OID_BSS_CHANNEL_LIST = 0x00020005, + MLAN_OID_BSS_MAC_ADDR = 0x00020006, + MLAN_OID_BSS_MULTICAST_LIST = 0x00020007, + MLAN_OID_BSS_FIND_BSS = 0x00020008, + MLAN_OID_IBSS_BCN_INTERVAL = 0x00020009, + MLAN_OID_IBSS_ATIM_WINDOW = 0x0002000A, + MLAN_OID_IBSS_CHANNEL = 0x0002000B, +#ifdef UAP_SUPPORT + MLAN_OID_UAP_BSS_CONFIG = 0x0002000C, + MLAN_OID_UAP_DEAUTH_STA = 0x0002000D, + MLAN_OID_UAP_BSS_RESET = 0x0002000E, +#endif +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) + MLAN_OID_BSS_ROLE = 0x0002000F, +#endif +#ifdef WIFI_DIRECT_SUPPORT + MLAN_OID_WIFI_DIRECT_MODE = 0x00020010, +#endif +#ifdef STA_SUPPORT + MLAN_OID_BSS_LISTEN_INTERVAL = 0x00020011, +#endif + MLAN_OID_BSS_REMOVE = 0x00020014, +#ifdef UAP_SUPPORT + MLAN_OID_UAP_CFG_WMM_PARAM = 0x00020015, +#endif + MLAN_OID_BSS_11D_CHECK_CHANNEL = 0x00020016, +#ifdef UAP_SUPPORT + MLAN_OID_UAP_SCAN_CHANNELS = 0x00020018, + MLAN_OID_UAP_CHANNEL = 0x00020019, + MLAN_OID_UAP_OPER_CTRL = 0x0002001A, +#endif +#ifdef STA_SUPPORT + MLAN_OID_BSS_CHAN_INFO = 0x0002001B, +#endif + + /* Radio Configuration Group */ + MLAN_IOCTL_RADIO_CFG = 0x00030000, + MLAN_OID_RADIO_CTRL = 0x00030001, + MLAN_OID_BAND_CFG = 0x00030002, + MLAN_OID_ANT_CFG = 0x00030003, + MLAN_OID_REMAIN_CHAN_CFG = 0x00030004, + + /* SNMP MIB Group */ + MLAN_IOCTL_SNMP_MIB = 0x00040000, + MLAN_OID_SNMP_MIB_RTS_THRESHOLD = 0x00040001, + MLAN_OID_SNMP_MIB_FRAG_THRESHOLD = 0x00040002, + MLAN_OID_SNMP_MIB_RETRY_COUNT = 0x00040003, + MLAN_OID_SNMP_MIB_DOT11D = 0x00040004, +#if defined(UAP_SUPPORT) + MLAN_OID_SNMP_MIB_DOT11H = 0x00040005, +#endif + MLAN_OID_SNMP_MIB_DTIM_PERIOD = 0x00040006, + MLAN_OID_SNMP_MIB_SIGNALEXT_ENABLE = 0x00040007, + MLAN_OID_SNMP_MIB_CTRL_DEAUTH = 0x00040008, + + /* Status Information Group */ + MLAN_IOCTL_GET_INFO = 0x00050000, + MLAN_OID_GET_STATS = 0x00050001, + MLAN_OID_GET_SIGNAL = 0x00050002, + MLAN_OID_GET_FW_INFO = 0x00050003, + MLAN_OID_GET_VER_EXT = 0x00050004, + MLAN_OID_GET_BSS_INFO = 0x00050005, + MLAN_OID_GET_DEBUG_INFO = 0x00050006, +#ifdef UAP_SUPPORT + MLAN_OID_UAP_STA_LIST = 0x00050007, +#endif + MLAN_OID_GET_SIGNAL_EXT = 0x00050008, + MLAN_OID_GET_UAP_STATS_LOG = 0x0005000A, + /* Security Configuration Group */ + MLAN_IOCTL_SEC_CFG = 0x00060000, + MLAN_OID_SEC_CFG_AUTH_MODE = 0x00060001, + MLAN_OID_SEC_CFG_ENCRYPT_MODE = 0x00060002, + MLAN_OID_SEC_CFG_WPA_ENABLED = 0x00060003, + MLAN_OID_SEC_CFG_ENCRYPT_KEY = 0x00060004, + MLAN_OID_SEC_CFG_PASSPHRASE = 0x00060005, + MLAN_OID_SEC_CFG_EWPA_ENABLED = 0x00060006, + MLAN_OID_SEC_CFG_ESUPP_MODE = 0x00060007, + MLAN_OID_SEC_CFG_WAPI_ENABLED = 0x00060009, + MLAN_OID_SEC_CFG_PORT_CTRL_ENABLED = 0x0006000A, + + /* Rate Group */ + MLAN_IOCTL_RATE = 0x00070000, + MLAN_OID_RATE_CFG = 0x00070001, + MLAN_OID_GET_DATA_RATE = 0x00070002, + MLAN_OID_SUPPORTED_RATES = 0x00070003, + + /* Power Configuration Group */ + MLAN_IOCTL_POWER_CFG = 0x00080000, + MLAN_OID_POWER_CFG = 0x00080001, + MLAN_OID_POWER_CFG_EXT = 0x00080002, + + /* Power Management Configuration Group */ + MLAN_IOCTL_PM_CFG = 0x00090000, + MLAN_OID_PM_CFG_IEEE_PS = 0x00090001, + MLAN_OID_PM_CFG_HS_CFG = 0x00090002, + MLAN_OID_PM_CFG_INACTIVITY_TO = 0x00090003, + MLAN_OID_PM_CFG_DEEP_SLEEP = 0x00090004, + MLAN_OID_PM_CFG_SLEEP_PD = 0x00090005, + MLAN_OID_PM_CFG_PS_CFG = 0x00090006, + MLAN_OID_PM_CFG_FW_WAKEUP_METHOD = 0x00090007, + MLAN_OID_PM_CFG_SLEEP_PARAMS = 0x00090008, +#ifdef UAP_SUPPORT + MLAN_OID_PM_CFG_PS_MODE = 0x00090009, +#endif /* UAP_SUPPORT */ + MLAN_OID_PM_INFO = 0x0009000A, + MLAN_OID_PM_HS_WAKEUP_REASON = 0x0009000B, + MLAN_OID_PM_MGMT_FILTER = 0x0009000C, + MLAN_OID_PM_CFG_BCN_TIMEOUT = 0x0009000D, + + /* WMM Configuration Group */ + MLAN_IOCTL_WMM_CFG = 0x000A0000, + MLAN_OID_WMM_CFG_ENABLE = 0x000A0001, + MLAN_OID_WMM_CFG_QOS = 0x000A0002, + MLAN_OID_WMM_CFG_ADDTS = 0x000A0003, + MLAN_OID_WMM_CFG_DELTS = 0x000A0004, + MLAN_OID_WMM_CFG_QUEUE_CONFIG = 0x000A0005, + MLAN_OID_WMM_CFG_QUEUE_STATS = 0x000A0006, + MLAN_OID_WMM_CFG_QUEUE_STATUS = 0x000A0007, + MLAN_OID_WMM_CFG_TS_STATUS = 0x000A0008, + + /* WPS Configuration Group */ + MLAN_IOCTL_WPS_CFG = 0x000B0000, + MLAN_OID_WPS_CFG_SESSION = 0x000B0001, + + /* 802.11n Configuration Group */ + MLAN_IOCTL_11N_CFG = 0x000C0000, + MLAN_OID_11N_CFG_TX = 0x000C0001, + MLAN_OID_11N_HTCAP_CFG = 0x000C0002, + MLAN_OID_11N_CFG_ADDBA_REJECT = 0x000C0003, + MLAN_OID_11N_CFG_AGGR_PRIO_TBL = 0x000C0004, + MLAN_OID_11N_CFG_ADDBA_PARAM = 0x000C0005, + MLAN_OID_11N_CFG_MAX_TX_BUF_SIZE = 0x000C0006, + MLAN_OID_11N_CFG_AMSDU_AGGR_CTRL = 0x000C0007, + MLAN_OID_11N_CFG_SUPPORTED_MCS_SET = 0x000C0008, + MLAN_OID_11N_CFG_TX_BF_CAP = 0x000C0009, + MLAN_OID_11N_CFG_TX_BF_CFG = 0x000C000A, + MLAN_OID_11N_CFG_DELBA = 0x000C000C, + MLAN_OID_11N_CFG_REJECT_ADDBA_REQ = 0x000C000D, + MLAN_OID_11N_CFG_COEX_RX_WINSIZE = 0x000C000E, + MLAN_OID_11N_CFG_TX_AGGR_CTRL = 0x000C000F, + MLAN_OID_11N_CFG_IBSS_AMPDU_PARAM = 0x000C0010, + + /* 802.11d Configuration Group */ + MLAN_IOCTL_11D_CFG = 0x000D0000, +#ifdef STA_SUPPORT + MLAN_OID_11D_CFG_ENABLE = 0x000D0001, + MLAN_OID_11D_CLR_CHAN_TABLE = 0x000D0002, +#endif /* STA_SUPPORT */ + MLAN_OID_11D_DOMAIN_INFO = 0x000D0003, + + /* Register Memory Access Group */ + MLAN_IOCTL_REG_MEM = 0x000E0000, + MLAN_OID_REG_RW = 0x000E0001, + MLAN_OID_EEPROM_RD = 0x000E0002, + MLAN_OID_MEM_RW = 0x000E0003, + + /* Multi-Radio Configuration Group */ + MLAN_IOCTL_MFR_CFG = 0x00100000, + + /* 802.11h Configuration Group */ + MLAN_IOCTL_11H_CFG = 0x00110000, + MLAN_OID_11H_CHANNEL_CHECK = 0x00110001, + MLAN_OID_11H_LOCAL_POWER_CONSTRAINT = 0x00110002, +#if defined(DFS_TESTING_SUPPORT) + MLAN_OID_11H_DFS_TESTING = 0x00110003, +#endif + MLAN_OID_11H_CHAN_REPORT_REQUEST = 0x00110004, + MLAN_OID_11H_CHAN_SWITCH_COUNT = 0x00110005, +#ifdef DFS_TESTING_SUPPORT + MLAN_OID_11H_CHAN_NOP_INFO = 0x00110006, +#endif + + MLAN_IOCTL_11K_CFG = 0x00130000, + MLAN_OID_11K_CFG_ENABLE = 0x00130001, + MLAN_OID_11K_GET_NLIST = 0x00130002, + + /* Miscellaneous Configuration Group */ + MLAN_IOCTL_MISC_CFG = 0x00200000, + MLAN_OID_MISC_GEN_IE = 0x00200001, + MLAN_OID_MISC_REGION = 0x00200002, + MLAN_OID_MISC_WARM_RESET = 0x00200003, +#if defined(SDIO_MULTI_PORT_TX_AGGR) || defined(SDIO_MULTI_PORT_RX_AGGR) + MLAN_OID_MISC_SDIO_MPA_CTRL = 0x00200006, +#endif + MLAN_OID_MISC_HOST_CMD = 0x00200007, + MLAN_OID_MISC_SYS_CLOCK = 0x00200009, + MLAN_OID_MISC_SOFT_RESET = 0x0020000A, + MLAN_OID_MISC_WWS = 0x0020000B, + MLAN_OID_MISC_ASSOC_RSP = 0x0020000C, + MLAN_OID_MISC_INIT_SHUTDOWN = 0x0020000D, + MLAN_OID_MISC_COALESCING_STATUS = 0x0020000E, + MLAN_OID_MISC_CUSTOM_IE = 0x0020000F, + MLAN_OID_MISC_TDLS_CONFIG = 0x00200010, + MLAN_OID_MISC_NET_MONITOR = 0x00200011, + MLAN_OID_MISC_TX_DATAPAUSE = 0x00200012, + MLAN_OID_MISC_IP_ADDR = 0x00200013, + MLAN_OID_MISC_MAC_CONTROL = 0x00200014, + MLAN_OID_MISC_MEF_CFG = 0x00200015, + MLAN_OID_MISC_CFP_CODE = 0x00200016, + MLAN_OID_MISC_COUNTRY_CODE = 0x00200017, + MLAN_OID_MISC_THERMAL = 0x00200018, + MLAN_OID_MISC_RX_MGMT_IND = 0x00200019, + MLAN_OID_MISC_SUBSCRIBE_EVENT = 0x0020001A, +#ifdef DEBUG_LEVEL1 + MLAN_OID_MISC_DRVDBG = 0x0020001B, +#endif + MLAN_OID_MISC_OTP_USER_DATA = 0x0020001D, + MLAN_OID_MISC_TXCONTROL = 0x00200020, +#ifdef STA_SUPPORT + MLAN_OID_MISC_EXT_CAP_CFG = 0x00200021, +#endif +#if defined(STA_SUPPORT) + MLAN_OID_MISC_PMFCFG = 0x00200022, +#endif + MLAN_OID_MISC_MULTI_CHAN_CFG = 0x00200023, + MLAN_OID_MISC_MULTI_CHAN_POLICY = 0x00200024, +#ifdef WIFI_DIRECT_SUPPORT + MLAN_OID_MISC_WIFI_DIRECT_CONFIG = 0x00200025, +#endif + MLAN_OID_MISC_TDLS_OPER = 0x00200026, + MLAN_OID_MISC_GET_TDLS_IES = 0x00200027, + MLAN_OID_MISC_DFS_REAPTER_MODE = 0x0020002B, +#ifdef RX_PACKET_COALESCE + MLAN_OID_MISC_RX_PACKET_COALESCE = 0x0020002C, +#endif + MLAN_OID_MISC_TDLS_CS_CHANNEL = 0x0020002D, + MLAN_OID_MISC_COALESCE_CFG = 0x0020002E, + MLAN_OID_MISC_TDLS_IDLE_TIME = 0x0020002F, + MLAN_OID_MISC_GET_SENSOR_TEMP = 0x00200030, + MLAN_OID_MISC_GTK_REKEY_OFFLOAD = 0x00200037, + MLAN_OID_MISC_OPER_CLASS = 0x00200038, + MLAN_OID_MISC_PMIC_CFG = 0x00200039, + MLAN_OID_MISC_IND_RST_CFG = 0x00200040, + MLAN_OID_MISC_ROAM_OFFLOAD = 0x00200042, + MLAN_OID_MISC_ROAM_OFFLOAD_APLIST = 0x00200043, + MLAN_OID_MISC_GET_TSF = 0x00200045, + MLAN_OID_MISC_GET_CHAN_REGION_CFG = 0x00200046, + MLAN_OID_MISC_OPER_CLASS_CHECK = 0x00200049, + MLAN_OID_MISC_DRCS_CFG = 0x00200050, + + MLAN_OID_MISC_CWMODE_CTRL = 0x00200051, + MLAN_OID_MISC_FW_DUMP_EVENT = 0x00200054, + MLAN_OID_MISC_PER_PKT_CFG = 0x00200055, + MLAN_OID_MISC_ROBUSTCOEX = 0x00200056, + MLAN_OID_MISC_GET_CORRELATED_TIME = 0x00200058, + MLAN_OID_MISC_CFP_INFO = 0x00200060, + MLAN_OID_MISC_BOOT_SLEEP = 0x00200061, +}; + +/** Sub command size */ +#define MLAN_SUB_COMMAND_SIZE 4 + +/** Enumeration for the action of IOCTL request */ +enum _mlan_act_ioctl { + MLAN_ACT_SET = 1, + MLAN_ACT_GET, + MLAN_ACT_CANCEL, + MLAN_ACT_CLEAR, + MLAN_ACT_RESET +}; + +/** Enumeration for generic enable/disable */ +enum _mlan_act_generic { + MLAN_ACT_DISABLE = 0, + MLAN_ACT_ENABLE = 1 +}; + +/** Enumeration for scan mode */ +enum _mlan_scan_mode { + MLAN_SCAN_MODE_UNCHANGED = 0, + MLAN_SCAN_MODE_BSS, + MLAN_SCAN_MODE_IBSS, + MLAN_SCAN_MODE_ANY +}; + +/** Enumeration for scan type */ +enum _mlan_scan_type { + MLAN_SCAN_TYPE_UNCHANGED = 0, + MLAN_SCAN_TYPE_ACTIVE, + MLAN_SCAN_TYPE_PASSIVE +}; + +/** Max number of supported rates */ +#define MLAN_SUPPORTED_RATES 32 + +/** RSSI scan */ +#define SCAN_RSSI(RSSI) (0x100 - ((t_u8)(RSSI))) + +/** Max passive scan time for each channel in milliseconds */ +#define MRVDRV_MAX_PASSIVE_SCAN_CHAN_TIME 2000 + +/** Max active scan time for each channel in milliseconds */ +#define MRVDRV_MAX_ACTIVE_SCAN_CHAN_TIME 500 + +/** Maximum number of probes to send on each channel */ +#define MAX_PROBES 4 + +/** Default number of probes to send on each channel */ +#define DEFAULT_PROBES 4 + +/** + * @brief Sub-structure passed in wlan_ioctl_get_scan_table_entry for each BSS + * + * Fixed field information returned for the scan response in the IOCTL + * response. + */ +typedef struct _wlan_get_scan_table_fixed { + /** BSSID of this network */ + t_u8 bssid[MLAN_MAC_ADDR_LENGTH]; + /** Channel this beacon/probe response was detected */ + t_u8 channel; + /** RSSI for the received packet */ + t_u8 rssi; + /** TSF value in microseconds from the firmware at packet reception */ + t_u64 network_tsf; +} wlan_get_scan_table_fixed; + +/** mlan_802_11_ssid data structure */ +typedef struct _mlan_802_11_ssid { + /** SSID Length */ + t_u32 ssid_len; + /** SSID information field */ + t_u8 ssid[MLAN_MAX_SSID_LENGTH]; +} mlan_802_11_ssid, *pmlan_802_11_ssid; + +/** tx status event structure */ +typedef MLAN_PACK_START struct _tx_status_event { + /** packet type */ + t_u8 packet_type; + /** tx_token_id */ + t_u8 tx_token_id; + /** 0--success, 1--fail, 2--watchdogtimeout */ + t_u8 status; + /** t1 time stamp */ + t_u64 t1_tstamp; + /** t4 time stamp */ + t_u64 t4_tstamp; + /** t1 error */ + t_u64 t1_error; + /** t4 error */ + t_u64 t4_error; + /** egress time */ + t_u64 egress_time; +} MLAN_PACK_END tx_status_event; + +/** + * Sructure to retrieve the scan table + */ +typedef struct { + /** + * - Zero based scan entry to start retrieval in command request + * - Number of scans entries returned in command response + */ + t_u32 scan_number; + /** + * Buffer marker for multiple wlan_ioctl_get_scan_table_entry structures. + * Each struct is padded to the nearest 32 bit boundary. + */ + t_u8 scan_table_entry_buf[1]; +} wlan_ioctl_get_scan_table_info; + +/** + * Structure passed in the wlan_ioctl_get_scan_table_info for each + * BSS returned in the WLAN_GET_SCAN_RESP IOCTL + */ +typedef struct _wlan_ioctl_get_scan_table_entry { + /** + * Fixed field length included in the response. + * + * Length value is included so future fixed fields can be added to the + * response without breaking backwards compatibility. Use the length + * to find the offset for the bssInfoLength field, not a sizeof() calc. + */ + t_u32 fixed_field_length; + + /** + * Length of the BSS Information (probe resp or beacon) that + * follows after the fixed_field_length + */ + t_u32 bss_info_length; + + /** + * Always present, fixed length data fields for the BSS + */ + wlan_get_scan_table_fixed fixed_fields; + + /* + * Probe response or beacon scanned for the BSS. + * + * Field layout: + * - TSF 8 octets + * - Beacon Interval 2 octets + * - Capability Info 2 octets + * + * - IEEE Infomation Elements; variable number & length per 802.11 spec + */ + /* t_u8 bss_info_buffer[0]; */ +} wlan_ioctl_get_scan_table_entry; + +/** Type definition of mlan_scan_time_params */ +typedef struct _mlan_scan_time_params { + /** Scan channel time for specific scan in milliseconds */ + t_u32 specific_scan_time; + /** Scan channel time for active scan in milliseconds */ + t_u32 active_scan_time; + /** Scan channel time for passive scan in milliseconds */ + t_u32 passive_scan_time; +} mlan_scan_time_params, *pmlan_scan_time_params; + +/** Type definition of mlan_user_scan */ +typedef struct _mlan_user_scan { + /** Length of scan_cfg_buf */ + t_u32 scan_cfg_len; + /** Buffer of scan config */ + t_u8 scan_cfg_buf[1]; +} mlan_user_scan, *pmlan_user_scan; + +/** Type definition of mlan_scan_req */ +typedef struct _mlan_scan_req { + /** BSS mode for scanning */ + t_u32 scan_mode; + /** Scan type */ + t_u32 scan_type; + /** SSID */ + mlan_802_11_ssid scan_ssid; + /** Scan time parameters */ + mlan_scan_time_params scan_time; + /** Scan config parameters in user scan */ + mlan_user_scan user_scan; +} mlan_scan_req, *pmlan_scan_req; + +/** Type defnition of mlan_scan_resp */ +typedef struct _mlan_scan_resp { + /** Number of scan result */ + t_u32 num_in_scan_table; + /** Scan table */ + t_u8 *pscan_table; + /* Age in seconds */ + t_u32 age_in_secs; + /** channel statstics */ + t_u8 *pchan_stats; + /** Number of records in the chan_stats */ + t_u32 num_in_chan_stats; +} mlan_scan_resp, *pmlan_scan_resp; + +#define EXT_SCAN_TYPE_ENH 2 +/** Type definition of mlan_scan_cfg */ +typedef struct _mlan_scan_cfg { + /** Scan type */ + t_u32 scan_type; + /** BSS mode for scanning */ + t_u32 scan_mode; + /** Scan probe */ + t_u32 scan_probe; + /** Scan time parameters */ + mlan_scan_time_params scan_time; + /** Ext_scan: 0 disable, 1: enable, 2: enhance scan*/ + t_u32 ext_scan; +} mlan_scan_cfg, *pmlan_scan_cfg; + +/** Type defnition of mlan_ds_scan for MLAN_IOCTL_SCAN */ +typedef struct _mlan_ds_scan { + /** Sub-command */ + t_u32 sub_command; + /** Scan request/response */ + union { + /** Scan request */ + mlan_scan_req scan_req; + /** Scan response */ + mlan_scan_resp scan_resp; + /** Scan config parameters in user scan */ + mlan_user_scan user_scan; + /** Scan config parameters */ + mlan_scan_cfg scan_cfg; + } param; +} mlan_ds_scan, *pmlan_ds_scan; + +/*-----------------------------------------------------------------*/ +/** BSS Configuration Group */ +/*-----------------------------------------------------------------*/ +/** Enumeration for BSS mode */ +enum _mlan_bss_mode { + MLAN_BSS_MODE_INFRA = 1, + MLAN_BSS_MODE_IBSS, + MLAN_BSS_MODE_AUTO +}; + +/** Maximum key length */ +#define MLAN_MAX_KEY_LENGTH 32 +/** Maximum PMK R0 NAME key length */ +#define MLAN_MAX_PMKR0_NAME_LENGTH 16 + +/** Maximum atim window in milliseconds */ +#define MLAN_MAX_ATIM_WINDOW 50 + +/** Minimum beacon interval */ +#define MLAN_MIN_BEACON_INTERVAL 20 +/** Maximum beacon interval */ +#define MLAN_MAX_BEACON_INTERVAL 1000 +/** Default beacon interval */ +#define MLAN_BEACON_INTERVAL 100 + +/** Receive all packets */ +#define MLAN_PROMISC_MODE 1 +/** Receive multicast packets in multicast list */ +#define MLAN_MULTICAST_MODE 2 +/** Receive all multicast packets */ +#define MLAN_ALL_MULTI_MODE 4 + +/** Maximum size of multicast list */ +#define MLAN_MAX_MULTICAST_LIST_SIZE 32 + +/** mlan_multicast_list data structure for MLAN_OID_BSS_MULTICAST_LIST */ +typedef struct _mlan_multicast_list { + /** Multicast mode */ + t_u32 mode; + /** Number of multicast addresses in the list */ + t_u32 num_multicast_addr; + /** Multicast address list */ + mlan_802_11_mac_addr mac_list[MLAN_MAX_MULTICAST_LIST_SIZE]; +} mlan_multicast_list, *pmlan_multicast_list; + +/** Max channel */ +#define MLAN_MAX_CHANNEL 165 +/** Maximum number of channels in table */ +#define MLAN_MAX_CHANNEL_NUM 128 + +/** Channel/frequence for MLAN_OID_BSS_CHANNEL */ +typedef struct _chan_freq { + /** Channel Number */ + t_u32 channel; + /** Frequency of this Channel */ + t_u32 freq; +} chan_freq; + +/** mlan_chan_list data structure for MLAN_OID_BSS_CHANNEL_LIST */ +typedef struct _mlan_chan_list { + /** Number of channel */ + t_u32 num_of_chan; + /** Channel-Frequency table */ + chan_freq cf[MLAN_MAX_CHANNEL_NUM]; +} mlan_chan_list; + +/* This channel is disabled.*/ +#define CHAN_FLAGS_DISABLED MBIT(0) +/* do not initiate radiation, this includes sending probe requests or beaconing */ +#define CHAN_FLAGS_NO_IR MBIT(1) +/* Radar detection is required on this channel */ +#define CHAN_FLAGS_RADAR MBIT(3) +/* extension channel above this channel is not permitted */ +#define CHAN_FLAGS_NO_HT40PLUS MBIT(4) +/* extension channel below this channel is not permitted */ +#define CHAN_FLAGS_NO_HT40MINUS MBIT(5) +/* OFDM is not allowed on this channel */ +#define CHAN_FLAGS_NO_OFDM MBIT(6) +/** 80Mhz can not used on this channel */ +#define CHAN_FLAGS_NO_80MHZ MBIT(7) +/** 180Mhz can not used on this channel */ +#define CHAN_FLAGS_NO_160MHZ MBIT(8) +/* Only indoor use is permitted on this channel */ +#define CHAN_FLAGS_INDOOR_ONLY MBIT(9) +/* IR operation is allowed on this channel if it's + * connected concurrently to a BSS on the same channel on + * the 2 GHz band or to a channel in the same UNII band (on the 5 GHz + * band), and IEEE80211_CHAN_RADAR is not set */ +#define CHAN_FLAGS_IR_CONCURRENT MBIT(10) +/* 20 MHz operation is not allowed on this channel */ +#define CHAN_FLAGS_20MHZ MBIT(11) +/* 10 MHz operation is not allowed on this channel */ +#define CHAN_FLAGS_NO_10MHZ MBIT(12) + +/** mlan_ssid_bssid data structure for + * MLAN_OID_BSS_START and MLAN_OID_BSS_FIND_BSS + */ +typedef struct _mlan_ssid_bssid { + /** SSID */ + mlan_802_11_ssid ssid; + /** BSSID */ + mlan_802_11_mac_addr bssid; + /** index in BSSID list, start from 1 */ + t_u32 idx; + /** Receive signal strength in dBm */ + t_s32 rssi; + /**channel*/ + t_u16 channel; + /**mobility domain value*/ + t_u16 ft_md; + /**ft capability*/ + t_u8 ft_cap; + /**band*/ + t_u16 bss_band; + t_u32 channel_flags; +} mlan_ssid_bssid; + +/** Data structure of WMM ECW */ +typedef struct _wmm_ecw_t { +#ifdef BIG_ENDIAN_SUPPORT + /** Maximum Ecw */ + t_u8 ecw_max:4; + /** Minimum Ecw */ + t_u8 ecw_min:4; +#else + /** Minimum Ecw */ + t_u8 ecw_min:4; + /** Maximum Ecw */ + t_u8 ecw_max:4; +#endif /* BIG_ENDIAN_SUPPORT */ +} wmm_ecw_t, *pwmm_ecw_t; + +/** Data structure of WMM Aci/Aifsn */ +typedef struct _wmm_aci_aifsn_t { +#ifdef BIG_ENDIAN_SUPPORT + /** Reserved */ + t_u8 reserved:1; + /** Aci */ + t_u8 aci:2; + /** Acm */ + t_u8 acm:1; + /** Aifsn */ + t_u8 aifsn:4; +#else + /** Aifsn */ + t_u8 aifsn:4; + /** Acm */ + t_u8 acm:1; + /** Aci */ + t_u8 aci:2; + /** Reserved */ + t_u8 reserved:1; +#endif /* BIG_ENDIAN_SUPPORT */ +} wmm_aci_aifsn_t, *pwmm_aci_aifsn_t; + +/** Data structure of WMM AC parameters */ +typedef struct _wmm_ac_parameters_t { + wmm_aci_aifsn_t aci_aifsn; /**< AciAifSn */ + wmm_ecw_t ecw; /**< Ecw */ + t_u16 tx_op_limit; /**< Tx op limit */ +} wmm_ac_parameters_t, *pwmm_ac_parameters_t; + +/** mlan_deauth_param */ +typedef struct _mlan_deauth_param { + /** STA mac addr */ + t_u8 mac_addr[MLAN_MAC_ADDR_LENGTH]; + /** deauth reason */ + t_u16 reason_code; +} mlan_deauth_param; + +#ifdef UAP_SUPPORT +/** Maximum packet forward control value */ +#define MAX_PKT_FWD_CTRL 15 +/** Maximum BEACON period */ +#define MAX_BEACON_PERIOD 4000 +/** Minimum BEACON period */ +#define MIN_BEACON_PERIOD 50 +/** Maximum DTIM period */ +#define MAX_DTIM_PERIOD 100 +/** Minimum DTIM period */ +#define MIN_DTIM_PERIOD 1 +/** Maximum TX Power Limit */ +#define MAX_TX_POWER 20 +/** Minimum TX Power Limit */ +#define MIN_TX_POWER 0 +/** MAX station count */ +#define MAX_STA_COUNT 10 +/** Maximum RTS threshold */ +#define MAX_RTS_THRESHOLD 2347 +/** Maximum fragmentation threshold */ +#define MAX_FRAG_THRESHOLD 2346 +/** Minimum fragmentation threshold */ +#define MIN_FRAG_THRESHOLD 256 +/** data rate 54 M */ +#define DATA_RATE_54M 108 +/** Maximum value of bcast_ssid_ctl */ +#define MAX_BCAST_SSID_CTL 2 +/** antenna A */ +#define ANTENNA_MODE_A 0 +/** antenna B */ +#define ANTENNA_MODE_B 1 +/** transmit antenna */ +#define TX_ANTENNA 1 +/** receive antenna */ +#define RX_ANTENNA 0 +/** Maximum stage out time */ +#define MAX_STAGE_OUT_TIME 864000 +/** Minimum stage out time */ +#define MIN_STAGE_OUT_TIME 300 +/** Maximum Retry Limit */ +#define MAX_RETRY_LIMIT 14 + +/** Maximum group key timer in seconds */ +#define MAX_GRP_TIMER 86400 + +/** Maximum value of 4 byte configuration */ +#define MAX_VALID_DWORD 0x7FFFFFFF /* (1 << 31) - 1 */ + +/** default UAP BAND 2.4G */ +#define DEFAULT_UAP_BAND 0 +/** default UAP channel 6 */ +#define DEFAULT_UAP_CHANNEL 6 + +/** Maximum data rates */ +#define MAX_DATA_RATES 14 + +/** auto data rate */ +#define DATA_RATE_AUTO 0 + +/**filter mode: disable */ +#define MAC_FILTER_MODE_DISABLE 0 +/**filter mode: block mac address */ +#define MAC_FILTER_MODE_ALLOW_MAC 1 +/**filter mode: block mac address */ +#define MAC_FILTER_MODE_BLOCK_MAC 2 +/** Maximum mac filter num */ +#define MAX_MAC_FILTER_NUM 16 + +/* Bitmap for protocol to use */ +/** No security */ +#define PROTOCOL_NO_SECURITY 0x01 +/** Static WEP */ +#define PROTOCOL_STATIC_WEP 0x02 +/** WPA */ +#define PROTOCOL_WPA 0x08 +/** WPA2 */ +#define PROTOCOL_WPA2 0x20 +/** WP2 Mixed */ +#define PROTOCOL_WPA2_MIXED 0x28 +/** EAP */ +#define PROTOCOL_EAP 0x40 +/** WAPI */ +#define PROTOCOL_WAPI 0x80 + +/** Key_mgmt_psk */ +#define KEY_MGMT_NONE 0x04 +/** Key_mgmt_none */ +#define KEY_MGMT_PSK 0x02 +/** Key_mgmt_eap */ +#define KEY_MGMT_EAP 0x01 +/** Key_mgmt_psk_sha256 */ +#define KEY_MGMT_PSK_SHA256 0x100 + +/** TKIP */ +#define CIPHER_TKIP 0x04 +/** AES CCMP */ +#define CIPHER_AES_CCMP 0x08 + +/** Valid cipher bitmap */ +#define VALID_CIPHER_BITMAP 0x0c + +/** Packet forwarding to be done by FW or host */ +#define PKT_FWD_FW_BIT 0x01 +/** Intra-BSS broadcast packet forwarding allow bit */ +#define PKT_FWD_INTRA_BCAST 0x02 +/** Intra-BSS unicast packet forwarding allow bit */ +#define PKT_FWD_INTRA_UCAST 0x04 +/** Inter-BSS unicast packet forwarding allow bit */ +#define PKT_FWD_INTER_UCAST 0x08 +/** Intra-BSS unicast packet */ +#define PKT_INTRA_UCAST 0x01 +/** Inter-BSS unicast packet */ +#define PKT_INTER_UCAST 0x02 +/** Enable Host PKT forwarding */ +#define PKT_FWD_ENABLE_BIT 0x01 + +/** Channel List Entry */ +typedef struct _channel_list { + /** Channel Number */ + t_u8 chan_number; + /** Band Config */ + Band_Config_t bandcfg; +} scan_chan_list; + +/** mac_filter data structure */ +typedef struct _mac_filter { + /** mac filter mode */ + t_u16 filter_mode; + /** mac adress count */ + t_u16 mac_count; + /** mac address list */ + mlan_802_11_mac_addr mac_list[MAX_MAC_FILTER_NUM]; +} mac_filter; + +/** wpa parameter */ +typedef struct _wpa_param { + /** Pairwise cipher WPA */ + t_u8 pairwise_cipher_wpa; + /** Pairwise cipher WPA2 */ + t_u8 pairwise_cipher_wpa2; + /** group cipher */ + t_u8 group_cipher; + /** RSN replay protection */ + t_u8 rsn_protection; + /** passphrase length */ + t_u32 length; + /** passphrase */ + t_u8 passphrase[64]; + /**group key rekey time in seconds */ + t_u32 gk_rekey_time; +} wpa_param; + +/** wep key */ +typedef struct _wep_key { + /** key index 0-3 */ + t_u8 key_index; + /** is default */ + t_u8 is_default; + /** length */ + t_u16 length; + /** key data */ + t_u8 key[26]; +} wep_key; + +/** wep param */ +typedef struct _wep_param { + /** key 0 */ + wep_key key0; + /** key 1 */ + wep_key key1; + /** key 2 */ + wep_key key2; + /** key 3 */ + wep_key key3; +} wep_param; + +/** Data structure of WMM QoS information */ +typedef struct _wmm_qos_info_t { +#ifdef BIG_ENDIAN_SUPPORT + /** QoS UAPSD */ + t_u8 qos_uapsd:1; + /** Reserved */ + t_u8 reserved:3; + /** Parameter set count */ + t_u8 para_set_count:4; +#else + /** Parameter set count */ + t_u8 para_set_count:4; + /** Reserved */ + t_u8 reserved:3; + /** QoS UAPSD */ + t_u8 qos_uapsd:1; +#endif /* BIG_ENDIAN_SUPPORT */ +} wmm_qos_info_t, *pwmm_qos_info_t; + +/** Data structure of WMM parameter IE */ +typedef struct _wmm_parameter_t { + /** OuiType: 00:50:f2:02 */ + t_u8 ouitype[4]; + /** Oui subtype: 01 */ + t_u8 ouisubtype; + /** version: 01 */ + t_u8 version; + /** QoS information */ + t_u8 qos_info; + /** Reserved */ + t_u8 reserved; + /** AC Parameters Record WMM_AC_BE, WMM_AC_BK, WMM_AC_VI, WMM_AC_VO */ + wmm_ac_parameters_t ac_params[MAX_AC_QUEUES]; +} wmm_parameter_t, *pwmm_parameter_t; + +/** 5G band */ +#define BAND_CONFIG_5G 0x01 +/** 2.4 G band */ +#define BAND_CONFIG_2G 0x00 +/** MAX BG channel */ +#define MAX_BG_CHANNEL 14 +/** mlan_bss_param + * Note: For each entry you must enter an invalid value + * in the MOAL function woal_set_sys_config_invalid_data(). + * Otherwise for a valid data an unwanted TLV will be + * added to that command. + */ +typedef struct _mlan_uap_bss_param { + /** AP mac addr */ + mlan_802_11_mac_addr mac_addr; + /** SSID */ + mlan_802_11_ssid ssid; + /** Broadcast ssid control */ + t_u8 bcast_ssid_ctl; + /** Radio control: on/off */ + t_u8 radio_ctl; + /** dtim period */ + t_u8 dtim_period; + /** beacon period */ + t_u16 beacon_period; + /** rates */ + t_u8 rates[MAX_DATA_RATES]; + /** Tx data rate */ + t_u16 tx_data_rate; + /** Tx beacon rate */ + t_u16 tx_beacon_rate; + /** multicast/broadcast data rate */ + t_u16 mcbc_data_rate; + /** Tx power level in dBm */ + t_u8 tx_power_level; + /** Tx antenna */ + t_u8 tx_antenna; + /** Rx antenna */ + t_u8 rx_antenna; + /** packet forward control */ + t_u8 pkt_forward_ctl; + /** max station count */ + t_u16 max_sta_count; + /** mac filter */ + mac_filter filter; + /** station ageout timer in unit of 100ms */ + t_u32 sta_ageout_timer; + /** PS station ageout timer in unit of 100ms */ + t_u32 ps_sta_ageout_timer; + /** RTS threshold */ + t_u16 rts_threshold; + /** fragmentation threshold */ + t_u16 frag_threshold; + /** retry_limit */ + t_u16 retry_limit; + /** pairwise update timeout in milliseconds */ + t_u32 pairwise_update_timeout; + /** pairwise handshake retries */ + t_u32 pwk_retries; + /** groupwise update timeout in milliseconds */ + t_u32 groupwise_update_timeout; + /** groupwise handshake retries */ + t_u32 gwk_retries; + /** preamble type */ + t_u8 preamble_type; + /** band cfg */ + Band_Config_t bandcfg; + /** channel */ + t_u8 channel; + /** auth mode */ + t_u16 auth_mode; + /** encryption protocol */ + t_u16 protocol; + /** key managment type */ + t_u16 key_mgmt; + /** wep param */ + wep_param wep_cfg; + /** wpa param */ + wpa_param wpa_cfg; + /** Mgmt IE passthru mask */ + t_u32 mgmt_ie_passthru_mask; + /* + * 11n HT Cap HTCap_t ht_cap + */ + /** HT Capabilities Info field */ + t_u16 ht_cap_info; + /** A-MPDU Parameters field */ + t_u8 ampdu_param; + /** Supported MCS Set field */ + t_u8 supported_mcs_set[16]; + /** HT Extended Capabilities field */ + t_u16 ht_ext_cap; + /** Transmit Beamforming Capabilities field */ + t_u32 tx_bf_cap; + /** Antenna Selection Capability field */ + t_u8 asel; + /** Enable 2040 Coex */ + t_u8 enable_2040coex; + /** key management operation */ + t_u16 key_mgmt_operation; + /** BSS status */ + t_u16 bss_status; +#ifdef WIFI_DIRECT_SUPPORT + /* pre shared key */ + t_u8 psk[MLAN_MAX_KEY_LENGTH]; +#endif /* WIFI_DIRECT_SUPPORT */ + /** Number of channels in scan_channel_list */ + t_u32 num_of_chan; + /** scan channel list in ACS mode */ + scan_chan_list chan_list[MLAN_MAX_CHANNEL]; + /** Wmm parameters */ + wmm_parameter_t wmm_para; + +} mlan_uap_bss_param; + +/** mlan_uap_scan_channels */ +typedef struct _mlan_uap_scan_channels { + /** flag for remove nop channel*/ + t_u8 remove_nop_channel; + /** num of removed channel */ + t_u8 num_remvoed_channel; + /** Number of channels in scan_channel_list */ + t_u32 num_of_chan; + /** scan channel list in ACS mode */ + scan_chan_list chan_list[MLAN_MAX_CHANNEL]; +} mlan_uap_scan_channels; + +/** mlan_uap_oper_ctrl */ +typedef struct _mlan_uap_oper_ctrl { + /** control value + * 0: do nothing, + * 2: uap stops and restarts automaticaly + */ + t_u16 ctrl_value; + /** channel opt + * 1: uap restart on default 2.4G/channel 6 + * 2: uap restart on the band/channel configured by driver previously + * 3: uap restart on the band/channel specified by band_cfg and channel + */ + t_u16 chan_opt; + /** band cfg 0 + * 0: 20Mhz 2: 40 Mhz 3: 80Mhz + */ + t_u8 band_cfg; + /** channel */ + t_u8 channel; +} mlan_uap_oper_ctrl; + +#endif + +#ifdef WIFI_DIRECT_SUPPORT +/** mode: disable wifi direct */ +#define WIFI_DIRECT_MODE_DISABLE 0 +/** mode: listen */ +#define WIFI_DIRECT_MODE_LISTEN 1 +/** mode: GO */ +#define WIFI_DIRECT_MODE_GO 2 +/** mode: client */ +#define WIFI_DIRECT_MODE_CLIENT 3 +/** mode: find */ +#define WIFI_DIRECT_MODE_FIND 4 +/** mode: stop find */ +#define WIFI_DIRECT_MODE_STOP_FIND 5 +#endif + +/** Type definition of mlan_ds_bss for MLAN_IOCTL_BSS */ +typedef struct _mlan_ds_bss { + /** Sub-command */ + t_u32 sub_command; + /** BSS parameter */ + union { + /** SSID-BSSID for MLAN_OID_BSS_START */ + mlan_ssid_bssid ssid_bssid; + /** BSSID for MLAN_OID_BSS_STOP */ + mlan_802_11_mac_addr bssid; + /** BSS mode for MLAN_OID_BSS_MODE */ + t_u32 bss_mode; + /** BSS channel/frequency for MLAN_OID_BSS_CHANNEL */ + chan_freq bss_chan; + /** BSS channel list for MLAN_OID_BSS_CHANNEL_LIST */ + mlan_chan_list chanlist; + /** MAC address for MLAN_OID_BSS_MAC_ADDR */ + mlan_802_11_mac_addr mac_addr; + /** Multicast list for MLAN_OID_BSS_MULTICAST_LIST */ + mlan_multicast_list multicast_list; + /** Beacon interval for MLAN_OID_IBSS_BCN_INTERVAL */ + t_u32 bcn_interval; + /** ATIM window for MLAN_OID_IBSS_ATIM_WINDOW */ + t_u32 atim_window; + /** deauth param for MLAN_OID_BSS_STOP & MLAN_OID_UAP_DEAUTH_STA */ + mlan_deauth_param deauth_param; +#ifdef UAP_SUPPORT + /** host based uap flag for MLAN_OID_BSS_START */ + t_u8 host_based; + /** BSS param for AP mode for MLAN_OID_UAP_BSS_CONFIG */ + mlan_uap_bss_param bss_config; + /** AP Wmm parameters for MLAN_OID_UAP_CFG_WMM_PARAM */ + wmm_parameter_t ap_wmm_para; + /** ap scan channels for MLAN_OID_UAP_SCAN_CHANNELS*/ + mlan_uap_scan_channels ap_scan_channels; + /** ap channel for MLAN_OID_UAP_CHANNEL*/ + chan_band_info ap_channel; + /** ap operation control for MLAN_OID_UAP_OPER_CTRL*/ + mlan_uap_oper_ctrl ap_oper_ctrl; +#endif +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) + /** BSS role for MLAN_OID_BSS_ROLE */ + t_u8 bss_role; +#endif +#ifdef WIFI_DIRECT_SUPPORT + /** wifi direct mode for MLAN_OID_WIFI_DIRECT_MODE */ + t_u16 wfd_mode; +#endif +#ifdef STA_SUPPORT + /** Listen interval for MLAN_OID_BSS_LISTEN_INTERVAL */ + t_u16 listen_interval; + /** STA channel info for MLAN_OID_BSS_CHAN_INFO */ + chan_band_info sta_channel; +#endif + } param; +} mlan_ds_bss, *pmlan_ds_bss; + +/** Type definition of mlan_ds_custom_reg_domain */ +typedef struct _mlan_ds_custom_reg_domain { + t_u8 cfg_len; + t_u8 cfg_buf[0]; +} mlan_ds_custom_reg_domain; +/*-----------------------------------------------------------------*/ +/** Radio Control Group */ +/*-----------------------------------------------------------------*/ +/** Enumeration for band */ +enum _mlan_band_def { + BAND_B = 1, + BAND_G = 2, + BAND_A = 4, + BAND_GN = 8, + BAND_AN = 16, +}; + +/** Channel bandwidth */ +#define CHANNEL_BW_20MHZ 0 +#define CHANNEL_BW_40MHZ_ABOVE 1 +#define CHANNEL_BW_40MHZ_BELOW 3 + +/** RF antenna selection */ +#define RF_ANTENNA_MASK(n) ((1<<(n))-1) +/** RF antenna auto select */ +#define RF_ANTENNA_AUTO 0xFFFF + +/** Type definition of mlan_ds_band_cfg for MLAN_OID_BAND_CFG */ +typedef struct _mlan_ds_band_cfg { + /** Infra band */ + t_u32 config_bands; + /** Ad-hoc start band */ + t_u32 adhoc_start_band; + /** Ad-hoc start channel */ + t_u32 adhoc_channel; + /** Ad-hoc channel bandwidth */ + t_u32 adhoc_chan_bandwidth; + /** fw supported band */ + t_u32 fw_bands; +} mlan_ds_band_cfg; + +/** Type definition of mlan_ds_ant_cfg_1x1 for MLAN_OID_ANT_CFG */ +typedef struct _mlan_ds_ant_cfg_1x1 { + /** Antenna mode */ + t_u32 antenna; + /** Evaluate time */ + t_u16 evaluate_time; + /** Current antenna */ + t_u16 current_antenna; +} mlan_ds_ant_cfg_1x1, *pmlan_ds_ant_cfg_1x1; + +/** Type definition of mlan_ds_remain_chan for MLAN_OID_REMAIN_CHAN_CFG */ +typedef struct _mlan_ds_remain_chan { + /** remove flag */ + t_u16 remove; + /** status */ + t_u8 status; + /** Band cfg */ + Band_Config_t bandcfg; + /** channel */ + t_u8 channel; + /** remain time: Unit ms*/ + t_u32 remain_period; +} mlan_ds_remain_chan, *pmlan_ds_remain_chan; + +/** Type definition of mlan_ds_radio_cfg for MLAN_IOCTL_RADIO_CFG */ +typedef struct _mlan_ds_radio_cfg { + /** Sub-command */ + t_u32 sub_command; + /** Radio control parameter */ + union { + /** Radio on/off for MLAN_OID_RADIO_CTRL */ + t_u32 radio_on_off; + /** Band info for MLAN_OID_BAND_CFG */ + mlan_ds_band_cfg band_cfg; + /** Antenna info for MLAN_OID_ANT_CFG */ + mlan_ds_ant_cfg_1x1 ant_cfg_1x1; + /** remain on channel for MLAN_OID_REMAIN_CHAN_CFG */ + mlan_ds_remain_chan remain_chan; + } param; +} mlan_ds_radio_cfg, *pmlan_ds_radio_cfg; + +enum COALESCE_OPERATION { + RECV_FILTER_MATCH_TYPE_EQ = 0x80, + RECV_FILTER_MATCH_TYPE_NE, +}; + +enum COALESCE_PACKET_TYPE { + PACKET_TYPE_UNICAST = 1, + PACKET_TYPE_MULTICAST = 2, + PACKET_TYPE_BROADCAST = 3 +}; + +#define COALESCE_MAX_RULES 8 +#define COALESCE_MAX_BYTESEQ 4 /* non-adjustable */ +#define COALESCE_MAX_FILTERS 4 +#define MAX_COALESCING_DELAY 100 /* in msecs */ +#define MAX_PATTERN_LEN 20 +#define MAX_OFFSET_LEN 100 + +/** filt field param structure */ +struct filt_field_param { + /** Operation */ + t_u8 operation; + /** Operand len */ + t_u8 operand_len; + /** offset */ + t_u16 offset; + /** Operand byte stream */ + t_u8 operand_byte_stream[COALESCE_MAX_BYTESEQ]; +}; + +/** coalesce rule structure */ +struct coalesce_rule { + /** max coalescing delay */ + t_u16 max_coalescing_delay; + /** number of fields */ + t_u8 num_of_fields; + /** packet type */ + t_u8 pkt_type; + struct filt_field_param params[COALESCE_MAX_FILTERS]; +}; + +/** coalesce configuration structure */ +typedef struct _mlan_ds_coalesce_cfg { + t_u16 num_of_rules; + struct coalesce_rule rule[COALESCE_MAX_RULES]; +} mlan_ds_coalesce_cfg; + +/*-----------------------------------------------------------------*/ +/** SNMP MIB Group */ +/*-----------------------------------------------------------------*/ +/** Type definition of mlan_ds_snmp_mib for MLAN_IOCTL_SNMP_MIB */ +typedef struct _mlan_ds_snmp_mib { + /** Sub-command */ + t_u32 sub_command; + /** SNMP MIB parameter */ + union { + /** RTS threshold for MLAN_OID_SNMP_MIB_RTS_THRESHOLD */ + t_u32 rts_threshold; + /** Fragment threshold for MLAN_OID_SNMP_MIB_FRAG_THRESHOLD */ + t_u32 frag_threshold; + /** Retry count for MLAN_OID_SNMP_MIB_RETRY_COUNT */ + t_u32 retry_count; + /** OID value for MLAN_OID_SNMP_MIB_DOT11D/H */ + t_u32 oid_value; + /** DTIM period for MLAN_OID_SNMP_MIB_DTIM_PERIOD */ + t_u32 dtim_period; + /** Singal_ext Enable for MLAN_OID_SNMP_MIB_SIGNALEXT_ENABLE */ + t_u8 signalext_enable; + /** Control deauth when uap switch channel */ + t_u8 deauthctrl; + } param; +} mlan_ds_snmp_mib, *pmlan_ds_snmp_mib; + +/*-----------------------------------------------------------------*/ +/** Status Information Group */ +/*-----------------------------------------------------------------*/ +/** Enumeration for ad-hoc status */ +enum _mlan_adhoc_status { + ADHOC_IDLE, + ADHOC_STARTED, + ADHOC_JOINED, + ADHOC_COALESCED, ADHOC_STARTING +}; + +/** Get stats org structure */ +typedef struct _mlan_ds_get_stats_org { + /** Statistics counter */ + /** Multicast transmitted frame count */ + t_u32 mcast_tx_frame; + /** Failure count */ + t_u32 failed; + /** Retry count */ + t_u32 retry; + /** Multi entry count */ + t_u32 multi_retry; + /** Duplicate frame count */ + t_u32 frame_dup; + /** RTS success count */ + t_u32 rts_success; + /** RTS failure count */ + t_u32 rts_failure; + /** Ack failure count */ + t_u32 ack_failure; + /** Rx fragmentation count */ + t_u32 rx_frag; + /** Multicast Tx frame count */ + t_u32 mcast_rx_frame; + /** FCS error count */ + t_u32 fcs_error; + /** Tx frame count */ + t_u32 tx_frame; + /** WEP ICV error count */ + t_u32 wep_icv_error[4]; + /** beacon recv count */ + t_u32 bcn_rcv_cnt; + /** beacon miss count */ + t_u32 bcn_miss_cnt; + /** received amsdu count*/ + t_u32 amsdu_rx_cnt; + /** received msdu count in amsdu*/ + t_u32 msdu_in_rx_amsdu_cnt; + /** tx amsdu count*/ + t_u32 amsdu_tx_cnt; + /** tx msdu count in amsdu*/ + t_u32 msdu_in_tx_amsdu_cnt; +} mlan_ds_get_stats_org; + +/** Type definition of mlan_ds_get_stats for MLAN_OID_GET_STATS */ +typedef struct _mlan_ds_get_stats { + /** Statistics counter */ + /** Multicast transmitted frame count */ + t_u32 mcast_tx_frame; + /** Failure count */ + t_u32 failed; + /** Retry count */ + t_u32 retry; + /** Multi entry count */ + t_u32 multi_retry; + /** Duplicate frame count */ + t_u32 frame_dup; + /** RTS success count */ + t_u32 rts_success; + /** RTS failure count */ + t_u32 rts_failure; + /** Ack failure count */ + t_u32 ack_failure; + /** Rx fragmentation count */ + t_u32 rx_frag; + /** Multicast Tx frame count */ + t_u32 mcast_rx_frame; + /** FCS error count */ + t_u32 fcs_error; + /** Tx frame count */ + t_u32 tx_frame; + /** WEP ICV error count */ + t_u32 wep_icv_error[4]; + /** beacon recv count */ + t_u32 bcn_rcv_cnt; + /** beacon miss count */ + t_u32 bcn_miss_cnt; + /** received amsdu count*/ + t_u32 amsdu_rx_cnt; + /** received msdu count in amsdu*/ + t_u32 msdu_in_rx_amsdu_cnt; + /** tx amsdu count*/ + t_u32 amsdu_tx_cnt; + /** tx msdu count in amsdu*/ + t_u32 msdu_in_tx_amsdu_cnt; + + /** Tx frag count */ + t_u32 tx_frag_cnt; + /** Qos Tx frag count */ + t_u32 qos_tx_frag_cnt[8]; + /** Qos failed count */ + t_u32 qos_failed_cnt[8]; + /** Qos retry count */ + t_u32 qos_retry_cnt[8]; + /** Qos multi retry count */ + t_u32 qos_multi_retry_cnt[8]; + /** Qos frame dup count */ + t_u32 qos_frm_dup_cnt[8]; + /** Qos rts success count */ + t_u32 qos_rts_suc_cnt[8]; + /** Qos rts failure count */ + t_u32 qos_rts_failure_cnt[8]; + /** Qos ack failure count */ + t_u32 qos_ack_failure_cnt[8]; + /** Qos Rx frag count */ + t_u32 qos_rx_frag_cnt[8]; + /** Qos Tx frame count */ + t_u32 qos_tx_frm_cnt[8]; + /** Qos discarded frame count */ + t_u32 qos_discarded_frm_cnt[8]; + /** Qos mpdus Rx count */ + t_u32 qos_mpdus_rx_cnt[8]; + /** Qos retry rx count */ + t_u32 qos_retries_rx_cnt[8]; + /** CMAC ICV errors count */ + t_u32 cmacicv_errors; + /** CMAC replays count */ + t_u32 cmac_replays; + /** mgmt CCMP replays count */ + t_u32 mgmt_ccmp_replays; + /** TKIP ICV errors count */ + t_u32 tkipicv_errors; + /** TKIP replays count */ + t_u32 tkip_replays; + /** CCMP decrypt errors count */ + t_u32 ccmp_decrypt_errors; + /** CCMP replays count */ + t_u32 ccmp_replays; + /** Tx amsdu count */ + t_u32 tx_amsdu_cnt; + /** failed amsdu count */ + t_u32 failed_amsdu_cnt; + /** retry amsdu count */ + t_u32 retry_amsdu_cnt; + /** multi-retry amsdu count */ + t_u32 multi_retry_amsdu_cnt; + /** Tx octets in amsdu count */ + t_u64 tx_octets_in_amsdu_cnt; + /** amsdu ack failure count */ + t_u32 amsdu_ack_failure_cnt; + /** Rx amsdu count */ + t_u32 rx_amsdu_cnt; + /** Rx octets in amsdu count */ + t_u64 rx_octets_in_amsdu_cnt; + /** Tx ampdu count */ + t_u32 tx_ampdu_cnt; + /** tx mpdus in ampdu count */ + t_u32 tx_mpdus_in_ampdu_cnt; + /** tx octets in ampdu count */ + t_u64 tx_octets_in_ampdu_cnt; + /** ampdu Rx count */ + t_u32 ampdu_rx_cnt; + /** mpdu in Rx ampdu count */ + t_u32 mpdu_in_rx_ampdu_cnt; + /** Rx octets ampdu count */ + t_u64 rx_octets_in_ampdu_cnt; + /** ampdu delimiter CRC error count */ + t_u32 ampdu_delimiter_crc_error_cnt; +} mlan_ds_get_stats, *pmlan_ds_get_stats; + +/** Type definition of mlan_ds_get_stats for MLAN_OID_GET_STATS */ +typedef struct _mlan_ds_get_correlated_time { + /** System time */ + t_u64 sys_time; + /** FW time */ + t_u64 hw_time; +} mlan_ds_get_correlated_time, *pmlan_ds_get_correlated_time; + +/** Type definition of mlan_ds_uap_stats for MLAN_OID_GET_STATS */ +typedef struct _mlan_ds_uap_stats { + /** tkip mic failures */ + t_u32 tkip_mic_failures; + /** ccmp decrypt errors */ + t_u32 ccmp_decrypt_errors; + /** wep undecryptable count */ + t_u32 wep_undecryptable_count; + /** wep icv error count */ + t_u32 wep_icv_error_count; + /** decrypt failure count */ + t_u32 decrypt_failure_count; + /** dot11 multicast tx count */ + t_u32 mcast_tx_count; + /** dot11 failed count */ + t_u32 failed_count; + /** dot11 retry count */ + t_u32 retry_count; + /** dot11 multi retry count */ + t_u32 multi_retry_count; + /** dot11 frame duplicate count */ + t_u32 frame_dup_count; + /** dot11 rts success count */ + t_u32 rts_success_count; + /** dot11 rts failure count */ + t_u32 rts_failure_count; + /** dot11 ack failure count */ + t_u32 ack_failure_count; + /** dot11 rx ragment count */ + t_u32 rx_fragment_count; + /** dot11 mcast rx frame count */ + t_u32 mcast_rx_frame_count; + /** dot11 fcs error count */ + t_u32 fcs_error_count; + /** dot11 tx frame count */ + t_u32 tx_frame_count; + /** dot11 rsna tkip cm invoked */ + t_u32 rsna_tkip_cm_invoked; + /** dot11 rsna 4way handshake failures */ + t_u32 rsna_4way_hshk_failures; +} mlan_ds_uap_stats, *pmlan_ds_uap_stats; + +/** Mask of last beacon RSSI */ +#define BCN_RSSI_LAST_MASK 0x00000001 +/** Mask of average beacon RSSI */ +#define BCN_RSSI_AVG_MASK 0x00000002 +/** Mask of last data RSSI */ +#define DATA_RSSI_LAST_MASK 0x00000004 +/** Mask of average data RSSI */ +#define DATA_RSSI_AVG_MASK 0x00000008 +/** Mask of last beacon SNR */ +#define BCN_SNR_LAST_MASK 0x00000010 +/** Mask of average beacon SNR */ +#define BCN_SNR_AVG_MASK 0x00000020 +/** Mask of last data SNR */ +#define DATA_SNR_LAST_MASK 0x00000040 +/** Mask of average data SNR */ +#define DATA_SNR_AVG_MASK 0x00000080 +/** Mask of last beacon NF */ +#define BCN_NF_LAST_MASK 0x00000100 +/** Mask of average beacon NF */ +#define BCN_NF_AVG_MASK 0x00000200 +/** Mask of last data NF */ +#define DATA_NF_LAST_MASK 0x00000400 +/** Mask of average data NF */ +#define DATA_NF_AVG_MASK 0x00000800 +/** Mask of all RSSI_INFO */ +#define ALL_RSSI_INFO_MASK 0x00000fff +#define MAX_PATH_NUM 1 +/** path A */ +#define PATH_A 0x01 +/** path B */ +#define PATH_B 0x02 +/** path AB */ +#define PATH_AB 0x03 +/** ALL the path */ +#define PATH_ALL 0 +/** Type definition of mlan_ds_get_signal for MLAN_OID_GET_SIGNAL */ +typedef struct _mlan_ds_get_signal { + /** Selector of get operation */ + /* + * Bit0: Last Beacon RSSI, Bit1: Average Beacon RSSI, + * Bit2: Last Data RSSI, Bit3: Average Data RSSI, + * Bit4: Last Beacon SNR, Bit5: Average Beacon SNR, + * Bit6: Last Data SNR, Bit7: Average Data SNR, + * Bit8: Last Beacon NF, Bit9: Average Beacon NF, + * Bit10: Last Data NF, Bit11: Average Data NF + * + * Bit0: PATH A + * Bit1: PATH B + */ + t_u16 selector; + + /** RSSI */ + /** RSSI of last beacon */ + t_s16 bcn_rssi_last; + /** RSSI of beacon average */ + t_s16 bcn_rssi_avg; + /** RSSI of last data packet */ + t_s16 data_rssi_last; + /** RSSI of data packet average */ + t_s16 data_rssi_avg; + + /** SNR */ + /** SNR of last beacon */ + t_s16 bcn_snr_last; + /** SNR of beacon average */ + t_s16 bcn_snr_avg; + /** SNR of last data packet */ + t_s16 data_snr_last; + /** SNR of data packet average */ + t_s16 data_snr_avg; + + /** NF */ + /** NF of last beacon */ + t_s16 bcn_nf_last; + /** NF of beacon average */ + t_s16 bcn_nf_avg; + /** NF of last data packet */ + t_s16 data_nf_last; + /** NF of data packet average */ + t_s16 data_nf_avg; +} mlan_ds_get_signal, *pmlan_ds_get_signal; + +/** bit for 2.4 G antenna diversity */ +#define ANT_DIVERSITY_2G MBIT(3) +/** bit for 5 G antenna diversity */ +#define ANT_DIVERSITY_5G MBIT(7) + +/** mlan_fw_info data structure for MLAN_OID_GET_FW_INFO */ +typedef struct _mlan_fw_info { + /** Firmware version */ + t_u32 fw_ver; + /** MAC address */ + mlan_802_11_mac_addr mac_addr; + /** 802.11n device capabilities */ + t_u32 hw_dot_11n_dev_cap; + /** Device support for MIMO abstraction of MCSs */ + t_u8 hw_dev_mcs_support; + /** user's MCS setting */ + t_u8 usr_dev_mcs_support; + /** fw supported band */ + t_u8 fw_bands; + /** region code */ + t_u16 region_code; + /** ECSA support */ + t_u8 ecsa_enable; + /** Get log support */ + t_u8 getlog_enable; + /** FW support for embedded supplicant */ + t_u8 fw_supplicant_support; + /** ant info */ + t_u8 antinfo; + /** max AP associated sta count supported by fw */ + t_u8 max_ap_assoc_sta; + /** FW support roaming offload */ + t_u8 fw_roaming_support; +} mlan_fw_info, *pmlan_fw_info; + +/** Version string buffer length */ +#define MLAN_MAX_VER_STR_LEN 128 + +/** mlan_ver_ext data structure for MLAN_OID_GET_VER_EXT */ +typedef struct _mlan_ver_ext { + /** Selected version string */ + t_u32 version_str_sel; + /** Version string */ + char version_str[MLAN_MAX_VER_STR_LEN]; +} mlan_ver_ext, *pmlan_ver_ext; + +#ifdef BIG_ENDIAN_SUPPORT +/** Extended Capabilities Data */ +typedef struct MLAN_PACK_START _ExtCap_t { + /** Extended Capabilities value */ + t_u8 rsvdBit79:1; /* bit 79 */ + t_u8 rsvdBit78:1; /* bit 78 */ + t_u8 rsvdBit77:1; /* bit 77 */ + t_u8 rsvdBit76:1; /* bit 76 */ + t_u8 rsvdBit75:1; /* bit 75 */ + t_u8 rsvdBit74:1; /* bit 74 */ + t_u8 rsvdBit73:1; /* bit 73 */ + t_u8 FILS:1; /* bit 72 */ + t_u8 FTMI:1; /* bit 71 */ + t_u8 FTMR:1; /* bit 70 */ + t_u8 CAQ:1; /* bit 69 */ + t_u8 rsvdBit68:1; /* bit 68 */ + t_u8 NCC:1; /* bit 67 */ + t_u8 rsvdBit66:1; /* bit 66 */ + t_u8 chanSchedMgnt:1; /* bit 65 */ + t_u8 MaxAMSDU:2; /* bit 63-bit 64 */ + t_u8 OperModeNtf:1; /* bit 62 */ + t_u8 TDLSWildBandwidth:1; /* bit 61 */ + t_u8 rsvdBit60:1; /* bit 60 */ + t_u8 rsvdBit59:1; /* bit 59 */ + t_u8 rsvdBit58:1; /* bit 58 */ + t_u8 rsvdBit57:1; /* bit 57 */ + t_u8 rsvdBit56:1; /* bit 56 */ + t_u8 rsvdBit55:1; /* bit 55 */ + t_u8 rsvdBit54:1; /* bit 54 */ + t_u8 rsvdBit53:1; /* bit 53 */ + t_u8 rsvdBit52:1; /* bit 52 */ + t_u8 rsvdBit51:1; /* bit 51 */ + t_u8 rsvdBit50:1; /* bit 50 */ + t_u8 rsvdBit49:1; /* bit 49 */ + t_u8 rsvdBit48:1; /* bit 48 */ + t_u8 rsvdBit47:1; /* bit 47 */ + t_u8 rsvdBit46:1; /* bit 46 */ + t_u8 rsvdBit45:1; /* bit 45 */ + t_u8 rsvdBit44:1; /* bit 44 */ + t_u8 rsvdBit43:1; /* bit 43 */ + t_u8 rsvdBit42:1; /* bit 42 */ + t_u8 rsvdBit41:1; /* bit 41 */ + t_u8 rsvdBit40:1; /* bit 40 */ + t_u8 TDLSChlSwitchProhib:1; /* bit 39 */ + t_u8 TDLSProhibited:1; /* bit 38 */ + t_u8 TDLSSupport:1; /* bit 37 */ + t_u8 MSGCF_Capa:1; /* bit 36 */ + t_u8 Reserved35:1; /* bit 35 */ + t_u8 SSPN_Interface:1; /* bit 34 */ + t_u8 EBR:1; /* bit 33 */ + t_u8 Qos_Map:1; /* bit 32 */ + t_u8 Interworking:1; /* bit 31 */ + t_u8 TDLSChannelSwitching:1; /* bit 30 */ + t_u8 TDLSPeerPSMSupport:1; /* bit 29 */ + t_u8 TDLSPeerUAPSDSupport:1; /* bit 28 */ + t_u8 UTC:1; /* bit 27 */ + t_u8 DMS:1; /* bit 26 */ + t_u8 SSID_List:1; /* bit 25 */ + t_u8 ChannelUsage:1; /* bit 24 */ + t_u8 TimingMeasurement:1; /* bit 23 */ + t_u8 MultipleBSSID:1; /* bit 22 */ + t_u8 AC_StationCount:1; /* bit 21 */ + t_u8 QoSTrafficCap:1; /* bit 20 */ + t_u8 BSS_Transition:1; /* bit 19 */ + t_u8 TIM_Broadcast:1; /* bit 18 */ + t_u8 WNM_Sleep:1; /* bit 17 */ + t_u8 TFS:1; /* bit 16 */ + t_u8 GeospatialLocation:1; /* bit 15 */ + t_u8 CivicLocation:1; /* bit 14 */ + t_u8 CollocatedIntf:1; /* bit 13 */ + t_u8 ProxyARPService:1; /* bit 12 */ + t_u8 FMS:1; /* bit 11 */ + t_u8 LocationTracking:1; /* bit 10 */ + t_u8 MulticastDiagnostics:1; /* bit 9 */ + t_u8 Diagnostics:1; /* bit 8 */ + t_u8 Event:1; /* bit 7 */ + t_u8 SPSMP_Support:1; /* bit 6 */ + t_u8 Reserved5:1; /* bit 5 */ + t_u8 PSMP_Capable:1; /* bit 4 */ + t_u8 RejectUnadmFrame:1; /* bit 3 */ + t_u8 ExtChanSwitching:1; /* bit 2 */ + t_u8 Reserved1:1; /* bit 1 */ + t_u8 BSS_CoexistSupport:1; /* bit 0 */ +} MLAN_PACK_END ExtCap_t, *pExtCap_t; +#else +/** Extended Capabilities Data */ +typedef struct MLAN_PACK_START _ExtCap_t { + /** Extended Capabilities value */ + t_u8 BSS_CoexistSupport:1; /* bit 0 */ + t_u8 Reserved1:1; /* bit 1 */ + t_u8 ExtChanSwitching:1; /* bit 2 */ + t_u8 RejectUnadmFrame:1; /* bit 3 */ + t_u8 PSMP_Capable:1; /* bit 4 */ + t_u8 Reserved5:1; /* bit 5 */ + t_u8 SPSMP_Support:1; /* bit 6 */ + t_u8 Event:1; /* bit 7 */ + t_u8 Diagnostics:1; /* bit 8 */ + t_u8 MulticastDiagnostics:1; /* bit 9 */ + t_u8 LocationTracking:1; /* bit 10 */ + t_u8 FMS:1; /* bit 11 */ + t_u8 ProxyARPService:1; /* bit 12 */ + t_u8 CollocatedIntf:1; /* bit 13 */ + t_u8 CivicLocation:1; /* bit 14 */ + t_u8 GeospatialLocation:1; /* bit 15 */ + t_u8 TFS:1; /* bit 16 */ + t_u8 WNM_Sleep:1; /* bit 17 */ + t_u8 TIM_Broadcast:1; /* bit 18 */ + t_u8 BSS_Transition:1; /* bit 19 */ + t_u8 QoSTrafficCap:1; /* bit 20 */ + t_u8 AC_StationCount:1; /* bit 21 */ + t_u8 MultipleBSSID:1; /* bit 22 */ + t_u8 TimingMeasurement:1; /* bit 23 */ + t_u8 ChannelUsage:1; /* bit 24 */ + t_u8 SSID_List:1; /* bit 25 */ + t_u8 DMS:1; /* bit 26 */ + t_u8 UTC:1; /* bit 27 */ + t_u8 TDLSPeerUAPSDSupport:1; /* bit 28 */ + t_u8 TDLSPeerPSMSupport:1; /* bit 29 */ + t_u8 TDLSChannelSwitching:1; /* bit 30 */ + t_u8 Interworking:1; /* bit 31 */ + t_u8 Qos_Map:1; /* bit 32 */ + t_u8 EBR:1; /* bit 33 */ + t_u8 SSPN_Interface:1; /* bit 34 */ + t_u8 Reserved35:1; /* bit 35 */ + t_u8 MSGCF_Capa:1; /* bit 36 */ + t_u8 TDLSSupport:1; /* bit 37 */ + t_u8 TDLSProhibited:1; /* bit 38 */ + t_u8 TDLSChlSwitchProhib:1; /* bit 39 */ + t_u8 rsvdBit40:1; /* bit 40 */ + t_u8 rsvdBit41:1; /* bit 41 */ + t_u8 rsvdBit42:1; /* bit 42 */ + t_u8 rsvdBit43:1; /* bit 43 */ + t_u8 rsvdBit44:1; /* bit 44 */ + t_u8 rsvdBit45:1; /* bit 45 */ + t_u8 rsvdBit46:1; /* bit 46 */ + t_u8 rsvdBit47:1; /* bit 47 */ + t_u8 rsvdBit48:1; /* bit 48 */ + t_u8 rsvdBit49:1; /* bit 49 */ + t_u8 rsvdBit50:1; /* bit 50 */ + t_u8 rsvdBit51:1; /* bit 51 */ + t_u8 rsvdBit52:1; /* bit 52 */ + t_u8 rsvdBit53:1; /* bit 53 */ + t_u8 rsvdBit54:1; /* bit 54 */ + t_u8 rsvdBit55:1; /* bit 55 */ + t_u8 rsvdBit56:1; /* bit 56 */ + t_u8 rsvdBit57:1; /* bit 57 */ + t_u8 rsvdBit58:1; /* bit 58 */ + t_u8 rsvdBit59:1; /* bit 59 */ + t_u8 rsvdBit60:1; /* bit 60 */ + t_u8 TDLSWildBandwidth:1; /* bit 61 */ + t_u8 OperModeNtf:1; /* bit 62 */ + t_u8 MaxAMSDU:2; /* bit 63-bit 64 */ + t_u8 chanSchedMgnt:1; /* bit 65 */ + t_u8 rsvdBit66:1; /* bit 66 */ + t_u8 NCC:1; /* bit 67 */ + t_u8 rsvdBit68:1; /* bit 68 */ + t_u8 CAQ:1; /* bit 69 */ + t_u8 FTMR:1; /* bit 70 */ + t_u8 FTMI:1; /* bit 71 */ + t_u8 FILS:1; /* bit 72 */ + t_u8 rsvdBit73:1; /* bit 73 */ + t_u8 rsvdBit74:1; /* bit 74 */ + t_u8 rsvdBit75:1; /* bit 75 */ + t_u8 rsvdBit76:1; /* bit 76 */ + t_u8 rsvdBit77:1; /* bit 77 */ + t_u8 rsvdBit78:1; /* bit 78 */ + t_u8 rsvdBit79:1; /* bit 79 */ +} MLAN_PACK_END ExtCap_t, *pExtCap_t; +#endif + +/** ExtCap : TDLS prohibited */ +#define IS_EXTCAP_TDLS_PROHIBITED(ext_cap) (ext_cap.TDLSProhibited) +/** ExtCap : TDLS channel switch prohibited */ +#define IS_EXTCAP_TDLS_CHLSWITCHPROHIB(ext_cap) (ext_cap.TDLSChlSwitchProhib) + +/** mlan_bss_info data structure for MLAN_OID_GET_BSS_INFO */ +typedef struct _mlan_bss_info { + /** BSS mode */ + t_u32 bss_mode; + /** SSID */ + mlan_802_11_ssid ssid; + /** Table index */ + t_u32 scan_table_idx; + /** Channel */ + t_u32 bss_chan; + /** Band */ + t_u8 bss_band; + /** Region code */ + t_u32 region_code; + /** Connection status */ + t_u32 media_connected; + /** Radio on */ + t_u32 radio_on; + /** Max power level in dBm */ + t_s32 max_power_level; + /** Min power level in dBm */ + t_s32 min_power_level; + /** Adhoc state */ + t_u32 adhoc_state; + /** NF of last beacon */ + t_s32 bcn_nf_last; + /** wep status */ + t_u32 wep_status; + /** scan block status */ + t_u8 scan_block; + /** Host Sleep configured flag */ + t_u32 is_hs_configured; + /** Deep Sleep flag */ + t_u32 is_deep_sleep; + /** BSSID */ + mlan_802_11_mac_addr bssid; +#ifdef STA_SUPPORT + /** Capability Info */ + t_u16 capability_info; + /** Beacon Interval */ + t_u16 beacon_interval; + /** Listen Interval */ + t_u16 listen_interval; + /** Association Id */ + t_u16 assoc_id; + /** AP/Peer supported rates */ + t_u8 peer_supp_rates[MLAN_SUPPORTED_RATES]; + /** extend capability for AP */ + ExtCap_t ext_cap; +#endif /* STA_SUPPORT */ + /** Mobility Domain ID */ + t_u16 mdid; + /** FT Capability policy */ + t_u8 ft_cap; +} mlan_bss_info, *pmlan_bss_info; + +/** MAXIMUM number of TID */ +#define MAX_NUM_TID 8 + +/** Max RX Win size */ +#define MAX_RX_WINSIZE 64 + +/** rx_reorder_tbl */ +typedef struct { + /** TID */ + t_u16 tid; + /** TA */ + t_u8 ta[MLAN_MAC_ADDR_LENGTH]; + /** Start window */ + t_u32 start_win; + /** Window size */ + t_u32 win_size; + /** amsdu flag */ + t_u8 amsdu; + /** buffer status */ + t_u32 buffer[MAX_RX_WINSIZE]; +} rx_reorder_tbl; + +/** tx_ba_stream_tbl */ +typedef struct { + /** TID */ + t_u16 tid; + /** RA */ + t_u8 ra[MLAN_MAC_ADDR_LENGTH]; + /** amsdu flag */ + t_u8 amsdu; +} tx_ba_stream_tbl; + +/** Debug command number */ +#define DBG_CMD_NUM 10 + +#ifdef SDIO_MULTI_PORT_TX_AGGR +/** sdio mp debug number */ +#define SDIO_MP_DBG_NUM 10 +#endif + +/** Maximum size of IEEE Information Elements */ +#define IEEE_MAX_IE_SIZE 256 + +/** support up to 8 TDLS peer */ +#define MLAN_MAX_TDLS_PEER_SUPPORTED 8 +/** TDLS peer info */ +typedef struct _tdls_peer_info { + /** station mac address */ + t_u8 mac_addr[MLAN_MAC_ADDR_LENGTH]; + /** SNR */ + t_s8 snr; + /** Noise Floor */ + t_s8 nf; + /** Extended Capabilities IE */ + t_u8 ext_cap[IEEE_MAX_IE_SIZE]; + /** HT Capabilities IE */ + t_u8 ht_cap[IEEE_MAX_IE_SIZE]; +} tdls_peer_info; + +/** max ralist num */ +#define MLAN_MAX_RALIST_NUM 8 +/** ralist info */ +typedef struct _ralist_info { + /** RA list buffer */ + t_u8 ra[MLAN_MAC_ADDR_LENGTH]; + /** total packets in RA list */ + t_u16 total_pkts; + /** tid num */ + t_u8 tid; + /** tx_pause flag */ + t_u8 tx_pause; +} ralist_info; + +/** mlan_debug_info data structure for MLAN_OID_GET_DEBUG_INFO */ +typedef struct _mlan_debug_info { + /* WMM AC_BK count */ + t_u32 wmm_ac_bk; + /* WMM AC_BE count */ + t_u32 wmm_ac_be; + /* WMM AC_VI count */ + t_u32 wmm_ac_vi; + /* WMM AC_VO count */ + t_u32 wmm_ac_vo; + /** Corresponds to max_tx_buf_size member of mlan_adapter*/ + t_u32 max_tx_buf_size; + /** Corresponds to tx_buf_size member of mlan_adapter*/ + t_u32 tx_buf_size; + /** Corresponds to curr_tx_buf_size member of mlan_adapter*/ + t_u32 curr_tx_buf_size; + /** Tx table num */ + t_u32 tx_tbl_num; + /** Tx ba stream table */ + tx_ba_stream_tbl tx_tbl[MLAN_MAX_TX_BASTREAM_SUPPORTED]; + /** Rx table num */ + t_u32 rx_tbl_num; + /** Rx reorder table*/ + rx_reorder_tbl rx_tbl[MLAN_MAX_RX_BASTREAM_SUPPORTED]; + /** TDLS peer number */ + t_u32 tdls_peer_num; + /** TDLS peer list*/ + tdls_peer_info tdls_peer_list[MLAN_MAX_TDLS_PEER_SUPPORTED]; + /** ralist num */ + t_u32 ralist_num; + /** ralist info */ + ralist_info ralist[MLAN_MAX_RALIST_NUM]; + /** Corresponds to ps_mode member of mlan_adapter */ + t_u16 ps_mode; + /** Corresponds to ps_state member of mlan_adapter */ + t_u32 ps_state; +#ifdef STA_SUPPORT + /** Corresponds to is_deep_sleep member of mlan_adapter */ + t_u8 is_deep_sleep; +#endif /** STA_SUPPORT */ + /** Corresponds to pm_wakeup_card_req member of mlan_adapter */ + t_u8 pm_wakeup_card_req; + /** Corresponds to pm_wakeup_fw_try member of mlan_adapter */ + t_u32 pm_wakeup_fw_try; + /** time stamp when host try to wake up firmware */ + t_u32 pm_wakeup_in_secs; + /** Corresponds to is_hs_configured member of mlan_adapter */ + t_u8 is_hs_configured; + /** Corresponds to hs_activated member of mlan_adapter */ + t_u8 hs_activated; + /** Corresponds to pps_uapsd_mode member of mlan_adapter */ + t_u16 pps_uapsd_mode; + /** Corresponds to sleep_period.period member of mlan_adapter */ + t_u16 sleep_pd; + /** Corresponds to wmm_qosinfo member of mlan_private */ + t_u8 qos_cfg; + /** Corresponds to tx_lock_flag member of mlan_adapter */ + t_u8 tx_lock_flag; + /** Corresponds to port_open member of mlan_private */ + t_u8 port_open; + /** bypass pkt count */ + t_u16 bypass_pkt_count; + /** Corresponds to scan_processing member of mlan_adapter */ + t_u32 scan_processing; + /** Corresponds to mlan_processing member of mlan_adapter */ + t_u32 mlan_processing; + /** Corresponds to main_lock_flag member of mlan_adapter */ + t_u32 main_lock_flag; + /** Corresponds to main_process_cnt member of mlan_adapter */ + t_u32 main_process_cnt; + /** Corresponds to delay_task_flag member of mlan_adapter */ + t_u32 delay_task_flag; + /** mlan_rx_processing */ + t_u32 mlan_rx_processing; + /** rx pkts queued */ + t_u32 rx_pkts_queued; + /** Number of host to card command failures */ + t_u32 num_cmd_host_to_card_failure; + /** Number of host to card sleep confirm failures */ + t_u32 num_cmd_sleep_cfm_host_to_card_failure; + /** Number of host to card Tx failures */ + t_u32 num_tx_host_to_card_failure; + /** Number of allocate buffer failure */ + t_u32 num_alloc_buffer_failure; + /** Number of pkt dropped */ + t_u32 num_pkt_dropped; + /** Number of card to host command/event failures */ + t_u32 num_cmdevt_card_to_host_failure; + /** Number of card to host Rx failures */ + t_u32 num_rx_card_to_host_failure; + /** Number of interrupt read failures */ + t_u32 num_int_read_failure; + /** Last interrupt status */ + t_u32 last_int_status; + /** number of interrupt receive */ + t_u32 num_of_irq; + /** flag for sdio rx aggr */ + t_u8 sdio_rx_aggr; + /** FW update port number */ + t_u32 mp_update[SDIO_MP_AGGR_DEF_PKT_LIMIT * 2]; + /** Invalid port update count */ + t_u32 mp_invalid_update; +#ifdef SDIO_MULTI_PORT_TX_AGGR + /** Number of packets tx aggr */ + t_u32 mpa_tx_count[SDIO_MP_AGGR_DEF_PKT_LIMIT]; + /** no more packets count*/ + t_u32 mpa_sent_last_pkt; + /** no write_ports count */ + t_u32 mpa_sent_no_ports; + /** last recv wr_bitmap */ + t_u32 last_recv_wr_bitmap; + /** last mp_wr_bitmap */ + t_u32 last_mp_wr_bitmap[SDIO_MP_DBG_NUM]; + /** last ports for cmd53 write data */ + t_u32 last_mp_wr_ports[SDIO_MP_DBG_NUM]; + /** last len for cmd53 write data */ + t_u32 last_mp_wr_len[SDIO_MP_DBG_NUM]; + /** last curr_wr_port */ + t_u8 last_curr_wr_port[SDIO_MP_DBG_NUM]; + /** length info for cmd53 write data */ + t_u16 last_mp_wr_info[SDIO_MP_DBG_NUM * SDIO_MP_AGGR_DEF_PKT_LIMIT]; + /** last mp_index */ + t_u8 last_mp_index; + /** buffer for mp debug */ + t_u8 *mpa_buf; + /** length info for mp buf size */ + t_u32 mpa_buf_size; +#endif +#ifdef SDIO_MULTI_PORT_RX_AGGR + /** Number of packets rx aggr */ + t_u32 mpa_rx_count[SDIO_MP_AGGR_DEF_PKT_LIMIT]; +#endif + /** Number of deauthentication events */ + t_u32 num_event_deauth; + /** Number of disassosiation events */ + t_u32 num_event_disassoc; + /** Number of link lost events */ + t_u32 num_event_link_lost; + /** Number of deauthentication commands */ + t_u32 num_cmd_deauth; + /** Number of association comamnd successes */ + t_u32 num_cmd_assoc_success; + /** Number of association command failures */ + t_u32 num_cmd_assoc_failure; + /** Number of Tx timeouts */ + t_u32 num_tx_timeout; + /** Number of command timeouts */ + t_u32 num_cmd_timeout; + /** Number of command timeouts */ + t_u32 dbg_num_cmd_timeout; + /** Timeout command ID */ + t_u16 timeout_cmd_id; + /** Timeout command action */ + t_u16 timeout_cmd_act; + /** List of last command IDs */ + t_u16 last_cmd_id[DBG_CMD_NUM]; + /** List of last command actions */ + t_u16 last_cmd_act[DBG_CMD_NUM]; + /** Last command index */ + t_u16 last_cmd_index; + /** List of last command response IDs */ + t_u16 last_cmd_resp_id[DBG_CMD_NUM]; + /** Last command response index */ + t_u16 last_cmd_resp_index; + /** List of last events */ + t_u16 last_event[DBG_CMD_NUM]; + /** Last event index */ + t_u16 last_event_index; + /** Number of no free command node */ + t_u16 num_no_cmd_node; + /** pending command id */ + t_u16 pending_cmd; + /** time stamp for dnld last cmd */ + t_u32 dnld_cmd_in_secs; + /** Corresponds to data_sent member of mlan_adapter */ + t_u8 data_sent; + /** Corresponds to cmd_sent member of mlan_adapter */ + t_u8 cmd_sent; + /** SDIO multiple port read bitmap */ + t_u32 mp_rd_bitmap; + /** SDIO multiple port write bitmap */ + t_u32 mp_wr_bitmap; + /** Current available port for read */ + t_u8 curr_rd_port; + /** Current available port for write */ + t_u8 curr_wr_port; + /** Corresponds to cmdresp_received member of mlan_adapter */ + t_u8 cmd_resp_received; + /** Corresponds to event_received member of mlan_adapter */ + t_u8 event_received; + /** pendig tx pkts */ + t_u32 tx_pkts_queued; +#ifdef UAP_SUPPORT + /** pending bridge pkts */ + t_u16 num_bridge_pkts; + /** dropped pkts */ + t_u32 num_drop_pkts; +#endif + /** mlan_adapter pointer */ + t_void *mlan_adapter; + /** mlan_adapter_size */ + t_u32 mlan_adapter_size; + /** mlan_priv vector */ + t_void *mlan_priv[MLAN_MAX_BSS_NUM]; + /** mlan_priv_size */ + t_u32 mlan_priv_size[MLAN_MAX_BSS_NUM]; + /** mlan_priv_num */ + t_u8 mlan_priv_num; +} mlan_debug_info, *pmlan_debug_info; + +#ifdef UAP_SUPPORT +/** Maximum number of clients supported by AP */ +#define MAX_NUM_CLIENTS MAX_STA_COUNT + +/** station info */ +typedef struct _sta_info { + /** STA MAC address */ + t_u8 mac_address[MLAN_MAC_ADDR_LENGTH]; + /** Power mfg status */ + t_u8 power_mfg_status; + /** RSSI */ + t_s8 rssi; + /** station bandmode */ + t_u8 bandmode; +} sta_info; + +/** mlan_ds_sta_list structure for MLAN_OID_UAP_STA_LIST */ +typedef struct _mlan_ds_sta_list { + /** station count */ + t_u16 sta_count; + /** station list */ + sta_info info[MAX_NUM_CLIENTS]; +} mlan_ds_sta_list, *pmlan_ds_sta_list; +#endif + +/** Type definition of mlan_ds_get_info for MLAN_IOCTL_GET_INFO */ +typedef struct _mlan_ds_get_info { + /** Sub-command */ + t_u32 sub_command; + + /** Status information parameter */ + union { + /** Signal information for MLAN_OID_GET_SIGNAL */ + mlan_ds_get_signal signal; + /** Signal path id for MLAN_OID_GET_SIGNAL_EXT */ + t_u16 path_id; + /** Signal information for MLAN_OID_GET_SIGNAL_EXT */ + mlan_ds_get_signal signal_ext[MAX_PATH_NUM]; + /** Statistics information for MLAN_OID_GET_STATS */ + mlan_ds_get_stats stats; + /** Firmware information for MLAN_OID_GET_FW_INFO */ + mlan_fw_info fw_info; + /** Extended version information for MLAN_OID_GET_VER_EXT */ + mlan_ver_ext ver_ext; + /** BSS information for MLAN_OID_GET_BSS_INFO */ + mlan_bss_info bss_info; + /** Debug information for MLAN_OID_GET_DEBUG_INFO */ + t_u8 debug_info[1]; +#ifdef UAP_SUPPORT + /** UAP Statistics information for MLAN_OID_GET_STATS */ + mlan_ds_uap_stats ustats; + /** UAP station list for MLAN_OID_UAP_STA_LIST */ + mlan_ds_sta_list sta_list; +#endif + } param; +} mlan_ds_get_info, *pmlan_ds_get_info; + +/*-----------------------------------------------------------------*/ +/** Security Configuration Group */ +/*-----------------------------------------------------------------*/ +/** Enumeration for authentication mode */ +enum _mlan_auth_mode { + MLAN_AUTH_MODE_OPEN = 0x00, + MLAN_AUTH_MODE_SHARED = 0x01, + MLAN_AUTH_MODE_FT = 0x02, + MLAN_AUTH_MODE_NETWORKEAP = 0x80, + MLAN_AUTH_MODE_AUTO = 0xFF, +}; + +/**Enumeration for AssocAgent authentication mode, sync from FW.*/ +typedef enum { + AssocAgentAuth_Open, + AssocAgentAuth_Shared, + AssocAgentAuth_FastBss, + AssocAgentAuth_FastBss_Skip, + AssocAgentAuth_Network_EAP, + AssocAgentAuth_Auto, +} AssocAgentAuthType_e; + +/** Enumeration for encryption mode */ +enum _mlan_encryption_mode { + MLAN_ENCRYPTION_MODE_NONE = 0, + MLAN_ENCRYPTION_MODE_WEP40 = 1, + MLAN_ENCRYPTION_MODE_TKIP = 2, + MLAN_ENCRYPTION_MODE_CCMP = 3, + MLAN_ENCRYPTION_MODE_WEP104 = 4, +}; + +/** Enumeration for PSK */ +enum _mlan_psk_type { + MLAN_PSK_PASSPHRASE = 1, + MLAN_PSK_PMK, + MLAN_PSK_CLEAR, + MLAN_PSK_QUERY, +}; + +/** The bit to indicate the key is for unicast */ +#define MLAN_KEY_INDEX_UNICAST 0x40000000 +/** The key index to indicate default key */ +#define MLAN_KEY_INDEX_DEFAULT 0x000000ff +/** Maximum key length */ +/* #define MLAN_MAX_KEY_LENGTH 32 */ +/** Minimum passphrase length */ +#define MLAN_MIN_PASSPHRASE_LENGTH 8 +/** Maximum passphrase length */ +#define MLAN_MAX_PASSPHRASE_LENGTH 63 +/** PMK length */ +#define MLAN_PMK_HEXSTR_LENGTH 64 +/* A few details needed for WEP (Wireless Equivalent Privacy) */ +/** 104 bits */ +#define MAX_WEP_KEY_SIZE 13 +/** 40 bits RC4 - WEP */ +#define MIN_WEP_KEY_SIZE 5 +/** packet number size */ +#define PN_SIZE 16 +/** max seq size of wpa/wpa2 key */ +#define SEQ_MAX_SIZE 8 + +/** key flag for tx_seq */ +#define KEY_FLAG_TX_SEQ_VALID 0x00000001 +/** key flag for rx_seq */ +#define KEY_FLAG_RX_SEQ_VALID 0x00000002 +/** key flag for group key */ +#define KEY_FLAG_GROUP_KEY 0x00000004 +/** key flag for tx */ +#define KEY_FLAG_SET_TX_KEY 0x00000008 +/** key flag for mcast IGTK */ +#define KEY_FLAG_AES_MCAST_IGTK 0x00000010 +/** key flag for remove key */ +#define KEY_FLAG_REMOVE_KEY 0x80000000 + +/** Type definition of mlan_ds_encrypt_key for MLAN_OID_SEC_CFG_ENCRYPT_KEY */ +typedef struct _mlan_ds_encrypt_key { + /** Key disabled, all other fields will be + * ignore when this flag set to MTRUE + */ + t_u32 key_disable; + /** key removed flag, when this flag is set + * to MTRUE, only key_index will be check + */ + t_u32 key_remove; + /** Key index, used as current tx key index + * when is_current_wep_key is set to MTRUE + */ + t_u32 key_index; + /** Current Tx key flag */ + t_u32 is_current_wep_key; + /** Key length */ + t_u32 key_len; + /** Key */ + t_u8 key_material[MLAN_MAX_KEY_LENGTH]; + /** mac address */ + t_u8 mac_addr[MLAN_MAC_ADDR_LENGTH]; + /** wapi key flag */ + t_u32 is_wapi_key; + /** Initial packet number */ + t_u8 pn[PN_SIZE]; + /** key flags */ + t_u32 key_flags; +} mlan_ds_encrypt_key, *pmlan_ds_encrypt_key; + +/** Type definition of mlan_passphrase_t */ +typedef struct _mlan_passphrase_t { + /** Length of passphrase */ + t_u32 passphrase_len; + /** Passphrase */ + t_u8 passphrase[MLAN_MAX_PASSPHRASE_LENGTH]; +} mlan_passphrase_t; + +/** Type defnition of mlan_pmk_t */ +typedef struct _mlan_pmk_t { + /** PMK */ + t_u8 pmk[MLAN_MAX_KEY_LENGTH]; + t_u8 pmk_r0[MLAN_MAX_KEY_LENGTH]; + t_u8 pmk_r0_name[MLAN_MAX_PMKR0_NAME_LENGTH]; +} mlan_pmk_t; + +/** Embedded supplicant RSN type: No RSN */ +#define RSN_TYPE_NO_RSN MBIT(0) +/** Embedded supplicant RSN type: WPA */ +#define RSN_TYPE_WPA MBIT(3) +/** Embedded supplicant RSN type: WPA-NONE */ +#define RSN_TYPE_WPANONE MBIT(4) +/** Embedded supplicant RSN type: WPA2 */ +#define RSN_TYPE_WPA2 MBIT(5) +/** Embedded supplicant RSN type: RFU */ +#define RSN_TYPE_VALID_BITS (RSN_TYPE_NO_RSN | RSN_TYPE_WPA | RSN_TYPE_WPANONE | RSN_TYPE_WPA2) + +/** Embedded supplicant cipher type: TKIP */ +#define EMBED_CIPHER_TKIP MBIT(2) +/** Embedded supplicant cipher type: AES */ +#define EMBED_CIPHER_AES MBIT(3) +/** Embedded supplicant cipher type: RFU */ +#define EMBED_CIPHER_VALID_BITS (EMBED_CIPHER_TKIP | EMBED_CIPHER_AES) + +/** Type definition of mlan_ds_passphrase for MLAN_OID_SEC_CFG_PASSPHRASE */ +typedef struct _mlan_ds_passphrase { + /** SSID may be used */ + mlan_802_11_ssid ssid; + /** BSSID may be used */ + mlan_802_11_mac_addr bssid; + /** Flag for passphrase or pmk used */ + t_u16 psk_type; + /** Passphrase or PMK */ + union { + /** Passphrase */ + mlan_passphrase_t passphrase; + /** PMK */ + mlan_pmk_t pmk; + } psk; +} mlan_ds_passphrase, *pmlan_ds_passphrase; + +/** Type definition of mlan_ds_esupp_mode for MLAN_OID_SEC_CFG_ESUPP_MODE */ +typedef struct _mlan_ds_ewpa_mode { + /** RSN mode */ + t_u32 rsn_mode; + /** Active pairwise cipher */ + t_u32 act_paircipher; + /** Active pairwise cipher */ + t_u32 act_groupcipher; +} mlan_ds_esupp_mode, *pmlan_ds_esupp_mode; + +/* Security SSID MAX number support by firmware*/ +#define MAX_SEC_SSID_NUM 6 + +/** Type definition of mlan_ds_sec_cfg for MLAN_IOCTL_SEC_CFG */ +typedef struct _mlan_ds_sec_cfg { + /** Sub-command */ + t_u32 sub_command; + /** Flag to extend some structures to support multiple values. + ** For example, mlan_ds_passphrase can only contain one value, + ** if need use mlan_ds_passphrase[N], just set this flag and + ** use mlan_ds_passphrase[] instead to avoid modify + ** more already exist code. + */ + t_u8 multi_passphrase; + /** Security configuration parameter */ + union { + /** Authentication mode for MLAN_OID_SEC_CFG_AUTH_MODE */ + t_u32 auth_mode; + /** Encryption mode for MLAN_OID_SEC_CFG_ENCRYPT_MODE */ + t_u32 encrypt_mode; + /** WPA enabled flag for MLAN_OID_SEC_CFG_WPA_ENABLED */ + t_u32 wpa_enabled; + /** WAPI enabled flag for MLAN_OID_SEC_CFG_WAPI_ENABLED */ + t_u32 wapi_enabled; + /** Port Control enabled flag for MLAN_OID_SEC_CFG_PORT_CTRL */ + t_u32 port_ctrl_enabled; + /** Encryption key for MLAN_OID_SEC_CFG_ENCRYPT_KEY */ + mlan_ds_encrypt_key encrypt_key; + /** Passphrase for MLAN_OID_SEC_CFG_PASSPHRASE */ + mlan_ds_passphrase passphrase; + /** Embedded supplicant WPA enabled flag for + * MLAN_OID_SEC_CFG_EWPA_ENABLED + */ + t_u32 ewpa_enabled; + /** Embedded supplicant mode for MLAN_OID_SEC_CFG_ESUPP_MODE */ + mlan_ds_esupp_mode esupp_mode; + mlan_ds_passphrase roam_passphrase[MAX_SEC_SSID_NUM]; + } param; +} mlan_ds_sec_cfg, *pmlan_ds_sec_cfg; + +/*-----------------------------------------------------------------*/ +/** Rate Configuration Group */ +/*-----------------------------------------------------------------*/ +/** Enumeration for rate type */ +enum _mlan_rate_type { + MLAN_RATE_INDEX, + MLAN_RATE_VALUE, + MLAN_RATE_BITMAP +}; + +/** Enumeration for rate format */ +enum _mlan_rate_format { + MLAN_RATE_FORMAT_LG = 0, + MLAN_RATE_FORMAT_HT, + MLAN_RATE_FORMAT_AUTO = 0xFF, +}; +/** Max bitmap rates size */ +#define MAX_BITMAP_RATES_SIZE 18 + +/** Type definition of mlan_rate_cfg_t for MLAN_OID_RATE_CFG */ +typedef struct _mlan_rate_cfg_t { + /** Fixed rate: 0, auto rate: 1 */ + t_u32 is_rate_auto; + /** Rate type. 0: index; 1: value; 2: bitmap */ + t_u32 rate_type; + /** Rate/MCS index or rate value if fixed rate */ + t_u32 rate; + /** Rate Bitmap */ + t_u16 bitmap_rates[MAX_BITMAP_RATES_SIZE]; + /* LG rate: 0, HT rate: 1, VHT rate: 2 */ + t_u32 rate_format; +} mlan_rate_cfg_t; + +/** HT channel bandwidth */ +typedef enum _mlan_ht_bw { + MLAN_HT_BW20, + MLAN_HT_BW40, +} mlan_ht_bw; + +/** HT guard interval */ +typedef enum _mlan_ht_gi { + MLAN_HT_LGI, + MLAN_HT_SGI, +} mlan_ht_gi; + +/** Band and BSS mode */ +typedef struct _mlan_band_data_rate { + /** Band configuration */ + t_u8 config_bands; + /** BSS mode (Infra or IBSS) */ + t_u8 bss_mode; +} mlan_band_data_rate; + +/** Type definition of mlan_data_rate for MLAN_OID_GET_DATA_RATE */ +typedef struct _mlan_data_rate { + /** Tx data rate */ + t_u32 tx_data_rate; + /** Rx data rate */ + t_u32 rx_data_rate; + + /** Tx channel bandwidth */ + t_u32 tx_ht_bw; + /** Tx guard interval */ + t_u32 tx_ht_gi; + /** Rx channel bandwidth */ + t_u32 rx_ht_bw; + /** Rx guard interval */ + t_u32 rx_ht_gi; + /** MCS index */ + t_u32 tx_mcs_index; + t_u32 rx_mcs_index; + /* LG rate: 0, HT rate: 1, VHT rate: 2 */ + t_u32 tx_rate_format; + t_u32 rx_rate_format; +} mlan_data_rate; + +/** Type definition of mlan_ds_rate for MLAN_IOCTL_RATE */ +typedef struct _mlan_ds_rate { + /** Sub-command */ + t_u32 sub_command; + /** Rate configuration parameter */ + union { + /** Rate configuration for MLAN_OID_RATE_CFG */ + mlan_rate_cfg_t rate_cfg; + /** Data rate for MLAN_OID_GET_DATA_RATE */ + mlan_data_rate data_rate; + /** Supported rates for MLAN_OID_SUPPORTED_RATES */ + t_u8 rates[MLAN_SUPPORTED_RATES]; + /** Band/BSS mode for getting supported rates */ + mlan_band_data_rate rate_band_cfg; + } param; +} mlan_ds_rate, *pmlan_ds_rate; + +/*-----------------------------------------------------------------*/ +/** Power Configuration Group */ +/*-----------------------------------------------------------------*/ + +/** Type definition of mlan_power_cfg_t for MLAN_OID_POWER_CFG */ +typedef struct _mlan_power_cfg_t { + /** Is power auto */ + t_u32 is_power_auto; + /** Power level in dBm */ + t_s32 power_level; +} mlan_power_cfg_t; + +/** max power table size */ +#define MAX_POWER_TABLE_SIZE 128 +#define TX_PWR_CFG_AUTO_CTRL_OFF 0xFF +#define MAX_POWER_GROUP 64 +/** Type definition of mlan_power group info */ +typedef struct mlan_power_group { + + /** rate format (LG: 0, HT: 1, VHT: 2, no auto ctrl: 0xFF) */ + t_u32 rate_format; + /** bandwidth (LG: 20 MHz, HT: 20/40 MHz, VHT: 80/160/80+80 MHz) */ + t_u8 bandwidth; + /** LG: first rate index, HT/VHT: first MCS */ + t_u8 first_rate_ind; + /** LG: last rate index, HT/VHT: last MCS */ + t_u8 last_rate_ind; + /** minmum tx power (dBm) */ + t_s8 power_min; + /** maximum tx power (dBm) */ + t_s8 power_max; + /** tx power step (dB) */ + t_s8 power_step; +} mlan_power_group; + +/** Type definition of mlan_power_cfg_ext for MLAN_OID_POWER_CFG_EXT */ +typedef struct _mlan_power_cfg_ext { + /** number of power_groups */ + t_u32 num_pwr_grp; + /** array of power groups */ + mlan_power_group power_group[MAX_POWER_GROUP]; +} mlan_power_cfg_ext; + +/** Type definition of mlan_ds_power_cfg for MLAN_IOCTL_POWER_CFG */ +typedef struct _mlan_ds_power_cfg { + /** Sub-command */ + t_u32 sub_command; + /** Power configuration parameter */ + union { + /** Power configuration for MLAN_OID_POWER_CFG */ + mlan_power_cfg_t power_cfg; + /** Extended power configuration for MLAN_OID_POWER_CFG_EXT */ + mlan_power_cfg_ext power_ext; + } param; +} mlan_ds_power_cfg, *pmlan_ds_power_cfg; + +/*-----------------------------------------------------------------*/ +/** Power Management Configuration Group */ +/*-----------------------------------------------------------------*/ +/** Host sleep config conditions : Cancel */ +#define HOST_SLEEP_CFG_CANCEL 0xffffffff + +/** Host sleep config condition: broadcast data */ +#define HOST_SLEEP_COND_BROADCAST_DATA MBIT(0) +/** Host sleep config condition: unicast data */ +#define HOST_SLEEP_COND_UNICAST_DATA MBIT(1) +/** Host sleep config condition: mac event */ +#define HOST_SLEEP_COND_MAC_EVENT MBIT(2) +/** Host sleep config condition: multicast data */ +#define HOST_SLEEP_COND_MULTICAST_DATA MBIT(3) +/** Host sleep config condition: IPV6 packet */ +#define HOST_SLEEP_COND_IPV6_PACKET MBIT(31) + +/** Host sleep config conditions: Default */ +#define HOST_SLEEP_DEF_COND (HOST_SLEEP_COND_BROADCAST_DATA | HOST_SLEEP_COND_UNICAST_DATA | HOST_SLEEP_COND_MAC_EVENT) + +/** Host sleep config GPIO : Default */ +#define HOST_SLEEP_DEF_GPIO 0xff +/** Host sleep config gap : Default */ +#define HOST_SLEEP_DEF_GAP 200 +/** Host sleep config min wake holdoff */ +#define HOST_SLEEP_DEF_WAKE_HOLDOFF 0; +/** Host sleep config inactivity timeout */ +#define HOST_SLEEP_DEF_INACTIVITY_TIMEOUT 10; + +/** Type definition of mlan_ds_hs_cfg for MLAN_OID_PM_CFG_HS_CFG */ +typedef struct _mlan_ds_hs_cfg { + /** MTRUE to invoke the HostCmd, MFALSE otherwise */ + t_u32 is_invoke_hostcmd; + /** Host sleep config condition */ + /** Bit0: broadcast data + * Bit1: unicast data + * Bit2: mac event + * Bit3: multicast data + */ + t_u32 conditions; + /** GPIO pin or 0xff for interface */ + t_u32 gpio; + /** Gap in milliseconds or or 0xff for special + * setting when GPIO is used to wakeup host + */ + t_u32 gap; + /** Host sleep wake interval */ + t_u32 hs_wake_interval; + /** Parameter type for indication gpio*/ + t_u8 param_type_ind; + /** GPIO pin for indication wakeup source */ + t_u32 ind_gpio; + /** Level on ind_gpio pin for indication normal wakeup source */ + t_u32 level; + /** Parameter type for extend hscfg*/ + t_u8 param_type_ext; + /** Events that will be forced ignore*/ + t_u32 event_force_ignore; + /** Events that will use extend gap to inform host*/ + t_u32 event_use_ext_gap; + /** Ext gap*/ + t_u8 ext_gap; + /** GPIO wave level for extend hscfg*/ + t_u8 gpio_wave; +} mlan_ds_hs_cfg, *pmlan_ds_hs_cfg; + +#define MAX_MGMT_FRAME_FILTER 2 +typedef struct _mlan_mgmt_frame_wakeup { + /** action - bitmap + ** On matching rx'd pkt and filter during NON_HOSTSLEEP mode: + ** Action[1]=0 Discard + ** Action[1]=1 Allow + ** Note that default action on non-match is "Allow". + ** + ** On matching rx'd pkt and filter during HOSTSLEEP mode: + ** Action[1:0]=00 Discard and Not Wake host + ** Action[1:0]=01 Discard and Wake host + ** Action[1:0]=10 Invalid + ** Note that default action on non-match is "Discard and Not Wake host". + **/ + t_u32 action; + /** Frame type(p2p, tdls...) + ** type=0: invalid + ** type=1: p2p + ** type=others: reserved + **/ + t_u32 type; + /** Frame mask according to each type + ** When type=1 for p2p, frame-mask have following define: + ** Bit Frame + ** 0 GO Negotiation Request + ** 1 GO Negotiation Response + ** 2 GO Negotiation Confirmation + ** 3 P2P Invitation Request + ** 4 P2P Invitation Response + ** 5 Device Discoverability Request + ** 6 Device Discoverability Response + ** 7 Provision Discovery Request + ** 8 Provision Discovery Response + ** 9 Notice of Absence + ** 10 P2P Presence Request + ** 11 P2P Presence Response + ** 12 GO Discoverability Request + ** 13-31 Reserved + ** + ** When type=others, frame-mask is reserved. + **/ + t_u32 frame_mask; +} mlan_mgmt_frame_wakeup, *pmlan_mgmt_frame_wakeup; + +/** Enable deep sleep mode */ +#define DEEP_SLEEP_ON 1 +/** Disable deep sleep mode */ +#define DEEP_SLEEP_OFF 0 + +/** Default idle time in milliseconds for auto deep sleep */ +#define DEEP_SLEEP_IDLE_TIME 100 + +typedef struct _mlan_ds_auto_ds { + /** auto ds mode, 0 - disable, 1 - enable */ + t_u16 auto_ds; + /** auto ds idle time in milliseconds */ + t_u16 idletime; +} mlan_ds_auto_ds; + +/** Type definition of mlan_ds_inactivity_to + * for MLAN_OID_PM_CFG_INACTIVITY_TO + */ +typedef struct _mlan_ds_inactivity_to { + /** Timeout unit in microsecond, 0 means 1000us (1ms) */ + t_u32 timeout_unit; + /** Inactivity timeout for unicast data */ + t_u32 unicast_timeout; + /** Inactivity timeout for multicast data */ + t_u32 mcast_timeout; + /** Timeout for additional Rx traffic after Null PM1 packet exchange */ + t_u32 ps_entry_timeout; +} mlan_ds_inactivity_to, *pmlan_ds_inactivity_to; + +/** Minimum sleep period in milliseconds */ +#define MIN_SLEEP_PERIOD 10 +/** Maximum sleep period in milliseconds */ +#define MAX_SLEEP_PERIOD 60 +/** Special setting for UPSD certification tests */ +#define SLEEP_PERIOD_RESERVED_FF 0xFF + +/** PS null interval disable */ +#define PS_NULL_DISABLE (-1) + +/** Local listen interval disable */ +#define MRVDRV_LISTEN_INTERVAL_DISABLE (-1) +/** Minimum listen interval */ +#define MRVDRV_MIN_LISTEN_INTERVAL 0 + +/** Minimum multiple DTIM */ +#define MRVDRV_MIN_MULTIPLE_DTIM 0 +/** Maximum multiple DTIM */ +#define MRVDRV_MAX_MULTIPLE_DTIM 5 +/** Ignore multiple DTIM */ +#define MRVDRV_IGNORE_MULTIPLE_DTIM 0xfffe +/** Match listen interval to closest DTIM */ +#define MRVDRV_MATCH_CLOSEST_DTIM 0xfffd + +/** Minimum adhoc awake period */ +#define MIN_ADHOC_AWAKE_PD 0 +/** Maximum adhoc awake period */ +#define MAX_ADHOC_AWAKE_PD 31 +/** Special adhoc awake period */ +#define SPECIAL_ADHOC_AWAKE_PD 255 + +/** Minimum beacon miss timeout in milliseconds */ +#define MIN_BCN_MISS_TO 0 +/** Maximum beacon miss timeout in milliseconds */ +#define MAX_BCN_MISS_TO 50 +/** Disable beacon miss timeout */ +#define DISABLE_BCN_MISS_TO 65535 + +/** Minimum delay to PS in milliseconds */ +#define MIN_DELAY_TO_PS 0 +/** Maximum delay to PS in milliseconds */ +#define MAX_DELAY_TO_PS 65535 +/** Delay to PS unchanged */ +#define DELAY_TO_PS_UNCHANGED (-1) +/** Default delay to PS in milliseconds */ +#define DELAY_TO_PS_DEFAULT 1000 + +/** PS mode: Unchanged */ +#define PS_MODE_UNCHANGED 0 +/** PS mode: Auto */ +#define PS_MODE_AUTO 1 +/** PS mode: Poll */ +#define PS_MODE_POLL 2 +/** PS mode: Null */ +#define PS_MODE_NULL 3 + +/** Type definition of mlan_ds_ps_cfg for MLAN_OID_PM_CFG_PS_CFG */ +typedef struct _mlan_ds_ps_cfg { + /** PS null interval in seconds */ + t_u32 ps_null_interval; + /** Multiple DTIM interval */ + t_u32 multiple_dtim_interval; + /** Listen interval */ + t_u32 listen_interval; + /** Adhoc awake period */ + t_u32 adhoc_awake_period; + /** Beacon miss timeout in milliseconds */ + t_u32 bcn_miss_timeout; + /** Delay to PS in milliseconds */ + t_s32 delay_to_ps; + /** PS mode */ + t_u32 ps_mode; +} mlan_ds_ps_cfg, *pmlan_ds_ps_cfg; + +/** Type definition of mlan_ds_sleep_params for MLAN_OID_PM_CFG_SLEEP_PARAMS */ +typedef struct _mlan_ds_sleep_params { + /** Error */ + t_u32 error; + /** Offset in microseconds */ + t_u32 offset; + /** Stable time in microseconds */ + t_u32 stable_time; + /** Calibration control */ + t_u32 cal_control; + /** External sleep clock */ + t_u32 ext_sleep_clk; + /** Reserved */ + t_u32 reserved; +} mlan_ds_sleep_params, *pmlan_ds_sleep_params; + +/** sleep_param */ +typedef struct _ps_sleep_param { + /** control bitmap */ + t_u32 ctrl_bitmap; + /** minimum sleep period (micro second) */ + t_u32 min_sleep; + /** maximum sleep period (micro second) */ + t_u32 max_sleep; +} ps_sleep_param; + +/** inactivity sleep_param */ +typedef struct _inact_sleep_param { + /** inactivity timeout (micro second) */ + t_u32 inactivity_to; + /** miniumu awake period (micro second) */ + t_u32 min_awake; + /** maximum awake period (micro second) */ + t_u32 max_awake; +} inact_sleep_param; + +/** flag for ps mode */ +#define PS_FLAG_PS_MODE 1 +/** flag for sleep param */ +#define PS_FLAG_SLEEP_PARAM 2 +/** flag for inactivity sleep param */ +#define PS_FLAG_INACT_SLEEP_PARAM 4 + +/** Enable Robust Coex mode */ +#define ROBUSTCOEX_GPIOCFG_ENABLE 1 +/** Disable Robust Coex mode */ +#define ROBUSTCOEX_GPIOCFG_DISABLE 0 + +/** Disable power mode */ +#define PS_MODE_DISABLE 0 +/** Enable periodic dtim ps */ +#define PS_MODE_PERIODIC_DTIM 1 +/** Enable inactivity ps */ +#define PS_MODE_INACTIVITY 2 +/** FW wake up method interface */ +#define FW_WAKEUP_METHOD_INTERFACE 1 +/** FW wake up method gpio */ +#define FW_WAKEUP_METHOD_GPIO 2 +/** mlan_ds_ps_mgmt */ +typedef struct _mlan_ds_ps_mgmt { + /** flags for valid field */ + t_u16 flags; + /** power mode */ + t_u16 ps_mode; + /** sleep param */ + ps_sleep_param sleep_param; + /** inactivity sleep param */ + inact_sleep_param inact_param; +} mlan_ds_ps_mgmt; + +/** mlan_ds_ps_info */ +typedef struct _mlan_ds_ps_info { + /** suspend allowed flag */ + t_u32 is_suspend_allowed; +} mlan_ds_ps_info; + +/** Type definition of mlan_ds_wakeup_reason for MLAN_OID_PM_HS_WAKEUP_REASON */ +typedef struct _mlan_ds_hs_wakeup_reason { + t_u16 hs_wakeup_reason; +} mlan_ds_hs_wakeup_reason; + +/** Type definition of mlan_fw_wakeup_params for MLAN_OID_PM_CFG_FW_WAKEUP_METHOD */ +typedef struct _mlan_fw_wakeup_params { + /** FW wakeup method */ + t_u16 method; + /** GPIO pin NO.*/ + t_u8 gpio_pin; +} mlan_fw_wakeup_params, *pmlan_fw_wakeup_params; + +/** Type definition of mlan_ds_ps_cfg for MLAN_OID_PM_CFG_PS_CFG */ +typedef struct _mlan_ds_bcn_timeout { + /** Beacon miss timeout period window */ + t_u16 bcn_miss_tmo_window; + /** Beacon miss timeout period */ + t_u16 bcn_miss_tmo_period; + /** Beacon reacquire timeout period window */ + t_u16 bcn_rq_tmo_window; + /** Beacon reacquire timeout period */ + t_u16 bcn_rq_tmo_period; +} mlan_ds_bcn_timeout, *pmlan_ds_bcn_timeout; + +/** Type definition of mlan_ds_pm_cfg for MLAN_IOCTL_PM_CFG */ +typedef struct _mlan_ds_pm_cfg { + /** Sub-command */ + t_u32 sub_command; + /** Power management parameter */ + union { + /** Power saving mode for MLAN_OID_PM_CFG_IEEE_PS */ + t_u32 ps_mode; + /** Host Sleep configuration for MLAN_OID_PM_CFG_HS_CFG */ + mlan_ds_hs_cfg hs_cfg; + /** Deep sleep mode for MLAN_OID_PM_CFG_DEEP_SLEEP */ + mlan_ds_auto_ds auto_deep_sleep; + /** Inactivity timeout for MLAN_OID_PM_CFG_INACTIVITY_TO */ + mlan_ds_inactivity_to inactivity_to; + /** Sleep period for MLAN_OID_PM_CFG_SLEEP_PD */ + t_u32 sleep_period; + /** PS configuration parameters for MLAN_OID_PM_CFG_PS_CFG */ + mlan_ds_ps_cfg ps_cfg; + /** FW wakeup method for MLAN_OID_PM_CFG_FW_WAKEUP_METHOD */ + mlan_fw_wakeup_params fw_wakeup_params; + /** PS configuration parameters for MLAN_OID_PM_CFG_SLEEP_PARAMS */ + mlan_ds_sleep_params sleep_params; + /** PS configuration parameters for MLAN_OID_PM_CFG_PS_MODE */ + mlan_ds_ps_mgmt ps_mgmt; + /** power info for MLAN_OID_PM_INFO */ + mlan_ds_ps_info ps_info; + /** hs wakeup reason for MLAN_OID_PM_HS_WAKEUP_REASON */ + mlan_ds_hs_wakeup_reason wakeup_reason; + /** config manamgement frame for hs wakeup */ + mlan_mgmt_frame_wakeup mgmt_filter[MAX_MGMT_FRAME_FILTER]; + /** Beacon timout parameters for MLAN_OID_PM_CFG_BCN_TIMEOUT */ + mlan_ds_bcn_timeout bcn_timeout; + } param; +} mlan_ds_pm_cfg, *pmlan_ds_pm_cfg; + +#ifdef RX_PACKET_COALESCE +typedef struct { + mlan_cmd_result_e cmd_result; /**< Firmware execution result */ + + t_u32 pkt_threshold; + /** Packet threshold */ + t_u16 delay; + /** Timeout value in milliseconds */ +} wlan_ioctl_rx_pkt_coalesce_config_t; +#endif + +/*-----------------------------------------------------------------*/ +/** WMM Configuration Group */ +/*-----------------------------------------------------------------*/ + +/** WMM TSpec size */ +#define MLAN_WMM_TSPEC_SIZE 63 +/** WMM Add TS extra IE bytes */ +#define MLAN_WMM_ADDTS_EXTRA_IE_BYTES 256 +/** WMM statistics for packets hist bins */ +#define MLAN_WMM_STATS_PKTS_HIST_BINS 7 +/** Maximum number of AC QOS queues available */ +#define MLAN_WMM_MAX_AC_QUEUES 4 + +/** + * @brief IOCTL structure to send an ADDTS request and retrieve the response. + * + * IOCTL structure from the application layer relayed to firmware to + * instigate an ADDTS management frame with an appropriate TSPEC IE as well + * as any additional IEs appended in the ADDTS Action frame. + * + * @sa woal_wmm_addts_req_ioctl + */ +typedef struct { + mlan_cmd_result_e cmd_result; /**< Firmware execution result */ + + t_u32 timeout_ms; /**< Timeout value in milliseconds */ + t_u8 ieee_status_code; /**< IEEE status code */ + + t_u32 ie_data_len; /**< Length of ie block in ie_data */ + t_u8 ie_data[MLAN_WMM_TSPEC_SIZE + /**< TSPEC to send in the ADDTS */ + + MLAN_WMM_ADDTS_EXTRA_IE_BYTES]; + /**< Extra IE buf*/ +} wlan_ioctl_wmm_addts_req_t; + +/** + * @brief IOCTL structure to send a DELTS request. + * + * IOCTL structure from the application layer relayed to firmware to + * instigate an DELTS management frame with an appropriate TSPEC IE. + * + * @sa woal_wmm_delts_req_ioctl + */ +typedef struct { + mlan_cmd_result_e cmd_result; + /**< Firmware execution result */ + t_u8 ieee_reason_code; /**< IEEE reason code sent, unused for WMM */ + t_u32 ie_data_len; /**< Length of ie block in ie_data */ + t_u8 ie_data[MLAN_WMM_TSPEC_SIZE]; + /**< TSPEC to send in the DELTS */ +} wlan_ioctl_wmm_delts_req_t; + +/** + * @brief IOCTL structure to configure a specific AC Queue's parameters + * + * IOCTL structure from the application layer relayed to firmware to + * get, set, or default the WMM AC queue parameters. + * + * - msdu_lifetime_expiry is ignored if set to 0 on a set command + * + * @sa woal_wmm_queue_config_ioctl + */ +typedef struct { + mlan_wmm_queue_config_action_e action;/**< Set, Get, or Default */ + mlan_wmm_ac_e access_category; /**< WMM_AC_BK(0) to WMM_AC_VO(3) */ + t_u16 msdu_lifetime_expiry; /**< lifetime expiry in TUs */ + t_u8 supported_rates[10]; /**< Not supported yet */ +} wlan_ioctl_wmm_queue_config_t; + +/** + * @brief IOCTL structure to start, stop, and get statistics for a WMM AC + * + * IOCTL structure from the application layer relayed to firmware to + * start or stop statistical collection for a given AC. Also used to + * retrieve and clear the collected stats on a given AC. + * + * @sa woal_wmm_queue_stats_ioctl + */ +typedef struct { + /** Action of Queue Config : Start, Stop, or Get */ + mlan_wmm_queue_stats_action_e action; + /** User Priority */ + t_u8 user_priority; + /** Number of successful packets transmitted */ + t_u16 pkt_count; + /** Packets lost; not included in pkt_count */ + t_u16 pkt_loss; + /** Average Queue delay in microseconds */ + t_u32 avg_queue_delay; + /** Average Transmission delay in microseconds */ + t_u32 avg_tx_delay; + /** Calculated used time in units of 32 microseconds */ + t_u16 used_time; + /** Calculated policed time in units of 32 microseconds */ + t_u16 policed_time; + /** Queue Delay Histogram; number of packets per queue delay range + * + * [0] - 0ms <= delay < 5ms + * [1] - 5ms <= delay < 10ms + * [2] - 10ms <= delay < 20ms + * [3] - 20ms <= delay < 30ms + * [4] - 30ms <= delay < 40ms + * [5] - 40ms <= delay < 50ms + * [6] - 50ms <= delay < msduLifetime (TUs) + */ + t_u16 delay_histogram[MLAN_WMM_STATS_PKTS_HIST_BINS]; +} wlan_ioctl_wmm_queue_stats_t, +/** Type definition of mlan_ds_wmm_queue_stats + * for MLAN_OID_WMM_CFG_QUEUE_STATS + */ +mlan_ds_wmm_queue_stats, *pmlan_ds_wmm_queue_stats; + +/** + * @brief IOCTL sub structure for a specific WMM AC Status + */ +typedef struct { + /** WMM Acm */ + t_u8 wmm_acm; + /** Flow required flag */ + t_u8 flow_required; + /** Flow created flag */ + t_u8 flow_created; + /** Disabled flag */ + t_u8 disabled; +} wlan_ioctl_wmm_queue_status_ac_t; + +/** + * @brief IOCTL structure to retrieve the WMM AC Queue status + * + * IOCTL structure from the application layer to retrieve: + * - ACM bit setting for the AC + * - Firmware status (flow required, flow created, flow disabled) + * + * @sa woal_wmm_queue_status_ioctl + */ +typedef struct { + /** WMM AC queue status */ + wlan_ioctl_wmm_queue_status_ac_t ac_status[MLAN_WMM_MAX_AC_QUEUES]; +} wlan_ioctl_wmm_queue_status_t, +/** Type definition of mlan_ds_wmm_queue_status + * for MLAN_OID_WMM_CFG_QUEUE_STATUS + */ +mlan_ds_wmm_queue_status, *pmlan_ds_wmm_queue_status; + +/** Type definition of mlan_ds_wmm_addts for MLAN_OID_WMM_CFG_ADDTS */ +typedef struct _mlan_ds_wmm_addts { + /** Result of ADDTS request */ + mlan_cmd_result_e result; + /** Timeout value in milliseconds */ + t_u32 timeout; + /** IEEE status code */ + t_u32 status_code; + /** Dialog token */ + t_u8 dialog_tok; + /** TSPEC data length */ + t_u32 ie_data_len; + /** TSPEC to send in the ADDTS + buffering for any extra IEs */ + t_u8 ie_data[MLAN_WMM_TSPEC_SIZE + MLAN_WMM_ADDTS_EXTRA_IE_BYTES]; +} mlan_ds_wmm_addts, *pmlan_ds_wmm_addts; + +/** Type definition of mlan_ds_wmm_delts for MLAN_OID_WMM_CFG_DELTS */ +typedef struct _mlan_ds_wmm_delts { + /** Result of DELTS request */ + mlan_cmd_result_e result; + /** IEEE status code */ + t_u32 status_code; + /** TSPEC data length */ + t_u8 ie_data_len; + /** TSPEC to send in the DELTS */ + t_u8 ie_data[MLAN_WMM_TSPEC_SIZE]; +} mlan_ds_wmm_delts, *pmlan_ds_wmm_delts; + +/** Type definition of mlan_ds_wmm_queue_config + * for MLAN_OID_WMM_CFG_QUEUE_CONFIG + */ +typedef struct _mlan_ds_wmm_queue_config { + /** Action of Queue Config : Set, Get, or Default */ + mlan_wmm_queue_config_action_e action; + /** WMM Access Category: WMM_AC_BK(0) to WMM_AC_VO(3) */ + mlan_wmm_ac_e access_category; + /** Lifetime expiry in TUs */ + t_u16 msdu_lifetime_expiry; + /** Reserve for future use */ + t_u8 reserved[10]; +} mlan_ds_wmm_queue_config, *pmlan_ds_wmm_queue_config; + +/** Type definition of mlan_ds_wmm_cfg for MLAN_IOCTL_WMM_CFG */ +typedef struct _mlan_ds_wmm_cfg { + /** Sub-command */ + t_u32 sub_command; + /** WMM configuration parameter */ + union { + /** WMM enable for MLAN_OID_WMM_CFG_ENABLE */ + t_u32 wmm_enable; + /** QoS configuration for MLAN_OID_WMM_CFG_QOS */ + t_u8 qos_cfg; + /** WMM add TS for MLAN_OID_WMM_CFG_ADDTS */ + mlan_ds_wmm_addts addts; + /** WMM delete TS for MLAN_OID_WMM_CFG_DELTS */ + mlan_ds_wmm_delts delts; + /** WMM queue configuration for MLAN_OID_WMM_CFG_QUEUE_CONFIG */ + mlan_ds_wmm_queue_config q_cfg; + /** AC Parameters Record WMM_AC_BE, WMM_AC_BK, WMM_AC_VI, WMM_AC_VO */ + wmm_ac_parameters_t ac_params[MAX_AC_QUEUES]; + /** WMM queue status for MLAN_OID_WMM_CFG_QUEUE_STATS */ + mlan_ds_wmm_queue_stats q_stats; + /** WMM queue status for MLAN_OID_WMM_CFG_QUEUE_STATUS */ + mlan_ds_wmm_queue_status q_status; + /** WMM TS status for MLAN_OID_WMM_CFG_TS_STATUS */ + mlan_ds_wmm_ts_status ts_status; + } param; +} mlan_ds_wmm_cfg, *pmlan_ds_wmm_cfg; + +/*-----------------------------------------------------------------*/ +/** WPS Configuration Group */ +/*-----------------------------------------------------------------*/ +/** Enumeration for WPS session */ +enum _mlan_wps_status { + MLAN_WPS_CFG_SESSION_START = 1, + MLAN_WPS_CFG_SESSION_END = 0 +}; + +/** Type definition of mlan_ds_wps_cfg for MLAN_IOCTL_WPS_CFG */ +typedef struct _mlan_ds_wps_cfg { + /** Sub-command */ + t_u32 sub_command; + /** WPS configuration parameter */ + union { + /** WPS session for MLAN_OID_WPS_CFG_SESSION */ + t_u32 wps_session; + } param; +} mlan_ds_wps_cfg, *pmlan_ds_wps_cfg; + +/*-----------------------------------------------------------------*/ +/** 802.11n Configuration Group */ +/*-----------------------------------------------------------------*/ +/** Maximum MCS */ +#define NUM_MCS_FIELD 16 + +/* Both 2.4G and 5G band selected */ +#define BAND_SELECT_BOTH 0 +/* Band 2.4G selected */ +#define BAND_SELECT_BG 1 +/* Band 5G selected */ +#define BAND_SELECT_A 2 + +/** Type definition of mlan_ds_11n_htcap_cfg for MLAN_OID_11N_HTCAP_CFG */ +typedef struct _mlan_ds_11n_htcap_cfg { + /** HT Capability information */ + t_u32 htcap; + /** Band selection */ + t_u32 misc_cfg; + /** Hardware HT cap information required */ + t_u32 hw_cap_req; +} mlan_ds_11n_htcap_cfg, *pmlan_ds_11n_htcap_cfg; + +/** Type definition of mlan_ds_11n_addba_param + * for MLAN_OID_11N_CFG_ADDBA_PARAM + */ +typedef struct _mlan_ds_11n_addba_param { + /** Timeout */ + t_u32 timeout; + /** Buffer size for ADDBA request */ + t_u32 txwinsize; + /** Buffer size for ADDBA response */ + t_u32 rxwinsize; + /** amsdu for ADDBA request */ + t_u8 txamsdu; + /** amsdu for ADDBA response */ + t_u8 rxamsdu; +} mlan_ds_11n_addba_param, *pmlan_ds_11n_addba_param; + +/** Type definition of mlan_ds_11n_tx_cfg for MLAN_OID_11N_CFG_TX */ +typedef struct _mlan_ds_11n_tx_cfg { + /** HTTxCap */ + t_u16 httxcap; + /** HTTxInfo */ + t_u16 httxinfo; + /** Band selection */ + t_u32 misc_cfg; +} mlan_ds_11n_tx_cfg, *pmlan_ds_11n_tx_cfg; + +/** BF Global Configuration */ +#define BF_GLOBAL_CONFIGURATION 0x00 +/** Performs NDP sounding for PEER specified */ +#define TRIGGER_SOUNDING_FOR_PEER 0x01 +/** TX BF interval for channel sounding */ +#define SET_GET_BF_PERIODICITY 0x02 +/** Tell FW not to perform any sounding for peer */ +#define TX_BF_FOR_PEER_ENBL 0x03 +/** TX BF SNR threshold for peer */ +#define SET_SNR_THR_PEER 0x04 +/** TX Sounding*/ +#define TX_SOUNDING_CFG 0x05 + +/* Maximum number of peer MAC and status/SNR tuples */ +#define MAX_PEER_MAC_TUPLES 10 + +/** Any new subcommand structure should be declare here */ + +/** bf global cfg args */ +typedef struct _mlan_bf_global_cfg_args { + /** Global enable/disable bf */ + t_u8 bf_enbl; + /** Global enable/disable sounding */ + t_u8 sounding_enbl; + /** FB Type */ + t_u8 fb_type; + /** SNR Threshold */ + t_u8 snr_threshold; + /** Sounding interval in milliseconds */ + t_u16 sounding_interval; + /** BF mode */ + t_u8 bf_mode; + /** Reserved */ + t_u8 reserved; +} mlan_bf_global_cfg_args; + +/** trigger sounding args */ +typedef struct _mlan_trigger_sound_args { + /** Peer MAC address */ + t_u8 peer_mac[MLAN_MAC_ADDR_LENGTH]; + /** Status */ + t_u8 status; +} mlan_trigger_sound_args; + +/** bf periodicity args */ +typedef struct _mlan_bf_periodicity_args { + /** Peer MAC address */ + t_u8 peer_mac[MLAN_MAC_ADDR_LENGTH]; + /** Current Tx BF Interval in milliseconds */ + t_u16 interval; + /** Status */ + t_u8 status; +} mlan_bf_periodicity_args; + +/** tx bf peer args */ +typedef struct _mlan_tx_bf_peer_args { + /** Peer MAC address */ + t_u8 peer_mac[MLAN_MAC_ADDR_LENGTH]; + /** Reserved */ + t_u16 reserved; + /** Enable/Disable Beamforming */ + t_u8 bf_enbl; + /** Enable/Disable sounding */ + t_u8 sounding_enbl; + /** FB Type */ + t_u8 fb_type; +} mlan_tx_bf_peer_args; + +/** SNR threshold args */ +typedef struct _mlan_snr_thr_args { + /** Peer MAC address */ + t_u8 peer_mac[MLAN_MAC_ADDR_LENGTH]; + /** SNR for peer */ + t_u8 snr; +} mlan_snr_thr_args; + +/** Type definition of mlan_ds_11n_tx_bf_cfg for MLAN_OID_11N_CFG_TX_BF_CFG */ +typedef struct _mlan_ds_11n_tx_bf_cfg { + /** BF Action */ + t_u16 bf_action; + /** Action */ + t_u16 action; + /** Number of peers */ + t_u32 no_of_peers; + union { + mlan_bf_global_cfg_args bf_global_cfg; + mlan_trigger_sound_args bf_sound[MAX_PEER_MAC_TUPLES]; + mlan_bf_periodicity_args bf_periodicity[MAX_PEER_MAC_TUPLES]; + mlan_tx_bf_peer_args tx_bf_peer[MAX_PEER_MAC_TUPLES]; + mlan_snr_thr_args bf_snr[MAX_PEER_MAC_TUPLES]; + } body; +} mlan_ds_11n_tx_bf_cfg, *pmlan_ds_11n_tx_bf_cfg; + +/** Type definition of mlan_ds_11n_amsdu_aggr_ctrl for + * MLAN_OID_11N_AMSDU_AGGR_CTRL*/ +typedef struct _mlan_ds_11n_amsdu_aggr_ctrl { + /** Enable/Disable */ + t_u16 enable; + /** Current AMSDU size valid */ + t_u16 curr_buf_size; +} mlan_ds_11n_amsdu_aggr_ctrl, *pmlan_ds_11n_amsdu_aggr_ctrl; + +/** Type definition of mlan_ds_11n_aggr_prio_tbl + * for MLAN_OID_11N_CFG_AGGR_PRIO_TBL + */ +typedef struct _mlan_ds_11n_aggr_prio_tbl { + /** ampdu priority table */ + t_u8 ampdu[MAX_NUM_TID]; + /** amsdu priority table */ + t_u8 amsdu[MAX_NUM_TID]; +} mlan_ds_11n_aggr_prio_tbl, *pmlan_ds_11n_aggr_prio_tbl; + +/** DelBA All TIDs */ +#define DELBA_ALL_TIDS 0xff +/** DelBA Tx */ +#define DELBA_TX MBIT(0) +/** DelBA Rx */ +#define DELBA_RX MBIT(1) + +/** Type definition of mlan_ds_11n_delba for MLAN_OID_11N_CFG_DELBA */ +typedef struct _mlan_ds_11n_delba { + /** TID */ + t_u8 tid; + /** Peer MAC address */ + t_u8 peer_mac_addr[MLAN_MAC_ADDR_LENGTH]; + /** Direction (Tx: bit 0, Rx: bit 1) */ + t_u8 direction; +} mlan_ds_11n_delba, *pmlan_ds_11n_delba; + +/** Type definition of mlan_ds_delba for MLAN_OID_11N_CFG_REJECT_ADDBA_REQ */ +typedef struct _mlan_ds_reject_addba_req { + /** Bit0 : host sleep activated + * Bit1 : auto reconnect enabled + * Others : reserved + */ + t_u32 conditions; +} mlan_ds_reject_addba_req, *pmlan_ds_reject_addba_req; + +/** Type definition of mlan_ds_ibss_ampdu_param */ +typedef struct _mlan_ds_ibss_ampdu_param { + /** ampdu priority table */ + t_u8 ampdu[MAX_NUM_TID]; + /** rx amdpdu setting */ + t_u8 addba_reject[MAX_NUM_TID]; +} mlan_ds_ibss_ampdu_param, *pmlan_ds_ibss_ampdu_param; + +/** Type definition of mlan_ds_11n_cfg for MLAN_IOCTL_11N_CFG */ +typedef struct _mlan_ds_11n_cfg { + /** Sub-command */ + t_u32 sub_command; + /** 802.11n configuration parameter */ + union { + /** Tx param for 11n for MLAN_OID_11N_CFG_TX */ + mlan_ds_11n_tx_cfg tx_cfg; + /** Aggr priority table for MLAN_OID_11N_CFG_AGGR_PRIO_TBL */ + mlan_ds_11n_aggr_prio_tbl aggr_prio_tbl; + /** Add BA param for MLAN_OID_11N_CFG_ADDBA_PARAM */ + mlan_ds_11n_addba_param addba_param; + /** Add BA Reject paramters for MLAN_OID_11N_CFG_ADDBA_REJECT */ + t_u8 addba_reject[MAX_NUM_TID]; + /** Tx buf size for MLAN_OID_11N_CFG_MAX_TX_BUF_SIZE */ + t_u32 tx_buf_size; + /** HT cap info configuration for MLAN_OID_11N_HTCAP_CFG */ + mlan_ds_11n_htcap_cfg htcap_cfg; + /** Tx param for 11n for MLAN_OID_11N_AMSDU_AGGR_CTRL */ + mlan_ds_11n_amsdu_aggr_ctrl amsdu_aggr_ctrl; + /** Supported MCS Set field */ + t_u8 supported_mcs_set[NUM_MCS_FIELD]; + /** Transmit Beamforming Capabilities field */ + t_u32 tx_bf_cap; + /** Transmit Beamforming configuration */ + mlan_ds_11n_tx_bf_cfg tx_bf; + /** DelBA for MLAN_OID_11N_CFG_DELBA */ + mlan_ds_11n_delba del_ba; + /** Reject Addba Req for MLAN_OID_11N_CFG_REJECT_ADDBA_REQ */ + mlan_ds_reject_addba_req reject_addba_req; + /** Control coex RX window size configuration */ + t_u32 coex_rx_winsize; + /** Control TX AMPDU configuration */ + t_u32 txaggrctrl; + /** aggrprirotity table for MLAN_OID_11N_CFG_IBSS_AMPDU_PARAM */ + mlan_ds_ibss_ampdu_param ibss_ampdu; + } param; +} mlan_ds_11n_cfg, *pmlan_ds_11n_cfg; + +/** Country code length */ +#define COUNTRY_CODE_LEN 3 + +/*-----------------------------------------------------------------*/ +/** 802.11d Configuration Group */ +/*-----------------------------------------------------------------*/ +/** Maximum subbands for 11d */ +#define MRVDRV_MAX_SUBBAND_802_11D 83 + +#ifdef STA_SUPPORT +/** Data structure for subband set */ +typedef struct _mlan_ds_subband_set_t { + /** First channel */ + t_u8 first_chan; + /** Number of channels */ + t_u8 no_of_chan; + /** Maximum Tx power in dBm */ + t_u8 max_tx_pwr; +} mlan_ds_subband_set_t; + +/** Domain regulatory information */ +typedef struct _mlan_ds_11d_domain_info { + /** Country Code */ + t_u8 country_code[COUNTRY_CODE_LEN]; + /** Band that channels in sub_band belong to */ + t_u8 band; + /** No. of subband in below */ + t_u8 no_of_sub_band; + /** Subband data to send/last sent */ + mlan_ds_subband_set_t sub_band[MRVDRV_MAX_SUBBAND_802_11D]; +} mlan_ds_11d_domain_info; +#endif + +/** Type definition of mlan_ds_11d_cfg for MLAN_IOCTL_11D_CFG */ +typedef struct _mlan_ds_11d_cfg { + /** Sub-command */ + t_u32 sub_command; + /** 802.11d configuration parameter */ + union { +#ifdef STA_SUPPORT + /** Enable for MLAN_OID_11D_CFG_ENABLE */ + t_u32 enable_11d; + /** Domain info for MLAN_OID_11D_DOMAIN_INFO */ + mlan_ds_11d_domain_info domain_info; +#endif /* STA_SUPPORT */ +#ifdef UAP_SUPPORT + /** tlv data for MLAN_OID_11D_DOMAIN_INFO */ + t_u8 domain_tlv[MAX_IE_SIZE]; +#endif /* UAP_SUPPORT */ + } param; +} mlan_ds_11d_cfg, *pmlan_ds_11d_cfg; + +typedef struct _mlan_ds_11k_cfg { + /** Sub-command */ + t_u32 sub_command; + union { + t_u32 enable_11k; + } param; +} mlan_ds_11k_cfg; + +/*-----------------------------------------------------------------*/ +/** Register Memory Access Group */ +/*-----------------------------------------------------------------*/ +/** Enumeration for CSU target device type */ +enum _mlan_csu_target_type { + MLAN_CSU_TARGET_CAU = 1, + MLAN_CSU_TARGET_PSU, +}; + +/** Enumeration for register type */ +enum _mlan_reg_type { + MLAN_REG_MAC = 1, + MLAN_REG_BBP, + MLAN_REG_RF, + MLAN_REG_CAU = 5, + MLAN_REG_PSU = 6, +}; + +/** Type definition of mlan_ds_reg_rw for MLAN_OID_REG_RW */ +typedef struct _mlan_ds_reg_rw { + /** Register type */ + t_u32 type; + /** Offset */ + t_u32 offset; + /** Value */ + t_u32 value; +} mlan_ds_reg_rw; + +/** Maximum EEPROM data */ +#define MAX_EEPROM_DATA 256 + +/** Type definition of mlan_ds_read_eeprom for MLAN_OID_EEPROM_RD */ +typedef struct _mlan_ds_read_eeprom { + /** Multiples of 4 */ + t_u16 offset; + /** Number of bytes */ + t_u16 byte_count; + /** Value */ + t_u8 value[MAX_EEPROM_DATA]; +} mlan_ds_read_eeprom; + +/** Type definition of mlan_ds_mem_rw for MLAN_OID_MEM_RW */ +typedef struct _mlan_ds_mem_rw { + /** Address */ + t_u32 addr; + /** Value */ + t_u32 value; +} mlan_ds_mem_rw; + +/** Type definition of mlan_ds_reg_mem for MLAN_IOCTL_REG_MEM */ +typedef struct _mlan_ds_reg_mem { + /** Sub-command */ + t_u32 sub_command; + /** Register memory access parameter */ + union { + /** Register access for MLAN_OID_REG_RW */ + mlan_ds_reg_rw reg_rw; + /** EEPROM access for MLAN_OID_EEPROM_RD */ + mlan_ds_read_eeprom rd_eeprom; + /** Memory access for MLAN_OID_MEM_RW */ + mlan_ds_mem_rw mem_rw; + } param; +} mlan_ds_reg_mem, *pmlan_ds_reg_mem; + +/*-----------------------------------------------------------------*/ +/** Multi-Radio Configuration Group */ +/*-----------------------------------------------------------------*/ + +/*-----------------------------------------------------------------*/ +/** 802.11h Configuration Group */ +/*-----------------------------------------------------------------*/ +#if defined(DFS_TESTING_SUPPORT) +/** Type definition of mlan_ds_11h_dfs_testing for MLAN_OID_11H_DFS_TESTING */ +typedef struct _mlan_ds_11h_dfs_testing { + /** User-configured CAC period in milliseconds, 0 to use default */ + t_u32 usr_cac_period_msec; + /** User-configured NOP period in seconds, 0 to use default */ + t_u16 usr_nop_period_sec; + /** User-configured skip channel change, 0 to disable */ + t_u8 usr_no_chan_change; + /** User-configured fixed channel to change to, 0 to use random channel */ + t_u8 usr_fixed_new_chan; +} mlan_ds_11h_dfs_testing, *pmlan_ds_11h_dfs_testing; + +/** Type definition of mlan_ds_11h_dfs_testing for MLAN_OID_11H_CHAN_NOP_INFO */ +typedef struct _mlan_ds_11h_chan_nop_info { + /** current channel */ + t_u8 curr_chan; + /** channel_width */ + t_u8 chan_width; + /** flag for chan under nop */ + t_bool chan_under_nop; + /** chan_ban_info for new channel */ + chan_band_info new_chan; +} mlan_ds_11h_chan_nop_info; +#endif + +typedef struct _mlan_ds_11h_chan_rep_req { + t_u16 startFreq; + Band_Config_t bandcfg; + t_u8 chanNum; + t_u32 millisec_dwell_time; + /**< Channel dwell time in milliseconds */ + t_u8 host_based; +} mlan_ds_11h_chan_rep_req; + +/** Type definition of mlan_ds_11h_cfg for MLAN_IOCTL_11H_CFG */ +typedef struct _mlan_ds_11h_cfg { + /** Sub-command */ + t_u32 sub_command; + union { + /** Local power constraint for MLAN_OID_11H_LOCAL_POWER_CONSTRAINT */ + t_s8 usr_local_power_constraint; +#if defined(DFS_TESTING_SUPPORT) + /** User-configuation for MLAN_OID_11H_DFS_TESTING */ + mlan_ds_11h_dfs_testing dfs_testing; + /** channel NOP information for MLAN_OID_11H_CHAN_NOP_INFO */ + mlan_ds_11h_chan_nop_info ch_nop_info; +#endif + mlan_ds_11h_chan_rep_req chan_rpt_req; + t_s8 cs_count; + } param; +} mlan_ds_11h_cfg, *pmlan_ds_11h_cfg; + +/*-----------------------------------------------------------------*/ +/** Miscellaneous Configuration Group */ +/*-----------------------------------------------------------------*/ + +/** CMD buffer size */ +#define MLAN_SIZE_OF_CMD_BUFFER 2048 + +/** LDO Internal */ +#define LDO_INTERNAL 0 +/** LDO External */ +#define LDO_EXTERNAL 1 + +/** Enumeration for IE type */ +enum _mlan_ie_type { + MLAN_IE_TYPE_GEN_IE = 0, +#ifdef STA_SUPPORT + MLAN_IE_TYPE_ARP_FILTER, +#endif /* STA_SUPPORT */ +}; + +/** Type definition of mlan_ds_misc_gen_ie for MLAN_OID_MISC_GEN_IE */ +typedef struct _mlan_ds_misc_gen_ie { + /** IE type */ + t_u32 type; + /** IE length */ + t_u32 len; + /** IE buffer */ + t_u8 ie_data[MAX_IE_SIZE]; +} mlan_ds_misc_gen_ie; + +#if defined(SDIO_MULTI_PORT_TX_AGGR) || defined(SDIO_MULTI_PORT_RX_AGGR) +/** Type definition of mlan_ds_misc_sdio_mpa_ctrl + * for MLAN_OID_MISC_SDIO_MPA_CTRL + */ +typedef struct _mlan_ds_misc_sdio_mpa_ctrl { + /** SDIO MP-A TX enable/disable */ + t_u16 tx_enable; + /** SDIO MP-A RX enable/disable */ + t_u16 rx_enable; + /** SDIO MP-A TX buf size */ + t_u16 tx_buf_size; + /** SDIO MP-A RX buf size */ + t_u16 rx_buf_size; + /** SDIO MP-A TX Max Ports */ + t_u16 tx_max_ports; + /** SDIO MP-A RX Max Ports */ + t_u16 rx_max_ports; +} mlan_ds_misc_sdio_mpa_ctrl; +#endif + +/** Type definition of mlan_ds_misc_cmd for MLAN_OID_MISC_HOST_CMD */ +typedef struct _mlan_ds_misc_cmd { + /** Command length */ + t_u32 len; + /** Command buffer */ + t_u8 cmd[MLAN_SIZE_OF_CMD_BUFFER]; +} mlan_ds_misc_cmd; + +/** Maximum number of system clocks */ +#define MLAN_MAX_CLK_NUM 16 + +/** Clock type : Configurable */ +#define MLAN_CLK_CONFIGURABLE 0 +/** Clock type : Supported */ +#define MLAN_CLK_SUPPORTED 1 + +/** Type definition of mlan_ds_misc_sys_clock for MLAN_OID_MISC_SYS_CLOCK */ +typedef struct _mlan_ds_misc_sys_clock { + /** Current system clock */ + t_u16 cur_sys_clk; + /** Clock type */ + t_u16 sys_clk_type; + /** Number of clocks */ + t_u16 sys_clk_num; + /** System clocks */ + t_u16 sys_clk[MLAN_MAX_CLK_NUM]; +} mlan_ds_misc_sys_clock; + +/** Maximum response buffer length */ +#define ASSOC_RSP_BUF_SIZE 500 + +/** Type definition of mlan_ds_misc_assoc_rsp for MLAN_OID_MISC_ASSOC_RSP */ +typedef struct _mlan_ds_misc_assoc_rsp { + /** Associate response buffer */ + t_u8 assoc_resp_buf[ASSOC_RSP_BUF_SIZE]; + /** Response buffer length */ + t_u32 assoc_resp_len; +} mlan_ds_misc_assoc_rsp; + +/** Enumeration for function init/shutdown */ +enum _mlan_func_cmd { + MLAN_FUNC_INIT = 1, + MLAN_FUNC_SHUTDOWN, +}; + +/** Enumeration for Coalescing status */ +enum _mlan_coal_status { + MLAN_MISC_COALESCING_ENABLE = 1, + MLAN_MISC_COALESCING_DISABLE = 0 +}; + +/** Net monitor filter: management frame */ +#define MLAN_NETMON_MANAGEMENT_FRAME MBIT(0) +/** Net monitor filter: control frame */ +#define MLAN_NETMON_CONTROL_FRAME MBIT(1) +/** Net monitor filter: data frame */ +#define MLAN_NETMON_DATA_FRAME MBIT(2) + +typedef struct _mlan_ds_misc_net_monitor { + /** Enable/disable network monitor */ + t_u32 enable_net_mon; + /** Set net monitor filer flag */ + t_u32 filter_flag; + /** Radio type */ + t_u32 band; + /** Channel */ + t_u32 channel; + /** Secondary channel bandwidth */ + t_u32 chan_bandwidth; +} mlan_ds_misc_net_monitor; + +/** Type definition of mlan_ds_misc_tx_datapause + * for MLAN_OID_MISC_TX_DATAPAUSE + */ +typedef struct _mlan_ds_misc_tx_datapause { + /** Tx data pause flag */ + t_u16 tx_pause; + /** Max number of Tx buffers for all PS clients */ + t_u16 tx_buf_cnt; +} mlan_ds_misc_tx_datapause; + +/** IP address length */ +#define IPADDR_LEN (16) +/** Max number of ip */ +#define MAX_IPADDR (4) +/** IP address type - NONE*/ +#define IPADDR_TYPE_NONE (0) +/** IP address type - IPv4*/ +#define IPADDR_TYPE_IPV4 (1) +/** IP operation remove */ +#define MLAN_IPADDR_OP_IP_REMOVE (0) +/** IP operation ARP filter */ +#define MLAN_IPADDR_OP_ARP_FILTER MBIT(0) +/** IP operation ARP response */ +#define MLAN_IPADDR_OP_AUTO_ARP_RESP MBIT(1) + +/** Type definition of mlan_ds_misc_ipaddr_cfg for MLAN_OID_MISC_IP_ADDR */ +typedef struct _mlan_ds_misc_ipaddr_cfg { + /** Operation code */ + t_u32 op_code; + /** IP address type */ + t_u32 ip_addr_type; + /** Number of IP */ + t_u32 ip_addr_num; + /** IP address */ + t_u8 ip_addr[MAX_IPADDR][IPADDR_LEN]; +} mlan_ds_misc_ipaddr_cfg; + +/* MEF configuration disable */ +#define MEF_CFG_DISABLE 0 +/* MEF configuration Rx filter enable */ +#define MEF_CFG_RX_FILTER_ENABLE 1 +/* MEF configuration auto ARP response */ +#define MEF_CFG_AUTO_ARP_RESP 2 +/* MEF configuration host command */ +#define MEF_CFG_HOSTCMD 0xFFFF + +/** Type definition of mlan_ds_misc_mef_cfg for MLAN_OID_MISC_MEF_CFG */ +typedef struct _mlan_ds_misc_mef_cfg { + /** Sub-ID for operation */ + t_u32 sub_id; + /** Parameter according to sub-ID */ + union { + /** MEF command buffer for MEF_CFG_HOSTCMD */ + mlan_ds_misc_cmd cmd_buf; + } param; +} mlan_ds_misc_mef_cfg; + +/** Type definition of mlan_ds_misc_cfp_code for MLAN_OID_MISC_CFP_CODE */ +typedef struct _mlan_ds_misc_cfp_code { + /** CFP table code for 2.4GHz */ + t_u32 cfp_code_bg; + /** CFP table code for 5GHz */ + t_u32 cfp_code_a; +} mlan_ds_misc_cfp_code; + +/** Type definition of mlan_ds_misc_country_code + * for MLAN_OID_MISC_COUNTRY_CODE + */ +typedef struct _mlan_ds_misc_country_code { + /** Country Code */ + t_u8 country_code[COUNTRY_CODE_LEN]; +} mlan_ds_misc_country_code; + +/** Type definition of mlan_ds_host_clock */ +typedef struct _mlan_ds_host_clock { + /** host time in secs */ + t_u64 time; + /** fw time */ + t_u64 fw_time; + /** host-bbu clock delta */ + t_u64 host_bbu_clk_delta; +} mlan_ds_host_clock; + +/** action for set */ +#define SUBSCRIBE_EVT_ACT_BITWISE_SET 0x0002 +/** action for clear */ +#define SUBSCRIBE_EVT_ACT_BITWISE_CLR 0x0003 +/** BITMAP for subscribe event rssi low */ +#define SUBSCRIBE_EVT_RSSI_LOW MBIT(0) +/** BITMAP for subscribe event snr low */ +#define SUBSCRIBE_EVT_SNR_LOW MBIT(1) +/** BITMAP for subscribe event max fail */ +#define SUBSCRIBE_EVT_MAX_FAIL MBIT(2) +/** BITMAP for subscribe event beacon missed */ +#define SUBSCRIBE_EVT_BEACON_MISSED MBIT(3) +/** BITMAP for subscribe event rssi high */ +#define SUBSCRIBE_EVT_RSSI_HIGH MBIT(4) +/** BITMAP for subscribe event snr high */ +#define SUBSCRIBE_EVT_SNR_HIGH MBIT(5) +/** BITMAP for subscribe event data rssi low */ +#define SUBSCRIBE_EVT_DATA_RSSI_LOW MBIT(6) +/** BITMAP for subscribe event data snr low */ +#define SUBSCRIBE_EVT_DATA_SNR_LOW MBIT(7) +/** BITMAP for subscribe event data rssi high */ +#define SUBSCRIBE_EVT_DATA_RSSI_HIGH MBIT(8) +/** BITMAP for subscribe event data snr high */ +#define SUBSCRIBE_EVT_DATA_SNR_HIGH MBIT(9) +/** BITMAP for subscribe event link quality */ +#define SUBSCRIBE_EVT_LINK_QUALITY MBIT(10) +/** BITMAP for subscribe event pre_beacon_lost */ +#define SUBSCRIBE_EVT_PRE_BEACON_LOST MBIT(11) +/** default PRE_BEACON_MISS_COUNT */ +#define DEFAULT_PRE_BEACON_MISS 30 + +/** Type definition of mlan_ds_subscribe_evt for MLAN_OID_MISC_CFP_CODE */ +typedef struct _mlan_ds_subscribe_evt { + /** evt action */ + t_u16 evt_action; + /** bitmap for subscribe event */ + t_u16 evt_bitmap; + /** Absolute value of RSSI threshold value (dBm) */ + t_u8 low_rssi; + /** 0--report once, 1--report everytime happen, + * N -- report only happend > N consecutive times + */ + t_u8 low_rssi_freq; + /** SNR threshold value (dB) */ + t_u8 low_snr; + /** 0--report once, 1--report everytime happen, + * N -- report only happend > N consecutive times + */ + t_u8 low_snr_freq; + /** Failure count threshold */ + t_u8 failure_count; + /** 0--report once, 1--report everytime happen, + * N -- report only happend > N consecutive times + */ + t_u8 failure_count_freq; + /** num of missed beacons */ + t_u8 beacon_miss; + /** 0--report once, 1--report everytime happen, + * N -- report only happend > N consecutive times + */ + t_u8 beacon_miss_freq; + /** Absolute value of RSSI threshold value (dBm) */ + t_u8 high_rssi; + /** 0--report once, 1--report everytime happen, + * N -- report only happend > N consecutive times + */ + t_u8 high_rssi_freq; + /** SNR threshold value (dB) */ + t_u8 high_snr; + /** 0--report once, 1--report everytime happen, + * N -- report only happend > N consecutive times + */ + t_u8 high_snr_freq; + /** Absolute value of data RSSI threshold value (dBm) */ + t_u8 data_low_rssi; + /** 0--report once, 1--report everytime happen, + * N -- report only happend > N consecutive times + */ + t_u8 data_low_rssi_freq; + /** Absolute value of data SNR threshold value (dBm) */ + t_u8 data_low_snr; + /** 0--report once, 1--report everytime happen, + * N -- report only happend > N consecutive times + */ + t_u8 data_low_snr_freq; + /** Absolute value of data RSSI threshold value (dBm) */ + t_u8 data_high_rssi; + /** 0--report once, 1--report everytime happen, + * N -- report only happend > N consecutive times + */ + t_u8 data_high_rssi_freq; + /** Absolute value of data SNR threshold value (dBm) */ + t_u8 data_high_snr; + /** 0--report once, 1--report everytime happen, + * N -- report only happend > N consecutive times + */ + t_u8 data_high_snr_freq; + /* Link SNR threshold (dB) */ + t_u16 link_snr; + /* Link SNR frequency */ + t_u16 link_snr_freq; + /* Second minimum rate value as per the rate table below */ + t_u16 link_rate; + /* Second minimum rate frequency */ + t_u16 link_rate_freq; + /* Tx latency value (us) */ + t_u16 link_tx_latency; + /* Tx latency frequency */ + t_u16 link_tx_lantency_freq; + /* Number of pre missed beacons */ + t_u8 pre_beacon_miss; +} mlan_ds_subscribe_evt; + +/** Max OTP user data length */ +#define MAX_OTP_USER_DATA_LEN 252 + +/** Type definition of mlan_ds_misc_otp_user_data + * for MLAN_OID_MISC_OTP_USER_DATA + */ +typedef struct _mlan_ds_misc_otp_user_data { + /** Reserved */ + t_u16 reserved; + /** OTP user data length */ + t_u16 user_data_length; + /** User data buffer */ + t_u8 user_data[MAX_OTP_USER_DATA_LEN]; +} mlan_ds_misc_otp_user_data; + +#ifdef WIFI_DIRECT_SUPPORT +/** flag for NOA */ +#define WIFI_DIRECT_NOA 1 +/** flag for OPP_PS */ +#define WIFI_DIRECT_OPP_PS 2 +/** Type definition of mlan_ds_wifi_direct_config + * for MLAN_OID_MISC_WIFI_DIRECT_CONFIG + */ +typedef struct _mlan_ds_wifi_direct_config { + /** flags for NOA/OPP_PS */ + t_u8 flags; + /** NoA enable/disable */ + t_u8 noa_enable; + /** index */ + t_u16 index; + /** NoA count */ + t_u8 noa_count; + /** NoA duration */ + t_u32 noa_duration; + /** NoA interval */ + t_u32 noa_interval; + /** opp ps enable/disable */ + t_u8 opp_ps_enable; + /** CT window value */ + t_u8 ct_window; +} mlan_ds_wifi_direct_config; +#endif + +#if defined(STA_SUPPORT) +/** mlan_ds_misc_pmfcfg structure */ +typedef struct _mlan_ds_misc_pmfcfg { + /** Management Frame Protection Capable */ + t_u8 mfpc; + /** Management Frame Protection Required */ + t_u8 mfpr; +} mlan_ds_misc_pmfcfg; +#endif + +/** mlan_ds_multi_chan_cfg structure */ +typedef MLAN_PACK_START struct _mlan_ds_multi_chan_cfg { + /** Channel Time */ + t_u32 channel_time; + /** Buffer Weight */ + t_u8 buffer_weight; + /** tlv len */ + t_u16 tlv_len; + /** TLV buffer */ + t_u8 tlv_buf[0]; +} MLAN_PACK_END mlan_ds_multi_chan_cfg; + +/** mlan_ds_drcs_cfg structure */ +typedef MLAN_PACK_START struct _mlan_ds_drcs_cfg { + /** Channel Index*/ + t_u16 chan_idx; + /** Channel time (in TU) for chan_idx */ + t_u8 chantime; + /** Channel swith time (in TU) for chan_idx */ + t_u8 switchtime; + /** Undoze time (in TU) for chan_idx */ + t_u8 undozetime; + /** Rx traffic control scheme when channel switch*/ + /** only valid for GC/STA interface*/ + t_u8 mode; +} MLAN_PACK_END mlan_ds_drcs_cfg; + +#define MAX_SSID_NUM 16 +#define MAX_AP_LIST 8 +#define RETRY_UNLIMITED_TIME 0xFF + +#define FW_ROAM_ENABLE MBIT(0) +#define FW_ROAM_TRIGGER_COND MBIT(1) +#define FW_ROAM_BSSID MBIT(2) +#define FW_ROAM_SSID MBIT(3) +#define FW_ROAM_RETRY_COUNT MBIT(4) +#define FW_ROAM_RSSI_PARA MBIT(5) +#define FW_ROAM_BAND_RSSI MBIT(6) +#define FW_ROAM_BGSCAN_PARAM MBIT(7) +#define FW_ROAM_EES_PARAM MBIT(8) +#define FW_ROAM_BCN_MISS_THRESHOLD MBIT(9) +#define FW_ROAM_PRE_BCN_MISS_THRESHOLD MBIT(10) +#define FW_ROAM_BLACKLIST MBIT(11) +#define FW_ROAM_REPEAT_CNT MBIT(12) + +/*Roam offload configuration for auto reconnection when suspend and resume*/ +typedef enum _roam_offload_config_mode { + ROAM_OFFLOAD_ENABLE = 1, + ROAM_OFFLOAD_SUSPEND_CFG, + ROAM_OFFLOAD_RESUME_CFG, + ROAM_OFFLOAD_PARAM_CFG, +} roam_offload_config_mode; + +typedef enum _roam_offload_set_mode { + ROAM_OFFLOAD_DISABLE = 0, + ROAM_OFFLOAD_WITH_APLIST, + ROAM_OFFLOAD_WITHOUT_APLIST, + ROAM_OFFLOAD_WITH_BSSID, + ROAM_OFFLOAD_WITH_SSID, + AUTO_RECONNECT, +} roam_offload_set_mode; + +typedef enum _roam_offload_trigger_mode { + NO_TRIGGER = 0x00, + RSSI_LOW_TRIGGER = 0x01, + PRE_BEACON_LOST_TRIGGER = 0x02, + LINK_LOST_TRIGGER = 0x04, + DEAUTH_WITH_EXT_AP_TRIGGER = 0x08, +} roam_offload_trigger_mode; + +/** mlan_ds_misc_ees_cfg structure */ +typedef MLAN_PACK_START struct _mlan_ds_misc_ees_cfg { + /* EES mode */ + t_u16 ees_mode; + /* EES report condition */ + t_u16 ees_rpt_condition; + /* High scan period(milliseconds) */ + t_u16 high_scan_period; + /* High scan count */ + t_u16 high_scan_count; + /* Middle scan period(milliseconds) */ + t_u16 mid_scan_period; + /* Middle scan count */ + t_u16 mid_scan_count; + /* Low scan period(milliseconds) */ + t_u16 low_scan_period; + /* Low scan count */ + t_u16 low_scan_count; +} MLAN_PACK_END mlan_ds_misc_ees_cfg; + +/** mlan_ds_misc_bgscan_cfg structure */ +typedef MLAN_PACK_START struct _mlan_ds_misc_bgscan_cfg { + /* BSS Type 0x1-bss independent, 0x2-bss infrastructure, 0x3-bss any */ + t_u8 bss_type; + /* Number of channels scanned for each scan */ + t_u8 channels_per_scan; + /* Interval between consective scans */ + t_u32 scan_interval; + /* Conditons to trigger report to host */ + t_u32 bg_rpt_condition; +} MLAN_PACK_END mlan_ds_misc_bgscan_cfg; + +/** mlan_ds_misc_band_rssi structure */ +typedef MLAN_PACK_START struct _mlan_ds_misc_band_rssi { + /* RSSI hysteresis */ + t_u8 rssi_hysteresis; + /* Preferred channel band for fw roaming + * 0:2.4G band; 1: 5G band; 2:4G band; 0xFF:band not set(invalid) + */ + t_u8 band_preferred; +} MLAN_PACK_END mlan_ds_misc_band_rssi; + +/** mlan_ds_misc_ssid_list structure */ +typedef MLAN_PACK_START struct _mlan_ds_misc_ssid_list { + /* SSID number */ + t_u8 ssid_num; + /* SSID for fw roaming/auto_reconnect */ + mlan_802_11_ssid ssids[MAX_SSID_NUM]; +} MLAN_PACK_END mlan_ds_misc_ssid_list; + +/** mlan_ds_misc_roam_offload_aplist structure */ +typedef MLAN_PACK_START struct _mlan_ds_misc_roam_offload_aplist { + /** Number of AP**/ + t_u8 ap_num; + /** AP mac addrs**/ + t_u8 ap_mac[MAX_AP_LIST][MLAN_MAC_ADDR_LENGTH]; +} MLAN_PACK_END mlan_ds_misc_roam_offload_aplist; + +/** _mlan_ds_misc_roam_offload_para_rssi structure */ +typedef MLAN_PACK_START struct _mlan_ds_misc_roam_offload_para_rssi { + /** Setting flag**/ + t_u8 set_flag; + /** Max value of RSSI threshold**/ + t_u8 max_rssi; + /** Min value of RSSI threshold**/ + t_u8 min_rssi; + /** Adjusting step value of RSSI threshold**/ + t_u8 step_rssi; +} MLAN_PACK_END mlan_ds_misc_roam_offload_para_rssi; + +/** mlan_ds_misc_roam_offload structure */ +typedef MLAN_PACK_START struct _mlan_ds_misc_roam_offload { + /** Enable roam offload**/ + t_u8 enable; + /** User set passphrase**/ + t_u8 userset_passphrase; + /* Condition to trigger roaming + * Bit0 : RSSI low trigger + * Bit1 : Pre-beacon lost trigger + * Bit2 : Link Lost trigger + * Bit3 : Deauth by ext-AP trigger + * Bit4 ~ Bit15 : Reserved + * value 0 : no trigger + * value 0xff : invalid + */ + t_u16 trigger_condition; + /** AP list**/ + mlan_ds_misc_roam_offload_aplist aplist; + /*Roam offload configuration mode for auto connection when suspend and resume */ + roam_offload_config_mode config_mode; + /** Retry count**/ + t_u8 retry_count; + /** RSSI para**/ + mlan_ds_misc_roam_offload_para_rssi para_rssi; + /** BSSID of reconnection**/ + mlan_802_11_mac_addr bssid_reconnect; + /* SSID List(White list) */ + mlan_ds_misc_ssid_list ssid_list; + /* Black list(BSSID list) */ + mlan_ds_misc_roam_offload_aplist black_list; + /* BAND and RSSI_HYSTERESIS set flag */ + t_u8 band_rssi_flag; + mlan_ds_misc_band_rssi band_rssi; + + /* BGSCAN params set flag */ + t_u8 bgscan_set_flag; + mlan_ds_misc_bgscan_cfg bgscan_cfg; + + /* EES mode params set flag */ + t_u8 ees_param_set_flag; + mlan_ds_misc_ees_cfg ees_cfg; + + /* Beacon miss threshold */ + t_u8 bcn_miss_threshold; + + /* Beacon miss threshold */ + t_u8 pre_bcn_miss_threshold; + + /* Scan repeat count */ + t_u16 repeat_count; +} MLAN_PACK_END mlan_ds_misc_roam_offload; + +/**Action ID for TDLS disable link*/ +#define WLAN_TDLS_DISABLE_LINK 0x00 +/**Action ID for TDLS enable link*/ +#define WLAN_TDLS_ENABLE_LINK 0x01 +/**Action ID for TDLS create link*/ +#define WLAN_TDLS_CREATE_LINK 0x02 +/**Action ID for TDLS config link*/ +#define WLAN_TDLS_CONFIG_LINK 0x03 +/*reason code*/ +#define MLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED 26 +/** TDLS operation buffer */ +typedef struct _mlan_ds_misc_tdls_oper { + /** TDLS Action */ + t_u16 tdls_action; + /** TDLS peer address */ + t_u8 peer_mac[MLAN_MAC_ADDR_LENGTH]; + /** peer capability */ + t_u16 capability; + /** peer qos info */ + t_u8 qos_info; + /** peer extend capability */ + t_u8 *ext_capab; + /** extend capability len */ + t_u8 ext_capab_len; + /** support rates */ + t_u8 *supported_rates; + /** supported rates len */ + t_u8 supported_rates_len; + /** peer ht_cap */ + t_u8 *ht_capa; +} mlan_ds_misc_tdls_oper; + +/** flag for TDLS extcap */ +#define TDLS_IE_FLAGS_EXTCAP 0x0001 +/** flag for TDLS HTCAP */ +#define TDLS_IE_FLAGS_HTCAP 0x0002 +/** flag for TDLS HTINFO */ +#define TDLS_IE_FLAGS_HTINFO 0x0004 +/** flag for TDLS Supported channels and regulatory class IE*/ +#define TDLS_IE_FLAGS_SUPP_CS_IE 0x0040 +/** flag for TDLS Qos info */ +#define TDLS_IE_FLAGS_QOS_INFO 0x0080 +/** flag for TDLS SETUP */ +#define TDLS_IE_FLAGS_SETUP 0x0100 + +/** TDLS ie buffer */ +typedef struct _mlan_ds_misc_tdls_ies { + /** TDLS peer address */ + t_u8 peer_mac[MLAN_MAC_ADDR_LENGTH]; + /** flags for request IEs */ + t_u16 flags; + /** Qos info */ + t_u8 QosInfo; + /** Extended Capabilities IE */ + t_u8 ext_cap[IEEE_MAX_IE_SIZE]; + /** HT Capabilities IE */ + t_u8 ht_cap[IEEE_MAX_IE_SIZE]; + /** HT Information IE */ + t_u8 ht_info[IEEE_MAX_IE_SIZE]; + /** supported channels */ + t_u8 supp_chan[IEEE_MAX_IE_SIZE]; + /** supported regulatory class */ + t_u8 regulatory_class[IEEE_MAX_IE_SIZE]; +} mlan_ds_misc_tdls_ies; + +#ifdef RX_PACKET_COALESCE +typedef struct _mlan_ds_misc_rx_packet_coalesce { + /** packet threshold */ + t_u32 packet_threshold; + /** timeout value */ + t_u16 delay; +} mlan_ds_misc_rx_packet_coalesce; +#endif + +/** mlan_ds_misc_dfs_repeater structure */ +typedef struct _mlan_ds_misc_dfs_repeater { + /** Set or Get */ + t_u16 action; + /** 1 on or 0 off */ + t_u16 mode; +} mlan_ds_misc_dfs_repeater; + +#define WOWLAN_MAX_PATTERN_LEN 20 +#define WOWLAN_MAX_OFFSET_LEN 50 +#define MAX_NUM_FILTERS 10 + +/** Temperature Sensor structure */ +typedef struct _mlan_ds_sensor_temp { + /** Temperature */ + t_u32 temperature; +} mlan_ds_sensor_temp; + +#define MLAN_KCK_LEN 16 +#define MLAN_KEK_LEN 16 +#define MLAN_REPLAY_CTR_LEN 8 +/** mlan_ds_misc_gtk_rekey_data */ +typedef struct _mlan_ds_misc_gtk_rekey_data { + /** key encryption key */ + t_u8 kek[MLAN_KEK_LEN]; + /** key confirmation key */ + t_u8 kck[MLAN_KCK_LEN]; + /** replay counter */ + t_u8 replay_ctr[MLAN_REPLAY_CTR_LEN]; +} mlan_ds_misc_gtk_rekey_data; +typedef struct _mlan_ds_bw_chan_oper { + /* bandwidth 20:20M 40:40M 80:80M */ + t_u8 bandwidth; + /* channel number */ + t_u8 channel; + /* Non-global operating class */ + t_u8 oper_class; +} mlan_ds_bw_chan_oper; + +typedef struct _mlan_ds_ind_rst_cfg { + /** Set or Get */ + t_u16 action; + /** oob mode enable/ disable */ + t_u8 ir_mode; + /** gpio pin */ + t_u8 gpio_pin; +} mlan_ds_ind_rst_cfg; + +/** mlan_ds_cw_mode_ctrl structure */ +typedef MLAN_PACK_START struct _mlan_ds_cw_mode_ctrl { + /** Mode of Operation 0: Disable 1: Tx Continuous Packet 2: Tx Continuous Wave */ + t_u8 mode; + /*channel */ + t_u8 channel; + /* channel info */ + t_u8 chanInfo; + /** Tx Power level in dBm */ + t_u16 txPower; + /** Packet Length */ + t_u16 pktLength; + /** bit rate Info */ + t_u32 rateInfo; +} MLAN_PACK_END mlan_ds_cw_mode_ctrl; + +#define RX_PKT_INFO MBIT(1) +/** Struct for per-packet configuration */ +typedef struct _mlan_per_pkt_cfg { + /** Type ID*/ + t_u16 type; + /** Length of payload*/ + t_u16 len; + /** Tx/Rx per-packet control */ + t_u8 tx_rx_control; + /** Number of ethernet types in ether_type array */ + t_u8 proto_type_num; + /** Array of ether_type for per-packet control */ + t_u16 ether_type[0]; +} mlan_per_pkt_cfg; + +/** Type definition of mlan_ds_misc_robustcoex_params for MLAN_IOCTL_MISC_CFG */ +typedef struct _mlan_ds_misc_robustcoex_params { + t_u16 method; + /** enable/disable robustcoex gpio cfg */ + t_u8 enable; + /** Number of GPIO */ + t_u8 gpio_num; + /** Polarity of GPIO */ + t_u8 gpio_polarity; +} mlan_ds_misc_robustcoex_params; + +/** Type definition of mlan_ds_misc_cfg for MLAN_IOCTL_MISC_CFG */ +typedef struct _mlan_ds_misc_cfg { + /** Sub-command */ + t_u32 sub_command; + /** Miscellaneous configuration parameter */ + union { + /** Generic IE for MLAN_OID_MISC_GEN_IE */ + mlan_ds_misc_gen_ie gen_ie; + /** Region code for MLAN_OID_MISC_REGION */ + t_u32 region_code; +#if defined(SDIO_MULTI_PORT_TX_AGGR) || defined(SDIO_MULTI_PORT_RX_AGGR) + /** SDIO MP-A Ctrl command for MLAN_OID_MISC_SDIO_MPA_CTRL */ + mlan_ds_misc_sdio_mpa_ctrl mpa_ctrl; +#endif + /** Hostcmd for MLAN_OID_MISC_HOST_CMD */ + mlan_ds_misc_cmd hostcmd; + /** System clock for MLAN_OID_MISC_SYS_CLOCK */ + mlan_ds_misc_sys_clock sys_clock; + /** WWS set/get for MLAN_OID_MISC_WWS */ + t_u32 wws_cfg; + /** Get associate response for MLAN_OID_MISC_ASSOC_RSP */ + mlan_ds_misc_assoc_rsp assoc_resp; + /** Function init/shutdown for MLAN_OID_MISC_INIT_SHUTDOWN */ + t_u32 func_init_shutdown; + /** Coalescing status for MLAN_OID_MISC_COALESCING_STATUS */ + t_u16 coalescing_status; + /** Custom IE for MLAN_OID_MISC_CUSTOM_IE */ + mlan_ds_misc_custom_ie cust_ie; + t_u16 tdls_idle_time; + /** TDLS configuration for MLAN_OID_MISC_TDLS_CONFIG */ + mlan_ds_misc_tdls_config tdls_config; + /** TDLS operation for MLAN_OID_MISC_TDLS_OPER */ + mlan_ds_misc_tdls_oper tdls_oper; + /** TDLS ies for MLAN_OID_MISC_GET_TDLS_IES */ + mlan_ds_misc_tdls_ies tdls_ies; + /**tdls cs off channel*/ + t_u8 tdls_cs_channel; + /** Net monitor for MLAN_OID_MISC_NET_MONITOR */ + mlan_ds_misc_net_monitor net_mon; + /** Tx data pause for MLAN_OID_MISC_TX_DATAPAUSE */ + mlan_ds_misc_tx_datapause tx_datapause; + /** IP address configuration */ + mlan_ds_misc_ipaddr_cfg ipaddr_cfg; + /** MAC control for MLAN_OID_MISC_MAC_CONTROL */ + t_u32 mac_ctrl; + /** MEF configuration for MLAN_OID_MISC_MEF_CFG */ + mlan_ds_misc_mef_cfg mef_cfg; + /** CFP code for MLAN_OID_MISC_CFP_CODE */ + mlan_ds_misc_cfp_code cfp_code; + /** Country code for MLAN_OID_MISC_COUNTRY_CODE */ + mlan_ds_misc_country_code country_code; + /** Thermal reading for MLAN_OID_MISC_THERMAL */ + t_u32 thermal; + /** Mgmt subtype mask for MLAN_OID_MISC_RX_MGMT_IND */ + t_u32 mgmt_subtype_mask; + /** subscribe event for MLAN_OID_MISC_SUBSCRIBE_EVENT */ + mlan_ds_subscribe_evt subscribe_event; +#ifdef DEBUG_LEVEL1 + /** Driver debug bit masks */ + t_u32 drvdbg; +#endif +#ifdef STA_SUPPORT + ExtCap_t ext_cap; +#endif + mlan_ds_misc_otp_user_data otp_user_data; + /** Tx control */ + t_u32 tx_control; +#if defined(STA_SUPPORT) + mlan_ds_misc_pmfcfg pmfcfg; +#endif + /** Multi-channel config for MLAN_OID_MISC_MULTI_CHAN_CFG */ + mlan_ds_multi_chan_cfg multi_chan_cfg; + /** Multi-channel policy for MLAN_OID_MISC_MULTI_CHAN_POLICY */ + t_u16 multi_chan_policy; + /** channel drcs time slicing config for MLAN_OID_MISC_DRCS_CFG */ + mlan_ds_drcs_cfg drcs_cfg[2]; +#ifdef WIFI_DIRECT_SUPPORT + mlan_ds_wifi_direct_config p2p_config; +#endif + mlan_ds_coalesce_cfg coalesce_cfg; + mlan_ds_misc_dfs_repeater dfs_repeater; +#ifdef RX_PACKET_COALESCE + mlan_ds_misc_rx_packet_coalesce rx_coalesce; +#endif + /** FW reload flag */ + t_u8 fw_reload; + /** Sensor temperature */ + mlan_ds_sensor_temp sensor_temp; + /** GTK rekey data */ + mlan_ds_misc_gtk_rekey_data gtk_rekey; + /** Bandwidth Channel operation */ + mlan_ds_bw_chan_oper bw_chan_oper; + /** Independent Reset Configuration */ + mlan_ds_ind_rst_cfg ind_rst_cfg; + /** Roam offload */ + mlan_ds_misc_roam_offload roam_offload; + /** misc tsf */ + t_u64 misc_tsf; + /** Custom regulatory domain */ + mlan_ds_custom_reg_domain custom_reg_domain; + /** cwmmode */ + mlan_ds_cw_mode_ctrl cwmode; + /** Tx/Rx per-packet control */ + t_u8 txrx_pkt_ctrl; + mlan_ds_misc_robustcoex_params robustcoexparams; + mlan_ds_host_clock host_clock; + /** boot sleep enable or disable */ + t_u16 boot_sleep; + } param; +} mlan_ds_misc_cfg, *pmlan_ds_misc_cfg; + +#endif /* !_MLAN_IOCTL_H_ */ diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_cfg80211.c b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_cfg80211.c new file mode 100644 index 000000000000..cf29d44708a4 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_cfg80211.c @@ -0,0 +1,3878 @@ +/** @file moal_cfg80211.c + * + * @brief This file contains the functions for CFG80211. + * + * Copyright (C) 2011-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +#include "moal_cfg80211.h" +#ifdef UAP_CFG80211 +#ifdef UAP_SUPPORT +#include "moal_uap.h" +#endif +#endif + +/******************************************************** + Local Variables +********************************************************/ +/** Supported rates to be advertised to the cfg80211 */ +static struct ieee80211_rate cfg80211_rates[] = { + {.bitrate = 10,.hw_value = 2,}, + {.bitrate = 20,.hw_value = 4,}, + {.bitrate = 55,.hw_value = 11}, + {.bitrate = 110,.hw_value = 22,}, + {.bitrate = 220,.hw_value = 44,}, + {.bitrate = 60,.hw_value = 12,}, + {.bitrate = 90,.hw_value = 18,}, + {.bitrate = 120,.hw_value = 24,}, + {.bitrate = 180,.hw_value = 36,}, + {.bitrate = 240,.hw_value = 48,}, + {.bitrate = 360,.hw_value = 72,}, + {.bitrate = 480,.hw_value = 96,}, + {.bitrate = 540,.hw_value = 108,}, + {.bitrate = 720,.hw_value = 144,}, +}; + +/** Channel definitions for 2 GHz to be advertised to cfg80211 */ +static struct ieee80211_channel cfg80211_channels_2ghz[] = { + {.center_freq = 2412,.hw_value = 1,.max_power = 20}, + {.center_freq = 2417,.hw_value = 2,.max_power = 20}, + {.center_freq = 2422,.hw_value = 3,.max_power = 20}, + {.center_freq = 2427,.hw_value = 4,.max_power = 20}, + {.center_freq = 2432,.hw_value = 5,.max_power = 20}, + {.center_freq = 2437,.hw_value = 6,.max_power = 20}, + {.center_freq = 2442,.hw_value = 7,.max_power = 20}, + {.center_freq = 2447,.hw_value = 8,.max_power = 20}, + {.center_freq = 2452,.hw_value = 9,.max_power = 20}, + {.center_freq = 2457,.hw_value = 10,.max_power = 20}, + {.center_freq = 2462,.hw_value = 11,.max_power = 20}, + {.center_freq = 2467,.hw_value = 12,.max_power = 20}, + {.center_freq = 2472,.hw_value = 13,.max_power = 20}, + {.center_freq = 2484,.hw_value = 14,.max_power = 20}, +}; + +/** Channel definitions for 5 GHz to be advertised to cfg80211 */ +static struct ieee80211_channel cfg80211_channels_5ghz[] = { + {.center_freq = 5180,.hw_value = 36,.max_power = 20}, + {.center_freq = 5200,.hw_value = 40,.max_power = 20}, + {.center_freq = 5220,.hw_value = 44,.max_power = 20}, + {.center_freq = 5240,.hw_value = 48,.max_power = 20}, + {.center_freq = 5260,.hw_value = 52,.max_power = 20}, + {.center_freq = 5280,.hw_value = 56,.max_power = 20}, + {.center_freq = 5300,.hw_value = 60,.max_power = 20}, + {.center_freq = 5320,.hw_value = 64,.max_power = 20}, + {.center_freq = 5500,.hw_value = 100,.max_power = 20}, + {.center_freq = 5520,.hw_value = 104,.max_power = 20}, + {.center_freq = 5540,.hw_value = 108,.max_power = 20}, + {.center_freq = 5560,.hw_value = 112,.max_power = 20}, + {.center_freq = 5580,.hw_value = 116,.max_power = 20}, + {.center_freq = 5600,.hw_value = 120,.max_power = 20}, + {.center_freq = 5620,.hw_value = 124,.max_power = 20}, + {.center_freq = 5640,.hw_value = 128,.max_power = 20}, + {.center_freq = 5660,.hw_value = 132,.max_power = 20}, + {.center_freq = 5680,.hw_value = 136,.max_power = 20}, + {.center_freq = 5700,.hw_value = 140,.max_power = 20}, + {.center_freq = 5745,.hw_value = 149,.max_power = 20}, + {.center_freq = 5765,.hw_value = 153,.max_power = 20}, + {.center_freq = 5785,.hw_value = 157,.max_power = 20}, + {.center_freq = 5805,.hw_value = 161,.max_power = 20}, + {.center_freq = 5825,.hw_value = 165,.max_power = 20}, +}; + +/******************************************************** + Global Variables +********************************************************/ +extern int cfg80211_wext; + +struct ieee80211_supported_band cfg80211_band_2ghz = { + .channels = cfg80211_channels_2ghz, + .n_channels = ARRAY_SIZE(cfg80211_channels_2ghz), + .bitrates = cfg80211_rates, + .n_bitrates = ARRAY_SIZE(cfg80211_rates), +}; + +struct ieee80211_supported_band cfg80211_band_5ghz = { + .channels = cfg80211_channels_5ghz, + .n_channels = ARRAY_SIZE(cfg80211_channels_5ghz), + .bitrates = cfg80211_rates + 5, + .n_bitrates = ARRAY_SIZE(cfg80211_rates) - 5, +}; + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) +#ifdef UAP_SUPPORT +/** Network device handlers for uAP */ +extern const struct net_device_ops woal_uap_netdev_ops; +#endif +#ifdef STA_SUPPORT +/** Network device handlers for STA */ +extern const struct net_device_ops woal_netdev_ops; +#endif +#endif + +/** gtk rekey offload mode */ +extern int gtk_rekey_offload; +/******************************************************** + Local Functions +********************************************************/ + +/******************************************************** + Global Functions +********************************************************/ + +/** + * @brief Get the private structure from wiphy + * + * @param wiphy A pointer to wiphy structure + * + * @return Pointer to moal_private + */ +void * +woal_get_wiphy_priv(struct wiphy *wiphy) +{ + return (void *)(*(unsigned long *)wiphy_priv(wiphy)); +} + +/** + * @brief Get the private structure from net device + * + * @param dev A pointer to net_device structure + * + * @return Pointer to moal_private + */ +void * +woal_get_netdev_priv(struct net_device *dev) +{ + return (void *)netdev_priv(dev); +} + +/** + * @brief Get current frequency of active interface + * + * @param priv A pointer to moal_private + * + * @return channel frequency + */ +int +woal_get_active_intf_freq(moal_private *priv) +{ + moal_handle *handle = priv->phandle; + int i; + for (i = 0; i < handle->priv_num; i++) { +#ifdef STA_SUPPORT + if (GET_BSS_ROLE(handle->priv[i]) == MLAN_BSS_ROLE_STA) { + if ((handle->priv[i]->media_connected == MTRUE) && + (handle->priv[i]->bss_type == priv->bss_type)) + return ieee80211_channel_to_frequency(handle-> + priv[i]-> + channel +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + , + (handle-> + priv[i]-> + channel + <= + 14 ? + IEEE80211_BAND_2GHZ + : + IEEE80211_BAND_5GHZ) +#endif + ); + + } +#endif +#ifdef UAP_SUPPORT + if (GET_BSS_ROLE(handle->priv[i]) == MLAN_BSS_ROLE_UAP) { + if ((handle->priv[i]->bss_started == MTRUE) && + (handle->priv[i]->bss_type == priv->bss_type)) + return ieee80211_channel_to_frequency(handle-> + priv[i]-> + channel +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + , + (handle-> + priv[i]-> + channel + <= + 14 ? + IEEE80211_BAND_2GHZ + : + IEEE80211_BAND_5GHZ) +#endif + ); + } +#endif + } + return 0; +} + +/** + * @brief Convert driver band configuration to IEEE band type + * + * @param band Driver band configuration + * + * @return IEEE band type + */ +t_u8 +woal_band_cfg_to_ieee_band(t_u32 band) +{ + t_u8 ret_radio_type; + + ENTER(); + + switch (band) { + case BAND_A: + case BAND_AN: + case BAND_A | BAND_AN: + ret_radio_type = IEEE80211_BAND_5GHZ; + break; + case BAND_B: + case BAND_G: + case BAND_B | BAND_G: + case BAND_GN: + case BAND_B | BAND_GN: + default: + ret_radio_type = IEEE80211_BAND_2GHZ; + break; + } + + LEAVE(); + return ret_radio_type; +} + +/** + * @brief Set/Enable encryption key + * + * @param priv A pointer to moal_private structure + * @param is_enable_wep Enable WEP default key + * @param cipher Cipher suite selector + * @param key A pointer to key + * @param key_len Key length + * @param seq A pointer to sequence + * @param seq_len Sequence length + * @param key_index Key index + * @param addr Mac for which key is to be set + * @param disable Key disabled or not + * @param wait_option wait option + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status +woal_cfg80211_set_key(moal_private *priv, t_u8 is_enable_wep, + t_u32 cipher, const t_u8 *key, int key_len, + const t_u8 *seq, int seq_len, t_u8 key_index, + const t_u8 *addr, int disable, t_u8 wait_option) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_sec_cfg *sec = NULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u8 bcast_addr[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + + ENTER(); + +#ifdef UAP_CFG80211 +#ifdef UAP_SUPPORT + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) { + if (is_enable_wep) { + PRINTM(MIOCTL, "Enable UAP default key=%d\n", + key_index); + priv->uap_wep_key[key_index].is_default = MTRUE; + goto done; + } + if (key && key_len && + ((cipher == WLAN_CIPHER_SUITE_WEP40) || + (cipher == WLAN_CIPHER_SUITE_WEP104))) { + priv->uap_wep_key[key_index].length = key_len; + memcpy(priv->uap_wep_key[key_index].key, key, key_len); + priv->cipher = cipher; + priv->uap_wep_key[key_index].key_index = key_index; + priv->uap_wep_key[key_index].is_default = MFALSE; + PRINTM(MIOCTL, "Set UAP WEP key: key_index=%d len=%d\n", + key_index, key_len); + goto done; + } + } +#endif +#endif + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* Fill request buffer */ + sec = (mlan_ds_sec_cfg *)req->pbuf; + sec->sub_command = MLAN_OID_SEC_CFG_ENCRYPT_KEY; + req->req_id = MLAN_IOCTL_SEC_CFG; + req->action = MLAN_ACT_SET; + + if (is_enable_wep) { + sec->param.encrypt_key.key_index = key_index; + sec->param.encrypt_key.is_current_wep_key = MTRUE; + } else if (!disable) { + if (cipher != WLAN_CIPHER_SUITE_WEP40 && + cipher != WLAN_CIPHER_SUITE_WEP104 && + cipher != WLAN_CIPHER_SUITE_TKIP && + cipher != WLAN_CIPHER_SUITE_SMS4 && + cipher != WLAN_CIPHER_SUITE_AES_CMAC && + cipher != WLAN_CIPHER_SUITE_CCMP) { + PRINTM(MERROR, "Invalid cipher suite specified\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + sec->param.encrypt_key.key_index = key_index; + if (key && key_len) { + memcpy(sec->param.encrypt_key.key_material, key, + key_len); + sec->param.encrypt_key.key_len = key_len; + } + /* Set WAPI key */ + if (cipher == WLAN_CIPHER_SUITE_SMS4) { + sec->param.encrypt_key.is_wapi_key = MTRUE; + if (seq_len) { + memcpy(sec->param.encrypt_key.pn, seq, PN_SIZE); + DBG_HEXDUMP(MCMD_D, "WAPI PN", + sec->param.encrypt_key.pn, seq_len); + } + } + if (addr) { + memcpy(sec->param.encrypt_key.mac_addr, addr, ETH_ALEN); + if (0 == + memcmp(sec->param.encrypt_key.mac_addr, bcast_addr, + ETH_ALEN)) + sec->param.encrypt_key.key_flags = + KEY_FLAG_GROUP_KEY; + else + sec->param.encrypt_key.key_flags = + KEY_FLAG_SET_TX_KEY; + } else { + memcpy(sec->param.encrypt_key.mac_addr, bcast_addr, + ETH_ALEN); + sec->param.encrypt_key.key_flags = + KEY_FLAG_GROUP_KEY | KEY_FLAG_SET_TX_KEY; + } + if (seq && seq_len) { + memcpy(sec->param.encrypt_key.pn, seq, seq_len); + sec->param.encrypt_key.key_flags |= + KEY_FLAG_RX_SEQ_VALID; + } + + if (cipher == WLAN_CIPHER_SUITE_AES_CMAC) { + sec->param.encrypt_key.key_flags |= + KEY_FLAG_AES_MCAST_IGTK; + } + } else { + if (key_index == KEY_INDEX_CLEAR_ALL) + sec->param.encrypt_key.key_disable = MTRUE; + else { + sec->param.encrypt_key.key_remove = MTRUE; + sec->param.encrypt_key.key_index = key_index; + } + sec->param.encrypt_key.key_flags = KEY_FLAG_REMOVE_KEY; + if (addr) + memcpy(sec->param.encrypt_key.mac_addr, addr, ETH_ALEN); + } + + /* Send IOCTL request to MLAN */ + ret = woal_request_ioctl(priv, req, wait_option); + +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Enable the WEP key to driver + * + * @param priv A pointer to moal_private structure + * @param key A pointer to key data + * @param key_len Length of the key data + * @param index Key index + * @param wait_option wait_option + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +woal_cfg80211_set_wep_keys(moal_private *priv, const t_u8 *key, int key_len, + t_u8 index, t_u8 wait_option) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u32 cipher = 0; + + ENTER(); + + if (key_len) { + if (key_len == 5) + cipher = WLAN_CIPHER_SUITE_WEP40; + else + cipher = WLAN_CIPHER_SUITE_WEP104; + ret = woal_cfg80211_set_key(priv, 0, cipher, key, key_len, NULL, + 0, index, NULL, 0, wait_option); + } else { + /* No key provided so it is enable key. We + * want to just set the transmit key index */ + woal_cfg80211_set_key(priv, 1, cipher, key, key_len, NULL, 0, + index, NULL, 0, wait_option); + } + + LEAVE(); + return ret; +} + +/** + * @brief clear all mgmt ies + * + * @param priv A pointer to moal private structure + * @param wait_option wait_option + * @return N/A + */ +void +woal_clear_all_mgmt_ies(moal_private *priv, t_u8 wait_option) +{ + t_u16 mask = 0; + /* clear BEACON WPS/P2P IE */ + if (priv->beacon_wps_index != MLAN_CUSTOM_IE_AUTO_IDX_MASK) { + PRINTM(MCMND, "Clear BEACON WPS ie\n"); + woal_cfg80211_mgmt_frame_ie(priv, NULL, 0, NULL, 0, NULL, 0, + NULL, 0, MGMT_MASK_BEACON_WPS_P2P, + wait_option); + priv->beacon_wps_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + } + if (priv->assocresp_qos_map_index != MLAN_CUSTOM_IE_AUTO_IDX_MASK) { + PRINTM(MCMND, "Clear associate response QOS map ie\n"); + woal_cfg80211_mgmt_frame_ie(priv, NULL, 0, NULL, 0, NULL, 0, + NULL, 0, + MGMT_MASK_ASSOC_RESP_QOS_MAP, + wait_option); + priv->assocresp_qos_map_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + } + /* clear mgmt frame ies */ + if (priv->probereq_index != MLAN_CUSTOM_IE_AUTO_IDX_MASK) + mask |= MGMT_MASK_PROBE_REQ; + if (priv->beacon_index != MLAN_CUSTOM_IE_AUTO_IDX_MASK) + mask |= MGMT_MASK_BEACON; + if (priv->proberesp_index != MLAN_CUSTOM_IE_AUTO_IDX_MASK) + mask |= MGMT_MASK_PROBE_RESP; + if (priv->assocresp_index != MLAN_CUSTOM_IE_AUTO_IDX_MASK) + mask |= MGMT_MASK_ASSOC_RESP; + if (priv->proberesp_p2p_index != MLAN_CUSTOM_IE_AUTO_IDX_MASK) + mask |= MGMT_MASK_PROBE_RESP; + if (priv->proberesp_vendor_index != MLAN_CUSTOM_IE_AUTO_IDX_MASK) + mask |= MGMT_MASK_PROBE_RESP; + if (mask) { + PRINTM(MCMND, "Clear IES: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n", + priv->beacon_index, priv->probereq_index, + priv->proberesp_index, priv->assocresp_index, + priv->proberesp_p2p_index, priv->proberesp_vendor_index); + woal_cfg80211_mgmt_frame_ie(priv, NULL, 0, NULL, 0, NULL, 0, + NULL, 0, mask, wait_option); + } + priv->probereq_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + priv->beacon_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + priv->proberesp_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + priv->assocresp_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + priv->proberesp_p2p_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + priv->proberesp_vendor_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; +} + +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) +/** + * @brief set bss role + * + * @param priv A pointer to moal private structure + * @param action Action: set or get + * @param role A pointer to bss role + * + * @return 0 -- success, otherwise fail + */ +int +woal_cfg80211_bss_role_cfg(moal_private *priv, t_u16 action, t_u8 *bss_role) +{ + int ret = 0; + + ENTER(); + + if (action == MLAN_ACT_SET) { + /* Reset interface */ + woal_reset_intf(priv, MOAL_IOCTL_WAIT, MFALSE); + } + + if (MLAN_STATUS_SUCCESS != + woal_bss_role_cfg(priv, action, MOAL_IOCTL_WAIT, bss_role)) { + ret = -EFAULT; + goto done; + } + + if (action == MLAN_ACT_SET) { + /* set back the mac address */ + woal_request_set_mac_address(priv); + /* clear the mgmt ies */ + woal_clear_all_mgmt_ies(priv, MOAL_IOCTL_WAIT); + /* Initialize private structures */ + woal_init_priv(priv, MOAL_IOCTL_WAIT); + + /* Enable interfaces */ + netif_device_attach(priv->netdev); + woal_start_queue(priv->netdev); + } + +done: + LEAVE(); + return ret; +} +#endif + +#if defined(WIFI_DIRECT_SUPPORT) +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION +/** + * @brief This function display P2P public action frame type + * + * @param buf A buffer to a management frame + * @param len buffer len + * @param chan the channel + * @param flag Tx/Rx flag. Tx:flag = 1;Rx:flag = 0; + * + * @return N/A + */ +void +woal_cfg80211_display_p2p_actframe(const t_u8 *buf, int len, + struct ieee80211_channel *chan, + const t_u8 flag) +{ + const t_u8 p2p_oui[] = { 0x50, 0x6f, 0x9a, 0x09 }; + t_u8 subtype; + + ENTER(); + + if (!buf || len < (P2P_ACT_FRAME_OUI_SUBTYPE_OFFSET + 1)) { + LEAVE(); + return; + } + + if (((struct ieee80211_mgmt *)buf)->u.action.category == + P2P_ACT_FRAME_CATEGORY && + !memcmp(buf + P2P_ACT_FRAME_OUI_OFFSET, p2p_oui, sizeof(p2p_oui))) { + subtype = *(buf + P2P_ACT_FRAME_OUI_SUBTYPE_OFFSET); + switch (subtype) { + case P2P_GO_NEG_REQ: + PRINTM(MMSG, + "wlan: %s P2P Group Owner Negotiation Req Frame, channel=%d\n", + (flag) ? "TX" : "RX", + (chan) ? chan->hw_value : 0); + break; + case P2P_GO_NEG_RSP: + PRINTM(MMSG, + "wlan: %s P2P Group Owner Negotiation Rsp Frame, channel=%d\n", + (flag) ? "TX" : "RX", + (chan) ? chan->hw_value : 0); + break; + case P2P_GO_NEG_CONF: + PRINTM(MMSG, + "wlan: %s P2P Group Owner Negotiation Confirm Frame, channel=%d\n", + (flag) ? "TX" : "RX", + (chan) ? chan->hw_value : 0); + break; + case P2P_INVITE_REQ: + PRINTM(MMSG, + "wlan: %s P2P Invitation Request, channel=%d\n", + (flag) ? "TX" : "RX", + (chan) ? chan->hw_value : 0); + break; + case P2P_INVITE_RSP: + PRINTM(MMSG, + "wlan: %s P2P Invitation Response, channel=%d\n", + (flag) ? "TX" : "RX", + (chan) ? chan->hw_value : 0); + break; + case P2P_DEVDIS_REQ: + PRINTM(MMSG, + "wlan: %s P2P Device Discoverability Request, channel=%d\n", + (flag) ? "TX" : "RX", + (chan) ? chan->hw_value : 0); + break; + case P2P_DEVDIS_RSP: + PRINTM(MIOCTL, + "wlan: %s P2P Device Discoverability Response, channel=%d\n", + (flag) ? "TX" : "RX", + (chan) ? chan->hw_value : 0); + break; + case P2P_PROVDIS_REQ: + PRINTM(MMSG, + "wlan: %s P2P Provision Discovery Request, channel=%d\n", + (flag) ? "TX" : "RX", + (chan) ? chan->hw_value : 0); + break; + case P2P_PROVDIS_RSP: + PRINTM(MMSG, + "wlan: %s P2P Provision Discovery Response, channnel=%d\n", + (flag) ? "TX" : "RX", + (chan) ? chan->hw_value : 0); + break; + default: + PRINTM(MMSG, + "wlan: %s Unknow P2P Action Frame, channel=%d, subtype=%d\n", + (flag) ? "TX" : "RX", + (chan) ? chan->hw_value : 0, subtype); + break; + } + } + + LEAVE(); + return; +} + +/** + * @brief initialize p2p client for wpa_supplicant + * + * @param priv A pointer to moal private structure + * + * @return 0 -- success, otherwise fail + */ +int +woal_cfg80211_init_p2p_client(moal_private *priv) +{ + int ret = MLAN_STATUS_SUCCESS; + t_u16 wifi_direct_mode = WIFI_DIRECT_MODE_DISABLE; + t_u8 bss_role; + + ENTER(); + + /* bss type check */ + if (priv->bss_type != MLAN_BSS_TYPE_WIFIDIRECT) { + PRINTM(MERROR, "Unexpected bss type when init p2p client\n"); + ret = -EFAULT; + goto done; + } + + /* get the bss role */ + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_bss_role_cfg(priv, MLAN_ACT_GET, &bss_role)) { + ret = -EFAULT; + goto done; + } + + if (bss_role != MLAN_BSS_ROLE_STA) { + bss_role = MLAN_BSS_ROLE_STA; + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_bss_role_cfg(priv, MLAN_ACT_SET, &bss_role)) { + ret = -EFAULT; + goto done; + } + } + + wifi_direct_mode = WIFI_DIRECT_MODE_DISABLE; + if (MLAN_STATUS_SUCCESS != + woal_wifi_direct_mode_cfg(priv, MLAN_ACT_SET, &wifi_direct_mode)) { + ret = -EFAULT; + goto done; + } + + /* first, init wifi direct to listen mode */ + wifi_direct_mode = WIFI_DIRECT_MODE_LISTEN; + if (MLAN_STATUS_SUCCESS != + woal_wifi_direct_mode_cfg(priv, MLAN_ACT_SET, &wifi_direct_mode)) { + ret = -EFAULT; + goto done; + } + + /* second, init wifi direct client */ + wifi_direct_mode = WIFI_DIRECT_MODE_CLIENT; + if (MLAN_STATUS_SUCCESS != + woal_wifi_direct_mode_cfg(priv, MLAN_ACT_SET, &wifi_direct_mode)) { + ret = -EFAULT; + goto done; + } +done: + LEAVE(); + return ret; +} + +/** + * @brief initialize p2p GO for wpa_supplicant + * + * @param priv A pointer to moal private structure + * + * @return 0 -- success, otherwise fail + */ +int +woal_cfg80211_init_p2p_go(moal_private *priv) +{ + int ret = MLAN_STATUS_SUCCESS; + t_u16 wifi_direct_mode; + t_u8 bss_role; + mlan_ds_wifi_direct_config p2p_config; + mlan_ds_ps_mgmt ps_mgmt; + + ENTER(); + + /* bss type check */ + if (priv->bss_type != MLAN_BSS_TYPE_WIFIDIRECT) { + PRINTM(MERROR, "Unexpected bss type when init p2p GO\n"); + ret = -EFAULT; + goto done; + } + + wifi_direct_mode = WIFI_DIRECT_MODE_DISABLE; + if (MLAN_STATUS_SUCCESS != + woal_wifi_direct_mode_cfg(priv, MLAN_ACT_SET, &wifi_direct_mode)) { + ret = -EFAULT; + goto done; + } + + /* first, init wifi direct to listen mode */ + wifi_direct_mode = WIFI_DIRECT_MODE_LISTEN; + if (MLAN_STATUS_SUCCESS != + woal_wifi_direct_mode_cfg(priv, MLAN_ACT_SET, &wifi_direct_mode)) { + ret = -EFAULT; + goto done; + } + + /* second, init wifi direct to GO mode */ + wifi_direct_mode = WIFI_DIRECT_MODE_GO; + if (MLAN_STATUS_SUCCESS != + woal_wifi_direct_mode_cfg(priv, MLAN_ACT_SET, &wifi_direct_mode)) { + ret = -EFAULT; + goto done; + } + + /* get the bss role, and set it to uAP */ + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_bss_role_cfg(priv, MLAN_ACT_GET, &bss_role)) { + ret = -EFAULT; + goto done; + } + + if (bss_role != MLAN_BSS_ROLE_UAP) { + bss_role = MLAN_BSS_ROLE_UAP; + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_bss_role_cfg(priv, MLAN_ACT_SET, &bss_role)) { + ret = -EFAULT; + goto done; + } + } +/* NoA:-- Interval = 100TUs and Duration= 50TUs, count=255*/ +#define DEF_NOA_COUNT 255 + if (priv->phandle->noa_duration) { + memset(&p2p_config, 0, sizeof(p2p_config)); + p2p_config.noa_enable = MTRUE; + p2p_config.index = 0; + p2p_config.noa_count = DEF_NOA_COUNT; + p2p_config.noa_duration = priv->phandle->noa_duration; + p2p_config.noa_interval = priv->phandle->noa_interval; + p2p_config.flags = WIFI_DIRECT_NOA; + woal_p2p_config(priv, MLAN_ACT_SET, &p2p_config); + memset(&ps_mgmt, 0, sizeof(ps_mgmt)); + ps_mgmt.flags = PS_FLAG_PS_MODE; + ps_mgmt.ps_mode = PS_MODE_INACTIVITY; + woal_set_get_uap_power_mode(priv, MLAN_ACT_SET, &ps_mgmt); + PRINTM(MMSG, "Enable NOA: duration=%d, interval=%d\n", + priv->phandle->noa_duration, + priv->phandle->noa_interval); + } +done: + LEAVE(); + return ret; +} + +/** + * @brief reset bss role and wifi direct mode for wpa_supplicant + * + * @param priv A pointer to moal private structure + * + * @return 0 -- success, otherwise fail + */ +int +woal_cfg80211_deinit_p2p(moal_private *priv) +{ + int ret = MLAN_STATUS_SUCCESS; + t_u16 wifi_direct_mode; + t_u8 bss_role; + t_u8 channel_status; + moal_private *remain_priv = NULL; + mlan_ds_ps_mgmt ps_mgmt; + + ENTER(); + + /* bss type check */ + if (priv->bss_type != MLAN_BSS_TYPE_WIFIDIRECT) { + PRINTM(MERROR, "Unexpected bss type when deinit p2p\n"); + ret = -EFAULT; + goto done; + } + /* unregister mgmt frame from FW */ + if (priv->mgmt_subtype_mask) { + priv->mgmt_subtype_mask = 0; + if (woal_reg_rx_mgmt_ind(priv, MLAN_ACT_SET, + &priv->mgmt_subtype_mask, + MOAL_IOCTL_WAIT)) { + PRINTM(MERROR, + "deinit_p2p: fail to unregister mgmt frame\n"); + ret = -EFAULT; + goto done; + } + } + /* cancel previous remain on channel */ + if (priv->phandle->remain_on_channel) { + remain_priv = + priv->phandle->priv[priv->phandle->remain_bss_index]; + if (!remain_priv) { + PRINTM(MERROR, + "deinit_p2p: wrong remain_bss_index=%d\n", + priv->phandle->remain_bss_index); + ret = -EFAULT; + goto done; + } + if (woal_cfg80211_remain_on_channel_cfg + (remain_priv, MOAL_IOCTL_WAIT, MTRUE, &channel_status, NULL, + 0, 0)) { + PRINTM(MERROR, + "deinit_p2p: Fail to cancel remain on channel\n"); + ret = -EFAULT; + goto done; + } + if (priv->phandle->cookie) { + cfg80211_remain_on_channel_expired( +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 6, 0) + remain_priv-> + netdev, +#else + remain_priv-> + wdev, +#endif + priv-> + phandle-> + cookie, + &priv-> + phandle->chan, +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 8, 0) + priv-> + phandle-> + channel_type, +#endif + GFP_ATOMIC); + priv->phandle->cookie = 0; + } + priv->phandle->remain_on_channel = MFALSE; + } + + /* get the bss role */ + if (MLAN_STATUS_SUCCESS != woal_cfg80211_bss_role_cfg(priv, + MLAN_ACT_GET, + &bss_role)) { + ret = -EFAULT; + goto done; + } + + /* reset bss role */ + if (bss_role != MLAN_BSS_ROLE_STA) { + memset(&ps_mgmt, 0, sizeof(ps_mgmt)); + ps_mgmt.flags = PS_FLAG_PS_MODE; + ps_mgmt.ps_mode = PS_MODE_DISABLE; + woal_set_get_uap_power_mode(priv, MLAN_ACT_SET, &ps_mgmt); + bss_role = MLAN_BSS_ROLE_STA; + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_bss_role_cfg(priv, MLAN_ACT_SET, &bss_role)) { + ret = -EFAULT; + goto done; + } + } + + wifi_direct_mode = WIFI_DIRECT_MODE_DISABLE; + if (MLAN_STATUS_SUCCESS != + woal_wifi_direct_mode_cfg(priv, MLAN_ACT_SET, &wifi_direct_mode)) { + ret = -EFAULT; + goto done; + } +done: + LEAVE(); + return ret; +} +#endif /* KERNEL_VERSION */ +#endif /* WIFI_DIRECT_SUPPORT && V14_FEATURE */ + +/** + * @brief Request the driver to change the interface type + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param type Virtual interface types + * @param flags Flags + * @param params A pointer to vif_params structure + * + * @return 0 -- success, otherwise fail + */ +int +woal_cfg80211_change_virtual_intf(struct wiphy *wiphy, + struct net_device *dev, + enum nl80211_iftype type, +#if CFG80211_VERSION_CODE < KERNEL_VERSION(4, 12, 0) + u32 *flags, +#endif + struct vif_params *params) +{ + int ret = 0; + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + mlan_ds_bss *bss = NULL; + mlan_ioctl_req *req = NULL; +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) + t_u8 bss_role; +#endif + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (dev->ieee80211_ptr->iftype == NL80211_IFTYPE_MONITOR) { + ret = -EFAULT; + goto done; + } + + if (priv->wdev->iftype == type) { + PRINTM(MINFO, "Already set to required type\n"); + goto done; + } +#ifdef UAP_SUPPORT + if ((priv->bss_type == MLAN_BSS_TYPE_UAP) && (priv->bss_index > 0)) { + priv->wdev->iftype = type; + PRINTM(MMSG, "%s: Skip change virtual intf on uap: type=%d\n", + dev->name, type); + goto done; + } +#endif + + PRINTM(MIOCTL, "%s: change virturl intf=%d\n", dev->name, type); + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + /** cancel previous remain on channel to avoid firmware hang */ + if (priv->phandle->remain_on_channel) { + t_u8 channel_status; + moal_private *remain_priv = NULL; + remain_priv = + priv->phandle->priv[priv->phandle->remain_bss_index]; + if (!remain_priv) { + PRINTM(MERROR, + "change_virtual_intf:wrong remain_bss_index=%d\n", + priv->phandle->remain_bss_index); + ret = -EFAULT; + goto done; + } + if (woal_cfg80211_remain_on_channel_cfg + (remain_priv, MOAL_IOCTL_WAIT, MTRUE, &channel_status, NULL, + 0, 0)) { + PRINTM(MERROR, + "change_virtual_intf: Fail to cancel remain on channel\n"); + ret = -EFAULT; + goto done; + } + if (priv->phandle->cookie) { + cfg80211_remain_on_channel_expired( +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 6, 0) + remain_priv-> + netdev, +#else + remain_priv-> + wdev, +#endif + priv-> + phandle-> + cookie, + &priv-> + phandle->chan, +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 8, 0) + priv-> + phandle-> + channel_type, +#endif + GFP_ATOMIC); + priv->phandle->cookie = 0; + } + priv->phandle->remain_on_channel = MFALSE; + } +#endif + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + bss = (mlan_ds_bss *)req->pbuf; + bss->sub_command = MLAN_OID_BSS_MODE; + req->req_id = MLAN_IOCTL_BSS; + req->action = MLAN_ACT_SET; + + switch (type) { + case NL80211_IFTYPE_ADHOC: + bss->param.bss_mode = MLAN_BSS_MODE_IBSS; + priv->wdev->iftype = NL80211_IFTYPE_ADHOC; + PRINTM(MINFO, "Setting interface type to adhoc\n"); + break; + case NL80211_IFTYPE_STATION: +#if defined(WIFI_DIRECT_SUPPORT) +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION + if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT + && (priv->wdev->iftype == NL80211_IFTYPE_AP + || priv->wdev->iftype == NL80211_IFTYPE_P2P_GO + || priv->wdev->iftype == NL80211_IFTYPE_P2P_CLIENT)) { + if (priv->phandle->is_go_timer_set) { + woal_cancel_timer(&priv->phandle->go_timer); + priv->phandle->is_go_timer_set = MFALSE; + } + /* if we support wifi direct && priv->bss_type == wifi_direct, + * and currently the interface type is AP or GO or client, + * that means wpa_supplicant deinit() wifi direct interface, + * so we should deinit bss_role and wifi direct mode, + * for other bss_type, we should not update bss_role + * and wifi direct mode */ + + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_deinit_p2p(priv)) { + ret = -EFAULT; + goto done; + } + } +#endif /* KERNEL_VERSION */ +#endif /* WIFI_DIRECT_SUPPORT && V14_FEATURE */ +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) + if (priv->bss_type == MLAN_BSS_TYPE_UAP) { + woal_cfg80211_del_beacon(wiphy, dev); + bss_role = MLAN_BSS_ROLE_STA; + woal_cfg80211_bss_role_cfg(priv, MLAN_ACT_SET, + &bss_role); + PRINTM(MIOCTL, "set bss role for STA\n"); + } +#endif + bss->param.bss_mode = MLAN_BSS_MODE_INFRA; + priv->wdev->iftype = NL80211_IFTYPE_STATION; + PRINTM(MINFO, "Setting interface type to managed\n"); + break; +#if defined(WIFI_DIRECT_SUPPORT) +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION + case NL80211_IFTYPE_P2P_CLIENT: + if (priv->phandle->is_go_timer_set) { + woal_cancel_timer(&priv->phandle->go_timer); + priv->phandle->is_go_timer_set = MFALSE; + } + + if (MLAN_STATUS_SUCCESS != woal_cfg80211_init_p2p_client(priv)) { + ret = -EFAULT; + goto done; + } + + bss->param.bss_mode = MLAN_BSS_MODE_INFRA; + priv->wdev->iftype = NL80211_IFTYPE_P2P_CLIENT; + PRINTM(MINFO, "Setting interface type to P2P client\n"); + + break; +#endif /* KERNEL_VERSION */ +#endif /* WIFI_DIRECT_SUPPORT && V14_FEATURE */ + case NL80211_IFTYPE_AP: +#if defined(WIFI_DIRECT_SUPPORT) +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION + case NL80211_IFTYPE_P2P_GO: + if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT) { + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_init_p2p_go(priv)) { + ret = -EFAULT; + goto done; + } + priv->phandle->is_go_timer_set = MTRUE; + woal_mod_timer(&priv->phandle->go_timer, + MOAL_TIMER_10S); + } + if (type == NL80211_IFTYPE_P2P_GO) + priv->wdev->iftype = NL80211_IFTYPE_P2P_GO; +#endif +#endif +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) + if (priv->bss_type == MLAN_BSS_TYPE_STA) { +#ifdef STA_CFG80211 + /** cancel pending scan */ + woal_cancel_scan(priv, MOAL_IOCTL_WAIT); + +#endif + if (priv->probereq_index != + MLAN_CUSTOM_IE_AUTO_IDX_MASK) + woal_cfg80211_mgmt_frame_ie(priv, NULL, 0, NULL, + 0, NULL, 0, NULL, 0, + MGMT_MASK_PROBE_REQ, + MOAL_IOCTL_WAIT); + + bss_role = MLAN_BSS_ROLE_UAP; + woal_cfg80211_bss_role_cfg(priv, MLAN_ACT_SET, + &bss_role); + PRINTM(MIOCTL, "set bss role for AP\n"); + } +#endif + if (type == NL80211_IFTYPE_AP) + priv->wdev->iftype = NL80211_IFTYPE_AP; + PRINTM(MINFO, "Setting interface type to P2P GO\n"); + + /* there is no need for P2P GO to set bss_mode */ + goto done; + + break; + + case NL80211_IFTYPE_UNSPECIFIED: + bss->param.bss_mode = MLAN_BSS_MODE_AUTO; + priv->wdev->iftype = NL80211_IFTYPE_STATION; + PRINTM(MINFO, "Setting interface type to auto\n"); + break; + default: + ret = -EINVAL; + break; + } + if (ret) + goto done; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (MLAN_STATUS_SUCCESS != status) { + ret = -EFAULT; + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Request the driver to change the value of fragment + * threshold or rts threshold or retry limit + * + * @param wiphy A pointer to wiphy structure + * @param changed Change flags + * + * @return 0 -- success, otherwise fail + */ +int +woal_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed) +{ + moal_private *priv = NULL; + moal_handle *handle = (moal_handle *)woal_get_wiphy_priv(wiphy); +#ifdef UAP_CFG80211 +#ifdef UAP_SUPPORT + mlan_uap_bss_param *sys_cfg = NULL; +#endif +#endif + int frag_thr = wiphy->frag_threshold; + int rts_thr = wiphy->rts_threshold; + int retry = wiphy->retry_long; + + ENTER(); + + priv = woal_get_priv(handle, MLAN_BSS_ROLE_ANY); + if (!priv) { + LEAVE(); + return -EFAULT; + } + if (rts_thr == MLAN_FRAG_RTS_DISABLED) + rts_thr = MLAN_RTS_MAX_VALUE; + if (frag_thr == MLAN_FRAG_RTS_DISABLED) + frag_thr = MLAN_FRAG_MAX_VALUE; + +#ifdef UAP_CFG80211 +#ifdef UAP_SUPPORT + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) { + sys_cfg = kzalloc(sizeof(mlan_uap_bss_param), GFP_ATOMIC); + if (!sys_cfg) { + PRINTM(MERROR, + "Fail to alloc memory for mlan_uap_bss_param\n"); + LEAVE(); + return -EFAULT; + } + /* Initialize the invalid values so that the correct + * values below are downloaded to firmware */ + woal_set_sys_config_invalid_data(sys_cfg); + sys_cfg->frag_threshold = frag_thr; + sys_cfg->rts_threshold = rts_thr; + sys_cfg->retry_limit = retry; + + if ((changed & WIPHY_PARAM_RTS_THRESHOLD) || + (changed & WIPHY_PARAM_FRAG_THRESHOLD) || + (changed & + (WIPHY_PARAM_RETRY_LONG | WIPHY_PARAM_RETRY_SHORT))) { + if (woal_set_get_sys_config + (priv, MLAN_ACT_SET, MOAL_IOCTL_WAIT, sys_cfg)) { + kfree(sys_cfg); + goto fail; + } + } + kfree(sys_cfg); + } +#endif +#endif + +#ifdef STA_CFG80211 +#ifdef STA_SUPPORT + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) { + if (changed & WIPHY_PARAM_RTS_THRESHOLD) { + if (woal_set_get_rts + (priv, MLAN_ACT_SET, MOAL_IOCTL_WAIT, &rts_thr)) + goto fail; + } + if (changed & WIPHY_PARAM_FRAG_THRESHOLD) { + if (woal_set_get_frag + (priv, MLAN_ACT_SET, MOAL_IOCTL_WAIT, &frag_thr)) + goto fail; + } + if (changed & + (WIPHY_PARAM_RETRY_LONG | WIPHY_PARAM_RETRY_SHORT)) + if (woal_set_get_retry + (priv, MLAN_ACT_SET, MOAL_IOCTL_WAIT, &retry)) + goto fail; + } +#endif +#endif + LEAVE(); + return 0; + +fail: + PRINTM(MERROR, "Failed to change wiphy params %x\n", changed); + LEAVE(); + return -EFAULT; +} + +#if CFG80211_VERSION_CODE > KERNEL_VERSION(2, 6, 36) +/** + * @brief Request the driver to add a key + * + * @param wiphy A pointer to wiphy structure + * @param netdev A pointer to net_device structure + * @param key_index Key index + * @param pairwise Flag to indicate pairwise or group (for kernel > 2.6.36) + * @param mac_addr MAC address (NULL for group key) + * @param params A pointer to key_params structure + * + * @return 0 -- success, otherwise fail + */ +#else +/** + * @brief Request the driver to add a key + * + * @param wiphy A pointer to wiphy structure + * @param netdev A pointer to net_device structure + * @param key_index Key index + * @param mac_addr MAC address (NULL for group key) + * @param params A pointer to key_params structure + * + * @return 0 -- success, otherwise fail + */ +#endif +int +woal_cfg80211_add_key(struct wiphy *wiphy, struct net_device *netdev, + t_u8 key_index, +#if CFG80211_VERSION_CODE > KERNEL_VERSION(2, 6, 36) + bool pairwise, +#endif + const t_u8 *mac_addr, struct key_params *params) +{ + moal_private *priv = (moal_private *)woal_get_netdev_priv(netdev); + + ENTER(); + if (priv->ft_pre_connect) { + PRINTM(MINFO, "Skip set keys during ft connecting\n"); + return -EFAULT; + } + if (woal_cfg80211_set_key(priv, 0, params->cipher, params->key, + params->key_len, params->seq, params->seq_len, + key_index, mac_addr, 0, MOAL_IOCTL_WAIT)) { + PRINTM(MERROR, "Error adding the crypto keys\n"); + LEAVE(); + return -EFAULT; + } + + PRINTM(MINFO, "Crypto keys added\n"); + + LEAVE(); + return 0; +} + +#if CFG80211_VERSION_CODE > KERNEL_VERSION(2, 6, 36) +/** + * @brief Request the driver to delete a key + * + * @param wiphy A pointer to wiphy structure + * @param netdev A pointer to net_device structure + * @param key_index Key index + * @param pairwise Flag to indicate pairwise or group (for kernel > 2.6.36) + * @param mac_addr MAC address (NULL for group key) + * + * @return 0 -- success, otherwise fail + */ +#else +/** + * @brief Request the driver to delete a key + * + * @param wiphy A pointer to wiphy structure + * @param netdev A pointer to net_device structure + * @param key_index Key index + * @param mac_addr MAC address (NULL for group key) + * + * @return 0 -- success, otherwise fail + */ +#endif +int +woal_cfg80211_del_key(struct wiphy *wiphy, struct net_device *netdev, + t_u8 key_index, +#if CFG80211_VERSION_CODE > KERNEL_VERSION(2, 6, 36) + bool pairwise, +#endif + const t_u8 *mac_addr) +{ + moal_private *priv = (moal_private *)woal_get_netdev_priv(netdev); + + ENTER(); + priv->phandle->driver_state = woal_check_driver_status(priv->phandle); + if (priv->phandle->driver_state) { + PRINTM(MERROR, + "Block woal_cfg80211_del_key in abnormal driver state\n"); + LEAVE(); + return -EFAULT; + } + + if (MLAN_STATUS_FAILURE == + woal_cfg80211_set_key(priv, 0, 0, NULL, 0, NULL, 0, key_index, + mac_addr, 1, MOAL_NO_WAIT)) { + PRINTM(MERROR, "Error deleting the crypto keys\n"); + LEAVE(); + return -EFAULT; + } + + PRINTM(MINFO, "Crypto keys deleted\n"); + LEAVE(); + return 0; +} + +#if CFG80211_VERSION_CODE > KERNEL_VERSION(2, 6, 37) +/** + * @brief Request to enable WEP key to driver + * + * @param wiphy A pointer to wiphy structure + * @param netdev A pointer to net_device structure + * @param key_index Key index + * @param ucast Unicast flag (for kernel > 2.6.37) + * @param mcast Multicast flag (for kernel > 2.6.37) + * + * @return 0 -- success, otherwise fail + */ +#else +/** + * @brief Request to enable WEP key to driver + * + * @param wiphy A pointer to wiphy structure + * @param netdev A pointer to net_device structure + * @param key_index Key index + * + * @return 0 -- success, otherwise fail + */ +#endif +int +woal_cfg80211_set_default_key(struct wiphy *wiphy, + struct net_device *netdev, t_u8 key_index +#if CFG80211_VERSION_CODE > KERNEL_VERSION(2, 6, 37) + , bool ucast, bool mcast +#endif + ) +{ + int ret = 0; + moal_private *priv = (moal_private *)woal_get_netdev_priv(netdev); + mlan_bss_info bss_info; + + ENTER(); + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) { + woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info); + if (!bss_info.wep_status) { + LEAVE(); + return ret; + } + } + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_set_wep_keys(priv, NULL, 0, key_index, + MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + } + LEAVE(); + return ret; +} + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) +int +woal_cfg80211_set_default_mgmt_key(struct wiphy *wiphy, + struct net_device *netdev, t_u8 key_index) +{ + PRINTM(MINFO, "set default mgmt key, key index=%d\n", key_index); + + return 0; +} +#endif + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 1, 0) +/** + * @brief Set GTK rekey data to driver + * + * @param priv A pointer to moal_private structure + * @param gtk_rekey A pointer to mlan_ds_misc_gtk_rekey_data structure + * @param action MLAN_ACT_SET or MLAN_ACT_GET + * + * @return 0 --success, otherwise fail + */ +mlan_status +woal_set_rekey_data(moal_private *priv, mlan_ds_misc_gtk_rekey_data * gtk_rekey, + t_u8 action) +{ + mlan_ioctl_req *req; + mlan_ds_misc_cfg *misc_cfg; + int ret = 0; + mlan_status status; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + + if (NULL == req) { + ret = -ENOMEM; + } else { + misc_cfg = (mlan_ds_misc_cfg *)req->pbuf; + misc_cfg->sub_command = MLAN_OID_MISC_GTK_REKEY_OFFLOAD; + req->req_id = MLAN_IOCTL_MISC_CFG; + + req->action = action; + if (action == MLAN_ACT_SET) + memcpy(&misc_cfg->param.gtk_rekey, gtk_rekey, + sizeof(mlan_ds_misc_gtk_rekey_data)); + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (MLAN_STATUS_SUCCESS != status) + ret = -EFAULT; + if (status != MLAN_STATUS_PENDING) + kfree(req); + } + + LEAVE(); + return ret; +} + +/** + * @brief Give the data necessary for GTK rekeying to the driver + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param data A pointer to cfg80211_gtk_rekey_data structure + * + * @return 0 -- success, otherwise fail + */ +int +woal_cfg80211_set_rekey_data(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_gtk_rekey_data *data) +{ + int ret = 0; + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + mlan_ds_misc_gtk_rekey_data rekey; + mlan_fw_info fw_info; + + ENTER(); + + if (gtk_rekey_offload == GTK_REKEY_OFFLOAD_DISABLE) { + PRINTM(MMSG, + "woal_cfg80211_set_rekey_data return: gtk_rekey_offload is DISABLE\n"); + LEAVE(); + return ret; + } + + woal_request_get_fw_info(priv, MOAL_IOCTL_WAIT, &fw_info); + if (!fw_info.fw_supplicant_support) { + LEAVE(); + return -1; + } + + memcpy(rekey.kek, data->kek, MLAN_KEK_LEN); + memcpy(rekey.kck, data->kck, MLAN_KCK_LEN); + memcpy(rekey.replay_ctr, data->replay_ctr, MLAN_REPLAY_CTR_LEN); + + memcpy(&priv->gtk_rekey_data, &rekey, + sizeof(mlan_ds_misc_gtk_rekey_data)); + if (gtk_rekey_offload == GTK_REKEY_OFFLOAD_SUSPEND) { + priv->gtk_data_ready = MTRUE; + LEAVE(); + return ret; + } + + if (MLAN_STATUS_SUCCESS != + woal_set_rekey_data(priv, &rekey, MLAN_ACT_SET)) { + ret = -EFAULT; + } + + LEAVE(); + return ret; +} +#endif + +#ifdef STA_SUPPORT +/* Opportunistic Key Caching APIs functions support + * + * this function get pmksa entry list in private structure + * @param priv A pointer to moal_private structure + * @param bssid A pointer to bssid + * @return pointer to target entry or NULL + */ +struct pmksa_entry * +woal_get_pmksa_entry(moal_private *priv, const u8 *bssid) +{ + struct pmksa_entry *entry = NULL; + unsigned long flags; + + if (!priv || priv->bss_type != MLAN_BSS_TYPE_STA) { + PRINTM(MERROR, "Invalid interface structure\n"); + return NULL; + } + + spin_lock_irqsave(&priv->pmksa_list_lock, flags); + list_for_each_entry(entry, &priv->pmksa_cache_list, link) { + if (!memcmp(entry->bssid, bssid, ETH_ALEN)) { + spin_unlock_irqrestore(&priv->pmksa_list_lock, flags); + return entry; + } + } + spin_unlock_irqrestore(&priv->pmksa_list_lock, flags); + + return NULL; +} + +/** + * This function flush pmksa entry list in private structure + * @param priv A pointer to moal_private structure + * @return success of failure + */ +int +woal_flush_pmksa_list(moal_private *priv) +{ + struct pmksa_entry *entry, *tmp; + unsigned long flags; + + if (!priv || priv->bss_type != MLAN_BSS_TYPE_STA) { + PRINTM(MERROR, "Invalid interface structure\n"); + return -1; + } + + spin_lock_irqsave(&priv->pmksa_list_lock, flags); + list_for_each_entry_safe(entry, tmp, &priv->pmksa_cache_list, link) { + list_del(&entry->link); + kfree(entry); + } + INIT_LIST_HEAD(&priv->pmksa_cache_list); + spin_unlock_irqrestore(&priv->pmksa_list_lock, flags); + + return 0; +} + +/** + * This function add new pmksa entry to list + * @param wiphy A pointer to struct wiphy + * @param dev A pointer to net_device structure + * @param pmksa A pointer to cfg80211_pmksa structure + * @return success of failure + */ +int +woal_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_pmksa *pmksa) +{ + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + struct pmksa_entry *entry = NULL; + unsigned long flags; + int ret = 0; + + ENTER(); + + if (!priv || priv->bss_type != MLAN_BSS_TYPE_STA) { + PRINTM(MERROR, "Invalid interface structure\n"); + ret = -1; + goto done; + } + + PRINTM(MIOCTL, "Set pmksa entry: bssid=" MACSTR "\n", + MAC2STR(pmksa->bssid)); + entry = woal_get_pmksa_entry(priv, pmksa->bssid); + if (!entry) { + entry = kzalloc(sizeof(struct pmksa_entry), GFP_ATOMIC); + if (!entry) { + PRINTM(MERROR, "Fail to allocate pmksa entry\n"); + goto done; + } + INIT_LIST_HEAD(&entry->link); + memcpy(entry->bssid, pmksa->bssid, ETH_ALEN); + memcpy(entry->pmkid, pmksa->pmkid, PMKID_LEN); + spin_lock_irqsave(&priv->pmksa_list_lock, flags); + list_add_tail(&entry->link, &priv->pmksa_cache_list); + spin_unlock_irqrestore(&priv->pmksa_list_lock, flags); + } else { + /** pmkid is differnt from previous value? */ + memset(entry->pmkid, 0, PMKID_LEN); + memcpy(entry->pmkid, pmksa->pmkid, PMKID_LEN); + } + + /** Check if current roaming is going and received target pmkid */ + if (priv->wait_target_ap_pmkid) { + struct cfg80211_connect_params *param = &priv->sme_current; + if (param && !memcmp(pmksa->bssid, param->bssid, ETH_ALEN)) { + PRINTM(MIOCTL, + "Current roaming target bssid=" MACSTR "\n", + MAC2STR(param->bssid)); + priv->target_ap_pmksa = entry; + priv->wait_target_ap_pmkid = MFALSE; + wake_up_interruptible(&priv->okc_wait_q); + } + } + +done: + LEAVE(); + return ret; +} + +/** + * This function delete pmksa entry + * @param wiphy A pointer to struct wiphy + * @param dev A pointer to net_device structure + * @param pmksa A pointer to cfg80211_pmksa structure + * @return success of failure + */ +int +woal_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_pmksa *pmksa) +{ + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + struct pmksa_entry *entry, *tmp; + unsigned long flags; + + ENTER(); + + if (!priv || priv->bss_type != MLAN_BSS_TYPE_STA) { + PRINTM(MERROR, "Invalid interface structure\n"); + LEAVE(); + return -1; + } + + PRINTM(MIOCTL, "Delete pmksa: bssid=" MACSTR "\n", + MAC2STR(pmksa->bssid)); + spin_lock_irqsave(&priv->pmksa_list_lock, flags); + list_for_each_entry_safe(entry, tmp, &priv->pmksa_cache_list, link) { + if (!memcmp(entry->bssid, pmksa->bssid, ETH_ALEN)) { + list_del(&entry->link); + kfree(entry); + } + } + spin_unlock_irqrestore(&priv->pmksa_list_lock, flags); + + LEAVE(); + return 0; +} + +/** + * This function flush pmksa entry list + * @param wiphy A pointer to struct wiphy + * @param dev A pointer to net_device structure + * @return success of failure + */ +int +woal_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *dev) +{ + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + + if (!priv || priv->bss_type != MLAN_BSS_TYPE_STA) { + return -1; + } + + PRINTM(MIOCTL, "Flush pmksa list.\n"); + return woal_flush_pmksa_list(priv); +} +#endif + +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 5, 0) +#if CFG80211_VERSION_CODE > KERNEL_VERSION(2, 6, 34) +/** + * @brief Request the driver to change the channel + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param chan A pointer to ieee80211_channel structure + * @param channel_type Channel type of nl80211_channel_type + * + * @return 0 -- success, otherwise fail + */ +#else +/** + * @brief Request the driver to change the channel + * + * @param wiphy A pointer to wiphy structure + * @param chan A pointer to ieee80211_channel structure + * @param channel_type Channel type of nl80211_channel_type + * + * @return 0 -- success, otherwise fail + */ +#endif +int +woal_cfg80211_set_channel(struct wiphy *wiphy, +#if CFG80211_VERSION_CODE > KERNEL_VERSION(2, 6, 34) + struct net_device *dev, +#endif + struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type) +{ + int ret = 0; + moal_private *priv = NULL; + + ENTER(); + +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 5, 0) +#if CFG80211_VERSION_CODE > KERNEL_VERSION(2, 6, 34) + if (dev) + priv = woal_get_netdev_priv(dev); +#endif +#endif + if (!priv) { + moal_handle *handle = (moal_handle *)woal_get_wiphy_priv(wiphy); + if (handle) + priv = woal_get_priv(handle, MLAN_BSS_ROLE_ANY); + } + if (priv) { +#ifdef STA_CFG80211 +#ifdef STA_SUPPORT + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) { + if (priv->media_connected == MTRUE) { + PRINTM(MERROR, + "This configuration is valid only when station " + "is not connected\n"); + LEAVE(); + return -EINVAL; + } + ret = woal_set_rf_channel(priv, chan, channel_type, + MOAL_IOCTL_WAIT); + } +#endif +#endif + priv->channel = + ieee80211_frequency_to_channel(chan->center_freq); + } + /* set monitor channel support */ + + LEAVE(); + return ret; +} +#endif + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) +/** + * @brief Functions check whether the pattern is supported + * + * @param pat A pointer to cfg80211_pkt_pattern structure + * @param byte_seq Byte sequence + * @param max_byte_seq Maximum byte sequence + * + * @return true -- success, otherwise false + */ +static bool +woal_is_pattern_supported(struct cfg80211_pkt_pattern *pat, t_u8 *byte_seq, + t_u8 max_byte_seq) +{ + int j, k, valid_byte_cnt = 0; + bool dont_care_byte = false; + + for (j = 0; j < DIV_ROUND_UP(pat->pattern_len, 8); j++) { + for (k = 0; k < 8; k++) { + if (pat->mask[j] & 1 << k) { + memcpy(byte_seq + valid_byte_cnt, + &pat->pattern[j * 8 + k], 1); + valid_byte_cnt++; + if (dont_care_byte) + return false; + } else { + if (valid_byte_cnt) + dont_care_byte = true; + } + + if (valid_byte_cnt > max_byte_seq) + return false; + } + } + + byte_seq[max_byte_seq] = valid_byte_cnt; + + return true; +} + +/** + * @brief Get coalesce packet type + * + * @param byte_seq Byte Sequence + + * @return 0 -- success, otherwise fail + */ +static int +woal_get_coalesce_pkt_type(t_u8 *byte_seq) +{ + const t_u8 ipv4_mc_mac[] = { 0x33, 0x33 }; + const t_u8 ipv6_mc_mac[] = { 0x01, 0x00, 0x5e }; + const t_u8 bc_mac[] = { 0xff, 0xff, 0xff, 0xff }; + + if ((byte_seq[0] & 0x01) && (byte_seq[COALESCE_MAX_BYTESEQ] == 1)) + return PACKET_TYPE_UNICAST; + else if (!memcmp(byte_seq, bc_mac, 4)) + return PACKET_TYPE_BROADCAST; + else if ((!memcmp(byte_seq, ipv4_mc_mac, 2) && + byte_seq[COALESCE_MAX_BYTESEQ] == 2) || + (!memcmp(byte_seq, ipv6_mc_mac, 3) && + byte_seq[COALESCE_MAX_BYTESEQ] == 3)) + return PACKET_TYPE_MULTICAST; + + return 0; +} + +/** + * @brief Functions fills the coalesce rule information + * + * @param crule A pointer to cfg80211_coalesce_rules structure + * @param mrule A pointer to coalesce_rules structure + * + * @return 0-- success, otherwise fail + */ +static int +woal_fill_coalesce_rule_info(struct cfg80211_coalesce_rules *crule, + struct coalesce_rule *mrule) +{ + t_u8 byte_seq[COALESCE_MAX_BYTESEQ + 1]; + struct filt_field_param *param; + int i; + + mrule->max_coalescing_delay = crule->delay; + + param = mrule->params; + + for (i = 0; i < crule->n_patterns; i++) { + memset(byte_seq, 0, sizeof(byte_seq)); + if (!woal_is_pattern_supported(&crule->patterns[i], + byte_seq, + COALESCE_MAX_BYTESEQ)) { + PRINTM(MERROR, "Pattern not supported\n"); + return -EOPNOTSUPP; + } + + if (!crule->patterns[i].pkt_offset) { + u8 pkt_type; + + pkt_type = woal_get_coalesce_pkt_type(byte_seq); + if (pkt_type && mrule->pkt_type) { + PRINTM(MERROR, + "Multiple packet types not allowed\n"); + return -EOPNOTSUPP; + } else if (pkt_type) { + mrule->pkt_type = pkt_type; + continue; + } + } + + if (crule->condition == NL80211_COALESCE_CONDITION_MATCH) + param->operation = RECV_FILTER_MATCH_TYPE_EQ; + else + param->operation = RECV_FILTER_MATCH_TYPE_NE; + + param->operand_len = byte_seq[COALESCE_MAX_BYTESEQ]; + memcpy(param->operand_byte_stream, byte_seq, + param->operand_len); + param->offset = crule->patterns[i].pkt_offset; + param++; + + mrule->num_of_fields++; + } + + if (!mrule->pkt_type) { + PRINTM(MERROR, "Packet type can not be determined\n"); + return -EOPNOTSUPP; + } + + return 0; +} + +/** + * @brief Set coalesce parameter + * + * @param priv A pointer to moal_private structure + * @param action MLAN_ACT_SET or MLAN_ACT_GET + * @param coalesce_cfg A pointer to coalesce structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +woal_set_coalesce(moal_private *priv, t_u16 action, + mlan_ds_coalesce_cfg * coalesce_cfg) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_misc_cfg *misc_cfg = NULL; + mlan_ioctl_req *req = NULL; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + misc_cfg = (mlan_ds_misc_cfg *)req->pbuf; + misc_cfg->sub_command = MLAN_OID_MISC_COALESCE_CFG; + req->req_id = MLAN_IOCTL_MISC_CFG; + req->action = action; + + memcpy(&misc_cfg->param.coalesce_cfg, coalesce_cfg, + sizeof(mlan_ds_coalesce_cfg)); + + ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (ret != MLAN_STATUS_SUCCESS) + goto done; + +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Request the driver to set the coalesce + * + * @param wiphy A pointer to wiphy structure + * @param coalesce A pointer to cfg80211_coalesce structure + * + * @return 0 -- success, otherwise fail + */ +int +woal_cfg80211_set_coalesce(struct wiphy *wiphy, + struct cfg80211_coalesce *coalesce) +{ + int ret = 0; + int i; + moal_handle *handle = (moal_handle *)woal_get_wiphy_priv(wiphy); + moal_private *priv = NULL; + mlan_ds_coalesce_cfg coalesce_cfg; + + ENTER(); + + if (!handle) { + PRINTM(MFATAL, "Unable to get handle\n"); + ret = -EINVAL; + goto done; + } + priv = woal_get_priv(handle, MLAN_BSS_ROLE_ANY); + if (!priv) { + ret = -EINVAL; + goto done; + } + + memset(&coalesce_cfg, 0, sizeof(coalesce_cfg)); + if (!coalesce) { + PRINTM(MMSG, "Disable coalesce and reset all previous rules\n"); + } else { + coalesce_cfg.num_of_rules = coalesce->n_rules; + for (i = 0; i < coalesce->n_rules; i++) { + ret = woal_fill_coalesce_rule_info(&coalesce->rules[i], + &coalesce_cfg. + rule[i]); + if (ret) { + PRINTM(MERROR, + "Recheck the patterns provided for rule %d\n", + i + 1); + return ret; + } + } + } + + if (MLAN_STATUS_SUCCESS != + woal_set_coalesce(priv, MLAN_ACT_SET, &coalesce_cfg)) { + PRINTM(MERROR, "wlan: Fail to set coalesce\n"); + ret = -EFAULT; + } + +done: + LEAVE(); + return ret; +} +#endif + +/** + * @brief Request the driver to set the bitrate + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param peer A pointer to peer address + * @param mask A pointer to cfg80211_bitrate_mask structure + * + * @return 0 -- success, otherwise fail + */ +int +woal_cfg80211_set_bitrate_mask(struct wiphy *wiphy, + struct net_device *dev, + const u8 *peer, + const struct cfg80211_bitrate_mask *mask) +{ + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + mlan_bss_info bss_info; + enum ieee80211_band band; + mlan_ioctl_req *req = NULL; + mlan_ds_rate *rate = NULL; + mlan_rate_cfg_t *rate_cfg = NULL; + + ENTER(); + + if (priv->media_connected == MFALSE) { + PRINTM(MERROR, "Can not set data rate in disconnected state\n"); + ret = -EINVAL; + goto done; + } + + status = woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info); + if (status) + goto done; + band = woal_band_cfg_to_ieee_band(bss_info.bss_band); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_rate)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + rate = (mlan_ds_rate *)req->pbuf; + rate_cfg = &rate->param.rate_cfg; + rate->sub_command = MLAN_OID_RATE_CFG; + req->req_id = MLAN_IOCTL_RATE; + req->action = MLAN_ACT_SET; + rate_cfg->rate_type = MLAN_RATE_BITMAP; + + /* Fill HR/DSSS rates. */ + if (band == IEEE80211_BAND_2GHZ) + rate_cfg->bitmap_rates[0] = mask->control[band].legacy & 0x000f; + + /* Fill OFDM rates */ + if (band == IEEE80211_BAND_2GHZ) + rate_cfg->bitmap_rates[1] = + (mask->control[band].legacy & 0x0ff0) >> 4; + else + rate_cfg->bitmap_rates[1] = mask->control[band].legacy; + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) + /* Fill MCS rates */ +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + rate_cfg->bitmap_rates[2] = mask->control[band].ht_mcs[0]; +#else + rate_cfg->bitmap_rates[2] = mask->control[band].mcs[0]; +#endif +#endif + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (MLAN_STATUS_SUCCESS != status) { + ret = -EFAULT; + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 38) +/** + * @brief Request the driver to get antenna configuration + * + * @param wiphy A pointer to wiphy structure + * @param tx_ant Bitmaps of allowed antennas to use for TX + * @param rx_ant Bitmaps of allowed antennas to use for RX + * + * @return 0 -- success, otherwise fail + */ +int +woal_cfg80211_get_antenna(struct wiphy *wiphy, u32 *tx_ant, u32 *rx_ant) +{ + moal_handle *handle = (moal_handle *)woal_get_wiphy_priv(wiphy); + moal_private *priv = NULL; + mlan_ds_radio_cfg *radio = NULL; + mlan_ioctl_req *req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + int ret = 0; + + ENTER(); + + if (!handle) { + PRINTM(MFATAL, "Unable to get handle\n"); + ret = -EINVAL; + goto done; + } + priv = woal_get_priv(handle, MLAN_BSS_ROLE_ANY); + if (!priv) { + ret = -EINVAL; + goto done; + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_radio_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + radio = (mlan_ds_radio_cfg *)req->pbuf; + radio->sub_command = MLAN_OID_ANT_CFG; + req->req_id = MLAN_IOCTL_RADIO_CFG; + req->action = MLAN_ACT_GET; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (MLAN_STATUS_SUCCESS != status) { + ret = -EFAULT; + goto done; + } + + *tx_ant = radio->param.ant_cfg_1x1.antenna; + *rx_ant = radio->param.ant_cfg_1x1.antenna; + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + /* Driver must return -EINVAL to cfg80211 */ + if (ret) + ret = -EINVAL; + LEAVE(); + return ret; + +} + +/** + * @brief Request the driver to set antenna configuration + * + * @param wiphy A pointer to wiphy structure + * @param tx_ant Bitmaps of allowed antennas to use for TX + * @param rx_ant Bitmaps of allowed antennas to use for RX + * + * @return 0 -- success, otherwise fail + */ +int +woal_cfg80211_set_antenna(struct wiphy *wiphy, u32 tx_ant, u32 rx_ant) +{ + moal_handle *handle = (moal_handle *)woal_get_wiphy_priv(wiphy); + moal_private *priv = NULL; + mlan_ds_radio_cfg *radio = NULL; + mlan_ioctl_req *req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + int ret = 0; + + ENTER(); + + if (!handle) { + PRINTM(MFATAL, "Unable to get handle\n"); + ret = -EINVAL; + goto done; + } + priv = woal_get_priv(handle, MLAN_BSS_ROLE_ANY); + if (!priv) { + ret = -EINVAL; + goto done; + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_radio_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + radio = (mlan_ds_radio_cfg *)req->pbuf; + radio->sub_command = MLAN_OID_ANT_CFG; + req->req_id = MLAN_IOCTL_RADIO_CFG; + req->action = MLAN_ACT_SET; + radio->param.ant_cfg_1x1.antenna = tx_ant; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (MLAN_STATUS_SUCCESS != status) { + ret = -EFAULT; + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + /* Driver must return -EINVAL to cfg80211 */ + if (ret) + ret = -EINVAL; + LEAVE(); + return ret; +} +#endif + +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 6, 0) +/** + * @brief register/unregister mgmt frame forwarding + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param frame_type Bit mask for mgmt frame type + * @param reg Register or unregister + * + * @return 0 -- success, otherwise fail + */ +void +woal_cfg80211_mgmt_frame_register(struct wiphy *wiphy, + struct net_device *dev, u16 frame_type, + bool reg) +#else +/** + * @brief register/unregister mgmt frame forwarding + * + * @param wiphy A pointer to wiphy structure + * @param wdev A pointer to wireless_dev structure + * @param frame_type Bit mask for mgmt frame type + * @param reg Register or unregister + * + * @return 0 -- success, otherwise fail + */ +void +woal_cfg80211_mgmt_frame_register(struct wiphy *wiphy, + struct wireless_dev *wdev, u16 frame_type, + bool reg) +#endif +{ +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) + struct net_device *dev = wdev->netdev; +#endif + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + mlan_status status = MLAN_STATUS_SUCCESS; + t_u32 mgmt_subtype_mask = 0x0; + t_u32 last_mgmt_subtype_mask = priv->mgmt_subtype_mask; + + ENTER(); + if (reg == MTRUE) { + /* set mgmt_subtype_mask based on origin value */ + mgmt_subtype_mask = + last_mgmt_subtype_mask | BIT(frame_type >> 4); + } else { + /* clear mgmt_subtype_mask */ + mgmt_subtype_mask = + last_mgmt_subtype_mask & ~BIT(frame_type >> 4); + } + PRINTM(MIOCTL, + "%s: mgmt_subtype_mask=0x%x last_mgmt_subtype_mask=0x%x\n", + dev->name, mgmt_subtype_mask, last_mgmt_subtype_mask); + if (mgmt_subtype_mask != last_mgmt_subtype_mask) { + + last_mgmt_subtype_mask = mgmt_subtype_mask; + /* Notify driver that a mgmt frame type was registered. + * Note that this callback may not sleep, and cannot run + * concurrently with itself. */ + status = woal_reg_rx_mgmt_ind(priv, MLAN_ACT_SET, + &mgmt_subtype_mask, MOAL_NO_WAIT); + priv->mgmt_subtype_mask = last_mgmt_subtype_mask; + } + + LEAVE(); +} + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 3, 0) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) +/** + * @brief tx mgmt frame + * + * @param wiphy A pointer to wiphy structure + * @param wdev A pointer to wireless_dev structure + * @param params A pointer to cfg80211_mgmt_tx_params structure + * @param cookie A pointer to frame cookie + * + * @return 0 -- success, otherwise fail + */ +#else +/** + * @brief tx mgmt frame + * + * @param wiphy A pointer to wiphy structure + * @param wdev A pointer to wireless_dev structure + * @param chan A pointer to ieee80211_channel structure + * @param offchan Off channel or not + * @param wait Duration to wait + * @param buf Frame buffer + * @param len Frame length + * @param no_cck No CCK check + * @param dont_wait_for_ack Do not wait for ACK + * @param cookie A pointer to frame cookie + * + * @return 0 -- success, otherwise fail + */ +#endif +#else +/** + * @brief tx mgmt frame + * + * @param wiphy A pointer to wiphy structure + * @param wdev A pointer to wireless_dev structure + * @param chan A pointer to ieee80211_channel structure + * @param offchan Off channel or not + * @param channel_type Channel type + * @param channel_type_valid Is channel type valid or not + * @param wait Duration to wait + * @param buf Frame buffer + * @param len Frame length + * @param no_cck No CCK check + * @param dont_wait_for_ack Do not wait for ACK + * @param cookie A pointer to frame cookie + * + * @return 0 -- success, otherwise fail + */ +#endif +#else +/** + * @brief tx mgmt frame + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param chan A pointer to ieee80211_channel structure + * @param offchan Off channel or not + * @param channel_type Channel type + * @param channel_type_valid Is channel type valid or not + * @param wait Duration to wait + * @param buf Frame buffer + * @param len Frame length + * @param no_cck No CCK check + * @param dont_wait_for_ack Do not wait for ACK + * @param cookie A pointer to frame cookie + * + * @return 0 -- success, otherwise fail + */ +#endif +#else +/** + * @brief tx mgmt frame + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param chan A pointer to ieee80211_channel structure + * @param offchan Off channel or not + * @param channel_type Channel type + * @param channel_type_valid Is channel type valid or not + * @param wait Duration to wait + * @param buf Frame buffer + * @param len Frame length + * @param no_cck No CCK check + * @param cookie A pointer to frame cookie + * + * @return 0 -- success, otherwise fail + */ +#endif +#else +/** + * @brief tx mgmt frame + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param chan A pointer to ieee80211_channel structure + * @param offchan Off channel or not + * @param channel_type Channel type + * @param channel_type_valid Is channel type valid or not + * @param wait Duration to wait + * @param buf Frame buffer + * @param len Frame length + * @param cookie A pointer to frame cookie + * + * @return 0 -- success, otherwise fail + */ +#endif +int +woal_cfg80211_mgmt_tx(struct wiphy *wiphy, +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 6, 0) + struct net_device *dev, +#else + struct wireless_dev *wdev, +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + struct cfg80211_mgmt_tx_params *params, +#else + struct ieee80211_channel *chan, bool offchan, +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 8, 0) + enum nl80211_channel_type channel_type, + bool channel_type_valid, +#endif + unsigned int wait, const u8 *buf, size_t len, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) + bool no_cck, +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 3, 0) + bool dont_wait_for_ack, +#endif +#endif + u64 * cookie) +{ +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) + struct net_device *dev = wdev->netdev; +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + struct ieee80211_channel *chan = params->chan; + unsigned int wait = params->wait; + const u8 *buf = params->buf; + size_t len = params->len; +#endif + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + int ret = 0; + pmlan_buffer pmbuf = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + t_u16 packet_len = 0; + t_u8 addr[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; + t_u32 pkt_type; + t_u32 tx_control; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + t_u8 channel_status; + t_u32 duration; + moal_private *remain_priv = NULL; +#endif + unsigned long flags; + struct sk_buff *skb = NULL; + struct tx_status_info *tx_info = NULL; + + ENTER(); + + if (buf == NULL || len == 0) { + PRINTM(MERROR, "woal_cfg80211_mgmt_tx() corrupt data\n"); + LEAVE(); + return -EFAULT; + } + + /* If the packet is probe response, that means we are in listen phase, + so we should not call remain_on_channel_cfg because + remain_on_channl already handled it. If the packet if action, that + means we are in PD/GO negotiation, so we should call + remain_on_channel_cfg in order to receive action frame from peer + device */ + if ((GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) && + ieee80211_is_probe_resp(((struct ieee80211_mgmt *)buf)-> + frame_control)) { + PRINTM(MIOCTL, "Skip send probe_resp in GO/UAP mode\n"); + goto done; + } +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + if (ieee80211_is_action(((struct ieee80211_mgmt *)buf)->frame_control)) { +#ifdef WIFI_DIRECT_SUPPORT + if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT) + woal_cfg80211_display_p2p_actframe(buf, len, chan, + MTRUE); + if (priv->phandle->is_go_timer_set) { + woal_cancel_timer(&priv->phandle->go_timer); + priv->phandle->is_go_timer_set = MFALSE; + } +#endif + if (priv->phandle->is_remain_timer_set) { + woal_cancel_timer(&priv->phandle->remain_timer); + woal_remain_timer_func(priv->phandle); + } + /* With sd8777 We have difficulty to receive response packet in 500ms */ +#define MGMT_TX_DEFAULT_WAIT_TIME 1500 + if (priv->phandle->remain_on_channel) + remain_priv = + priv->phandle->priv[priv->phandle-> + remain_bss_index]; + /** cancel previous remain on channel */ + if (priv->phandle->remain_on_channel && remain_priv) { + if ((priv->phandle->chan.center_freq != + chan->center_freq) + ) { + if (woal_cfg80211_remain_on_channel_cfg + (remain_priv, MOAL_IOCTL_WAIT, MTRUE, + &channel_status, NULL, 0, 0)) + PRINTM(MERROR, + "mgmt_tx:Fail to cancel remain on channel\n"); + } + if (priv->phandle->cookie) { + cfg80211_remain_on_channel_expired( +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 6, 0) + remain_priv-> + netdev, +#else + remain_priv-> + wdev, +#endif + priv-> + phandle-> + cookie, + &priv-> + phandle-> + chan, +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 8, 0) + priv-> + phandle-> + channel_type, +#endif + GFP_ATOMIC); + priv->phandle->cookie = 0; + } + priv->phandle->remain_on_channel = MFALSE; + } +#ifdef STA_CFG80211 + /** cancel pending scan */ + woal_cancel_scan(priv, MOAL_IOCTL_WAIT); +#endif + + duration = + (wait > + MGMT_TX_DEFAULT_WAIT_TIME) ? wait : + MGMT_TX_DEFAULT_WAIT_TIME; +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 8, 0) + if (channel_type_valid) + ret = woal_cfg80211_remain_on_channel_cfg(priv, + MOAL_IOCTL_WAIT, + MFALSE, + &channel_status, + chan, + channel_type, + duration); + else +#endif + ret = woal_cfg80211_remain_on_channel_cfg(priv, + MOAL_IOCTL_WAIT, + MFALSE, + &channel_status, + chan, 0, + duration); + if (ret) { + /* Return fail will cause p2p connnection fail */ + woal_sched_timeout(2); +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 8, 0) + if (channel_type_valid) + ret = woal_cfg80211_remain_on_channel_cfg(priv, + MOAL_IOCTL_WAIT, + MFALSE, + &channel_status, + chan, + channel_type, + duration); + else +#endif + ret = woal_cfg80211_remain_on_channel_cfg(priv, + MOAL_IOCTL_WAIT, + MFALSE, + &channel_status, + chan, + 0, + duration); + PRINTM(MERROR, + "Try configure remain on channel again, ret=%d\n", + ret); + ret = 0; + } else { + priv->phandle->remain_on_channel = MTRUE; + priv->phandle->remain_bss_index = priv->bss_index; +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 8, 0) + priv->phandle->channel_type = channel_type; +#endif + memcpy(&priv->phandle->chan, chan, + sizeof(struct ieee80211_channel)); + PRINTM(MIOCTL, + "%s: Mgmt Tx: Set remain channel=%d duration=%d\n", + dev->name, + ieee80211_frequency_to_channel(chan-> + center_freq), + duration); + } + } +#endif + + /* pkt_type + tx_control */ +#define HEADER_SIZE 8 + packet_len = (t_u16)len + MLAN_MAC_ADDR_LENGTH; + pmbuf = woal_alloc_mlan_buffer(priv->phandle, + MLAN_MIN_DATA_HEADER_LEN + HEADER_SIZE + + packet_len + sizeof(packet_len)); + if (!pmbuf) { + PRINTM(MERROR, "Fail to allocate mlan_buffer\n"); + ret = -ENOMEM; + goto done; + } +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 8, 0) + *cookie = random32() | 1; +#else + *cookie = prandom_u32() | 1; +#endif + pmbuf->data_offset = MLAN_MIN_DATA_HEADER_LEN; + pkt_type = MRVL_PKT_TYPE_MGMT_FRAME; + tx_control = 0; + /* Add pkt_type and tx_control */ + memcpy(pmbuf->pbuf + pmbuf->data_offset, &pkt_type, sizeof(pkt_type)); + memcpy(pmbuf->pbuf + pmbuf->data_offset + sizeof(pkt_type), &tx_control, + sizeof(tx_control)); + /* frmctl + durationid + addr1 + addr2 + addr3 + seqctl */ +#define PACKET_ADDR4_POS (2 + 2 + 6 + 6 + 6 + 2) + memcpy(pmbuf->pbuf + pmbuf->data_offset + HEADER_SIZE, &packet_len, + sizeof(packet_len)); + memcpy(pmbuf->pbuf + pmbuf->data_offset + HEADER_SIZE + + sizeof(packet_len), buf, PACKET_ADDR4_POS); + memcpy(pmbuf->pbuf + pmbuf->data_offset + HEADER_SIZE + + sizeof(packet_len) + + PACKET_ADDR4_POS, addr, MLAN_MAC_ADDR_LENGTH); + memcpy(pmbuf->pbuf + pmbuf->data_offset + HEADER_SIZE + + sizeof(packet_len) + + PACKET_ADDR4_POS + MLAN_MAC_ADDR_LENGTH, + buf + PACKET_ADDR4_POS, len - PACKET_ADDR4_POS); + + pmbuf->data_len = HEADER_SIZE + packet_len + sizeof(packet_len); + pmbuf->buf_type = MLAN_BUF_TYPE_RAW_DATA; + pmbuf->bss_index = priv->bss_index; + if (ieee80211_is_action(((struct ieee80211_mgmt *)buf)->frame_control)) { + pmbuf->flags = MLAN_BUF_FLAG_TX_STATUS; + pmbuf->tx_seq_num = ++priv->tx_seq_num; + tx_info = kzalloc(sizeof(struct tx_status_info), GFP_ATOMIC); + if (tx_info) { + skb = alloc_skb(len, GFP_ATOMIC); + if (skb) { + memcpy(skb->data, buf, len); + skb_put(skb, len); + spin_lock_irqsave(&priv->tx_stat_lock, flags); + tx_info->tx_cookie = *cookie; + tx_info->tx_skb = skb; + tx_info->tx_seq_num = pmbuf->tx_seq_num; + INIT_LIST_HEAD(&tx_info->link); + list_add_tail(&tx_info->link, + &priv->tx_stat_queue); + spin_unlock_irqrestore(&priv->tx_stat_lock, + flags); + } else { + kfree(tx_info); + tx_info = NULL; + } + } + } + + status = mlan_send_packet(priv->phandle->pmlan_adapter, pmbuf); + + switch (status) { + case MLAN_STATUS_PENDING: + atomic_inc(&priv->phandle->tx_pending); + queue_work(priv->phandle->workqueue, &priv->phandle->main_work); + + /* Delay 30ms to guarantee the packet has been already tx'ed, + * becuase if we call cfg80211_mgmt_tx_status() immediately, + * then wpa_supplicant will call cancel_remain_on_channel(), + * which may affect the mgmt frame tx. Meanwhile it is only + * necessary for P2P action handshake to wait 30ms. */ + if (ieee80211_is_action + (((struct ieee80211_mgmt *)buf)->frame_control)) { + if (tx_info) + break; + else + woal_sched_timeout(30); + } + + /* Notify the mgmt tx status */ +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 37) +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 6, 0) + cfg80211_mgmt_tx_status(dev, *cookie, buf, len, true, + GFP_ATOMIC); +#else + cfg80211_mgmt_tx_status(priv->wdev, *cookie, buf, len, true, + GFP_ATOMIC); +#endif +#endif + break; + case MLAN_STATUS_SUCCESS: + woal_free_mlan_buffer(priv->phandle, pmbuf); + break; + case MLAN_STATUS_FAILURE: + default: + woal_free_mlan_buffer(priv->phandle, pmbuf); + ret = -EFAULT; + break; + } + +done: + if (status != MLAN_STATUS_PENDING) { + if (tx_info) + woal_remove_tx_info(priv, tx_info->tx_seq_num); + + } + + LEAVE(); + return ret; +} + +/** + * @brief Add custom ie to mgmt frames. + * + * @param priv A pointer to moal private structure + * @param beacon_ies_data Beacon ie + * @param beacon_index The index for beacon when auto index + * @param proberesp_ies_data Probe resp ie + * @param proberesp_index The index for probe resp when auto index + * @param assocresp_ies_data Assoc resp ie + * @param assocresp_index The index for assoc resp when auto index + * @param probereq_ies_data Probe req ie + * @param probereq_index The index for probe req when auto index + * @param wait_option wait option + * + * @return 0 -- success, otherwise fail + */ +static int +woal_cfg80211_custom_ie(moal_private *priv, + custom_ie *beacon_ies_data, t_u16 *beacon_index, + custom_ie *proberesp_ies_data, t_u16 *proberesp_index, + custom_ie *assocresp_ies_data, t_u16 *assocresp_index, + custom_ie *probereq_ies_data, t_u16 *probereq_index, + t_u8 wait_option) +{ + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_misc_cfg *misc = NULL; + mlan_ds_misc_custom_ie *custom_ie = NULL; + t_u8 *pos = NULL; + t_u16 len = 0; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + custom_ie = kzalloc(sizeof(mlan_ds_misc_custom_ie), GFP_KERNEL); + if (!custom_ie) { + ret = -ENOMEM; + goto done; + } + + custom_ie->type = TLV_TYPE_MGMT_IE; + + pos = (t_u8 *)custom_ie->ie_data_list; + if (beacon_ies_data) { + len = sizeof(*beacon_ies_data) - MAX_IE_SIZE + + beacon_ies_data->ie_length; + memcpy(pos, beacon_ies_data, len); + pos += len; + custom_ie->len += len; + } + + if (proberesp_ies_data) { + len = sizeof(*proberesp_ies_data) - MAX_IE_SIZE + + proberesp_ies_data->ie_length; + memcpy(pos, proberesp_ies_data, len); + pos += len; + custom_ie->len += len; + } + + if (assocresp_ies_data) { + len = sizeof(*assocresp_ies_data) - MAX_IE_SIZE + + assocresp_ies_data->ie_length; + memcpy(pos, assocresp_ies_data, len); + custom_ie->len += len; + } + + if (probereq_ies_data) { + len = sizeof(*probereq_ies_data) - MAX_IE_SIZE + + probereq_ies_data->ie_length; + memcpy(pos, probereq_ies_data, len); + pos += len; + custom_ie->len += len; + } + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (ioctl_req == NULL) { + ret = -ENOMEM; + goto done; + } + + misc = (mlan_ds_misc_cfg *)ioctl_req->pbuf; + misc->sub_command = MLAN_OID_MISC_CUSTOM_IE; + ioctl_req->req_id = MLAN_IOCTL_MISC_CFG; + ioctl_req->action = MLAN_ACT_SET; + + memcpy(&misc->param.cust_ie, custom_ie, sizeof(mlan_ds_misc_custom_ie)); + + status = woal_request_ioctl(priv, ioctl_req, wait_option); + if (MLAN_STATUS_SUCCESS != status) { + ret = -EFAULT; + goto done; + } + + /* get the assigned index */ + pos = (t_u8 *)(&misc->param.cust_ie.ie_data_list[0].ie_index); + if (beacon_ies_data && beacon_ies_data->ie_length + && beacon_ies_data->ie_index == MLAN_CUSTOM_IE_AUTO_IDX_MASK) { + /* save beacon ie index after auto-indexing */ + *beacon_index = misc->param.cust_ie.ie_data_list[0].ie_index; + len = sizeof(*beacon_ies_data) - MAX_IE_SIZE + + beacon_ies_data->ie_length; + pos += len; + } + + if (proberesp_ies_data && proberesp_ies_data->ie_length + && proberesp_ies_data->ie_index == MLAN_CUSTOM_IE_AUTO_IDX_MASK) { + /* save probe resp ie index after auto-indexing */ + *proberesp_index = *((t_u16 *)pos); + len = sizeof(*proberesp_ies_data) - MAX_IE_SIZE + + proberesp_ies_data->ie_length; + pos += len; + } + + if (assocresp_ies_data && assocresp_ies_data->ie_length + && assocresp_ies_data->ie_index == MLAN_CUSTOM_IE_AUTO_IDX_MASK) { + /* save assoc resp ie index after auto-indexing */ + *assocresp_index = *((t_u16 *)pos); + len = sizeof(*assocresp_ies_data) - MAX_IE_SIZE + + assocresp_ies_data->ie_length; + pos += len; + } + if (probereq_ies_data && probereq_ies_data->ie_length + && probereq_ies_data->ie_index == MLAN_CUSTOM_IE_AUTO_IDX_MASK) { + /* save probe resp ie index after auto-indexing */ + *probereq_index = *((t_u16 *)pos); + len = sizeof(*probereq_ies_data) - MAX_IE_SIZE + + probereq_ies_data->ie_length; + pos += len; + } + + if (ioctl_req->status_code == MLAN_ERROR_IOCTL_FAIL) + ret = -EFAULT; + +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + kfree(custom_ie); + LEAVE(); + return ret; +} + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3,14,0) +/** + * @brief set Qos map + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param qos_map A pointer to cfg80211_qos_map structure + * + * + * @return 0 -- success, otherwise fail + */ +int +woal_cfg80211_set_qos_map(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_qos_map *qos_map) +{ + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + int i, j, ret = 0; + + ENTER(); + /**clear dscp map*/ + if (!qos_map) { + memset(priv->dscp_map, 0xFF, sizeof(priv->dscp_map)); + goto done; + } + + /**update dscp map*/ + for (i = 0; i < MAX_NUM_TID; i++) { + PRINTM(MINFO, "TID %d: dscp_low=%d, dscp_high=%d\n", i, + qos_map->up[i].low, qos_map->up[i].high); + if (qos_map->up[i].low != 0xff && qos_map->up[i].high != 0xff + && qos_map->up[i].high <= 63) { + for (j = qos_map->up[i].low; j <= qos_map->up[i].high; + j++) + priv->dscp_map[j] = i; + } + } + + for (i = 0; i < qos_map->num_des; i++) { + if ((qos_map->dscp_exception[i].dscp <= 63) && + (qos_map->dscp_exception[i].up <= 7)) { + PRINTM(MINFO, "dscp excpt: value=%d priority=%d\n", + qos_map->dscp_exception[i].dscp, + qos_map->dscp_exception[i].up); + priv->dscp_map[qos_map->dscp_exception[i].dscp] = + qos_map->dscp_exception[i].up; + } + } + + /**UAP update (re)associate response*/ + if (priv->bss_type == MLAN_BSS_TYPE_UAP) { + IEEEtypes_Generic_t qos_map_ie; + t_u16 qos_map_ies_len; + + memcpy(qos_map_ie.data, (t_u8 *)qos_map->dscp_exception, + 2 * qos_map->num_des); + memcpy(&qos_map_ie.data[2 * qos_map->num_des], + (t_u8 *)qos_map->up, sizeof(qos_map->up)); + qos_map_ie.ieee_hdr.element_id = QOS_MAPPING; + qos_map_ie.ieee_hdr.len = + 2 * qos_map->num_des + sizeof(qos_map->up); + qos_map_ies_len = + qos_map_ie.ieee_hdr.len + sizeof(qos_map_ie.ieee_hdr); + + /* set the assoc response ies */ + ret = woal_cfg80211_mgmt_frame_ie(priv, + NULL, 0, NULL, 0, + (t_u8 *)&qos_map_ie, + qos_map_ies_len, NULL, 0, + MGMT_MASK_ASSOC_RESP_QOS_MAP, + MOAL_IOCTL_WAIT); + if (ret) { + PRINTM(MERROR, "Failed to set beacon wps/p2p ie\n"); + goto done; + } + + } + +done: + LEAVE(); + return ret; +} +#endif + +/** + * @brief get specific ie + * + * @param ie Pointer to IEs + * @param len Total length of ie + * @param ie_out Pointer to out IE buf + * @param mask IE mask + * + * @return out IE length + */ +static t_u16 +woal_get_specific_ie(const t_u8 *ie, int len, t_u8 *ie_out, t_u16 mask) +{ + int left_len = len; + const t_u8 *pos = ie; + int length; + t_u8 id = 0; + t_u16 out_len = 0; + IEEEtypes_VendorSpecific_t *pvendor_ie = NULL; + const u8 wps_oui[4] = { 0x00, 0x50, 0xf2, 0x04 }; + const u8 p2p_oui[4] = { 0x50, 0x6f, 0x9a, 0x09 }; + const u8 wfd_oui[4] = { 0x50, 0x6f, 0x9a, 0x0a }; + const t_u8 wmm_oui[4] = { 0x00, 0x50, 0xf2, 0x02 }; + + ENTER(); + while (left_len >= 2) { + length = *(pos + 1); + id = *pos; + if ((length + 2) > left_len) + break; + if (id == VENDOR_SPECIFIC_221) { + pvendor_ie = (IEEEtypes_VendorSpecific_t *)pos; + if (!memcmp + (pvendor_ie->vend_hdr.oui, wmm_oui, + sizeof(pvendor_ie->vend_hdr.oui)) && + pvendor_ie->vend_hdr.oui_type == wmm_oui[3]) { + PRINTM(MIOCTL, "find WMM IE\n"); + } else if (!memcmp + (pvendor_ie->vend_hdr.oui, p2p_oui, + sizeof(pvendor_ie->vend_hdr.oui)) && + pvendor_ie->vend_hdr.oui_type == + p2p_oui[3]) { + if (mask & IE_MASK_P2P) { + /** only get first p2p ie here */ + memcpy(ie_out + out_len, pos, + length + 2); + out_len += length + 2; + break; + } + } else if (!memcmp + (pvendor_ie->vend_hdr.oui, wps_oui, + sizeof(pvendor_ie->vend_hdr.oui)) && + pvendor_ie->vend_hdr.oui_type == + wps_oui[3]) { + if (mask & IE_MASK_WPS) { + memcpy(ie_out + out_len, pos, + length + 2); + out_len += length + 2; + } + } else if (!memcmp + (pvendor_ie->vend_hdr.oui, wfd_oui, + sizeof(pvendor_ie->vend_hdr.oui)) && + pvendor_ie->vend_hdr.oui_type == + wfd_oui[3]) { + if (mask & IE_MASK_WFD) { + memcpy(ie_out + out_len, pos, + length + 2); + out_len += length + 2; + } + } else if (mask & IE_MASK_VENDOR) { + memcpy(ie_out + out_len, pos, length + 2); + out_len += length + 2; + } + } + pos += (length + 2); + left_len -= (length + 2); + } + LEAVE(); + return out_len; +} + +/** + * @brief Find specific IE from IE buffer + * + * @param ie Pointer to IEs + * @param len Total length of ie + * @param spec_ie Pointer to specific IE buffer + * @param spec_len Total length of specifc IE + * + * @return out IE length + */ +static t_u8 +woal_find_ie(const t_u8 *ie, int len, const t_u8 *spec_ie, int spec_len) +{ + int left_len = len; + const t_u8 *pos = ie; + int length; + t_u8 id = 0; + + while (left_len >= 2) { + length = *(pos + 1); + id = *pos; + if ((length + 2) > left_len) + break; + if ((length + 2) == spec_len) { + if (!memcmp(pos, spec_ie, spec_len)) { + return MTRUE; + } + } + pos += (length + 2); + left_len -= (length + 2); + } + return MFALSE; +} + +/** + * @brief Filter specific IE in ie buf + * + * @param priv pointer to moal private structure + * @param ie Pointer to IEs + * @param len Total length of ie + * @param ie_out Pointer to out IE buf + * @param wps_flag flag for wps/p2p + * @param dup_ie Pointer to duplicate ie + * @param dup_ie_len duplicate IE len + * + * @return out IE length + */ +static t_u16 +woal_filter_beacon_ies(moal_private *priv, const t_u8 *ie, int len, + t_u8 *ie_out, t_u16 wps_flag, const t_u8 *dup_ie, + int dup_ie_len) +{ + int left_len = len; + const t_u8 *pos = ie; + int length; + t_u8 id = 0; + t_u16 out_len = 0; + IEEEtypes_VendorSpecific_t *pvendor_ie = NULL; + const u8 wps_oui[4] = { 0x00, 0x50, 0xf2, 0x04 }; + const u8 p2p_oui[4] = { 0x50, 0x6f, 0x9a, 0x09 }; + const u8 wfd_oui[4] = { 0x50, 0x6f, 0x9a, 0x0a }; + const t_u8 wmm_oui[4] = { 0x00, 0x50, 0xf2, 0x02 }; + t_u8 find_p2p_ie = MFALSE; + t_u8 enable_11d = MFALSE; + + /* ERP_INFO/EXTENDED_SUPPORT_RATES/HT_CAPABILITY/HT_OPERATION/WMM + * and WPS/P2P/WFD IE will be fileter out */ + while (left_len >= 2) { + length = *(pos + 1); + id = *pos; + if ((length + 2) > left_len) + break; + if (dup_ie && dup_ie_len && + woal_find_ie(dup_ie, dup_ie_len, pos, length + 2)) { + PRINTM(MIOCTL, "skip duplicate IE\n"); + pos += (length + 2); + left_len -= (length + 2); + continue; + } + switch (id) { + case COUNTRY_INFO: + enable_11d = MTRUE; + break; + case EXTENDED_SUPPORTED_RATES: + case WLAN_EID_ERP_INFO: + case HT_CAPABILITY: + case HT_OPERATION: + case REGULATORY_CLASS: + case OVERLAPBSSSCANPARAM: + case WAPI_IE: + break; + case VENDOR_SPECIFIC_221: + /* filter out wmm ie */ + pvendor_ie = (IEEEtypes_VendorSpecific_t *)pos; + if (!memcmp + (pvendor_ie->vend_hdr.oui, wmm_oui, + sizeof(pvendor_ie->vend_hdr.oui)) && + pvendor_ie->vend_hdr.oui_type == wmm_oui[3]) { + break; + } + /* filter out wps ie */ + else if (!memcmp + (pvendor_ie->vend_hdr.oui, wps_oui, + sizeof(pvendor_ie->vend_hdr.oui)) && + pvendor_ie->vend_hdr.oui_type == wps_oui[3]) { + if (wps_flag & IE_MASK_WPS) + break; + } + /* filter out first p2p ie */ + else if (!memcmp + (pvendor_ie->vend_hdr.oui, p2p_oui, + sizeof(pvendor_ie->vend_hdr.oui)) && + pvendor_ie->vend_hdr.oui_type == p2p_oui[3]) { + if (!find_p2p_ie && (wps_flag & IE_MASK_P2P)) { + find_p2p_ie = MTRUE; + break; + } + } + /* filter out wfd ie */ + else if (!memcmp + (pvendor_ie->vend_hdr.oui, wfd_oui, + sizeof(pvendor_ie->vend_hdr.oui)) && + pvendor_ie->vend_hdr.oui_type == wfd_oui[3]) { + if (wps_flag & IE_MASK_WFD) + break; + } else if (wps_flag & IE_MASK_VENDOR) { + //filter out vendor IE + break; + } + memcpy(ie_out + out_len, pos, length + 2); + out_len += length + 2; + break; + default: + memcpy(ie_out + out_len, pos, length + 2); + out_len += length + 2; + break; + } + pos += (length + 2); + left_len -= (length + 2); + } + + if (enable_11d) + woal_set_11d(priv, MOAL_IOCTL_WAIT, MTRUE); + return out_len; +} + +#ifdef WIFI_DIRECT_SUPPORT +/** + * @brief Check if selected_registrar_on in wps_ie + * + * @param ie Pointer to IEs + * @param len Total length of ie + * + * @return MTRUE/MFALSE + */ +t_u8 +is_selected_registrar_on(const t_u8 *ie, int len) +{ +#define WPS_IE_FIX_LEN 6 +#define TLV_ID_SELECTED_REGISTRAR 0x1041 + int left_len = len - WPS_IE_FIX_LEN; + TLV_Generic_t *tlv = (TLV_Generic_t *)(ie + WPS_IE_FIX_LEN); + u16 tlv_type, tlv_len; + u8 *pos = NULL; + while (left_len > sizeof(TLV_Generic_t)) { + tlv_type = ntohs(tlv->type); + tlv_len = ntohs(tlv->len); + if (tlv_type == TLV_ID_SELECTED_REGISTRAR) { + PRINTM(MIOCTL, "Selected Registrar found !"); + pos = (u8 *)tlv + sizeof(TLV_Generic_t); + if (*pos == 1) + return MTRUE; + else + return MFALSE; + } + tlv = (TLV_Generic_t *)((u8 *)tlv + tlv_len + + sizeof(TLV_Generic_t)); + left_len -= tlv_len + sizeof(TLV_Generic_t); + } + return MFALSE; +} + +/** + * @brief Check if selected_registrar_on in ies + * + * @param ie Pointer to IEs + * @param len Total length of ie + * + * + * @return MTRUE/MFALSE + */ +static t_u16 +woal_is_selected_registrar_on(const t_u8 *ie, int len) +{ + int left_len = len; + const t_u8 *pos = ie; + int length; + t_u8 id = 0; + IEEEtypes_VendorSpecific_t *pvendor_ie = NULL; + const u8 wps_oui[4] = { 0x00, 0x50, 0xf2, 0x04 }; + + while (left_len >= 2) { + length = *(pos + 1); + id = *pos; + if ((length + 2) > left_len) + break; + switch (id) { + case VENDOR_SPECIFIC_221: + pvendor_ie = (IEEEtypes_VendorSpecific_t *)pos; + if (!memcmp + (pvendor_ie->vend_hdr.oui, wps_oui, + sizeof(pvendor_ie->vend_hdr.oui)) && + pvendor_ie->vend_hdr.oui_type == wps_oui[3]) { + PRINTM(MIOCTL, "Find WPS ie\n"); + return is_selected_registrar_on(pos, + length + 2); + } + break; + default: + break; + } + pos += (length + 2); + left_len -= (length + 2); + } + return MFALSE; +} +#endif + +/** + * @brief config AP or GO for mgmt frame ies. + * + * @param priv A pointer to moal private structure + * @param beacon_ies A pointer to beacon ies + * @param beacon_ies_len Beacon ies length + * @param proberesp_ies A pointer to probe resp ies + * @param proberesp_ies_len Probe resp ies length + * @param assocresp_ies A pointer to probe resp ies + * @param assocresp_ies_len Assoc resp ies length + * @param probereq_ies A pointer to probe req ies + * @param probereq_ies_len Probe req ies length * + * @param mask Mgmt frame mask + * @param wait_option wait_option + * + * @return 0 -- success, otherwise fail + */ +int +woal_cfg80211_mgmt_frame_ie(moal_private *priv, + const t_u8 *beacon_ies, size_t beacon_ies_len, + const t_u8 *proberesp_ies, size_t proberesp_ies_len, + const t_u8 *assocresp_ies, size_t assocresp_ies_len, + const t_u8 *probereq_ies, size_t probereq_ies_len, + t_u16 mask, t_u8 wait_option) +{ + int ret = 0; + t_u8 *pos = NULL; + custom_ie *beacon_ies_data = NULL; + custom_ie *proberesp_ies_data = NULL; + custom_ie *assocresp_ies_data = NULL; + custom_ie *probereq_ies_data = NULL; + + /* static variables for mgmt frame ie auto-indexing */ + t_u16 beacon_index = priv->beacon_index; + t_u16 proberesp_index = priv->proberesp_index; + t_u16 assocresp_index = priv->assocresp_index; + t_u16 probereq_index = priv->probereq_index; + t_u16 beacon_wps_index = priv->beacon_wps_index; + t_u16 proberesp_p2p_index = priv->proberesp_p2p_index; + t_u16 assocrep_qos_map_index = priv->assocresp_qos_map_index; + t_u16 proberesp_vendor_index = priv->proberesp_vendor_index; + + ENTER(); + + /* we need remove vendor IE from beacon extra IE, vendor IE will be configure through proberesp_vendor_index */ + if (mask & MGMT_MASK_BEACON_WPS_P2P) { + beacon_ies_data = kzalloc(sizeof(custom_ie), GFP_KERNEL); + if (!beacon_ies_data) { + ret = -ENOMEM; + goto done; + } + if (beacon_ies && beacon_ies_len) { +#ifdef WIFI_DIRECT_SUPPORT + if (woal_is_selected_registrar_on + (beacon_ies, beacon_ies_len)) { + PRINTM(MIOCTL, "selected_registrar is on\n"); + priv->phandle->is_go_timer_set = MTRUE; + woal_mod_timer(&priv->phandle->go_timer, + MOAL_TIMER_10S); + } else + PRINTM(MIOCTL, "selected_registrar is off\n"); +#endif + beacon_ies_data->ie_index = beacon_wps_index; + beacon_ies_data->mgmt_subtype_mask = MGMT_MASK_BEACON; + beacon_ies_data->ie_length = + woal_filter_beacon_ies(priv, beacon_ies, + beacon_ies_len, + beacon_ies_data-> + ie_buffer, + IE_MASK_VENDOR, NULL, 0); + DBG_HEXDUMP(MCMD_D, "beacon extra ie", + beacon_ies_data->ie_buffer, + beacon_ies_data->ie_length); + } else { + /* clear the beacon wps ies */ + if (beacon_wps_index > MAX_MGMT_IE_INDEX) { + PRINTM(MERROR, + "Invalid beacon wps index for mgmt frame ie.\n"); + goto done; + } + + beacon_ies_data->ie_index = beacon_wps_index; + beacon_ies_data->mgmt_subtype_mask = + MLAN_CUSTOM_IE_DELETE_MASK; + beacon_ies_data->ie_length = 0; + beacon_wps_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + } + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_custom_ie(priv, beacon_ies_data, + &beacon_wps_index, + proberesp_ies_data, + &proberesp_index, + assocresp_ies_data, + &assocresp_index, probereq_ies_data, + &probereq_index, wait_option)) { + PRINTM(MERROR, "Fail to set beacon wps IE\n"); + ret = -EFAULT; + } + priv->beacon_wps_index = beacon_wps_index; + PRINTM(MCMND, "beacon_wps_index=0x%x len=%d\n", + beacon_wps_index, beacon_ies_data->ie_length); + goto done; + } + + if (mask & MGMT_MASK_ASSOC_RESP_QOS_MAP) { + assocresp_ies_data = kzalloc(sizeof(custom_ie), GFP_KERNEL); + if (!assocresp_ies_data) { + ret = -ENOMEM; + goto done; + } + if (assocresp_ies && assocresp_ies_len) { + /* set the assoc response qos map ies */ + assocresp_ies_data->ie_index = assocrep_qos_map_index; + assocresp_ies_data->mgmt_subtype_mask = + MGMT_MASK_ASSOC_RESP; + if (MLAN_CUSTOM_IE_AUTO_IDX_MASK == + assocrep_qos_map_index) + assocresp_ies_data->mgmt_subtype_mask |= + MLAN_CUSTOM_IE_NEW_MASK; + assocresp_ies_data->ie_length = assocresp_ies_len; + pos = assocresp_ies_data->ie_buffer; + memcpy(pos, assocresp_ies, assocresp_ies_len); + DBG_HEXDUMP(MCMD_D, "Qos Map", + assocresp_ies_data->ie_buffer, + assocresp_ies_data->ie_length); + } else { + /* clear the assoc response qos map ie */ + if (assocrep_qos_map_index > MAX_MGMT_IE_INDEX) { + PRINTM(MERROR, + "Invalid Qos map index for mgmt frame ie.\n"); + goto done; + } + + assocresp_ies_data->ie_index = assocrep_qos_map_index; + assocresp_ies_data->mgmt_subtype_mask = + MLAN_CUSTOM_IE_DELETE_MASK; + assocresp_ies_data->ie_length = 0; + assocrep_qos_map_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + } + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_custom_ie(priv, NULL, &beacon_wps_index, + NULL, &proberesp_index, + assocresp_ies_data, + &assocrep_qos_map_index, NULL, + &probereq_index, wait_option)) { + PRINTM(MERROR, "Fail to set Qos map IE\n"); + ret = -EFAULT; + } + priv->assocresp_qos_map_index = assocrep_qos_map_index; + PRINTM(MCMND, "qos map ie index=0x%x len=%d\n", + assocrep_qos_map_index, assocresp_ies_data->ie_length); + goto done; + } + + if (mask & MGMT_MASK_BEACON) { + beacon_ies_data = kzalloc(sizeof(custom_ie), GFP_KERNEL); + if (!beacon_ies_data) { + ret = -ENOMEM; + goto done; + } + } + + if (mask & MGMT_MASK_PROBE_RESP) { + /** set or clear proberesp ie */ + if (proberesp_ies_len || + (!proberesp_ies_len && !beacon_ies_len)) { + proberesp_ies_data = + kzalloc(sizeof(custom_ie), GFP_KERNEL); + if (!proberesp_ies_data) { + ret = -ENOMEM; + goto done; + } + } + } + + if (mask & MGMT_MASK_ASSOC_RESP) { + /** set or clear assocresp ie */ + if (assocresp_ies_len || + (!assocresp_ies_len && !beacon_ies_len)) { + assocresp_ies_data = + kzalloc(sizeof(custom_ie), GFP_KERNEL); + if (!assocresp_ies_data) { + ret = -ENOMEM; + goto done; + } + } + } + if (mask & MGMT_MASK_PROBE_REQ) { + probereq_ies_data = kzalloc(sizeof(custom_ie), GFP_KERNEL); + if (!probereq_ies_data) { + ret = -ENOMEM; + goto done; + } + } + + if (beacon_ies_data) { + if (beacon_ies && beacon_ies_len) { + /* set the beacon ies */ + /* we need remove vendor IE from beacon tail, vendor IE will be configure through proberesp_vendor_index */ + beacon_ies_data->ie_index = beacon_index; + beacon_ies_data->mgmt_subtype_mask = MGMT_MASK_BEACON | + MGMT_MASK_ASSOC_RESP | MGMT_MASK_PROBE_RESP; + beacon_ies_data->ie_length = + woal_filter_beacon_ies(priv, beacon_ies, + beacon_ies_len, + beacon_ies_data-> + ie_buffer, + IE_MASK_WPS | IE_MASK_WFD + | IE_MASK_P2P | + IE_MASK_VENDOR, + proberesp_ies, + proberesp_ies_len); + DBG_HEXDUMP(MCMD_D, "beacon ie", + beacon_ies_data->ie_buffer, + beacon_ies_data->ie_length); + } else { + /* clear the beacon ies */ + if (beacon_index > MAX_MGMT_IE_INDEX) { + PRINTM(MINFO, + "Invalid beacon index for mgmt frame ie.\n"); + goto done; + } + + beacon_ies_data->ie_index = beacon_index; + beacon_ies_data->mgmt_subtype_mask = + MLAN_CUSTOM_IE_DELETE_MASK; + beacon_ies_data->ie_length = 0; + beacon_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + } + } + + if (proberesp_ies_data) { + if (proberesp_ies && proberesp_ies_len) { + /* set the probe response p2p ies */ + proberesp_ies_data->ie_index = proberesp_p2p_index; + proberesp_ies_data->mgmt_subtype_mask = + MGMT_MASK_PROBE_RESP; + proberesp_ies_data->ie_length = + woal_get_specific_ie(proberesp_ies, + proberesp_ies_len, + proberesp_ies_data-> + ie_buffer, IE_MASK_P2P); + DBG_HEXDUMP(MCMD_D, "proberesp p2p ie", + proberesp_ies_data->ie_buffer, + proberesp_ies_data->ie_length); + } else if (proberesp_p2p_index != MLAN_CUSTOM_IE_AUTO_IDX_MASK) { + /* clear the probe response p2p ies */ + if (proberesp_p2p_index > MAX_MGMT_IE_INDEX) { + PRINTM(MERROR, + "Invalid proberesp_p2p_index for mgmt frame ie.\n"); + goto done; + } + proberesp_ies_data->ie_index = proberesp_p2p_index; + proberesp_ies_data->mgmt_subtype_mask = + MLAN_CUSTOM_IE_DELETE_MASK; + proberesp_ies_data->ie_length = 0; + proberesp_p2p_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + } + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_custom_ie(priv, NULL, &beacon_index, + proberesp_ies_data, + &proberesp_p2p_index, NULL, + &assocresp_index, NULL, + &probereq_index, wait_option)) { + PRINTM(MERROR, "Fail to set proberesp p2p IE\n"); + ret = -EFAULT; + goto done; + } + priv->proberesp_p2p_index = proberesp_p2p_index; + PRINTM(MCMND, "proberesp_p2p=0x%x len=%d\n", + proberesp_p2p_index, proberesp_ies_data->ie_length); + memset(proberesp_ies_data, 0x00, sizeof(custom_ie)); + if (proberesp_ies && proberesp_ies_len) { + /* set the probe response/beacon vendor ies */ + proberesp_ies_data->ie_index = proberesp_vendor_index; + proberesp_ies_data->mgmt_subtype_mask = + MGMT_MASK_PROBE_RESP | MGMT_MASK_BEACON; + if (MLAN_CUSTOM_IE_AUTO_IDX_MASK == + proberesp_vendor_index) + proberesp_ies_data->mgmt_subtype_mask |= + MLAN_CUSTOM_IE_NEW_MASK; + proberesp_ies_data->ie_length = + woal_get_specific_ie(proberesp_ies, + proberesp_ies_len, + proberesp_ies_data-> + ie_buffer, IE_MASK_VENDOR); + DBG_HEXDUMP(MCMD_D, "proberesp vendor IE", + proberesp_ies_data->ie_buffer, + proberesp_ies_data->ie_length); + } else if (proberesp_vendor_index != + MLAN_CUSTOM_IE_AUTO_IDX_MASK) { + /* clear the probe response p2p ies */ + if (proberesp_vendor_index > MAX_MGMT_IE_INDEX) { + PRINTM(MERROR, + "Invalid proberesp_vendor_index for mgmt frame ie.\n"); + goto done; + } + proberesp_ies_data->ie_index = proberesp_vendor_index; + proberesp_ies_data->mgmt_subtype_mask = + MLAN_CUSTOM_IE_DELETE_MASK; + proberesp_ies_data->ie_length = 0; + proberesp_vendor_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + } + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_custom_ie(priv, NULL, &beacon_index, + proberesp_ies_data, + &proberesp_vendor_index, NULL, + &assocresp_index, NULL, + &probereq_index, wait_option)) { + PRINTM(MERROR, "Fail to set proberesp vendor IE\n"); + ret = -EFAULT; + goto done; + } + priv->proberesp_vendor_index = proberesp_vendor_index; + PRINTM(MCMND, "proberesp_vendor=0x%x len=%d\n", + proberesp_vendor_index, proberesp_ies_data->ie_length); + memset(proberesp_ies_data, 0x00, sizeof(custom_ie)); + if (proberesp_ies && proberesp_ies_len) { + /* set the probe response ies */ + proberesp_ies_data->ie_index = proberesp_index; + proberesp_ies_data->mgmt_subtype_mask = + MGMT_MASK_PROBE_RESP; + if (MLAN_CUSTOM_IE_AUTO_IDX_MASK == proberesp_index) + proberesp_ies_data->mgmt_subtype_mask |= + MLAN_CUSTOM_IE_NEW_MASK; + proberesp_ies_data->ie_length = + woal_filter_beacon_ies(priv, proberesp_ies, + proberesp_ies_len, + proberesp_ies_data-> + ie_buffer, + IE_MASK_P2P | + IE_MASK_VENDOR, NULL, 0); + DBG_HEXDUMP(MCMD_D, "proberesp ie", + proberesp_ies_data->ie_buffer, + proberesp_ies_data->ie_length); + } else { + /* clear the probe response ies */ + if (proberesp_index > MAX_MGMT_IE_INDEX) { + PRINTM(MERROR, + "Invalid probe resp index for mgmt frame ie.\n"); + goto done; + } + proberesp_ies_data->ie_index = proberesp_index; + proberesp_ies_data->mgmt_subtype_mask = + MLAN_CUSTOM_IE_DELETE_MASK; + proberesp_ies_data->ie_length = 0; + proberesp_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + } + } + if (assocresp_ies_data) { + if (assocresp_ies && assocresp_ies_len) { + /* set the assoc response ies */ + assocresp_ies_data->ie_index = assocresp_index; + assocresp_ies_data->mgmt_subtype_mask = + MGMT_MASK_ASSOC_RESP; + if (MLAN_CUSTOM_IE_AUTO_IDX_MASK == assocresp_index) + assocresp_ies_data->mgmt_subtype_mask |= + MLAN_CUSTOM_IE_NEW_MASK; + assocresp_ies_data->ie_length = assocresp_ies_len; + pos = assocresp_ies_data->ie_buffer; + memcpy(pos, assocresp_ies, assocresp_ies_len); + DBG_HEXDUMP(MCMD_D, "assocresp ie", + assocresp_ies_data->ie_buffer, + assocresp_ies_data->ie_length); + } else { + /* clear the assoc response ies */ + if (assocresp_index > MAX_MGMT_IE_INDEX) { + PRINTM(MERROR, + "Invalid assoc resp index for mgmt frame ie.\n"); + goto done; + } + + assocresp_ies_data->ie_index = assocresp_index; + assocresp_ies_data->mgmt_subtype_mask = + MLAN_CUSTOM_IE_DELETE_MASK; + assocresp_ies_data->ie_length = 0; + assocresp_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + } + } + + if (probereq_ies_data) { + if (probereq_ies && probereq_ies_len) { + /* set the probe req ies */ + probereq_ies_data->ie_index = probereq_index; + probereq_ies_data->mgmt_subtype_mask = + MGMT_MASK_PROBE_REQ; +#if defined(WIFI_DIRECT_SUPPORT) +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION + if (priv->bss_type != MLAN_BSS_TYPE_WIFIDIRECT) { + /* filter out P2P/WFD ie */ + probereq_ies_data->ie_length = + woal_filter_beacon_ies(priv, + probereq_ies, + probereq_ies_len, + probereq_ies_data-> + ie_buffer, + IE_MASK_P2P | + IE_MASK_WFD, + NULL, 0); + } else { +#endif /* KERNEL_VERSION */ +#endif /* WIFI_DIRECT_SUPPORT && V14_FEATURE */ + probereq_ies_data->ie_length = probereq_ies_len; + pos = probereq_ies_data->ie_buffer; + memcpy(pos, probereq_ies, probereq_ies_len); +#if defined(WIFI_DIRECT_SUPPORT) +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION + } +#endif /* KERNEL_VERSION */ +#endif /* WIFI_DIRECT_SUPPORT && V14_FEATURE */ + DBG_HEXDUMP(MCMD_D, "probereq ie", + probereq_ies_data->ie_buffer, + probereq_ies_data->ie_length); + } else { + /* clear the probe req ies */ + if (probereq_index > MAX_MGMT_IE_INDEX) { + PRINTM(MERROR, + "Invalid probe req index for mgmt frame ie.\n"); + goto done; + } + probereq_ies_data->ie_index = probereq_index; + probereq_ies_data->mgmt_subtype_mask = + MLAN_CUSTOM_IE_DELETE_MASK; + probereq_ies_data->ie_length = 0; + probereq_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + } + } + + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_custom_ie(priv, beacon_ies_data, &beacon_index, + proberesp_ies_data, &proberesp_index, + assocresp_ies_data, &assocresp_index, + probereq_ies_data, &probereq_index, + wait_option)) { + PRINTM(MERROR, + "Fail to set beacon proberesp assoc probereq IES\n"); + ret = -EFAULT; + goto done; + } + if (beacon_ies_data) { + priv->beacon_index = beacon_index; + PRINTM(MCMND, "beacon ie length = %d\n", + beacon_ies_data->ie_length); + } + if (assocresp_ies_data) { + priv->assocresp_index = assocresp_index; + PRINTM(MCMND, "assocresp ie length = %d\n", + assocresp_ies_data->ie_length); + } + if (proberesp_ies_data) { + priv->proberesp_index = proberesp_index; + PRINTM(MCMND, "proberesp ie length = %d\n", + proberesp_ies_data->ie_length); + } + if (probereq_ies_data) { + priv->probereq_index = probereq_index; + PRINTM(MCMND, "probereq ie length = %d\n", + probereq_ies_data->ie_length); + } + PRINTM(MCMND, "beacon=%x assocresp=%x proberesp=%x probereq=%x\n", + beacon_index, assocresp_index, proberesp_index, probereq_index); +done: + kfree(beacon_ies_data); + kfree(proberesp_ies_data); + kfree(assocresp_ies_data); + kfree(probereq_ies_data); + + LEAVE(); + + return ret; +} + +/** + * @brief Sets up the CFG802.11 specific HT capability fields + * with default values + * + * @param ht_info A pointer to ieee80211_sta_ht_cap structure + * @param dev_cap Device capability informations + * @param mcs_set Device MCS sets + * + * @return N/A + */ +void +woal_cfg80211_setup_ht_cap(struct ieee80211_sta_ht_cap *ht_info, + t_u32 dev_cap, t_u8 *mcs_set) +{ + ENTER(); + + ht_info->ht_supported = true; + ht_info->ampdu_factor = 0x3; + ht_info->ampdu_density = 0; + + memset(&ht_info->mcs, 0, sizeof(ht_info->mcs)); + ht_info->cap = 0; + if (mcs_set) + memcpy(ht_info->mcs.rx_mask, mcs_set, + sizeof(ht_info->mcs.rx_mask)); + if (dev_cap & MBIT(8)) /* 40Mhz intolarance enabled */ + ht_info->cap |= IEEE80211_HT_CAP_40MHZ_INTOLERANT; + if (dev_cap & MBIT(17)) /* Channel width 20/40Mhz support */ + ht_info->cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; + if ((dev_cap >> 20) & 0x03) /* Delayed ACK supported */ + ht_info->cap |= IEEE80211_HT_CAP_DELAY_BA; + if (dev_cap & MBIT(22)) /* Rx LDPC supported */ + ht_info->cap |= IEEE80211_HT_CAP_LDPC_CODING; + if (dev_cap & MBIT(23)) /* Short GI @ 20Mhz supported */ + ht_info->cap |= IEEE80211_HT_CAP_SGI_20; + if (dev_cap & MBIT(24)) /* Short GI @ 40Mhz supported */ + ht_info->cap |= IEEE80211_HT_CAP_SGI_40; + if (dev_cap & MBIT(25)) /* Tx STBC supported */ + ht_info->cap |= IEEE80211_HT_CAP_TX_STBC; + if (dev_cap & MBIT(26)) /* Rx STBC supported */ + ht_info->cap |= IEEE80211_HT_CAP_RX_STBC; + if (dev_cap & MBIT(27)) /* MIMO PS supported */ + ht_info->cap |= 0; /* WLAN_HT_CAP_SM_PS_STATIC */ + else /* Disable HT SM PS */ + ht_info->cap |= IEEE80211_HT_CAP_SM_PS; + if (dev_cap & MBIT(29)) /* Green field supported */ + ht_info->cap |= IEEE80211_HT_CAP_GRN_FLD; + if (dev_cap & MBIT(31)) /* MAX AMSDU supported */ + ht_info->cap |= IEEE80211_HT_CAP_MAX_AMSDU; + /* DSSS/CCK in 40Mhz supported */ + ht_info->cap |= IEEE80211_HT_CAP_DSSSCCK40; + ht_info->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; + + LEAVE(); +} + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) +/** + * @brief create cfg80211_chan_def structure based on chan_band info + * + * @param priv A pointer moal_private structure + * @param chandef A pointer to cfg80211_chan_def structure + * @param pchan_info A pointer to chan_band_info structure + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_FAILURE + */ +mlan_status +woal_chandef_create(moal_private *priv, struct cfg80211_chan_def *chandef, + chan_band_info * pchan_info) +{ + enum ieee80211_band band = IEEE80211_BAND_2GHZ; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + chandef->center_freq2 = 0; + if (pchan_info->bandcfg.chanBand == BAND_2GHZ) + band = IEEE80211_BAND_2GHZ; + else if (pchan_info->bandcfg.chanBand == BAND_5GHZ) + band = IEEE80211_BAND_5GHZ; + chandef->chan = ieee80211_get_channel(priv->wdev->wiphy, + ieee80211_channel_to_frequency + (pchan_info->channel, band)); + if (chandef->chan == NULL) { + PRINTM(MERROR, + "Fail on ieee80211_get_channel, channel=%d, band=%d\n", + pchan_info->channel, band); + status = MLAN_STATUS_FAILURE; + goto done; + } + switch (pchan_info->bandcfg.chanWidth) { + case CHAN_BW_20MHZ: + if (pchan_info->is_11n_enabled) + chandef->width = NL80211_CHAN_WIDTH_20; + else + chandef->width = NL80211_CHAN_WIDTH_20_NOHT; + chandef->center_freq1 = chandef->chan->center_freq; + break; + case CHAN_BW_40MHZ: + chandef->width = NL80211_CHAN_WIDTH_40; + if (pchan_info->bandcfg.chan2Offset == SEC_CHAN_ABOVE) + chandef->center_freq1 = chandef->chan->center_freq + 10; + else if (pchan_info->bandcfg.chan2Offset == SEC_CHAN_BELOW) + chandef->center_freq1 = chandef->chan->center_freq - 10; + break; + default: + break; + } +done: + LEAVE(); + return status; +} +#endif + +/** + * @brief Get second channel offset + * + * @param chan channel num + * @return second channel offset + */ +t_u8 +woal_get_second_channel_offset(int chan) +{ + t_u8 chan2Offset = SEC_CHAN_NONE; + + switch (chan) { + case 36: + case 44: + case 52: + case 60: + case 100: + case 108: + case 116: + case 124: + case 132: + case 149: + case 157: + chan2Offset = SEC_CHAN_ABOVE; + break; + case 40: + case 48: + case 56: + case 64: + case 104: + case 112: + case 120: + case 128: + case 136: + case 153: + case 161: + chan2Offset = SEC_CHAN_BELOW; + break; + case 165: + /* Special Case: 20Mhz-only Channel */ + chan2Offset = SEC_CHAN_NONE; + break; + } + return chan2Offset; +} diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_cfg80211.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_cfg80211.h new file mode 100644 index 000000000000..3fb267457470 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_cfg80211.h @@ -0,0 +1,454 @@ +/** @file moal_cfg80211.h + * + * @brief This file contains the CFG80211 specific defines. + * + * Copyright (C) 2011-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +#ifndef _MOAL_CFG80211_H_ +#define _MOAL_CFG80211_H_ + +#include "moal_main.h" + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) +#define IEEE80211_CHAN_PASSIVE_SCAN IEEE80211_CHAN_NO_IR +#define IEEE80211_CHAN_NO_IBSS IEEE80211_CHAN_NO_IR +#endif + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 16, 0) +#define MAX_CSA_COUNTERS_NUM 2 +#endif + +/* Clear all key indexes */ +#define KEY_INDEX_CLEAR_ALL (0x0000000F) + +/** RTS/FRAG disabled value */ +#define MLAN_FRAG_RTS_DISABLED (0xFFFFFFFF) + +#ifndef WLAN_CIPHER_SUITE_SMS4 +#define WLAN_CIPHER_SUITE_SMS4 0x00000020 +#endif + +#ifndef WLAN_CIPHER_SUITE_AES_CMAC +#define WLAN_CIPHER_SUITE_AES_CMAC 0x000FAC06 +#endif + +/* define for custom ie operation */ +#define MLAN_CUSTOM_IE_AUTO_IDX_MASK 0xffff +#define MLAN_CUSTOM_IE_DELETE_MASK 0x0 +#define MLAN_CUSTOM_IE_NEW_MASK 0x8000 +#define IE_MASK_WPS 0x0001 +#define IE_MASK_P2P 0x0002 +#define IE_MASK_WFD 0x0004 +#define IE_MASK_VENDOR 0x0008 + +#define MRVL_PKT_TYPE_MGMT_FRAME 0xE5 + +/** + * If multiple wiphys are registered e.g. a regular netdev with + * assigned ieee80211_ptr and you won't know whether it points + * to a wiphy your driver has registered or not. Assign this to + * something global to your driver to help determine whether + * you own this wiphy or not. + */ +static const void *const mrvl_wiphy_privid = &mrvl_wiphy_privid; + +/* Get the private structure from wiphy */ +void *woal_get_wiphy_priv(struct wiphy *wiphy); + +/* Get the private structure from net device */ +void *woal_get_netdev_priv(struct net_device *dev); +#ifdef STA_SUPPORT +/** get scan interface */ +moal_private *woal_get_scan_interface(moal_handle *handle); +#endif + +t_u8 woal_band_cfg_to_ieee_band(t_u32 band); + +int woal_cfg80211_change_virtual_intf(struct wiphy *wiphy, + struct net_device *dev, + enum nl80211_iftype type, +#if CFG80211_VERSION_CODE < KERNEL_VERSION(4, 12, 0) + u32 *flags, +#endif + struct vif_params *params); + +int woal_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed); + +int woal_cfg80211_add_key(struct wiphy *wiphy, + struct net_device *dev, t_u8 key_index, +#if CFG80211_VERSION_CODE > KERNEL_VERSION(2, 6, 36) + bool pairwise, +#endif + const t_u8 *mac_addr, struct key_params *params); + +int woal_cfg80211_del_key(struct wiphy *wiphy, + struct net_device *dev, t_u8 key_index, +#if CFG80211_VERSION_CODE > KERNEL_VERSION(2, 6, 36) + bool pairwise, +#endif + const t_u8 *mac_addr); +#ifdef STA_SUPPORT +/** Opportunistic Key Caching APIs support */ +struct pmksa_entry *woal_get_pmksa_entry(moal_private *priv, const u8 *bssid); + +int woal_flush_pmksa_list(moal_private *priv); + +int woal_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_pmksa *pmksa); + +int woal_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_pmksa *pmksa); + +int woal_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *dev); +#endif + +int woal_cfg80211_set_bitrate_mask(struct wiphy *wiphy, + struct net_device *dev, + const u8 *peer, + const struct cfg80211_bitrate_mask *mask); +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 38) +int woal_cfg80211_set_antenna(struct wiphy *wiphy, u32 tx_ant, u32 rx_ant); +int woal_cfg80211_get_antenna(struct wiphy *wiphy, u32 *tx_ant, u32 *rx_ant); +#endif + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) +int woal_cfg80211_set_qos_map(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_qos_map *qos_map); +#endif + +#ifdef STA_CFG80211 +#ifdef STA_SUPPORT +int woal_set_rf_channel(moal_private *priv, + struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type, + t_u8 wait_option); + +static inline int +woal_cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted) +{ +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 8, 0) + struct cfg80211_scan_info info; + info.aborted = aborted; + cfg80211_scan_done(request, &info); +#else + cfg80211_scan_done(request, aborted); +#endif + return 0; +} + +mlan_status woal_inform_bss_from_scan_result(moal_private *priv, + mlan_ssid_bssid *ssid_bssid, + t_u8 wait_option); +#endif +#endif + +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 5, 0) +int woal_cfg80211_set_channel(struct wiphy *wiphy, +#if CFG80211_VERSION_CODE > KERNEL_VERSION(2, 6, 34) + struct net_device *dev, +#endif + struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type); +#endif + +#if CFG80211_VERSION_CODE > KERNEL_VERSION(2, 6, 37) +int woal_cfg80211_set_default_key(struct wiphy *wiphy, + struct net_device *dev, t_u8 key_index, + bool ucast, bool mcast); +#else +int woal_cfg80211_set_default_key(struct wiphy *wiphy, + struct net_device *dev, t_u8 key_index); +#endif + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) +int woal_cfg80211_set_default_mgmt_key(struct wiphy *wiphy, + struct net_device *netdev, + t_u8 key_index); +#endif + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 1, 0) +int woal_cfg80211_set_rekey_data(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_gtk_rekey_data *data); +#endif + +void woal_cfg80211_mgmt_frame_register(struct wiphy *wiphy, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) + struct wireless_dev *wdev, +#else + struct net_device *dev, +#endif + t_u16 frame_type, bool reg); + +int woal_cfg80211_mgmt_tx(struct wiphy *wiphy, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) + struct wireless_dev *wdev, +#else + struct net_device *dev, +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + struct cfg80211_mgmt_tx_params *params, +#else + struct ieee80211_channel *chan, bool offchan, +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 8, 0) + enum nl80211_channel_type channel_type, + bool channel_type_valid, +#endif + unsigned int wait, const u8 *buf, size_t len, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) + bool no_cck, +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 3, 0) + bool dont_wait_for_ack, +#endif +#endif + u64 * cookie); + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) +void woal_update_radar_chans_dfs_state(struct wiphy *wiphy); +#endif + +mlan_status woal_register_cfg80211(moal_private *priv); + +extern struct ieee80211_supported_band cfg80211_band_2ghz; +extern struct ieee80211_supported_band cfg80211_band_5ghz; + +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) +int woal_cfg80211_bss_role_cfg(moal_private *priv, t_u16 action, + t_u8 *bss_role); +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) +struct wireless_dev *woal_cfg80211_add_virtual_intf(struct wiphy *wiphy, + const char *name, + unsigned char + name_assign_type, + enum nl80211_iftype type, +#if CFG80211_VERSION_CODE < KERNEL_VERSION(4, 12, 0) + u32 *flags, +#endif + struct vif_params *params); +#else +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 7, 0) +struct wireless_dev *woal_cfg80211_add_virtual_intf(struct wiphy *wiphy, + const char *name, + enum nl80211_iftype type, + u32 *flags, + struct vif_params *params); +#else +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) +struct wireless_dev *woal_cfg80211_add_virtual_intf(struct wiphy *wiphy, + char *name, + enum nl80211_iftype type, + u32 *flags, + struct vif_params *params); +#else +#if CFG80211_VERSION_CODE > KERNEL_VERSION(2, 6, 37) +struct net_device *woal_cfg80211_add_virtual_intf(struct wiphy *wiphy, + char *name, + enum nl80211_iftype type, + u32 *flags, + struct vif_params *params); +#else +int woal_cfg80211_add_virtual_intf(struct wiphy *wiphy, + char *name, enum nl80211_iftype type, + u32 *flags, struct vif_params *params); +#endif +#endif +#endif +#endif +int woal_cfg80211_del_virt_if(struct wiphy *wiphy, struct net_device *dev); +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) +int woal_cfg80211_del_virtual_intf(struct wiphy *wiphy, + struct wireless_dev *wdev); +#else +int woal_cfg80211_del_virtual_intf(struct wiphy *wiphy, struct net_device *dev); +#endif + +#if defined(WIFI_DIRECT_SUPPORT) +/* Group Owner Negotiation Req */ +#define P2P_GO_NEG_REQ 0 +/* Group Owner Negotiation Rsp */ +#define P2P_GO_NEG_RSP 1 +/* Group Owner Negotiation Confirm */ +#define P2P_GO_NEG_CONF 2 +/* P2P Invitation Request */ +#define P2P_INVITE_REQ 3 +/* P2P Invitation Response */ +#define P2P_INVITE_RSP 4 +/* Device Discoverability Request */ +#define P2P_DEVDIS_REQ 5 +/* Device Discoverability Response */ +#define P2P_DEVDIS_RSP 6 +/* Provision Discovery Request */ +#define P2P_PROVDIS_REQ 7 +/* Provision Discovery Response */ +#define P2P_PROVDIS_RSP 8 +/** P2P category */ +#define P2P_ACT_FRAME_CATEGORY 0x04 +/** P2P oui offset */ +#define P2P_ACT_FRAME_OUI_OFFSET 26 +/** P2P subtype offset */ +#define P2P_ACT_FRAME_OUI_SUBTYPE_OFFSET 30 +void woal_cfg80211_display_p2p_actframe(const t_u8 *buf, int len, + struct ieee80211_channel *chan, + const t_u8 flag); + +/** Define kernel version for wifi direct */ +#define WIFI_DIRECT_KERNEL_VERSION KERNEL_VERSION(2, 6, 39) +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION +int woal_cfg80211_init_p2p_client(moal_private *priv); + +int woal_cfg80211_init_p2p_go(moal_private *priv); + +int woal_cfg80211_deinit_p2p(moal_private *priv); + +void woal_remove_virtual_interface(moal_handle *handle); + +#endif /* KERNEL_VERSION */ +#endif /* WIFI_DIRECT_SUPPORT && V14_FEATURE */ + +/** Define for remain on channel duration timer */ +#define MAX_REMAIN_ON_CHANNEL_DURATION (1000) +int woal_cfg80211_remain_on_channel_cfg(moal_private *priv, + t_u8 wait_option, t_u8 remove, + t_u8 *status, + struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type, + t_u32 duration); + +#ifdef UAP_CFG80211 +int woal_uap_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 16, 0) + const u8 *mac, +#else + u8 *mac, +#endif + struct station_info *stainfo); + +int woal_uap_cfg80211_dump_station(struct wiphy *wiphy, + struct net_device *dev, int idx, + t_u8 *mac, struct station_info *sinfo); +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) +int woal_cfg80211_change_bss(struct wiphy *wiphy, struct net_device *dev, + struct bss_parameters *params); +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 9, 0) +int woal_cfg80211_set_mac_acl(struct wiphy *wiphy, struct net_device *dev, + const struct cfg80211_acl_data *params); +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 1, 0) +int woal_cfg80211_set_txq_params(struct wiphy *wiphy, struct net_device *dev, + struct ieee80211_txq_params *params); +#endif + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) +int woal_cfg80211_set_coalesce(struct wiphy *wiphy, + struct cfg80211_coalesce *coalesce); +#endif + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) +int woal_cfg80211_add_beacon(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_ap_settings *params); + +int woal_cfg80211_set_beacon(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_beacon_data *params); +#else +int woal_cfg80211_add_beacon(struct wiphy *wiphy, + struct net_device *dev, + struct beacon_parameters *params); + +int woal_cfg80211_set_beacon(struct wiphy *wiphy, + struct net_device *dev, + struct beacon_parameters *params); +#endif + +int woal_cfg80211_del_beacon(struct wiphy *wiphy, struct net_device *dev); +int woal_cfg80211_del_station(struct wiphy *wiphy, struct net_device *dev, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) + struct station_del_parameters *param); +#else +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 16, 0) + const u8 *mac_addr); +#else + u8 *mac_addr); +#endif +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 15, 0) +int woal_cfg80211_start_radar_detection(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_chan_def *chandef, + u32 cac_time_ms); +#else +int woal_cfg80211_start_radar_detection(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_chan_def *chandef); +#endif + +int woal_cfg80211_channel_switch(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_csa_settings *params); + +void woal_cac_timer_func(void *context); +void woal_csa_work_queue(struct work_struct *work); +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 5, 0) +void woal_cfg80211_notify_uap_channel(moal_private *priv, + chan_band_info * pchan_info); +#endif +#endif /* UAP_CFG80211 */ + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) +mlan_status woal_chandef_create(moal_private *priv, + struct cfg80211_chan_def *chandef, + chan_band_info * pchan_info); +#endif + +void woal_clear_all_mgmt_ies(moal_private *priv, t_u8 wait_option); +int woal_cfg80211_mgmt_frame_ie(moal_private *priv, + const t_u8 *beacon_ies, size_t beacon_ies_len, + const t_u8 *proberesp_ies, + size_t proberesp_ies_len, + const t_u8 *assocresp_ies, + size_t assocresp_ies_len, + const t_u8 *probereq_ies, + size_t probereq_ies_len, t_u16 mask, + t_u8 wait_option); + +int woal_get_active_intf_freq(moal_private *priv); + +void woal_cfg80211_setup_ht_cap(struct ieee80211_sta_ht_cap *ht_info, + t_u32 dev_cap, t_u8 *mcs_set); +int woal_cfg80211_assoc(moal_private *priv, void *sme, t_u8 wait_option); + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) +#define REGULATORY_CFG_LEN (NL80211_MAX_SUPP_REG_RULES << 1) +enum marvell_channel_flags { + MARVELL_CHANNEL_PASSIVE = BIT(0), + MARVELL_CHANNEL_DFS = BIT(1), + MARVELL_CHANNEL_NOHT40 = BIT(2), + MARVELL_CHANNEL_NOHT80 = BIT(3), + MARVELL_CHANNEL_DISABLED = BIT(7), +}; +#endif + +t_u8 woal_get_second_channel_offset(int chan); + +#endif /* _MOAL_CFG80211_H_ */ diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_cfgvendor.c b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_cfgvendor.c new file mode 100644 index 000000000000..40ba8c17b909 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_cfgvendor.c @@ -0,0 +1,1514 @@ +/** @file moal_cfgvendor.c + * + * @brief This file contains the functions for CFG80211 vendor. + * + * Copyright (C) 2011-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +#include "moal_cfgvendor.h" +#include "moal_cfg80211.h" + +/******************************************************** + Local Variables +********************************************************/ + +/******************************************************** + Global Variables +********************************************************/ +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) +extern int dfs_offload; +#endif +extern int roamoffload_in_hs; +/******************************************************** + Local Functions +********************************************************/ + +/******************************************************** + Global Functions +********************************************************/ + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) +/**marvell vendor command and event*/ +#define MRVL_VENDOR_ID 0x005043 +/** vendor events */ +const struct nl80211_vendor_cmd_info vendor_events[] = { + {.vendor_id = MRVL_VENDOR_ID,.subcmd = event_hang,}, /*event_id 0 */ + {.vendor_id = MRVL_VENDOR_ID,.subcmd = event_rssi_monitor,}, /*event_id 0x1501 */ + {.vendor_id = MRVL_VENDOR_ID,.subcmd = fw_roam_success,}, /*event_id 0x10002 */ + {.vendor_id = MRVL_VENDOR_ID,.subcmd = event_dfs_radar_detected,}, /*event_id 0x10004 */ + {.vendor_id = MRVL_VENDOR_ID,.subcmd = event_dfs_cac_started,}, /*event_id 0x10005 */ + {.vendor_id = MRVL_VENDOR_ID,.subcmd = event_dfs_cac_finished,}, /*event_id 0x10006 */ + {.vendor_id = MRVL_VENDOR_ID,.subcmd = event_dfs_cac_aborted,}, /*event_id 0x10007 */ + {.vendor_id = MRVL_VENDOR_ID,.subcmd = event_dfs_nop_finished,}, /*event_id 0x10008 */ + /**add vendor event here*/ +}; + +/** + * @brief get the event id of the events array + * + * @param event vendor event + * + * @return index of events array + */ +int +woal_get_event_id(int event) +{ + int i = 0; + + for (i = 0; i < ARRAY_SIZE(vendor_events); i++) { + if (vendor_events[i].subcmd == event) + return i; + } + + return event_max; +} + +/** + * @brief send vendor event to kernel + * + * @param priv A pointer to moal_private + * @param event vendor event + * @param len data length + * + * @return 0: success 1: fail + */ +int +woal_cfg80211_vendor_event(IN moal_private *priv, + IN int event, IN t_u8 *data, IN int len) +{ + struct wiphy *wiphy = NULL; + struct sk_buff *skb = NULL; + int event_id = 0; + t_u8 *pos = NULL; + int ret = 0; + + ENTER(); + + if (!priv || !priv->wdev || !priv->wdev->wiphy) { + LEAVE(); + return ret; + } + wiphy = priv->wdev->wiphy; + PRINTM(MEVENT, "vendor event :0x%x\n", event); + event_id = woal_get_event_id(event); + if (event_max == event_id) { + PRINTM(MERROR, "Not find this event %d \n", event_id); + ret = 1; + LEAVE(); + return ret; + } + + /**allocate skb*/ +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) + skb = cfg80211_vendor_event_alloc(wiphy, NULL, len, event_id, + GFP_ATOMIC); +#else + skb = cfg80211_vendor_event_alloc(wiphy, len, event_id, GFP_ATOMIC); +#endif + + if (!skb) { + PRINTM(MERROR, "allocate memory fail for vendor event\n"); + ret = 1; + LEAVE(); + return ret; + } + pos = skb_put(skb, len); + memcpy(pos, data, len); + /**send event*/ + cfg80211_vendor_event(skb, GFP_ATOMIC); + + LEAVE(); + return ret; +} + +/** + * @brief send vendor event to kernel + * + * @param priv A pointer to moal_private + * @param event vendor event + * @param data a pointer to data + * @param len data length + * + * @return 0: success 1: fail + */ +struct sk_buff * +woal_cfg80211_alloc_vendor_event(IN moal_private *priv, + IN int event, IN int len) +{ + struct wiphy *wiphy = NULL; + struct sk_buff *skb = NULL; + int event_id = 0; + + ENTER(); + + if (!priv || !priv->wdev || !priv->wdev->wiphy) { + PRINTM(MERROR, "Not find this event %d \n", event_id); + goto done; + } + wiphy = priv->wdev->wiphy; + PRINTM(MEVENT, "vendor event :0x%x\n", event); + event_id = woal_get_event_id(event); + if (event_max == event_id) { + PRINTM(MERROR, "Not find this event %d \n", event_id); + goto done; + } + + /**allocate skb*/ +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) + skb = cfg80211_vendor_event_alloc(wiphy, NULL, len, event_id, + GFP_ATOMIC); +#else + skb = cfg80211_vendor_event_alloc(wiphy, len, event_id, GFP_ATOMIC); +#endif + + if (!skb) { + PRINTM(MERROR, "allocate memory fail for vendor event\n"); + goto done; + } + +done: + LEAVE(); + return skb; +} + +/** + * @brief send dfs vendor event to kernel + * + * @param priv A pointer to moal_private + * @param event dfs vendor event + * @param chandef a pointer to struct cfg80211_chan_def + * + * @return N/A + */ +void +woal_cfg80211_dfs_vendor_event(moal_private *priv, int event, + struct cfg80211_chan_def *chandef) +{ + dfs_event evt; + ENTER(); + if (!chandef) { + LEAVE(); + return; + } + memset(&evt, 0, sizeof(dfs_event)); + evt.freq = chandef->chan->center_freq; + evt.chan_width = chandef->width; + evt.cf1 = chandef->center_freq1; + evt.cf2 = chandef->center_freq2; + switch (chandef->width) { + case NL80211_CHAN_WIDTH_20_NOHT: + evt.ht_enabled = 0; + break; + case NL80211_CHAN_WIDTH_20: + evt.ht_enabled = 1; + break; + case NL80211_CHAN_WIDTH_40: + evt.ht_enabled = 1; + if (chandef->center_freq1 < chandef->chan->center_freq) + evt.chan_offset = -1; + else + evt.chan_offset = 1; + break; + case NL80211_CHAN_WIDTH_80: + case NL80211_CHAN_WIDTH_80P80: + case NL80211_CHAN_WIDTH_160: + evt.ht_enabled = 1; + break; + default: + break; + } + woal_cfg80211_vendor_event(priv, event, (t_u8 *)&evt, + sizeof(dfs_event)); + LEAVE(); + return; +} + +/** + * @brief vendor command to set drvdbg + * + * @param wiphy A pointer to wiphy struct + * @param wdev A pointer to wireless_dev struct + * @param data a pointer to data + * @param len data length + * + * @return 0: success 1: fail + */ +static int +woal_cfg80211_subcmd_set_drvdbg(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ +#ifdef DEBUG_LEVEL1 + struct net_device *dev = wdev->netdev; + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + struct sk_buff *skb = NULL; + t_u8 *pos = NULL; +#endif + int ret = 1; + + ENTER(); +#ifdef DEBUG_LEVEL1 + /**handle this sub command*/ + DBG_HEXDUMP(MCMD_D, "Vendor drvdbg", (t_u8 *)data, data_len); + + if (data_len) { + /* Get the driver debug bit masks from user */ + drvdbg = *((t_u32 *)data); + PRINTM(MIOCTL, "new drvdbg %x\n", drvdbg); + /* Set the driver debug bit masks into mlan */ + if (woal_set_drvdbg(priv, drvdbg)) { + PRINTM(MERROR, "Set drvdbg failed!\n"); + ret = 1; + } + } + /** Allocate skb for cmd reply*/ + skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, sizeof(drvdbg)); + if (!skb) { + PRINTM(MERROR, "allocate memory fail for vendor cmd\n"); + ret = 1; + LEAVE(); + return ret; + } + pos = skb_put(skb, sizeof(drvdbg)); + memcpy(pos, &drvdbg, sizeof(drvdbg)); + ret = cfg80211_vendor_cmd_reply(skb); +#endif + LEAVE(); + return ret; +} + +/** + * @brief process one channel in bucket + * + * @param priv A pointer to moal_private struct + * + * @param channel a pointer to channel + * + * @return 0: success other: fail + */ +static mlan_status +woal_band_to_valid_channels(moal_private *priv, wifi_band w_band, int channel[], + t_u32 *nchannel) +{ + int band = 0; + struct ieee80211_supported_band *sband; + struct ieee80211_channel *ch; + int i = 0; + t_u8 cnt = 0; + int *ch_ptr = channel; + + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + if (!priv->wdev->wiphy->bands[band]) + continue; + if ((band == IEEE80211_BAND_2GHZ) && !(w_band & WIFI_BAND_BG)) + continue; + if ((band == IEEE80211_BAND_5GHZ) && + !((w_band & WIFI_BAND_A) || (w_band & WIFI_BAND_A_DFS))) + continue; + sband = priv->wdev->wiphy->bands[band]; + for (i = 0; (i < sband->n_channels); i++) { + ch = &sband->channels[i]; + if (ch->flags & IEEE80211_CHAN_DISABLED) { + PRINTM(MERROR, "Skip DISABLED channel %d\n", + ch->center_freq); + continue; + } + if ((band == IEEE80211_BAND_5GHZ)) { + if (((ch->flags & IEEE80211_CHAN_RADAR) && + !(w_band & WIFI_BAND_A_DFS)) || + (!(ch->flags & IEEE80211_CHAN_RADAR) && + !(w_band & WIFI_BAND_A))) + continue; + } + if (cnt >= *nchannel) { + PRINTM(MERROR, + "cnt=%d is exceed %d, cur ch=%d %dMHz\n", + cnt, *nchannel, ch->hw_value, + ch->center_freq); + break; + } + *ch_ptr = ch->center_freq; + ch_ptr++; + cnt++; + } + } + + PRINTM(MCMND, "w_band=%d cnt=%d\n", w_band, cnt); + *nchannel = cnt; + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief GSCAN subcmd - enable full scan results + * + * @param wiphy A pointer to wiphy struct + * @param wdev A pointer to wireless_dev struct + * + * @param data a pointer to data + * @param data_len data length + * + * @return 0: success other: fail + */ +static int +woal_cfg80211_subcmd_get_valid_channels(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int len) +{ + struct net_device *dev = wdev->netdev; + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + struct nlattr *tb[ATTR_WIFI_MAX]; + t_u32 band = 0; + int ch_out[MAX_CHANNEL_NUM]; + t_u32 nchannel = 0; + t_u32 mem_needed = 0; + struct sk_buff *skb = NULL; + int err = 0; + + ENTER(); + PRINTM(MCMND, "Enter woal_cfg80211_subcmd_get_valid_channels\n"); + + err = nla_parse(tb, ATTR_WIFI_MAX, data, len, NULL +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) + , NULL +#endif + ); + if (err) { + PRINTM(MERROR, "%s: nla_parse fail\n", __FUNCTION__); + err = -EFAULT; + goto done; + } + + if (!tb[ATTR_CHANNELS_BAND]) { + PRINTM(MERROR, "%s: null attr: tb[ATTR_GET_CH]=%p\n", + __FUNCTION__, tb[ATTR_CHANNELS_BAND]); + err = -EINVAL; + goto done; + } + band = nla_get_u32(tb[ATTR_CHANNELS_BAND]); + if (band > WIFI_BAND_MAX) { + PRINTM(MERROR, "%s: invalid band=%d\n", __FUNCTION__, band); + err = -EINVAL; + goto done; + } + + memset(ch_out, 0x00, sizeof(ch_out)); + nchannel = MAX_CHANNEL_NUM; + if (woal_band_to_valid_channels(priv, band, ch_out, &nchannel) != + MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, + "get_channel_list: woal_band_to_valid_channels fail\n"); + return -EFAULT; + } + + mem_needed = + nla_total_size(nchannel * sizeof(ch_out[0])) + + nla_total_size(sizeof(nchannel)) + + VENDOR_REPLY_OVERHEAD; + /* Alloc the SKB for vendor_event */ + skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, mem_needed); + if (unlikely(!skb)) { + PRINTM(MERROR, "skb alloc failed"); + err = -ENOMEM; + goto done; + } + + nla_put_u32(skb, ATTR_NUM_CHANNELS, nchannel); + nla_put(skb, ATTR_CHANNEL_LIST, nchannel * sizeof(ch_out[0]), ch_out); + err = cfg80211_vendor_cmd_reply(skb); + if (err) { + PRINTM(MERROR, "Vendor Command reply failed ret:%d \n", err); + goto done; + } + +done: + LEAVE(); + return err; +} + +/** + * @brief vendor command to get driver version + * + * @param wiphy A pointer to wiphy struct + * @param wdev A pointer to wireless_dev struct + * @param data a pointer to data + * @param len data length + * + * @return 0: success 1: fail + */ +static int +woal_cfg80211_subcmd_get_drv_version(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + struct net_device *dev = wdev->netdev; + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + struct sk_buff *skb = NULL; + t_u32 reply_len = 0; + int ret = 0; + char drv_version[MLAN_MAX_VER_STR_LEN] = { 0 }; + char *pos; + + ENTER(); + memcpy(drv_version, &priv->phandle->driver_version, + MLAN_MAX_VER_STR_LEN); + pos = strstr(drv_version, "%s"); + /* remove 3 char "-%s" in driver_version string */ + if (pos >= 0) + memcpy(pos, pos + 3, strlen(pos) + 1); + + reply_len = strlen(drv_version) + 1; + + /** Allocate skb for cmd reply*/ + skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, reply_len); + if (!skb) { + PRINTM(MERROR, "allocate memory fail for vendor cmd\n"); + ret = -ENOMEM; + goto done; + } + + nla_put(skb, MRVL_WLAN_VENDOR_ATTR_NAME, reply_len - 1, + (t_u8 *)drv_version); + ret = cfg80211_vendor_cmd_reply(skb); + if (ret) + PRINTM(MERROR, "Vendor command reply failed ret = %d\n", ret); +done: + LEAVE(); + return ret; +} + +/** + * @brief vendor command to get firmware version + * + * @param wiphy A pointer to wiphy struct + * @param wdev A pointer to wireless_dev struct + * @param data a pointer to data + * @param len data length + * + * @return 0: success 1: fail + */ +static int +woal_cfg80211_subcmd_get_fw_version(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + struct net_device *dev = wdev->netdev; + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + struct sk_buff *skb = NULL; + t_u32 reply_len = 0; + int ret = 0; + char fw_ver[32] = { 0 }; + union { + t_u32 l; + t_u8 c[4]; + } ver; + + ENTER(); + + ver.l = priv->phandle->fw_release_number; + snprintf(fw_ver, sizeof(fw_ver), "%u.%u.%u.p%u", + ver.c[2], ver.c[1], ver.c[0], ver.c[3]); + reply_len = strlen(fw_ver) + 1; + + /** Allocate skb for cmd reply*/ + skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, reply_len); + if (!skb) { + PRINTM(MERROR, "allocate memory fail for vendor cmd\n"); + ret = -ENOMEM; + goto done; + } + + nla_put(skb, MRVL_WLAN_VENDOR_ATTR_NAME, reply_len, (t_u8 *)fw_ver); + ret = cfg80211_vendor_cmd_reply(skb); + if (ret) + PRINTM(MERROR, "Vendor command reply failed ret = %d\n", ret); +done: + LEAVE(); + return ret; +} + +/** + * @brief vendor command to get supported feature set + * + * @param wiphy A pointer to wiphy struct + * @param wdev A pointer to wireless_dev struct + * @param data a pointer to data + * @param len data length + * + * @return 0: success 1: fail + */ +static int +woal_cfg80211_subcmd_get_supp_feature_set(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + struct sk_buff *skb = NULL; + t_u32 reply_len = 0; + int ret = 0; + t_u32 supp_feature_set = 0; + ENTER(); + + supp_feature_set = WIFI_FEATURE_INFRA +#if defined(UAP_SUPPORT) && defined(STA_SUPPORT) + | WIFI_FEATURE_AP_STA +#endif + | WIFI_FEATURE_RSSI_MONITOR | WIFI_FEATURE_CONFIG_NDO; + + reply_len = sizeof(supp_feature_set); + /** Allocate skb for cmd reply*/ + skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, reply_len); + if (!skb) { + PRINTM(MERROR, "allocate memory fail for vendor cmd\n"); + ret = -ENOMEM; + goto done; + } + nla_put_u32(skb, ATTR_FEATURE_SET, supp_feature_set); + ret = cfg80211_vendor_cmd_reply(skb); + if (ret) + PRINTM(MERROR, "Vendor command reply failed ret = %d\n", ret); +done: + LEAVE(); + return ret; +} + +/** + * @brief vendor command to set country code + * + * @param wiphy A pointer to wiphy struct + * @param wdev A pointer to wireless_dev struct + * @param data a pointer to data + * @param len data length + * + * @return 0: success 1: fail + */ +static int +woal_cfg80211_subcmd_set_country_code(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + struct sk_buff *skb = NULL; + t_u32 reply_len = 0; + int ret = 0, rem, type; + const struct nlattr *iter; + char country[COUNTRY_CODE_LEN] = { 0 }; + + ENTER(); + + nla_for_each_attr(iter, data, data_len, rem) { + type = nla_type(iter); + switch (type) { + case ATTR_COUNTRY_CODE: + strncpy(country, nla_data(iter), + MIN(sizeof(country) - 1, nla_len(iter))); + break; + default: + PRINTM(MERROR, "Unknown type: %d\n", type); + return ret; + } + } + + regulatory_hint(wiphy, country); + + /** Allocate skb for cmd reply*/ + skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, reply_len); + if (!skb) { + PRINTM(MERROR, "allocate memory fail for vendor cmd\n"); + ret = -ENOMEM; + goto done; + } + + ret = cfg80211_vendor_cmd_reply(skb); + if (ret) + PRINTM(MERROR, "Vendor command reply failed ret = %d\n", ret); +done: + LEAVE(); + return ret; +} + +/** + * @brief vendor command to get correlated HW and System time + * + * @param wiphy A pointer to wiphy struct + * @param wdev A pointer to wireless_dev struct + * @param data a pointer to data + * @param len data length + * + * @return 0: success -1: fail + */ +static int +woal_cfg80211_subcmd_get_correlated_time(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int len) +{ + struct net_device *dev = wdev->netdev; + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + struct sk_buff *skb = NULL; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *misc = NULL; + mlan_ds_get_correlated_time *info = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + int err = -1; + int length = 0; + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + PRINTM(MERROR, "Could not allocate mlan ioctl request!\n"); + return -ENOMEM; + } + + /* Fill request buffer */ + misc = (mlan_ds_misc_cfg *)req->pbuf; + req->req_id = MLAN_IOCTL_MISC_CFG; + req->action = MLAN_ACT_GET; + misc->sub_command = MLAN_OID_MISC_GET_CORRELATED_TIME; + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "get correleted time fail\n"); + goto done; + } + + length = sizeof(mlan_ds_get_correlated_time); + info = (mlan_ds_get_correlated_time *) (&misc->param.host_clock); + + DBG_HEXDUMP(MCMD_D, "get_correlated_time", (t_u8 *)info, length); + + /* Alloc the SKB for vendor_event */ + skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, length); + if (unlikely(!skb)) { + PRINTM(MERROR, "skb alloc failed\n"); + goto done; + } + + /* Push the data to the skb */ + nla_put_nohdr(skb, length, info); + + err = cfg80211_vendor_cmd_reply(skb); + if (unlikely(err)) { + PRINTM(MERROR, "Vendor Command reply failed ret:%d \n", err); + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + return err; +} + +#ifdef STA_CFG80211 +#define RSSI_MONOTOR_START 1 +#define RSSI_MONOTOR_STOP 0 + +/** + * @brief vendor command to control rssi monitor + * + * @param wiphy A pointer to wiphy struct + * @param wdev A pointer to wireless_dev struct + * @param data a pointer to data + * @param len data length + * + * @return 0: success -1: fail + */ +static int +woal_cfg80211_subcmd_rssi_monitor(struct wiphy *wiphy, + struct wireless_dev *wdev, const void *data, + int len) +{ + struct nlattr *tb[ATTR_RSSI_MONITOR_MAX + 1]; + moal_private *priv = (moal_private *)woal_get_netdev_priv(wdev->netdev); + u32 rssi_monitor_control = 0x0; + s8 rssi_min = 0, rssi_max = 0; + int err = 0; + t_u8 *pos = NULL; + struct sk_buff *skb = NULL; + t_u8 ret = 0; + + ENTER(); + + ret = nla_parse(tb, ATTR_RSSI_MONITOR_MAX, data, len, NULL +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) + , NULL +#endif + ); + if (ret) + goto done; + + if (!tb[ATTR_RSSI_MONITOR_CONTROL]) { + ret = -EINVAL; + goto done; + } + rssi_monitor_control = nla_get_u32(tb[ATTR_RSSI_MONITOR_CONTROL]); + + if (rssi_monitor_control == RSSI_MONOTOR_START) { + if ((!tb[ATTR_RSSI_MONITOR_MIN_RSSI]) || + (!tb[ATTR_RSSI_MONITOR_MAX_RSSI])) { + ret = -EINVAL; + goto done; + } + + rssi_min = nla_get_s8(tb[ATTR_RSSI_MONITOR_MIN_RSSI]); + rssi_max = nla_get_s8(tb[ATTR_RSSI_MONITOR_MAX_RSSI]); + + PRINTM(MEVENT, + "start rssi monitor rssi_min = %d, rssi_max= %d\n", + rssi_min, rssi_max); + + /* set rssi low/high threshold */ + priv->cqm_rssi_high_thold = rssi_max; + priv->cqm_rssi_thold = rssi_min; + priv->cqm_rssi_hyst = 4; + woal_set_rssi_threshold(priv, 0, MOAL_IOCTL_WAIT); + } else if (rssi_monitor_control == RSSI_MONOTOR_STOP) { + /* stop rssi monitor */ + PRINTM(MEVENT, "stop rssi monitor\n"); + /* set both rssi_thold/hyst to 0, will trigger subscribe event clear */ + priv->cqm_rssi_high_thold = 0; + priv->cqm_rssi_thold = 0; + priv->cqm_rssi_hyst = 0; + woal_set_rssi_threshold(priv, 0, MOAL_IOCTL_WAIT); + } else { + PRINTM(MERROR, "invalid rssi_monitor control request\n"); + ret = -EINVAL; + goto done; + } + + /* Alloc the SKB for cmd reply */ + skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, len); + if (unlikely(!skb)) { + PRINTM(MERROR, "skb alloc failed\n"); + ret = -EINVAL; + goto done; + } + pos = skb_put(skb, len); + memcpy(pos, data, len); + ret = cfg80211_vendor_cmd_reply(skb); + if (unlikely(ret)) { + PRINTM(MERROR, "Vendor Command reply failed ret:%d \n", err); + } +done: + LEAVE(); + return ret; +} + +/** + * @brief send rssi event to kernel + * + * @param priv A pointer to moal_private + * @param rssi current rssi value + * + * @return N/A + */ +void +woal_cfg80211_rssi_monitor_event(moal_private *priv, t_s16 rssi) +{ + struct sk_buff *skb = NULL; + t_s8 rssi_value = 0; + + ENTER(); + + skb = dev_alloc_skb(NLA_HDRLEN * 2 + ETH_ALEN + sizeof(t_s8)); + if (!skb) + goto done; + /* convert t_s16 to t_s8 */ + rssi_value = -abs(rssi); + if (nla_put + (skb, ATTR_RSSI_MONITOR_CUR_BSSID, ETH_ALEN, priv->conn_bssid) || + nla_put_s8(skb, ATTR_RSSI_MONITOR_CUR_RSSI, rssi_value)) { + PRINTM(MERROR, "nla_put failed!\n"); + kfree(skb); + goto done; + } + woal_cfg80211_vendor_event(priv, event_rssi_monitor, (t_u8 *)skb->data, + skb->len); + kfree(skb); +done: + LEAVE(); +} +#endif + +/** + * @brief vendor command to key_mgmt_set_key + * + * @param wiphy A pointer to wiphy struct + * @param wdev A pointer to wireless_dev struct + * @param data a pointer to data + * @param len data length + * + * @return 0: success fail otherwise + */ +static int +woal_cfg80211_subcmd_set_roaming_offload_key(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + moal_private *priv; + struct net_device *dev; + struct sk_buff *skb = NULL; + t_u8 *pos = (t_u8 *)data; + int ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (data) + DBG_HEXDUMP(MCMD_D, "Vendor pmk", (t_u8 *)data, data_len); + + if (!wdev || !wdev->netdev) { + LEAVE(); + return -EFAULT; + } + + dev = wdev->netdev; + priv = (moal_private *)woal_get_netdev_priv(dev); + if (!priv) { + LEAVE(); + return -EFAULT; + } + + if (data_len > MLAN_MAX_KEY_LENGTH) { + memcpy(&priv->pmk.pmk_r0, pos, MLAN_MAX_KEY_LENGTH); + pos += MLAN_MAX_KEY_LENGTH; + memcpy(&priv->pmk.pmk_r0_name, pos, + MIN(MLAN_MAX_PMKR0_NAME_LENGTH, + data_len - MLAN_MAX_KEY_LENGTH)); + } else { + memcpy(&priv->pmk.pmk, data, + MIN(MLAN_MAX_KEY_LENGTH, data_len)); + } + priv->pmk_saved = MTRUE; + + /** Allocate skb for cmd reply*/ + skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, data_len); + if (!skb) { + PRINTM(MERROR, "allocate memory fail for vendor cmd\n"); + LEAVE(); + return -EFAULT; + } + pos = skb_put(skb, data_len); + memcpy(pos, data, data_len); + ret = cfg80211_vendor_cmd_reply(skb); + + LEAVE(); + return ret; +} + +/** + * @brief vendor command to supplicant to update AP info + * + * @param priv A pointer to moal_private + * @param data a pointer to data + * @param len data length + * + * @return 0: success 1: fail + */ +int +woal_roam_ap_info(IN moal_private *priv, IN t_u8 *data, IN int len) +{ + struct wiphy *wiphy = priv->wdev->wiphy; + struct sk_buff *skb = NULL; + int ret = MLAN_STATUS_SUCCESS; + key_info *pkey = NULL; + apinfo *pinfo = NULL; + apinfo *req_tlv = NULL; + MrvlIEtypesHeader_t *tlv = NULL; + t_u16 tlv_type = 0, tlv_len = 0, tlv_buf_left = 0; + int event_id = 0; + t_u8 authorized = 1; + + ENTER(); + + event_id = woal_get_event_id(fw_roam_success); + if (event_max == event_id) { + PRINTM(MERROR, "Not find this event %d \n", event_id); + ret = 1; + LEAVE(); + return ret; + } + /**allocate skb*/ +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) + skb = cfg80211_vendor_event_alloc(wiphy, NULL, len + 50, +#else + skb = cfg80211_vendor_event_alloc(wiphy, len + 50, +#endif + event_id, GFP_ATOMIC); + + if (!skb) { + PRINTM(MERROR, "allocate memory fail for vendor event\n"); + ret = 1; + LEAVE(); + return ret; + } + + nla_put(skb, MRVL_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID, + MLAN_MAC_ADDR_LENGTH, (t_u8 *)data); + nla_put(skb, MRVL_WLAN_VENDOR_ATTR_ROAM_AUTH_AUTHORIZED, + sizeof(authorized), &authorized); + tlv = (MrvlIEtypesHeader_t *)(data + MLAN_MAC_ADDR_LENGTH); + tlv_buf_left = len - MLAN_MAC_ADDR_LENGTH; + while (tlv_buf_left >= sizeof(MrvlIEtypesHeader_t)) { + tlv_type = woal_le16_to_cpu(tlv->type); + tlv_len = woal_le16_to_cpu(tlv->len); + + if (tlv_buf_left < (tlv_len + sizeof(MrvlIEtypesHeader_t))) { + PRINTM(MERROR, + "Error processing firmware roam success TLVs, bytes left < TLV length\n"); + break; + } + + switch (tlv_type) { + case TLV_TYPE_APINFO: + pinfo = (apinfo *) tlv; + nla_put(skb, MRVL_WLAN_VENDOR_ATTR_ROAM_AUTH_RESP_IE, + pinfo->header.len, pinfo->rsp_ie); + break; + + case TLV_TYPE_ASSOC_REQ_IE: + req_tlv = (apinfo *) tlv; + nla_put(skb, MRVL_WLAN_VENDOR_ATTR_ROAM_AUTH_REQ_IE, + req_tlv->header.len, req_tlv->rsp_ie); + break; + + case TLV_TYPE_KEYINFO: + pkey = (key_info *) tlv; + nla_put(skb, + MRVL_WLAN_VENDOR_ATTR_ROAM_AUTH_KEY_REPLAY_CTR, + MLAN_REPLAY_CTR_LEN, pkey->key.replay_ctr); + nla_put(skb, MRVL_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KCK, + MLAN_KCK_LEN, pkey->key.kck); + nla_put(skb, MRVL_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KEK, + MLAN_KEK_LEN, pkey->key.kek); + break; + default: + break; + } + tlv_buf_left -= tlv_len + sizeof(MrvlIEtypesHeader_t); + tlv = (MrvlIEtypesHeader_t *)((t_u8 *)tlv + tlv_len + + sizeof(MrvlIEtypesHeader_t)); + } + + /**send event*/ + cfg80211_vendor_event(skb, GFP_ATOMIC); + + LEAVE(); + return ret; +} + +/** + * @brief vendor command to get fw roaming capability + * + * @param wiphy A pointer to wiphy struct + * @param wdev A pointer to wireless_dev struct + * @param data a pointer to data + * @param len data length + * + * @return 0: success fail otherwise + */ +static int +woal_cfg80211_subcmd_get_roaming_capability(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int len) +{ + int ret = MLAN_STATUS_SUCCESS; + wifi_roaming_capabilities capa; + struct sk_buff *skb = NULL; + int err = 0; + + ENTER(); + + if (!wdev || !wdev->netdev) { + LEAVE(); + return -EFAULT; + } + + capa.max_blacklist_size = MAX_AP_LIST; + capa.max_whitelist_size = MAX_SSID_NUM; + + /* Alloc the SKB for vendor_event */ + skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, + sizeof + (wifi_roaming_capabilities) + + 50); + if (unlikely(!skb)) { + PRINTM(MERROR, "skb alloc failed\n"); + goto done; + } + + /* Push the data to the skb */ + nla_put(skb, MRVL_WLAN_VENDOR_ATTR_FW_ROAMING_CAPA, + sizeof(wifi_roaming_capabilities), (t_u8 *)&capa); + + err = cfg80211_vendor_cmd_reply(skb); + if (unlikely(err)) { + PRINTM(MERROR, "Vendor Command reply failed ret:%d \n", err); + } + +done: + LEAVE(); + return ret; +} + +/** + * @brief vendor command to enable/disable fw roaming + * + * @param wiphy A pointer to wiphy struct + * @param wdev A pointer to wireless_dev struct + * @param data a pointer to data + * @param len data length + * + * @return 0: success fail otherwise + */ +static int +woal_cfg80211_subcmd_fw_roaming_enable(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int len) +{ + moal_private *priv; + struct net_device *dev; + int ret = MLAN_STATUS_SUCCESS; + struct sk_buff *skb = NULL; + const struct nlattr *iter; + int type, rem, err; + t_u32 fw_roaming_enable = 0; + + ENTER(); + + if (!wdev || !wdev->netdev) { + LEAVE(); + return -EFAULT; + } + + dev = wdev->netdev; + priv = (moal_private *)woal_get_netdev_priv(dev); + if (!priv || !priv->phandle) { + LEAVE(); + return -EFAULT; + } + + nla_for_each_attr(iter, data, len, rem) { + type = nla_type(iter); + switch (type) { + case MRVL_WLAN_VENDOR_ATTR_FW_ROAMING_CONTROL: + fw_roaming_enable = nla_get_u32(iter); + break; + default: + PRINTM(MERROR, "Unknown type: %d\n", type); + ret = -EINVAL; + goto done; + } + } + + PRINTM(MMSG, "FW roaming set enable=%d from wifi hal.\n", + fw_roaming_enable); + ret = woal_enable_fw_roaming(priv, fw_roaming_enable); + + /* Alloc the SKB for vendor_event */ + skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, sizeof(t_u32) + 50); + if (unlikely(!skb)) { + PRINTM(MERROR, "skb alloc failed\n"); + goto done; + } + + nla_put(skb, MRVL_WLAN_VENDOR_ATTR_FW_ROAMING_CONTROL, sizeof(t_u32), + &fw_roaming_enable); + err = cfg80211_vendor_cmd_reply(skb); + if (unlikely(err)) { + PRINTM(MERROR, "Vendor Command reply failed ret:%d \n", err); + } + +done: + LEAVE(); + return ret; +} + +/** + * @brief vendor command to config blacklist and whitelist for fw roaming + * + * @param wiphy A pointer to wiphy struct + * @param wdev A pointer to wireless_dev struct + * @param data a pointer to data + * @param len data length + * + * @return 0: success fail otherwise + */ +static int +woal_cfg80211_subcmd_fw_roaming_config(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int len) +{ + moal_private *priv; + struct net_device *dev; + int ret = MLAN_STATUS_SUCCESS; + const struct nlattr *iter; + int type, rem; + woal_roam_offload_cfg *roam_offload_cfg = NULL; + wifi_bssid_params blacklist; + wifi_ssid_params whitelist; + + ENTER(); + + if (!wdev || !wdev->netdev) { + LEAVE(); + return -EFAULT; + } + + dev = wdev->netdev; + priv = (moal_private *)woal_get_netdev_priv(dev); + if (!priv || !priv->phandle) { + LEAVE(); + return -EFAULT; + } + + memset((char *)&blacklist, 0, sizeof(wifi_bssid_params)); + memset((char *)&whitelist, 0, sizeof(wifi_ssid_params)); + nla_for_each_attr(iter, data, len, rem) { + type = nla_type(iter); + switch (type) { + case MRVL_WLAN_VENDOR_ATTR_FW_ROAMING_CONFIG_BSSID: + memcpy((t_u8 *)&blacklist, nla_data(iter), + sizeof(wifi_bssid_params)); + break; + case MRVL_WLAN_VENDOR_ATTR_FW_ROAMING_CONFIG_SSID: + memcpy((t_u8 *)&whitelist, nla_data(iter), + sizeof(wifi_ssid_params)); + break; + default: + PRINTM(MERROR, "Unknown type: %d\n", type); + ret = -EINVAL; + goto done; + } + } + + if (roamoffload_in_hs) { + /*save blacklist and whitelist in driver */ + priv->phandle->fw_roam_params.black_list.ap_num = + blacklist.num_bssid; + memcpy((t_u8 *)priv->phandle->fw_roam_params.black_list.ap_mac, + (t_u8 *)blacklist.mac_addr, + sizeof(wifi_bssid_params) - sizeof(blacklist.num_bssid)); + priv->phandle->fw_roam_params.ssid_list.ssid_num = + whitelist.num_ssid; + memcpy((t_u8 *)priv->phandle->fw_roam_params.ssid_list.ssids, + (t_u8 *)whitelist.whitelist_ssid, + sizeof(wifi_ssid_params) - sizeof(whitelist.num_ssid)); + } else { + roam_offload_cfg = + (woal_roam_offload_cfg *) + kmalloc(sizeof(woal_roam_offload_cfg), GFP_KERNEL); + if (!roam_offload_cfg) { + PRINTM(MERROR, "kmalloc failed!\n"); + ret = -ENOMEM; + goto done; + } + /*download parameters directly to fw */ + memset((char *)roam_offload_cfg, 0, + sizeof(woal_roam_offload_cfg)); + roam_offload_cfg->black_list.ap_num = blacklist.num_bssid; + memcpy((t_u8 *)&roam_offload_cfg->black_list.ap_mac, + (t_u8 *)blacklist.mac_addr, + sizeof(wifi_bssid_params) - sizeof(blacklist.num_bssid)); + roam_offload_cfg->ssid_list.ssid_num = whitelist.num_ssid; + memcpy((t_u8 *)&roam_offload_cfg->ssid_list.ssids, + (t_u8 *)whitelist.whitelist_ssid, + sizeof(wifi_ssid_params) - sizeof(whitelist.num_ssid)); + woal_config_fw_roaming(priv, ROAM_OFFLOAD_PARAM_CFG, + roam_offload_cfg); + } + +done: + LEAVE(); + return ret; +} + +/** + * @brief vendor command to get fw roaming support in driver/fw + * + * @param wiphy A pointer to wiphy struct + * @param wdev A pointer to wireless_dev struct + * @param data a pointer to data + * @param len data length + * + * @return 0: success fail otherwise + */ +static int +woal_cfg80211_subcmd_fw_roaming_support(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int len) +{ + moal_private *priv; + struct net_device *dev; + int ret = MLAN_STATUS_SUCCESS; + struct sk_buff *skb = NULL; + t_u8 fw_roaming_support = 0; + int err; + + ENTER(); + + if (!wdev || !wdev->netdev) { + LEAVE(); + return -EFAULT; + } + + dev = wdev->netdev; + priv = (moal_private *)woal_get_netdev_priv(dev); + if (!priv || !priv->phandle) { + LEAVE(); + return -EFAULT; + } + + /* Alloc the SKB for vendor_event */ + skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, sizeof(t_u8) + 30); + if (unlikely(!skb)) { + PRINTM(MERROR, "skb alloc failed\n"); + goto done; + } + + fw_roaming_support = priv->phandle->fw_roaming_support; + nla_put(skb, MRVL_WLAN_VENDOR_ATTR_FW_ROAMING_SUPPORT, sizeof(t_u8), + &fw_roaming_support); + err = cfg80211_vendor_cmd_reply(skb); + if (unlikely(err)) { + PRINTM(MERROR, "Vendor Command reply failed ret:%d \n", err); + } + +done: + LEAVE(); + return ret; +} + +/** + * @brief vendor command to enable/disable 11k + * + * @param wiphy A pointer to wiphy struct + * @param wdev A pointer to wireless_dev struct + * @param data a pointer to data + * @param data_len data length + * + * @return 0: success <0: fail + */ +static int +woal_cfg80211_subcmd_11k_cfg(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + struct net_device *dev = NULL; + moal_private *priv = NULL; + mlan_ioctl_req *req = NULL; + mlan_ds_11k_cfg *pcfg_11k = NULL; + struct nlattr *tb_vendor[ATTR_ND_OFFLOAD_MAX + 1]; + int ret = 0; + int status = MLAN_STATUS_SUCCESS; + + ENTER(); + if (!wdev || !wdev->netdev) { + LEAVE(); + return -EFAULT; + } + + dev = wdev->netdev; + priv = (moal_private *)woal_get_netdev_priv(dev); + + nla_parse(tb_vendor, ATTR_ND_OFFLOAD_MAX, + (struct nlattr *)data, data_len, NULL +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) + , NULL +#endif + ); + if (!tb_vendor[ATTR_ND_OFFLOAD_CONTROL]) { + PRINTM(MINFO, "%s: ATTR_ND_OFFLOAD not found\n", __FUNCTION__); + ret = -EFAULT; + goto done; + } + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11k_cfg)); + if (req == NULL) { + PRINTM(MERROR, "Could not allocate mlan ioctl request!\n"); + ret = -EFAULT; + goto done; + } + /* Fill request buffer */ + pcfg_11k = (mlan_ds_11k_cfg *) req->pbuf; + pcfg_11k->sub_command = MLAN_OID_11K_CFG_ENABLE; + req->req_id = MLAN_IOCTL_11K_CFG; + req->action = MLAN_ACT_SET; + if (nla_get_u32(tb_vendor[ATTR_ND_OFFLOAD_CONTROL])) + pcfg_11k->param.enable_11k = MTRUE; + else + pcfg_11k->param.enable_11k = MFALSE; + PRINTM(MCMND, "11k enable = %d\n", pcfg_11k->param.enable_11k); + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + + LEAVE(); + return ret; + +} + +/** + * @brief vendor command to set enable/disable dfs offload + * + * @param wiphy A pointer to wiphy struct + * @param wdev A pointer to wireless_dev struct + * @param data a pointer to data + * @param len data length + * + * @return 0: success 1: fail + */ +static int +woal_cfg80211_subcmd_set_dfs_offload(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + struct sk_buff *skb = NULL; + int ret = 1; + + ENTER(); + + /** Allocate skb for cmd reply*/ + skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, sizeof(dfs_offload)); + if (!skb) { + PRINTM(MERROR, "allocate memory fail for vendor cmd\n"); + ret = 1; + LEAVE(); + return ret; + } + nla_put(skb, MRVL_WLAN_VENDOR_ATTR_DFS, sizeof(t_u32), &dfs_offload); + ret = cfg80211_vendor_cmd_reply(skb); + + LEAVE(); + return ret; +} + +const struct wiphy_vendor_command vendor_commands[] = { + { + .info = {.vendor_id = MRVL_VENDOR_ID,.subcmd = sub_cmd_set_drvdbg,}, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = woal_cfg80211_subcmd_set_drvdbg, + }, + { + .info = {.vendor_id = MRVL_VENDOR_ID,.subcmd = + sub_cmd_get_valid_channels,}, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = woal_cfg80211_subcmd_get_valid_channels, + }, +#ifdef STA_CFG80211 + { + .info = {.vendor_id = MRVL_VENDOR_ID,.subcmd = sub_cmd_rssi_monitor,}, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = woal_cfg80211_subcmd_rssi_monitor, + }, +#endif + { + .info = {.vendor_id = MRVL_VENDOR_ID,.subcmd = + sub_cmd_set_roaming_offload_key,}, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = woal_cfg80211_subcmd_set_roaming_offload_key, + }, + { + .info = {.vendor_id = MRVL_VENDOR_ID,.subcmd = + sub_cmd_get_roaming_capability,}, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = woal_cfg80211_subcmd_get_roaming_capability, + }, + { + .info = {.vendor_id = MRVL_VENDOR_ID,.subcmd = + sub_cmd_fw_roaming_enable,}, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = woal_cfg80211_subcmd_fw_roaming_enable, + }, + { + .info = {.vendor_id = MRVL_VENDOR_ID,.subcmd = + sub_cmd_fw_roaming_config,}, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = woal_cfg80211_subcmd_fw_roaming_config, + }, + { + .info = {.vendor_id = MRVL_VENDOR_ID,.subcmd = + sub_cmd_fw_roaming_support,}, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = woal_cfg80211_subcmd_fw_roaming_support, + }, + + { + .info = {.vendor_id = MRVL_VENDOR_ID,.subcmd = + sub_cmd_dfs_capability,}, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = woal_cfg80211_subcmd_set_dfs_offload, + }, + + { + .info = {.vendor_id = MRVL_VENDOR_ID,.subcmd = + sub_cmd_get_correlated_time,}, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = woal_cfg80211_subcmd_get_correlated_time, + }, + + { + .info = {.vendor_id = MRVL_VENDOR_ID,.subcmd = sub_cmd_nd_offload}, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = woal_cfg80211_subcmd_11k_cfg, + }, + + { + .info = {.vendor_id = MRVL_VENDOR_ID,.subcmd = + sub_cmd_get_drv_version,}, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = woal_cfg80211_subcmd_get_drv_version, + }, + { + .info = {.vendor_id = MRVL_VENDOR_ID,.subcmd = + sub_cmd_get_fw_version,}, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = woal_cfg80211_subcmd_get_fw_version, + }, + { + .info = {.vendor_id = MRVL_VENDOR_ID,.subcmd = + sub_cmd_get_wifi_supp_feature_set,}, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = woal_cfg80211_subcmd_get_supp_feature_set, + }, + { + .info = {.vendor_id = MRVL_VENDOR_ID,.subcmd = + sub_cmd_set_country_code,}, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = woal_cfg80211_subcmd_set_country_code, + }, + +}; + +/** + * @brief register vendor commands and events + * + * @param wiphy A pointer to wiphy struct + * + * @return + */ +void +woal_register_cfg80211_vendor_command(struct wiphy *wiphy) +{ + ENTER(); + wiphy->vendor_commands = vendor_commands; + wiphy->n_vendor_commands = ARRAY_SIZE(vendor_commands); + wiphy->vendor_events = vendor_events; + wiphy->n_vendor_events = ARRAY_SIZE(vendor_events); + LEAVE(); +} +#endif diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_cfgvendor.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_cfgvendor.h new file mode 100644 index 000000000000..23db2bb7f1f0 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_cfgvendor.h @@ -0,0 +1,266 @@ +/** @file moal_cfgvendor.h + * + * @brief This file contains the CFG80211 vendor specific defines. + * + * Copyright (C) 2011-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +#ifndef _MOAL_CFGVENDOR_H_ +#define _MOAL_CFGVENDOR_H_ + +#include "moal_main.h" + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + +#define ATTRIBUTE_U32_LEN (nla_total_size(NLA_HDRLEN + 4)) +#define VENDOR_ID_OVERHEAD ATTRIBUTE_U32_LEN +#define VENDOR_SUBCMD_OVERHEAD ATTRIBUTE_U32_LEN +#define VENDOR_DATA_OVERHEAD (nla_total_size(NLA_HDRLEN)) + +#define VENDOR_REPLY_OVERHEAD (VENDOR_ID_OVERHEAD + \ + VENDOR_SUBCMD_OVERHEAD + \ + VENDOR_DATA_OVERHEAD) + +/* Feature enums */ +#define WIFI_FEATURE_INFRA 0x0001 // Basic infrastructure mode +#define WIFI_FEATURE_INFRA_5G 0x0002 // Support for 5 GHz Band +#define WIFI_FEATURE_HOTSPOT 0x0004 // Support for GAS/ANQP +#define WIFI_FEATURE_P2P 0x0008 // Wifi-Direct +#define WIFI_FEATURE_SOFT_AP 0x0010 // Soft AP +#define WIFI_FEATURE_GSCAN 0x0020 // Google-Scan APIs +#define WIFI_FEATURE_NAN 0x0040 // Neighbor Awareness Networking +#define WIFI_FEATURE_D2D_RTT 0x0080 // Device-to-device RTT +#define WIFI_FEATURE_D2AP_RTT 0x0100 // Device-to-AP RTT +#define WIFI_FEATURE_BATCH_SCAN 0x0200 // Batched Scan (legacy) +#define WIFI_FEATURE_PNO 0x0400 // Preferred network offload +#define WIFI_FEATURE_ADDITIONAL_STA 0x0800 // Support for two STAs +#define WIFI_FEATURE_TDLS 0x1000 // Tunnel directed link setup +#define WIFI_FEATURE_TDLS_OFFCHANNEL 0x2000 // Support for TDLS off channel +#define WIFI_FEATURE_EPR 0x4000 // Enhanced power reporting +#define WIFI_FEATURE_AP_STA 0x8000 // Support for AP STA Concurrency +#define WIFI_FEATURE_LINK_LAYER_STATS 0x10000 // Link layer stats collection +#define WIFI_FEATURE_LOGGER 0x20000 // WiFi Logger +#define WIFI_FEATURE_HAL_EPNO 0x40000 // WiFi PNO enhanced +#define WIFI_FEATURE_RSSI_MONITOR 0x80000 // RSSI Monitor +#define WIFI_FEATURE_MKEEP_ALIVE 0x100000 // WiFi mkeep_alive +#define WIFI_FEATURE_CONFIG_NDO 0x200000 // ND offload configure +#define WIFI_FEATURE_TX_TRANSMIT_POWER 0x400000 // Capture Tx transmit power levels +#define WIFI_FEATURE_CONTROL_ROAMING 0x800000 // Enable/Disable firmware roaming +#define WIFI_FEATURE_IE_WHITELIST 0x1000000 // Support Probe IE white listing +#define WIFI_FEATURE_SCAN_RAND 0x2000000 // Support MAC & Probe Sequence Number randomization +// Add more features here + +#define MAX_CHANNEL_NUM 200 + +/** Wifi Band */ +typedef enum { + WIFI_BAND_UNSPECIFIED, + /** 2.4 GHz */ + WIFI_BAND_BG = 1, + /** 5 GHz without DFS */ + WIFI_BAND_A = 2, + /** 5 GHz DFS only */ + WIFI_BAND_A_DFS = 4, + /** 5 GHz with DFS */ + WIFI_BAND_A_WITH_DFS = 6, + /** 2.4 GHz + 5 GHz; no DFS */ + WIFI_BAND_ABG = 3, + /** 2.4 GHz + 5 GHz with DFS */ + WIFI_BAND_ABG_WITH_DFS = 7, + + /** Keep it last */ + WIFI_BAND_LAST, + WIFI_BAND_MAX = WIFI_BAND_LAST - 1, +} wifi_band; + +typedef enum wifi_attr { + ATTR_FEATURE_SET_INVALID = 0, + ATTR_FEATURE_SET = 2, + ATTR_NODFS_VALUE = 3, + ATTR_COUNTRY_CODE = 4, + ATTR_CHANNELS_BAND = 5, + ATTR_NUM_CHANNELS = 6, + ATTR_CHANNEL_LIST = 7, + ATTR_GET_CONCURRENCY_MATRIX_SET_SIZE_MAX = 8, + ATTR_GET_CONCURRENCY_MATRIX_SET_SIZE = 9, + ATTR_GET_CONCURRENCY_MATRIX_SET = 10, + ATTR_WIFI_MAX, +} wifi_attr_t; + +enum mrvl_wlan_vendor_attr_wifi_logger { + MRVL_WLAN_VENDOR_ATTR_NAME = 10, +}; + +/**vendor event*/ +enum vendor_event { + event_hang = 0, + event_rssi_monitor = 0x1501, + fw_roam_success = 0x10002, + event_dfs_radar_detected = 0x10004, + event_dfs_cac_started = 0x10005, + event_dfs_cac_finished = 0x10006, + event_dfs_cac_aborted = 0x10007, + event_dfs_nop_finished = 0x10008, + event_max, +}; + +/** struct dfs_event */ +typedef struct _dfs_event { + int freq; + int ht_enabled; + int chan_offset; + enum nl80211_chan_width chan_width; + int cf1; + int cf2; +} dfs_event; + +void woal_cfg80211_dfs_vendor_event(moal_private *priv, int event, + struct cfg80211_chan_def *chandef); + +enum ATTR_RSSI_MONITOR { + ATTR_RSSI_MONITOR_CONTROL, + ATTR_RSSI_MONITOR_MIN_RSSI, + ATTR_RSSI_MONITOR_MAX_RSSI, + ATTR_RSSI_MONITOR_CUR_BSSID, + ATTR_RSSI_MONITOR_CUR_RSSI, + ATTR_RSSI_MONITOR_MAX, +}; +void woal_cfg80211_rssi_monitor_event(moal_private *priv, t_s16 rssi); + +/**vendor sub command*/ +enum vendor_sub_command { + sub_cmd_set_drvdbg = 0, + sub_cmd_set_roaming_offload_key = 0x0002, + sub_cmd_dfs_capability = 0x0005, + sub_cmd_get_correlated_time = 0x0006, + sub_cmd_nd_offload = 0x0100, + sub_cmd_get_valid_channels = 0x1009, + sub_cmd_get_wifi_supp_feature_set = 0x100a, + sub_cmd_set_country_code = 0x100d, + sub_cmd_get_fw_version = 0x1404, + sub_cmd_get_drv_version = 0x1406, + sub_cmd_rssi_monitor = 0x1500, + /*Sub-command for wifi hal */ + sub_cmd_get_roaming_capability = 0x1700, + sub_cmd_fw_roaming_enable = 0x1701, + sub_cmd_fw_roaming_config = 0x1702, + /*Sub-command for wpa_supplicant */ + sub_cmd_fw_roaming_support = 0x0010, + sub_cmd_max, +}; + +void woal_register_cfg80211_vendor_command(struct wiphy *wiphy); +int woal_cfg80211_vendor_event(IN moal_private *priv, + IN int event, IN t_u8 *data, IN int len); + +enum mrvl_wlan_vendor_attr { + MRVL_WLAN_VENDOR_ATTR_INVALID = 0, + /* Used by MRVL_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY */ + MRVL_WLAN_VENDOR_ATTR_DFS = 1, + MRVL_WLAN_VENDOR_ATTR_AFTER_LAST, + + MRVL_WLAN_VENDOR_ATTR_MAX = MRVL_WLAN_VENDOR_ATTR_AFTER_LAST - 1, +}; + +typedef enum { + ATTR_ND_OFFLOAD_INVALID = 0, + ATTR_ND_OFFLOAD_CONTROL, + ATTR_ND_OFFLOAD_MAX, +} ND_OFFLOAD_ATTR; + +int woal_roam_ap_info(IN moal_private *priv, IN t_u8 *data, IN int len); +#endif /*endif CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) */ + +typedef struct { + u32 max_blacklist_size; + u32 max_whitelist_size; +} wifi_roaming_capabilities; + +typedef struct { + u32 num_bssid; + t_u8 mac_addr[MAX_AP_LIST][MLAN_MAC_ADDR_LENGTH]; +} wifi_bssid_params; + +typedef struct { + u32 length; + char ssid[MLAN_MAX_SSID_LENGTH]; +} ssid_t; + +typedef struct { + u32 num_ssid; + ssid_t whitelist_ssid[MAX_SSID_NUM]; +} wifi_ssid_params; + +/*Attribute for wifi hal*/ +enum mrvl_wlan_vendor_attr_fw_roaming { + MRVL_WLAN_VENDOR_ATTR_FW_ROAMING_INVALID = 0, + MRVL_WLAN_VENDOR_ATTR_FW_ROAMING_CAPA, + MRVL_WLAN_VENDOR_ATTR_FW_ROAMING_CONTROL, + MRVL_WLAN_VENDOR_ATTR_FW_ROAMING_CONFIG_BSSID, + MRVL_WLAN_VENDOR_ATTR_FW_ROAMING_CONFIG_SSID, + /* keep last */ + MRVL_WLAN_VENDOR_ATTR_FW_ROAMING_AFTER_LAST, + MRVL_WLAN_VENDOR_ATTR_FW_ROAMING_MAX = + MRVL_WLAN_VENDOR_ATTR_FW_ROAMING_AFTER_LAST - 1 +}; + +/*Attribute for wpa_supplicant*/ +enum mrvl_wlan_vendor_attr_roam_auth { + MRVL_WLAN_VENDOR_ATTR_ROAM_AUTH_INVALID = 0, + MRVL_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID, + MRVL_WLAN_VENDOR_ATTR_ROAM_AUTH_REQ_IE, + MRVL_WLAN_VENDOR_ATTR_ROAM_AUTH_RESP_IE, + MRVL_WLAN_VENDOR_ATTR_ROAM_AUTH_AUTHORIZED, + MRVL_WLAN_VENDOR_ATTR_ROAM_AUTH_KEY_REPLAY_CTR, + MRVL_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KCK, + MRVL_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KEK, + MRVL_WLAN_VENDOR_ATTR_ROAM_AUTH_SUBNET_STATUS, + MRVL_WLAN_VENDOR_ATTR_FW_ROAMING_SUPPORT, + /* keep last */ + MRVL_WLAN_VENDOR_ATTR_ROAM_AUTH_AFTER_LAST, + MRVL_WLAN_VENDOR_ATTR_ROAM_AUTH_MAX = + MRVL_WLAN_VENDOR_ATTR_ROAM_AUTH_AFTER_LAST - 1 +}; + +#define PROPRIETARY_TLV_BASE_ID 0x100 +#define TLV_TYPE_APINFO (PROPRIETARY_TLV_BASE_ID + 249) +#define TLV_TYPE_KEYINFO (PROPRIETARY_TLV_BASE_ID + 250) +#define TLV_TYPE_ASSOC_REQ_IE (PROPRIETARY_TLV_BASE_ID + 292) + +/** MrvlIEtypesHeader_t */ +typedef struct MrvlIEtypesHeader { + /** Header type */ + t_u16 type; + /** Header length */ + t_u16 len; +} __ATTRIB_PACK__ MrvlIEtypesHeader_t; + +typedef struct _key_info_tlv { + /** Header */ + MrvlIEtypesHeader_t header; + /** kck, kek, key_replay*/ + mlan_ds_misc_gtk_rekey_data key; +} key_info; + +typedef struct _apinfo_tlv { + /** Header */ + MrvlIEtypesHeader_t header; + /** Assoc response buffer */ + t_u8 rsp_ie[1]; +} apinfo; + +#endif /* _MOAL_CFGVENDOR_H_ */ diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_debug.c b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_debug.c new file mode 100644 index 000000000000..fca8654058f9 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_debug.c @@ -0,0 +1,1273 @@ +/** @file moal_debug.c + * + * @brief This file contains functions for debug proc file. + * + * Copyright (C) 2008-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +/******************************************************** +Change log: + 11/03/2008: initial version +********************************************************/ + +#include "moal_main.h" + +/******************************************************** + Global Variables +********************************************************/ +/** MLAN debug info */ +extern mlan_debug_info info; + +/******************************************************** + Local Variables +********************************************************/ +#ifdef CONFIG_PROC_FS + +/** Get info item size */ +#define item_size(n) (sizeof(info.n)) +/** Get info item address */ +#define item_addr(n) ((t_ptr) &(info.n)) + +/** Get moal_private member size */ +#define item_priv_size(n) (sizeof((moal_private *)0)->n) +/** Get moal_private member address */ +#define item_priv_addr(n) ((t_ptr) &((moal_private *)0)->n) + +/** Get moal_handle member size */ +#define item_handle_size(n) (sizeof((moal_handle *)0)->n) +/** Get moal_handle member address */ +#define item_handle_addr(n) ((t_ptr) &((moal_handle *)0)->n) + +#ifdef STA_SUPPORT +static struct debug_data items[] = { +#ifdef DEBUG_LEVEL1 + {"drvdbg", sizeof(drvdbg), (t_ptr)&drvdbg}, +#endif + {"mlan_processing", item_size(mlan_processing), + item_addr(mlan_processing)}, + {"main_process_cnt", item_size(main_process_cnt), + item_addr(main_process_cnt)}, + {"main_lock_flag", item_size(main_lock_flag), + item_addr(main_lock_flag)}, + {"delay_task_flag", item_size(delay_task_flag), + item_addr(delay_task_flag)}, + {"mlan_rx_processing", item_size(mlan_rx_processing), + item_addr(mlan_rx_processing)}, + {"rx_pkts_queued", item_size(rx_pkts_queued), + item_addr(rx_pkts_queued)}, + {"wmm_ac_vo", item_size(wmm_ac_vo), item_addr(wmm_ac_vo)}, + {"wmm_ac_vi", item_size(wmm_ac_vi), item_addr(wmm_ac_vi)}, + {"wmm_ac_be", item_size(wmm_ac_be), item_addr(wmm_ac_be)}, + {"wmm_ac_bk", item_size(wmm_ac_bk), item_addr(wmm_ac_bk)}, + {"max_tx_buf_size", item_size(max_tx_buf_size), + item_addr(max_tx_buf_size)}, + {"tx_buf_size", item_size(tx_buf_size), item_addr(tx_buf_size)}, + {"curr_tx_buf_size", item_size(curr_tx_buf_size), + item_addr(curr_tx_buf_size)}, + {"ps_mode", item_size(ps_mode), item_addr(ps_mode)}, + {"ps_state", item_size(ps_state), item_addr(ps_state)}, + {"is_deep_sleep", item_size(is_deep_sleep), item_addr(is_deep_sleep)}, + {"wakeup_dev_req", item_size(pm_wakeup_card_req), + item_addr(pm_wakeup_card_req)}, + {"wakeup_tries", item_size(pm_wakeup_fw_try), + item_addr(pm_wakeup_fw_try)}, + {"hs_configured", item_size(is_hs_configured), + item_addr(is_hs_configured)}, + {"hs_activated", item_size(hs_activated), item_addr(hs_activated)}, + {"rx_pkts_queued", item_size(rx_pkts_queued), + item_addr(rx_pkts_queued)}, + {"tx_pkts_queued", item_size(tx_pkts_queued), + item_addr(tx_pkts_queued)}, + {"pps_uapsd_mode", item_size(pps_uapsd_mode), + item_addr(pps_uapsd_mode)}, + {"sleep_pd", item_size(sleep_pd), item_addr(sleep_pd)}, + {"qos_cfg", item_size(qos_cfg), item_addr(qos_cfg)}, + {"tx_lock_flag", item_size(tx_lock_flag), item_addr(tx_lock_flag)}, + {"port_open", item_size(port_open), item_addr(port_open)}, + {"bypass_pkt_count", item_size(bypass_pkt_count), + item_addr(bypass_pkt_count)}, + {"scan_processing", item_size(scan_processing), + item_addr(scan_processing)}, + {"num_tx_timeout", item_size(num_tx_timeout), + item_addr(num_tx_timeout)}, + {"num_cmd_timeout", item_size(num_cmd_timeout), + item_addr(num_cmd_timeout)}, + {"dbg.num_cmd_timeout", item_size(dbg_num_cmd_timeout), + item_addr(dbg_num_cmd_timeout)}, + {"timeout_cmd_id", item_size(timeout_cmd_id), + item_addr(timeout_cmd_id)}, + {"timeout_cmd_act", item_size(timeout_cmd_act), + item_addr(timeout_cmd_act)}, + {"last_cmd_id", item_size(last_cmd_id), item_addr(last_cmd_id)}, + {"last_cmd_act", item_size(last_cmd_act), item_addr(last_cmd_act)}, + {"last_cmd_index", item_size(last_cmd_index), + item_addr(last_cmd_index)}, + {"last_cmd_resp_id", item_size(last_cmd_resp_id), + item_addr(last_cmd_resp_id)}, + {"last_cmd_resp_index", item_size(last_cmd_resp_index), + item_addr(last_cmd_resp_index)}, + {"last_event", item_size(last_event), item_addr(last_event)}, + {"last_event_index", item_size(last_event_index), + item_addr(last_event_index)}, + {"num_no_cmd_node", item_size(num_no_cmd_node), + item_addr(num_no_cmd_node)}, + {"num_cmd_h2c_fail", item_size(num_cmd_host_to_card_failure), + item_addr(num_cmd_host_to_card_failure)}, + {"num_cmd_sleep_cfm_fail", + item_size(num_cmd_sleep_cfm_host_to_card_failure), + item_addr(num_cmd_sleep_cfm_host_to_card_failure)}, + {"num_tx_h2c_fail", item_size(num_tx_host_to_card_failure), + item_addr(num_tx_host_to_card_failure)}, + {"num_alloc_buffer_failure", item_size(num_alloc_buffer_failure), + item_addr(num_alloc_buffer_failure)}, + {"num_cmdevt_c2h_fail", item_size(num_cmdevt_card_to_host_failure), + item_addr(num_cmdevt_card_to_host_failure)}, + {"num_rx_c2h_fail", item_size(num_rx_card_to_host_failure), + item_addr(num_rx_card_to_host_failure)}, + {"num_int_read_fail", item_size(num_int_read_failure), + item_addr(num_int_read_failure)}, + {"last_int_status", item_size(last_int_status), + item_addr(last_int_status)}, + {"num_of_irq", item_size(num_of_irq), item_addr(num_of_irq)}, + {"mp_invalid_update", item_size(mp_invalid_update), + item_addr(mp_invalid_update)}, + {"sdio_rx_aggr", item_size(sdio_rx_aggr), item_addr(sdio_rx_aggr)}, +#ifdef SDIO_MULTI_PORT_TX_AGGR + {"mpa_sent_last_pkt", item_size(mpa_sent_last_pkt), + item_addr(mpa_sent_last_pkt)}, + {"mpa_sent_no_ports", item_size(mpa_sent_no_ports), + item_addr(mpa_sent_no_ports)}, +#endif + {"num_evt_deauth", item_size(num_event_deauth), + item_addr(num_event_deauth)}, + {"num_evt_disassoc", item_size(num_event_disassoc), + item_addr(num_event_disassoc)}, + {"num_evt_link_lost", item_size(num_event_link_lost), + item_addr(num_event_link_lost)}, + {"num_cmd_deauth", item_size(num_cmd_deauth), + item_addr(num_cmd_deauth)}, + {"num_cmd_assoc_ok", item_size(num_cmd_assoc_success), + item_addr(num_cmd_assoc_success)}, + {"num_cmd_assoc_fail", item_size(num_cmd_assoc_failure), + item_addr(num_cmd_assoc_failure)}, + {"cmd_sent", item_size(cmd_sent), item_addr(cmd_sent)}, + {"data_sent", item_size(data_sent), item_addr(data_sent)}, + {"mp_rd_bitmap", item_size(mp_rd_bitmap), item_addr(mp_rd_bitmap)}, + {"curr_rd_port", item_size(curr_rd_port), item_addr(curr_rd_port)}, + {"mp_wr_bitmap", item_size(mp_wr_bitmap), item_addr(mp_wr_bitmap)}, + {"curr_wr_port", item_size(curr_wr_port), item_addr(curr_wr_port)}, + {"cmd_resp_received", item_size(cmd_resp_received), + item_addr(cmd_resp_received)}, + {"event_received", item_size(event_received), + item_addr(event_received)}, + + {"ioctl_pending", item_handle_size(ioctl_pending), + item_handle_addr(ioctl_pending)}, + {"tx_pending", item_handle_size(tx_pending), + item_handle_addr(tx_pending)}, + {"rx_pending", item_handle_size(rx_pending), + item_handle_addr(rx_pending)}, + {"lock_count", item_handle_size(lock_count), + item_handle_addr(lock_count)}, + {"malloc_count", item_handle_size(malloc_count), + item_handle_addr(malloc_count)}, + {"vmalloc_count", item_handle_size(vmalloc_count), + item_handle_addr(vmalloc_count)}, + {"mbufalloc_count", item_handle_size(mbufalloc_count), + item_handle_addr(mbufalloc_count)}, + {"main_state", item_handle_size(main_state), + item_handle_addr(main_state)}, + {"driver_state", item_handle_size(driver_state), + item_handle_addr(driver_state)}, +#ifdef SDIO_MMC_DEBUG + {"sdiocmd53w", item_handle_size(cmd53w), item_handle_addr(cmd53w)}, + {"sdiocmd53r", item_handle_size(cmd53r), item_handle_addr(cmd53r)}, +#endif +#if defined(SDIO_SUSPEND_RESUME) + {"hs_skip_count", item_handle_size(hs_skip_count), + item_handle_addr(hs_skip_count)}, + {"hs_force_count", item_handle_size(hs_force_count), + item_handle_addr(hs_force_count)}, +#endif +}; + +#endif + +#ifdef UAP_SUPPORT +static struct debug_data uap_items[] = { +#ifdef DEBUG_LEVEL1 + {"drvdbg", sizeof(drvdbg), (t_ptr)&drvdbg}, +#endif + {"mlan_processing", item_size(mlan_processing), + item_addr(mlan_processing)}, + {"main_process_cnt", item_size(main_process_cnt), + item_addr(main_process_cnt)}, + {"main_lock_flag", item_size(main_lock_flag), + item_addr(main_lock_flag)}, + {"delay_task_flag", item_size(delay_task_flag), + item_addr(delay_task_flag)}, + {"mlan_rx_processing", item_size(mlan_rx_processing), + item_addr(mlan_rx_processing)}, + {"rx_pkts_queued", item_size(rx_pkts_queued), + item_addr(rx_pkts_queued)}, + {"wmm_ac_vo", item_size(wmm_ac_vo), item_addr(wmm_ac_vo)}, + {"wmm_ac_vi", item_size(wmm_ac_vi), item_addr(wmm_ac_vi)}, + {"wmm_ac_be", item_size(wmm_ac_be), item_addr(wmm_ac_be)}, + {"wmm_ac_bk", item_size(wmm_ac_bk), item_addr(wmm_ac_bk)}, + {"max_tx_buf_size", item_size(max_tx_buf_size), + item_addr(max_tx_buf_size)}, + {"tx_buf_size", item_size(tx_buf_size), item_addr(tx_buf_size)}, + {"curr_tx_buf_size", item_size(curr_tx_buf_size), + item_addr(curr_tx_buf_size)}, + {"ps_mode", item_size(ps_mode), item_addr(ps_mode)}, + {"ps_state", item_size(ps_state), item_addr(ps_state)}, + {"wakeup_dev_req", item_size(pm_wakeup_card_req), + item_addr(pm_wakeup_card_req)}, + {"wakeup_tries", item_size(pm_wakeup_fw_try), + item_addr(pm_wakeup_fw_try)}, + {"hs_configured", item_size(is_hs_configured), + item_addr(is_hs_configured)}, + {"hs_activated", item_size(hs_activated), item_addr(hs_activated)}, + {"rx_pkts_queued", item_size(rx_pkts_queued), + item_addr(rx_pkts_queued)}, + {"tx_pkts_queued", item_size(tx_pkts_queued), + item_addr(tx_pkts_queued)}, + {"bypass_pkt_count", item_size(bypass_pkt_count), + item_addr(bypass_pkt_count)}, + {"num_bridge_pkts", item_size(num_bridge_pkts), + item_addr(num_bridge_pkts)}, + {"num_drop_pkts", item_size(num_drop_pkts), item_addr(num_drop_pkts)}, + {"num_tx_timeout", item_size(num_tx_timeout), + item_addr(num_tx_timeout)}, + {"num_cmd_timeout", item_size(num_cmd_timeout), + item_addr(num_cmd_timeout)}, + {"timeout_cmd_id", item_size(timeout_cmd_id), + item_addr(timeout_cmd_id)}, + {"timeout_cmd_act", item_size(timeout_cmd_act), + item_addr(timeout_cmd_act)}, + {"last_cmd_id", item_size(last_cmd_id), item_addr(last_cmd_id)}, + {"last_cmd_act", item_size(last_cmd_act), item_addr(last_cmd_act)}, + {"last_cmd_index", item_size(last_cmd_index), + item_addr(last_cmd_index)}, + {"last_cmd_resp_id", item_size(last_cmd_resp_id), + item_addr(last_cmd_resp_id)}, + {"last_cmd_resp_index", item_size(last_cmd_resp_index), + item_addr(last_cmd_resp_index)}, + {"last_event", item_size(last_event), item_addr(last_event)}, + {"last_event_index", item_size(last_event_index), + item_addr(last_event_index)}, + {"num_no_cmd_node", item_size(num_no_cmd_node), + item_addr(num_no_cmd_node)}, + {"num_cmd_h2c_fail", item_size(num_cmd_host_to_card_failure), + item_addr(num_cmd_host_to_card_failure)}, + {"num_cmd_sleep_cfm_fail", + item_size(num_cmd_sleep_cfm_host_to_card_failure), + item_addr(num_cmd_sleep_cfm_host_to_card_failure)}, + {"num_tx_h2c_fail", item_size(num_tx_host_to_card_failure), + item_addr(num_tx_host_to_card_failure)}, + {"num_alloc_buffer_failure", item_size(num_alloc_buffer_failure), + item_addr(num_alloc_buffer_failure)}, + {"num_cmdevt_c2h_fail", item_size(num_cmdevt_card_to_host_failure), + item_addr(num_cmdevt_card_to_host_failure)}, + {"num_rx_c2h_fail", item_size(num_rx_card_to_host_failure), + item_addr(num_rx_card_to_host_failure)}, + {"num_int_read_fail", item_size(num_int_read_failure), + item_addr(num_int_read_failure)}, + {"last_int_status", item_size(last_int_status), + item_addr(last_int_status)}, + {"num_of_irq", item_size(num_of_irq), item_addr(num_of_irq)}, + {"mp_invalid_update", item_size(mp_invalid_update), + item_addr(mp_invalid_update)}, + {"sdio_rx_aggr", item_size(sdio_rx_aggr), item_addr(sdio_rx_aggr)}, +#ifdef SDIO_MULTI_PORT_TX_AGGR + {"mpa_sent_last_pkt", item_size(mpa_sent_last_pkt), + item_addr(mpa_sent_last_pkt)}, + {"mpa_sent_no_ports", item_size(mpa_sent_no_ports), + item_addr(mpa_sent_no_ports)}, +#endif + {"cmd_sent", item_size(cmd_sent), item_addr(cmd_sent)}, + {"data_sent", item_size(data_sent), item_addr(data_sent)}, + {"mp_rd_bitmap", item_size(mp_rd_bitmap), item_addr(mp_rd_bitmap)}, + {"curr_rd_port", item_size(curr_rd_port), item_addr(curr_rd_port)}, + {"mp_wr_bitmap", item_size(mp_wr_bitmap), item_addr(mp_wr_bitmap)}, + {"curr_wr_port", item_size(curr_wr_port), item_addr(curr_wr_port)}, + {"cmd_resp_received", item_size(cmd_resp_received), + item_addr(cmd_resp_received)}, + {"event_received", item_size(event_received), + item_addr(event_received)}, + + {"ioctl_pending", item_handle_size(ioctl_pending), + item_handle_addr(ioctl_pending)}, + {"tx_pending", item_handle_size(tx_pending), + item_handle_addr(tx_pending)}, + {"rx_pending", item_handle_size(rx_pending), + item_handle_addr(rx_pending)}, + {"lock_count", item_handle_size(lock_count), + item_handle_addr(lock_count)}, + {"malloc_count", item_handle_size(malloc_count), + item_handle_addr(malloc_count)}, + {"vmalloc_count", item_handle_size(vmalloc_count), + item_handle_addr(vmalloc_count)}, + {"mbufalloc_count", item_handle_size(mbufalloc_count), + item_handle_addr(mbufalloc_count)}, + {"main_state", item_handle_size(main_state), + item_handle_addr(main_state)}, + {"driver_state", item_handle_size(driver_state), + item_handle_addr(driver_state)}, +#ifdef SDIO_MMC_DEBUG + {"sdiocmd53w", item_handle_size(cmd53w), item_handle_addr(cmd53w)}, + {"sdiocmd53r", item_handle_size(cmd53r), item_handle_addr(cmd53r)}, +#endif +#if defined(SDIO_SUSPEND_RESUME) + {"hs_skip_count", item_handle_size(hs_skip_count), + item_handle_addr(hs_skip_count)}, + {"hs_force_count", item_handle_size(hs_force_count), + item_handle_addr(hs_force_count)}, +#endif +}; +#endif /* UAP_SUPPORT */ + +/** + * @brief This function reset histogram data + * + * @param priv A pointer to moal_private + * + * @return N/A + */ +void +woal_hist_do_reset(moal_private *priv, void *data) +{ + hgm_data *phist_data = (hgm_data *)data; + int ix; + t_u8 rx_rate_max_size = RX_RATE_MAX; + + if (!phist_data) + return; + atomic_set(&(phist_data->num_samples), 0); + for (ix = 0; ix < rx_rate_max_size; ix++) + atomic_set(&(phist_data->rx_rate[ix]), 0); + for (ix = 0; ix < SNR_MAX; ix++) + atomic_set(&(phist_data->snr[ix]), 0); + for (ix = 0; ix < NOISE_FLR_MAX; ix++) + atomic_set(&(phist_data->noise_flr[ix]), 0); + for (ix = 0; ix < SIG_STRENGTH_MAX; ix++) + atomic_set(&(phist_data->sig_str[ix]), 0); +} + +/** + * @brief This function reset all histogram data + * + * @param priv A pointer to moal_private + * + * @return N/A + */ +void +woal_hist_data_reset(moal_private *priv) +{ + int i = 0; + for (i = 0; i < priv->phandle->histogram_table_num; i++) + woal_hist_do_reset(priv, priv->hist_data[i]); +} + +/** + * @brief This function reset histogram data according to antenna + * + * @param priv A pointer to moal_private + * @param antenna Antenna + * @return N/A + */ +void +woal_hist_reset_table(moal_private *priv, t_u8 antenna) +{ + hgm_data *phist_data = priv->hist_data[antenna]; + + woal_hist_do_reset(priv, phist_data); +} + +/** + * @brief This function set histogram data + * + * @param priv A pointer to moal_private + * @param rx_rate rx rate + * @param snr snr + * @param nflr NF + * @param antenna Antenna + * @return N/A + */ +static void +woal_hist_data_set(moal_private *priv, t_u8 rx_rate, t_s8 snr, t_s8 nflr, + t_u8 antenna) +{ + hgm_data *phist_data = priv->hist_data[antenna]; + + atomic_inc(&(phist_data->num_samples)); + atomic_inc(&(phist_data->rx_rate[rx_rate])); + atomic_inc(&(phist_data->snr[snr])); + atomic_inc(&(phist_data->noise_flr[128 + nflr])); + atomic_inc(&(phist_data->sig_str[nflr - snr])); +} + +/** + * @brief This function add histogram data + * + * @param priv A pointer to moal_private + * @param rx_rate rx rate + * @param snr snr + * @param nflr NF + * @param antenna Antenna + * @return N/A + */ +void +woal_hist_data_add(moal_private *priv, t_u8 rx_rate, t_s8 snr, t_s8 nflr, + t_u8 antenna) +{ + hgm_data *phist_data = NULL; + unsigned long curr_size; + + if ((antenna + 1) > priv->phandle->histogram_table_num) + antenna = 0; + phist_data = priv->hist_data[antenna]; + curr_size = atomic_read(&(phist_data->num_samples)); + if (curr_size > HIST_MAX_SAMPLES) + woal_hist_reset_table(priv, antenna); + woal_hist_data_set(priv, rx_rate, snr, nflr, antenna); +} + +#define MAX_MCS_NUM_SUPP 16 +#define MAX_MCS_NUM_AC 10 +#define RATE_INDEX_MCS0 12 +/** + * @brief histogram info in proc + * + * @param sfp A pointer to seq_file structure + * @param data void pointer to data + * + * @return Number of output data or MLAN_STATUS_FAILURE + */ +static int +woal_histogram_info(struct seq_file *sfp, void *data) +{ + hgm_data *phist_data = (hgm_data *)data; + int i = 0; + int value = 0; + t_bool sgi_enable = 0; + t_u8 bw = 0; + t_u8 mcs_index = 0; + t_u8 rx_rate_max_size = RX_RATE_MAX; + + ENTER(); + if (MODULE_GET == 0) { + LEAVE(); + return -EFAULT; + } + + seq_printf(sfp, "total samples = %d \n", + atomic_read(&(phist_data->num_samples))); + seq_printf(sfp, "rx rates (in Mbps):\n"); + seq_printf(sfp, "\t0-3: B-MCS 0-3\n"); + seq_printf(sfp, "\t4-11: G-MCS 0-7\n"); + seq_printf(sfp, + "\t12-27: N-MCS 0-15(BW20) 28-43: N-MCS 0-15(BW40)\n"); + seq_printf(sfp, + "\t44-59: N-MCS 0-15(BW20:SGI) 60-75: N-MCS 0-15(BW40:SGI)\n"); + seq_printf(sfp, "\n"); + + for (i = 0; i < rx_rate_max_size; i++) { + value = atomic_read(&(phist_data->rx_rate[i])); + if (value) { + if (i <= 11) + seq_printf(sfp, "rx_rate[%03d] = %d\n", i, + value); + else if (i <= 75) { + sgi_enable = (i - 12) / (MAX_MCS_NUM_SUPP * 2); //0:LGI, 1:SGI + bw = ((i - 12) % (MAX_MCS_NUM_SUPP * 2)) / MAX_MCS_NUM_SUPP; //0:20MHz, 1:40MHz + mcs_index = (i - 12) % MAX_MCS_NUM_SUPP; + seq_printf(sfp, + "rx_rate[%03d] = %d (MCS:%d HT BW:%dMHz%s)\n", + i, value, mcs_index, (1 << bw) * 20, + sgi_enable ? " SGI" : ""); + } + } + } + for (i = 0; i < SNR_MAX; i++) { + value = atomic_read(&(phist_data->snr[i])); + if (value) + seq_printf(sfp, "snr[%02ddB] = %d\n", i, value); + } + for (i = 0; i < NOISE_FLR_MAX; i++) { + value = atomic_read(&(phist_data->noise_flr[i])); + if (value) + seq_printf(sfp, "noise_flr[-%02ddBm] = %d\n", + (int)(i - 128), value); + } + for (i = 0; i < SIG_STRENGTH_MAX; i++) { + value = atomic_read(&(phist_data->sig_str[i])); + if (value) + seq_printf(sfp, "sig_strength[-%02ddBm] = %d\n", i, + value); + } + + MODULE_PUT; + LEAVE(); + return 0; +} + +/** + * @brief Proc read function for histogram + * + * @param sfp A pointer to seq_file structure + * @param data Void pointer to data + * + * @return Number of output data or MLAN_STATUS_FAILURE + */ +static int +woal_histogram_read(struct seq_file *sfp, void *data) +{ + wlan_hist_proc_data *hist_data = (wlan_hist_proc_data *) sfp->private; + moal_private *priv = (moal_private *)hist_data->priv; + + ENTER(); + if (!priv) { + LEAVE(); + return -EFAULT; + } + + if (!priv->hist_data) { + LEAVE(); + return -EFAULT; + } + if (hist_data->ant_idx < priv->phandle->histogram_table_num) + woal_histogram_info(sfp, priv->hist_data[hist_data->ant_idx]); + + LEAVE(); + return 0; +} + +/** + * @brief Proc open function for histogram + * + * @param inode A pointer to inode structure + * @param file A pointer to file structure + * + * @return 0--sucess, otherise fail +**/ +static int +woal_histogram_proc_open(struct inode *inode, struct file *file) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) + return single_open(file, woal_histogram_read, PDE_DATA(inode)); +#else + return single_open(file, woal_histogram_read, PDE(inode)->data); +#endif +} + +/** + * @brief Proc write function for histogram + * + * @param f file pointer + * @param buf pointer to data buffer + * @param count data number to write + * @param off Offset + * + * @return number of data + */ +static ssize_t +woal_histogram_write(struct file *f, const char __user * buf, size_t count, + loff_t * off) +{ + struct seq_file *sfp = f->private_data; + wlan_hist_proc_data *hist_data = (wlan_hist_proc_data *) sfp->private; + moal_private *priv = (moal_private *)hist_data->priv; + woal_hist_reset_table(priv, hist_data->ant_idx); + return count; +} + +/** + * @brief Proc read function for log + * + * @param sfp A pointer to seq_file structure + * @param data void pointer to data + * + * @return Number of output data or MLAN_STATUS_FAILURE + */ +static int +woal_log_read(struct seq_file *sfp, void *data) +{ + moal_private *priv = (moal_private *)sfp->private; + mlan_ds_get_stats stats; + int i = 0; + ENTER(); + if (!priv) { + LEAVE(); + return -EFAULT; + } + if (MODULE_GET == 0) { + LEAVE(); + return -EFAULT; + } + + memset(&stats, 0x00, sizeof(stats)); + if (MLAN_STATUS_SUCCESS != + woal_get_stats_info(priv, MOAL_IOCTL_WAIT, &stats)) { + PRINTM(MERROR, + "woal_log_read: Get log: Failed to get stats info!"); + MODULE_PUT; + LEAVE(); + return -EFAULT; + } + + seq_printf(sfp, "dot11GroupTransmittedFrameCount = %d\n", + stats.mcast_tx_frame); + seq_printf(sfp, "dot11FailedCount = %d\n", stats.failed); + seq_printf(sfp, "dot11RetryCount = %d\n", stats.retry); + seq_printf(sfp, "dot11MultipleRetryCount = %d\n", stats.multi_retry); + seq_printf(sfp, "dot11FrameDuplicateCount = %d\n", stats.frame_dup); + seq_printf(sfp, "dot11RTSSuccessCount = %d\n", stats.rts_success); + seq_printf(sfp, "dot11RTSFailureCount = %d\n", stats.rts_failure); + seq_printf(sfp, "dot11ACKFailureCount = %d\n", stats.ack_failure); + seq_printf(sfp, "dot11ReceivedFragmentCount = %d\n", stats.rx_frag); + seq_printf(sfp, "dot11GroupReceivedFrameCount = %d\n", + stats.mcast_rx_frame); + seq_printf(sfp, "dot11FCSErrorCount = %d\n", stats.fcs_error); + seq_printf(sfp, "dot11TransmittedFrameCount = %d\n", stats.tx_frame); + seq_printf(sfp, "wepicverrcnt-1 = %d\n", stats.wep_icv_error[0]); + seq_printf(sfp, "wepicverrcnt-2 = %d\n", stats.wep_icv_error[1]); + seq_printf(sfp, "wepicverrcnt-3 = %d\n", stats.wep_icv_error[2]); + seq_printf(sfp, "wepicverrcnt-4 = %d\n", stats.wep_icv_error[3]); + seq_printf(sfp, "beaconReceivedCount = %d\n", stats.bcn_rcv_cnt); + seq_printf(sfp, "beaconMissedCount = %d\n", stats.bcn_miss_cnt); + if (stats.amsdu_rx_cnt) + seq_printf(sfp, "ReceivedMSDUinPerAMSDU = %d\n", + stats.msdu_in_rx_amsdu_cnt / stats.amsdu_rx_cnt); + seq_printf(sfp, "ReceivedMSDUinAMSDUCount = %d\n", + stats.msdu_in_rx_amsdu_cnt); + if (stats.amsdu_tx_cnt) + seq_printf(sfp, "TransmitMSDUinPerAMSDU = %d\n", + stats.msdu_in_tx_amsdu_cnt / stats.amsdu_tx_cnt); + seq_printf(sfp, "TransmitMSDUinAMSDUCount = %d\n", + stats.msdu_in_tx_amsdu_cnt); + if (priv->phandle->fw_getlog_enable) { + seq_printf(sfp, "dot11TransmittedFragmentCount = %u\n", + stats.tx_frag_cnt); + seq_printf(sfp, "dot11QosTransmittedFragmentCount = "); + for (i = 0; i < 8; i++) { + seq_printf(sfp, "%u ", stats.qos_tx_frag_cnt[i]); + } + seq_printf(sfp, "\ndot11QosFailedCount = "); + for (i = 0; i < 8; i++) { + seq_printf(sfp, "%u ", stats.qos_failed_cnt[i]); + } + seq_printf(sfp, "\ndot11QosRetryCount = "); + for (i = 0; i < 8; i++) { + seq_printf(sfp, "%u ", stats.qos_retry_cnt[i]); + } + seq_printf(sfp, "\ndot11QosMultipleRetryCount = "); + for (i = 0; i < 8; i++) { + seq_printf(sfp, "%u ", stats.qos_multi_retry_cnt[i]); + } + seq_printf(sfp, "\ndot11QosFrameDuplicateCount = "); + for (i = 0; i < 8; i++) { + seq_printf(sfp, "%u ", stats.qos_frm_dup_cnt[i]); + } + seq_printf(sfp, "\ndot11QosRTSSuccessCount = "); + for (i = 0; i < 8; i++) { + seq_printf(sfp, "%u ", stats.qos_rts_suc_cnt[i]); + } + seq_printf(sfp, "\ndot11QosRTSFailureCount = "); + for (i = 0; i < 8; i++) { + seq_printf(sfp, "%u ", stats.qos_rts_failure_cnt[i]); + } + seq_printf(sfp, "\ndot11QosACKFailureCount = "); + for (i = 0; i < 8; i++) { + seq_printf(sfp, "%u ", stats.qos_ack_failure_cnt[i]); + } + seq_printf(sfp, "\ndot11QosReceivedFragmentCount = "); + for (i = 0; i < 8; i++) { + seq_printf(sfp, "%u ", stats.qos_rx_frag_cnt[i]); + } + seq_printf(sfp, "\ndot11QosTransmittedFrameCount = "); + for (i = 0; i < 8; i++) { + seq_printf(sfp, "%u ", stats.qos_tx_frm_cnt[i]); + } + seq_printf(sfp, "\ndot11QosDiscardedFrameCount = "); + for (i = 0; i < 8; i++) { + seq_printf(sfp, "%u ", stats.qos_discarded_frm_cnt[i]); + } + seq_printf(sfp, "\ndot11QosMPDUsReceivedCount = "); + for (i = 0; i < 8; i++) { + seq_printf(sfp, "%u ", stats.qos_mpdus_rx_cnt[i]); + } + seq_printf(sfp, "\ndot11QosRetriesReceivedCount = "); + for (i = 0; i < 8; i++) { + seq_printf(sfp, "%u ", stats.qos_retries_rx_cnt[i]); + } + seq_printf(sfp, "\ndot11RSNAStatsCMACICVErrors = %u\n" + "dot11RSNAStatsCMACReplays = %u\n" + "dot11RSNAStatsRobustMgmtCCMPReplays = %u\n" + "dot11RSNAStatsTKIPICVErrors = %u\n" + "dot11RSNAStatsTKIPReplays = %u\n" + "dot11RSNAStatsCCMPDecryptErrors = %u\n" + "dot11RSNAstatsCCMPReplays = %u\n" + "dot11TransmittedAMSDUCount = %u\n" + "dot11FailedAMSDUCount = %u\n" + "dot11RetryAMSDUCount = %u\n" + "dot11MultipleRetryAMSDUCount = %u\n" + "dot11TransmittedOctetsInAMSDUCount = %llu\n" + "dot11AMSDUAckFailureCount = %u\n" + "dot11ReceivedAMSDUCount = %u\n" + "dot11ReceivedOctetsInAMSDUCount = %llu\n" + "dot11TransmittedAMPDUCount = %u\n" + "dot11TransmittedMPDUsInAMPDUCount = %u\n" + "dot11TransmittedOctetsInAMPDUCount = %llu\n" + "dot11AMPDUReceivedCount = %u\n" + "dot11MPDUInReceivedAMPDUCount = %u\n" + "dot11ReceivedOctetsInAMPDUCount = %llu\n" + "dot11AMPDUDelimiterCRCErrorCount = %u\n", + stats.cmacicv_errors, + stats.cmac_replays, + stats.mgmt_ccmp_replays, + stats.tkipicv_errors, + stats.tkip_replays, + stats.ccmp_decrypt_errors, + stats.ccmp_replays, + stats.tx_amsdu_cnt, + stats.failed_amsdu_cnt, + stats.retry_amsdu_cnt, + stats.multi_retry_amsdu_cnt, + stats.tx_octets_in_amsdu_cnt, + stats.amsdu_ack_failure_cnt, + stats.rx_amsdu_cnt, + stats.rx_octets_in_amsdu_cnt, + stats.tx_ampdu_cnt, + stats.tx_mpdus_in_ampdu_cnt, + stats.tx_octets_in_ampdu_cnt, + stats.ampdu_rx_cnt, + stats.mpdu_in_rx_ampdu_cnt, + stats.rx_octets_in_ampdu_cnt, + stats.ampdu_delimiter_crc_error_cnt); + + } + + MODULE_PUT; + LEAVE(); + return 0; +} + +/** + * @brief Proc read function for log + * + * @param inode pointer to inode + * @param file file pointer + * + * @return number of data + */ +static int +woal_log_proc_open(struct inode *inode, struct file *file) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) + return single_open(file, woal_log_read, PDE_DATA(inode)); +#else + return single_open(file, woal_log_read, PDE(inode)->data); +#endif +} + +/******************************************************** + Local Functions +********************************************************/ +/** + * @brief Proc read function + * + * @param sfp A pointer to seq_file structure + * @param data Void pointer to data + * + * @return Number of output data or MLAN_STATUS_FAILURE + */ +static int +woal_debug_read(struct seq_file *sfp, void *data) +{ + int val = 0; + unsigned int i; + + struct debug_data_priv *items_priv = + (struct debug_data_priv *)sfp->private; + struct debug_data *d = items_priv->items; + moal_private *priv = items_priv->priv; +#ifdef SDIO_MULTI_PORT_TX_AGGR + unsigned int j; + t_u8 mp_aggr_pkt_limit = SDIO_MP_AGGR_DEF_PKT_LIMIT; +#endif + + ENTER(); + + if (priv == NULL) { + LEAVE(); + return -EFAULT; + } + + if (MODULE_GET == 0) { + LEAVE(); + return -EFAULT; + } + + priv->phandle->driver_state = woal_check_driver_status(priv->phandle); + /* Get debug information */ + if (woal_get_debug_info(priv, MOAL_IOCTL_WAIT, &info)) + goto exit; + + for (i = 0; i < (unsigned int)items_priv->num_of_items; i++) { + if (d[i].size == 1) + val = *((t_u8 *)d[i].addr); + else if (d[i].size == 2) + val = *((t_u16 *)d[i].addr); + else if (d[i].size == 4) + val = *((t_u32 *)d[i].addr); + else { + unsigned int j; + seq_printf(sfp, "%s=", d[i].name); + for (j = 0; j < d[i].size; j += 2) { + val = *(t_u16 *)(d[i].addr + j); + seq_printf(sfp, "0x%x ", val); + } + seq_printf(sfp, "\n"); + continue; + } + if (strstr(d[i].name, "id") + || strstr(d[i].name, "bitmap") + ) + seq_printf(sfp, "%s=0x%x\n", d[i].name, val); + else + seq_printf(sfp, "%s=%d\n", d[i].name, val); + } +#ifdef SDIO_MULTI_PORT_TX_AGGR + seq_printf(sfp, "last_recv_wr_bitmap=0x%x last_mp_index=%d\n", + info.last_recv_wr_bitmap, info.last_mp_index); + for (i = 0; i < SDIO_MP_DBG_NUM; i++) { + seq_printf(sfp, + "mp_wr_bitmap: 0x%x mp_wr_ports=0x%x len=%d curr_wr_port=0x%x\n", + info.last_mp_wr_bitmap[i], info.last_mp_wr_ports[i], + info.last_mp_wr_len[i], info.last_curr_wr_port[i]); + for (j = 0; j < mp_aggr_pkt_limit; j++) { + seq_printf(sfp, "0x%02x ", + info.last_mp_wr_info[i * mp_aggr_pkt_limit + + j]); + } + seq_printf(sfp, "\n"); + } + seq_printf(sfp, "SDIO MPA Tx: "); + for (i = 0; i < mp_aggr_pkt_limit; i++) + seq_printf(sfp, "%d ", info.mpa_tx_count[i]); + seq_printf(sfp, "\n"); +#endif +#ifdef SDIO_MULTI_PORT_RX_AGGR + seq_printf(sfp, "SDIO MPA Rx: "); + for (i = 0; i < mp_aggr_pkt_limit; i++) + seq_printf(sfp, "%d ", info.mpa_rx_count[i]); + seq_printf(sfp, "\n"); +#endif + seq_printf(sfp, "SDIO MP Update: "); + for (i = 0; i < (mp_aggr_pkt_limit * 2); i++) + seq_printf(sfp, "%d ", info.mp_update[i]); + seq_printf(sfp, "\n"); + seq_printf(sfp, "tcp_ack_drop_cnt=%d\n", priv->tcp_ack_drop_cnt); + seq_printf(sfp, "tcp_ack_cnt=%d\n", priv->tcp_ack_cnt); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) + for (i = 0; i < 4; i++) + seq_printf(sfp, "wmm_tx_pending[%d]:%d\n", i, + atomic_read(&priv->wmm_tx_pending[i])); +#endif + if (info.tx_tbl_num) { + seq_printf(sfp, "Tx BA stream table:\n"); + for (i = 0; i < info.tx_tbl_num; i++) { + seq_printf(sfp, + "tid = %d, ra = %02x:%02x:%02x:%02x:%02x:%02x amsdu=%d\n", + (int)info.tx_tbl[i].tid, + info.tx_tbl[i].ra[0], info.tx_tbl[i].ra[1], + info.tx_tbl[i].ra[2], info.tx_tbl[i].ra[3], + info.tx_tbl[i].ra[4], info.tx_tbl[i].ra[5], + (int)info.tx_tbl[i].amsdu); + } + } + if (info.rx_tbl_num) { + seq_printf(sfp, "Rx reorder table:\n"); + for (i = 0; i < info.rx_tbl_num; i++) { + unsigned int j; + + seq_printf(sfp, + "tid = %d, ta = %02x:%02x:%02x:%02x:%02x:%02x, start_win = %d, " + "win_size = %d, amsdu=%d\n", + (int)info.rx_tbl[i].tid, + info.rx_tbl[i].ta[0], info.rx_tbl[i].ta[1], + info.rx_tbl[i].ta[2], info.rx_tbl[i].ta[3], + info.rx_tbl[i].ta[4], info.rx_tbl[i].ta[5], + (int)info.rx_tbl[i].start_win, + (int)info.rx_tbl[i].win_size, + (int)info.rx_tbl[i].amsdu); + seq_printf(sfp, "buffer: "); + for (j = 0; j < info.rx_tbl[i].win_size; j++) { + if (info.rx_tbl[i].buffer[j] == MTRUE) + seq_printf(sfp, "1 "); + else + seq_printf(sfp, "0 "); + } + seq_printf(sfp, "\n"); + } + } + for (i = 0; i < info.ralist_num; i++) { + seq_printf(sfp, + "ralist ra: %02x:%02x:%02x:%02x:%02x:%02x tid=%d pkts=%d pause=%d\n", + info.ralist[i].ra[0], info.ralist[i].ra[1], + info.ralist[i].ra[2], info.ralist[i].ra[3], + info.ralist[i].ra[4], info.ralist[i].ra[5], + info.ralist[i].tid, info.ralist[i].total_pkts, + info.ralist[i].tx_pause); + } + + for (i = 0; i < info.tdls_peer_num; i++) { + unsigned int j; + seq_printf(sfp, + "tdls peer: %02x:%02x:%02x:%02x:%02x:%02x snr=%d nf=%d\n", + info.tdls_peer_list[i].mac_addr[0], + info.tdls_peer_list[i].mac_addr[1], + info.tdls_peer_list[i].mac_addr[2], + info.tdls_peer_list[i].mac_addr[3], + info.tdls_peer_list[i].mac_addr[4], + info.tdls_peer_list[i].mac_addr[5], + info.tdls_peer_list[i].snr, + -info.tdls_peer_list[i].nf); + seq_printf(sfp, "htcap: "); + for (j = 0; j < sizeof(IEEEtypes_HTCap_t); j++) + seq_printf(sfp, "%02x ", + info.tdls_peer_list[i].ht_cap[j]); + seq_printf(sfp, "\nExtcap: "); + for (j = 0; j < sizeof(IEEEtypes_ExtCap_t); j++) + seq_printf(sfp, "%02x ", + info.tdls_peer_list[i].ext_cap[j]); + seq_printf(sfp, "\n"); + } +exit: + MODULE_PUT; + LEAVE(); + return 0; +} + +/** + * @brief Proc write function + * + * @param f file pointer + * @param buf pointer to data buffer + * @param count data number to write + * @param off Offset + * + * @return number of data + */ +static ssize_t +woal_debug_write(struct file *f, const char __user * buf, size_t count, + loff_t * off) +{ + int r, i; + char *pdata; + char *p; + char *p0; + char *p1; + char *p2; + struct seq_file *sfp = f->private_data; + struct debug_data_priv *items_priv = + (struct debug_data_priv *)sfp->private; + struct debug_data *d = items_priv->items; + moal_private *priv = items_priv->priv; +#ifdef DEBUG_LEVEL1 + t_u32 last_drvdbg = drvdbg; +#endif + gfp_t flag; + + ENTER(); + + if (MODULE_GET == 0) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + flag = (in_atomic() || irqs_disabled())? GFP_ATOMIC : GFP_KERNEL; + pdata = kzalloc(count + 1, flag); + if (pdata == NULL) { + MODULE_PUT; + LEAVE(); + return 0; + } + + if (copy_from_user(pdata, buf, count)) { + PRINTM(MERROR, "Copy from user failed\n"); + kfree(pdata); + MODULE_PUT; + LEAVE(); + return 0; + } + + if (woal_get_debug_info(priv, MOAL_IOCTL_WAIT, &info)) { + kfree(pdata); + MODULE_PUT; + LEAVE(); + return 0; + } + + p0 = pdata; + for (i = 0; i < items_priv->num_of_items; i++) { + do { + p = strstr(p0, d[i].name); + if (p == NULL) + break; + p1 = strchr(p, '\n'); + if (p1 == NULL) + break; + p0 = p1++; + p2 = strchr(p, '='); + if (!p2) + break; + p2++; + r = woal_string_to_number(p2); + if (d[i].size == 1) + *((t_u8 *)d[i].addr) = (t_u8)r; + else if (d[i].size == 2) + *((t_u16 *)d[i].addr) = (t_u16)r; + else if (d[i].size == 4) + *((t_u32 *)d[i].addr) = (t_u32)r; + break; + } while (MTRUE); + } + kfree(pdata); + +#ifdef DEBUG_LEVEL1 + if (last_drvdbg != drvdbg) + woal_set_drvdbg(priv, drvdbg); + +#endif + + MODULE_PUT; + LEAVE(); + return count; +} + +/** + * @brief debug proc open function + * + * @param inode A pointer to inode structure + * @param file A pointer to file structure + * + * @return 0--sucess, otherise fail +**/ +static int +woal_debug_proc_open(struct inode *inode, struct file *file) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) + return single_open(file, woal_debug_read, PDE_DATA(inode)); +#else + return single_open(file, woal_debug_read, PDE(inode)->data); +#endif +} + +static const struct file_operations debug_proc_fops = { + .owner = THIS_MODULE, + .open = woal_debug_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .write = woal_debug_write, +}; + +static const struct file_operations histogram_proc_fops = { + .owner = THIS_MODULE, + .open = woal_histogram_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .write = woal_histogram_write, +}; + +static const struct file_operations log_proc_fops = { + .owner = THIS_MODULE, + .open = woal_log_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +/******************************************************** + Global Functions +********************************************************/ +/** + * @brief Create debug proc file + * + * @param priv A pointer to a moal_private structure + * + * @return N/A + */ +void +woal_debug_entry(moal_private *priv) +{ + struct proc_dir_entry *r; + int i; + int handle_items; + char hist_entry[50]; + + ENTER(); + + if (priv->proc_entry == NULL) { + LEAVE(); + return; + } +#ifdef STA_SUPPORT + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) { + priv->items_priv.items = kmalloc(sizeof(items), GFP_KERNEL); + if (!priv->items_priv.items) { + PRINTM(MERROR, + "Failed to allocate memory for debug data\n"); + LEAVE(); + return; + } + memcpy(priv->items_priv.items, items, sizeof(items)); + priv->items_priv.num_of_items = ARRAY_SIZE(items); + } +#endif +#ifdef UAP_SUPPORT + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) { + priv->items_priv.items = kmalloc(sizeof(uap_items), GFP_KERNEL); + if (!priv->items_priv.items) { + PRINTM(MERROR, + "Failed to allocate memory for debug data\n"); + LEAVE(); + return; + } + memcpy(priv->items_priv.items, uap_items, sizeof(uap_items)); + priv->items_priv.num_of_items = ARRAY_SIZE(uap_items); + } +#endif + + priv->items_priv.priv = priv; + handle_items = 9; +#ifdef SDIO_MMC_DEBUG + handle_items += 2; +#endif +#if defined(SDIO_SUSPEND_RESUME) + handle_items += 2; +#endif + for (i = 1; i <= handle_items; i++) + priv->items_priv.items[priv->items_priv.num_of_items - + i].addr += (t_ptr)(priv->phandle); + + /* Create proc entry */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) + r = proc_create_data("debug", 0644, priv->proc_entry, &debug_proc_fops, + &priv->items_priv); + if (r == NULL) +#else + r = create_proc_entry("debug", 0644, priv->proc_entry); + if (r) { + r->data = &priv->items_priv; + r->proc_fops = &debug_proc_fops; + } else +#endif + { + PRINTM(MMSG, "Fail to create proc debug entry\n"); + LEAVE(); + return; + } + if (priv->bss_type == MLAN_BSS_TYPE_STA || + priv->bss_type == MLAN_BSS_TYPE_UAP) { + priv->hist_entry = proc_mkdir("histogram", priv->proc_entry); + if (!priv->hist_entry) { + PRINTM(MERROR, "Fail to mkdir histogram!\n"); + LEAVE(); + return; + } + for (i = 0; i < priv->phandle->histogram_table_num; i++) { + priv->hist_proc[i].ant_idx = i; + priv->hist_proc[i].priv = priv; + snprintf(hist_entry, sizeof(hist_entry), "wlan-ant%d", + i); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) + r = proc_create_data(hist_entry, 0644, priv->hist_entry, + &histogram_proc_fops, + &priv->hist_proc[i]); + if (r == NULL) +#else + r = create_proc_entry("histogram", 0644, + priv->hist_entry); + if (r) { + r->data = &priv->hist_proc[i]; + r->proc_fops = &histogram_proc_fops; + } else +#endif + { + PRINTM(MMSG, + "Fail to create proc histogram entry %s\n", + hist_entry); + LEAVE(); + return; + } + } + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) + r = proc_create_data("log", 0644, priv->proc_entry, &log_proc_fops, + priv); + if (r == NULL) +#else + r = create_proc_entry("log", 0644, priv->proc_entry); + if (r) { + r->data = priv; + r->proc_fops = &log_proc_fops; + } else +#endif + { + PRINTM(MMSG, "Fail to create proc log entry\n"); + LEAVE(); + return; + } + + LEAVE(); +} + +/** + * @brief Remove proc file + * + * @param priv A pointer to a moal_private structure + * + * @return N/A + */ +void +woal_debug_remove(moal_private *priv) +{ + char hist_entry[50]; + int i; + ENTER(); + + kfree(priv->items_priv.items); + /* Remove proc entry */ + remove_proc_entry("debug", priv->proc_entry); + if (priv->bss_type == MLAN_BSS_TYPE_STA || + priv->bss_type == MLAN_BSS_TYPE_UAP) { + for (i = 0; i < priv->phandle->histogram_table_num; i++) { + snprintf(hist_entry, sizeof(hist_entry), "wlan-ant%d", + i); + remove_proc_entry(hist_entry, priv->hist_entry); + } + remove_proc_entry("histogram", priv->proc_entry); + } + remove_proc_entry("log", priv->proc_entry); + + LEAVE(); +} +#endif diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_eth_ioctl.c b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_eth_ioctl.c new file mode 100644 index 000000000000..487d4856e2b7 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_eth_ioctl.c @@ -0,0 +1,14330 @@ +/** @file moal_eth_ioctl.c + * + * @brief This file contains private ioctl functions + * + * Copyright (C) 2014-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +/************************************************************************ +Change log: + 01/05/2012: initial version +************************************************************************/ + +#include "moal_main.h" +#include "moal_eth_ioctl.h" +#include "mlan_ioctl.h" +#if defined(STA_WEXT) || defined(UAP_WEXT) +#include "moal_priv.h" +#endif + +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#include "moal_cfg80211.h" +#endif +#ifdef UAP_SUPPORT +#include "moal_uap.h" +#endif +#include "moal_sdio.h" +#ifdef STA_CFG80211 +#include "moal_sta_cfg80211.h" +#endif + +/******************************************************** + Local Variables +********************************************************/ + +/** Bands supported in Infra mode */ +static t_u8 SupportedInfraBand[] = { + BAND_B, + BAND_B | BAND_G, BAND_G, + BAND_GN, BAND_B | BAND_G | BAND_GN, BAND_G | BAND_GN, + BAND_A, BAND_B | BAND_A, BAND_B | BAND_G | BAND_A, BAND_G | BAND_A, + BAND_A | BAND_B | BAND_G | BAND_AN | BAND_GN, + BAND_A | BAND_G | BAND_AN | BAND_GN, BAND_A | BAND_AN, +}; + +/** Bands supported in Ad-Hoc mode */ +static t_u8 SupportedAdhocBand[] = { + BAND_B, BAND_B | BAND_G, BAND_G, + BAND_GN, BAND_B | BAND_G | BAND_GN, BAND_G | BAND_GN, + BAND_A, + BAND_AN, BAND_A | BAND_AN, +}; + +/******************************************************** + Global Variables +********************************************************/ +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) +#ifdef UAP_SUPPORT +/** Network device handlers for uAP */ +extern const struct net_device_ops woal_uap_netdev_ops; +#endif +#ifdef STA_SUPPORT +/** Network device handlers for STA */ +extern const struct net_device_ops woal_netdev_ops; +#endif +#endif +extern int cfg80211_wext; + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) +extern int dfs_offload; +#endif +extern int roamoffload_in_hs; +extern int hw_test; +extern int cntry_txpwr; +/******************************************************** + Local Functions +********************************************************/ +/** + * @brief Parse a string to extract numerical arguments + * + * @param pos Pointer to the arguments string + * @param data Pointer to the arguments buffer + * @param datalen Length of the arguments buffer + * @param user_data_len Pointer to the number of arguments extracted + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +parse_arguments(t_u8 *pos, int *data, int datalen, int *user_data_len) +{ + unsigned int i, j, k; + char cdata[10]; + int is_hex = 0; + + if (strlen(pos) == 0) { + *user_data_len = 0; + return MLAN_STATUS_SUCCESS; + } + + memset(cdata, 0, sizeof(cdata)); + for (i = 0, j = 0, k = 0; i <= strlen(pos); i++) { + if ((k == 0) && (i <= (strlen(pos) - 2))) { + if ((pos[i] == '0') && (pos[i + 1] == 'x')) { + is_hex = 1; + i = i + 2; + } + } + if (pos[i] == '\0' || pos[i] == ' ') { + if (j >= datalen) { + j++; + break; + } + if (is_hex) { + data[j] = woal_atox(cdata); + is_hex = 0; + } else { + woal_atoi(&data[j], cdata); + } + j++; + k = 0; + memset(cdata, 0, sizeof(cdata)); + if (pos[i] == '\0') + break; + } else { + cdata[k] = pos[i]; + k++; + } + } + + *user_data_len = j; + return MLAN_STATUS_SUCCESS; +} + +/** Convert character to integer */ +#define CHAR2INT(x) (((x) >= 'A') ? ((x) - 'A' + 10) : ((x) - '0')) +/** + * @brief Converts a string to hex value + * + * @param str A pointer to the string + * @param raw A pointer to the raw data buffer + * @return Number of bytes read + **/ +int +string2raw(char *str, unsigned char *raw) +{ + int len = (strlen(str) + 1) / 2; + + do { + if (!isxdigit(*str)) { + return -1; + } + *str = toupper(*str); + *raw = CHAR2INT(*str) << 4; + ++str; + *str = toupper(*str); + if (*str == '\0') + break; + *raw |= CHAR2INT(*str); + ++raw; + } while (*++str != '\0'); + return len; +} + +#if defined(STA_CFG80211) && defined(UAP_CFG80211) +/** + * @brief Set wps & p2p ie in AP mode + * + * @param priv Pointer to priv stucture + * @param ie Pointer to ies data + * @param len Length of data + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status +woal_set_ap_wps_p2p_ie(moal_private *priv, t_u8 *ie, size_t len) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u8 *pos = ie; + t_u32 ie_len; + + ENTER(); + + ie_len = len - 2; + if (ie_len <= 0) { + PRINTM(MERROR, "IE len error: %d\n", ie_len); + ret = -EFAULT; + goto done; + } + + /* Android cmd format: + * "SET_AP_WPS_P2P_IE 1" -- beacon IE + * "SET_AP_WPS_P2P_IE 2" -- proberesp IE + * "SET_AP_WPS_P2P_IE 4" -- assocresp IE + */ + if (*pos == '1') { + /* set the beacon wps/p2p ies */ + pos += 2; + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_mgmt_frame_ie(priv, pos, ie_len, NULL, 0, + NULL, 0, NULL, 0, + MGMT_MASK_BEACON_WPS_P2P, + MOAL_IOCTL_WAIT)) { + PRINTM(MERROR, "Failed to set beacon wps/p2p ie\n"); + ret = -EFAULT; + goto done; + } + } else if (*pos == '2') { + /* set the probe resp ies */ + pos += 2; + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_mgmt_frame_ie(priv, NULL, 0, pos, ie_len, + NULL, 0, NULL, 0, + MGMT_MASK_PROBE_RESP, + MOAL_IOCTL_WAIT)) { + PRINTM(MERROR, "Failed to set probe resp ie\n"); + ret = -EFAULT; + goto done; + } + } else if (*pos == '4') { + /* set the assoc resp ies */ + pos += 2; + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_mgmt_frame_ie(priv, NULL, 0, NULL, 0, pos, + ie_len, NULL, 0, + MGMT_MASK_ASSOC_RESP, + MOAL_IOCTL_WAIT)) { + PRINTM(MERROR, "Failed to set assoc resp ie\n"); + ret = -EFAULT; + goto done; + } + } + +done: + LEAVE(); + return ret; +} +#endif + +#ifdef WIFI_DIRECT_SUPPORT +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +/** + * @brief Set miracast mode + * + * @param priv Pointer to priv stucture + * @param pdata Pointer to cmd buffer + * @param len Length of data + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status +woal_set_miracast_mode(moal_private *priv, t_u8 *pdata, size_t len) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u8 *pos = pdata; + + ENTER(); + if (!pos) { + PRINTM(MERROR, "%s: Null buf!\n", __func__); + ret = MLAN_STATUS_FAILURE; + goto done; + } + while (!isdigit(*pos) && --len > 0) + pos++; + switch (*pos) { + case '0': + /* disable miracast mode */ + priv->phandle->miracast_mode = 0; + break; + case '1': + /* Source */ + priv->phandle->miracast_mode = 1; + break; + case '2': + /* Sink */ + priv->phandle->miracast_mode = 2; + break; + default: + PRINTM(MERROR, "%s: Unknown miracast mode (%c)\n", + priv->netdev->name, *pos); + ret = MLAN_STATUS_FAILURE; + break; + } +done: + LEAVE(); + return ret; +} +#endif +#endif + +/** + * @brief Get Driver Version + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int +woal_get_priv_driver_version(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int len = 0, ret = -1; + char buf[MLAN_MAX_VER_STR_LEN]; + + ENTER(); + + if (!respbuf) { + LEAVE(); + return 0; + } + + memset(buf, 0, sizeof(buf)); + + /* Get version string to local buffer */ + woal_get_version(priv->phandle, buf, sizeof(buf) - 1); + len = strlen(buf); + + if (len) { + /* Copy back the retrieved version string */ + PRINTM(MINFO, "MOAL VERSION: %s\n", buf); + ret = MIN(len, (respbuflen - 1)); + memcpy(respbuf, buf, ret); + } else { + ret = -1; + PRINTM(MERROR, "Get version failed!\n"); + } + + LEAVE(); + return ret; +} + +/** + * @brief Hostcmd interface from application + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * @param wait_option Wait option + * + * @return Number of bytes written, negative for failure. + */ +int +woal_priv_hostcmd(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen, + t_u8 wait_option) +{ + int ret = 0; + t_u8 *data_ptr; + t_u32 buf_len = 0; + HostCmd_Header cmd_header; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *misc_cfg = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + data_ptr = respbuf + (strlen(CMD_MARVELL) + strlen(PRIV_CMD_HOSTCMD)); + buf_len = *((t_u32 *)data_ptr); + memcpy(&cmd_header, data_ptr + sizeof(buf_len), sizeof(HostCmd_Header)); + + PRINTM(MINFO, "Host command len = %d\n", + woal_le16_to_cpu(cmd_header.size)); + if (woal_le16_to_cpu(cmd_header.size) > MLAN_SIZE_OF_CMD_BUFFER) { + LEAVE(); + return -EINVAL; + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto error; + } + misc_cfg = (mlan_ds_misc_cfg *)req->pbuf; + misc_cfg->sub_command = MLAN_OID_MISC_HOST_CMD; + req->req_id = MLAN_IOCTL_MISC_CFG; + req->action = MLAN_ACT_SET; + misc_cfg->param.hostcmd.len = woal_le16_to_cpu(cmd_header.size); + /* get the whole command */ + memcpy(misc_cfg->param.hostcmd.cmd, data_ptr + sizeof(buf_len), + misc_cfg->param.hostcmd.len); + + status = woal_request_ioctl(priv, req, wait_option); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto error; + } + memcpy(data_ptr + sizeof(buf_len), misc_cfg->param.hostcmd.cmd, + misc_cfg->param.hostcmd.len); + ret = misc_cfg->param.hostcmd.len + sizeof(buf_len) + + strlen(CMD_MARVELL) + strlen(PRIV_CMD_HOSTCMD); + memcpy(data_ptr, (t_u8 *)&misc_cfg->param.hostcmd.len, sizeof(t_u32)); + +error: + if (status != MLAN_STATUS_PENDING) + kfree(req); + + LEAVE(); + return ret; +} + +/** + * @brief Custom IE setting + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int +woal_priv_customie(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int ret = 0; + t_u8 *data_ptr; + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_misc_cfg *misc = NULL; + mlan_ds_misc_custom_ie *custom_ie = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + data_ptr = respbuf + (strlen(CMD_MARVELL) + strlen(PRIV_CMD_CUSTOMIE)); + + custom_ie = (mlan_ds_misc_custom_ie *)data_ptr; + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (ioctl_req == NULL) { + ret = -ENOMEM; + goto done; + } + + misc = (mlan_ds_misc_cfg *)ioctl_req->pbuf; + misc->sub_command = MLAN_OID_MISC_CUSTOM_IE; + ioctl_req->req_id = MLAN_IOCTL_MISC_CFG; + if ((custom_ie->len == 0)||(custom_ie->len == + sizeof(custom_ie->ie_data_list[0]. + ie_index))) + ioctl_req->action = MLAN_ACT_GET; + else + ioctl_req->action = MLAN_ACT_SET; + + memcpy(&misc->param.cust_ie, custom_ie, sizeof(mlan_ds_misc_custom_ie)); + + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + custom_ie = (mlan_ds_misc_custom_ie *)data_ptr; + memcpy(custom_ie, &misc->param.cust_ie, sizeof(mlan_ds_misc_custom_ie)); + ret = sizeof(mlan_ds_misc_custom_ie); + if (ioctl_req->status_code == MLAN_ERROR_IOCTL_FAIL) { + /* send a separate error code to indicate error from driver */ + ret = EFAULT; + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get Band and Adhoc-band setting + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int +woal_setget_priv_bandcfg(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int ret = 0; + unsigned int i; + int data[4]; + int user_data_len = 0; + t_u32 infra_band = 0; + t_u32 adhoc_band = 0; + t_u32 adhoc_channel = 0; + t_u32 adhoc_chan_bandwidth = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_radio_cfg *radio_cfg = NULL; + mlan_ds_band_cfg *band_cfg = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (strlen(respbuf) == (strlen(CMD_MARVELL) + strlen(PRIV_CMD_BANDCFG))) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + memset((char *)data, 0, sizeof(data)); + parse_arguments(respbuf + strlen(CMD_MARVELL) + + strlen(PRIV_CMD_BANDCFG), data, + ARRAY_SIZE(data), &user_data_len); + } + + if (sizeof(int) * user_data_len > sizeof(data)) { + PRINTM(MERROR, "Too many arguments\n"); + LEAVE(); + return -EINVAL; + } + + if (user_data_len > 0) { + if (priv->media_connected == MTRUE) { + LEAVE(); + return -EOPNOTSUPP; + } + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_radio_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto error; + } + radio_cfg = (mlan_ds_radio_cfg *)req->pbuf; + radio_cfg->sub_command = MLAN_OID_BAND_CFG; + req->req_id = MLAN_IOCTL_RADIO_CFG; + + if (user_data_len == 0) { + /* Get config_bands, adhoc_start_band and adhoc_channel values + * from MLAN + */ + req->action = MLAN_ACT_GET; + } else { + /* To support only */ + infra_band = data[0]; + for (i = 0; i < sizeof(SupportedInfraBand); i++) + if (infra_band == SupportedInfraBand[i]) + break; + if (i == sizeof(SupportedInfraBand)) { + ret = -EINVAL; + goto error; + } + + /* Set Adhoc band */ + if (user_data_len >= 2) { + adhoc_band = data[1]; + for (i = 0; i < sizeof(SupportedAdhocBand); i++) + if (adhoc_band == SupportedAdhocBand[i]) + break; + if (i == sizeof(SupportedAdhocBand)) { + ret = -EINVAL; + goto error; + } + } + + /* Set Adhoc channel */ + if (user_data_len >= 3) { + adhoc_channel = data[2]; + if (adhoc_channel == 0) { + /* Check if specified adhoc channel is non-zero */ + ret = -EINVAL; + goto error; + } + } + if (user_data_len == 4) { + if (!(adhoc_band & (BAND_GN | BAND_AN))) { + PRINTM(MERROR, + "11n is not enabled for adhoc, can not set HT/VHT channel bandwidth\n"); + ret = -EINVAL; + goto error; + } + adhoc_chan_bandwidth = data[3]; + /* sanity test */ + if ((adhoc_chan_bandwidth != CHANNEL_BW_20MHZ) && + (adhoc_chan_bandwidth != CHANNEL_BW_40MHZ_ABOVE) && + (adhoc_chan_bandwidth != CHANNEL_BW_40MHZ_BELOW) + ) { + PRINTM(MERROR, + "Invalid secondary channel bandwidth, only allowed 0, 1, 3 or 4\n"); + ret = -EINVAL; + goto error; + } + + } + /* Set config_bands and adhoc_start_band values to MLAN */ + req->action = MLAN_ACT_SET; + radio_cfg->param.band_cfg.config_bands = infra_band; + radio_cfg->param.band_cfg.adhoc_start_band = adhoc_band; + radio_cfg->param.band_cfg.adhoc_channel = adhoc_channel; + radio_cfg->param.band_cfg.adhoc_chan_bandwidth = + adhoc_chan_bandwidth; + } + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto error; + } + + band_cfg = (mlan_ds_band_cfg *)respbuf; + + memcpy(band_cfg, &radio_cfg->param.band_cfg, sizeof(mlan_ds_band_cfg)); + + ret = sizeof(mlan_ds_band_cfg); + +error: + if (status != MLAN_STATUS_PENDING) + kfree(req); + + LEAVE(); + return ret; +} + +/** + * @brief Set/Get 11n configurations + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int +woal_setget_priv_httxcfg(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + t_u32 data[2]; + mlan_ioctl_req *req = NULL; + mlan_ds_11n_cfg *cfg_11n = NULL; + int ret = 0; + int user_data_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (strlen(respbuf) == (strlen(CMD_MARVELL) + strlen(PRIV_CMD_HTTXCFG))) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + memset((char *)data, 0, sizeof(data)); + parse_arguments(respbuf + strlen(CMD_MARVELL) + + strlen(PRIV_CMD_HTTXCFG), data, + ARRAY_SIZE(data), &user_data_len); + } + + if (user_data_len > 2) { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + cfg_11n = (mlan_ds_11n_cfg *)req->pbuf; + cfg_11n->sub_command = MLAN_OID_11N_CFG_TX; + req->req_id = MLAN_IOCTL_11N_CFG; + + if (user_data_len == 0) { + /* Get 11n tx parameters from MLAN */ + req->action = MLAN_ACT_GET; + cfg_11n->param.tx_cfg.misc_cfg = BAND_SELECT_BG; + } else { + cfg_11n->param.tx_cfg.httxcap = data[0]; + PRINTM(MINFO, "SET: httxcap:0x%x\n", data[0]); + cfg_11n->param.tx_cfg.misc_cfg = BAND_SELECT_BOTH; + if (user_data_len == 2) { + if (data[1] != BAND_SELECT_BG && + data[1] != BAND_SELECT_A && + data[1] != BAND_SELECT_BOTH) { + PRINTM(MERROR, "Invalid band selection\n"); + ret = -EINVAL; + goto done; + } + cfg_11n->param.tx_cfg.misc_cfg = data[1]; + PRINTM(MINFO, "SET: httxcap band:0x%x\n", data[1]); + } + /* Update 11n tx parameters in MLAN */ + req->action = MLAN_ACT_SET; + } + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + data[0] = cfg_11n->param.tx_cfg.httxcap; + PRINTM(MINFO, "GET: httxcap:0x%x\n", data[0]); + + if (req->action == MLAN_ACT_GET) { + cfg_11n->param.tx_cfg.httxcap = 0; + cfg_11n->param.tx_cfg.misc_cfg = BAND_SELECT_A; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + data[1] = cfg_11n->param.tx_cfg.httxcap; + PRINTM(MINFO, "GET: httxcap for 5GHz:0x%x\n", data[1]); + } + + memcpy(respbuf, data, sizeof(data)); + ret = sizeof(data); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get 11n capability information + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int +woal_setget_priv_htcapinfo(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int data[2]; + mlan_ioctl_req *req = NULL; + mlan_ds_11n_cfg *cfg_11n = NULL; + woal_ht_cap_info *ht_cap = NULL; + int ret = 0; + int user_data_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (strlen(respbuf) == + (strlen(CMD_MARVELL) + strlen(PRIV_CMD_HTCAPINFO))) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + memset((char *)data, 0, sizeof(data)); + parse_arguments(respbuf + strlen(CMD_MARVELL) + + strlen(PRIV_CMD_HTCAPINFO), data, + ARRAY_SIZE(data), &user_data_len); + } + + if (user_data_len > 2) { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + cfg_11n = (mlan_ds_11n_cfg *)req->pbuf; + cfg_11n->sub_command = MLAN_OID_11N_HTCAP_CFG; + req->req_id = MLAN_IOCTL_11N_CFG; + + if (user_data_len == 0) { + /* Get 11n tx parameters from MLAN */ + req->action = MLAN_ACT_GET; + cfg_11n->param.htcap_cfg.misc_cfg = BAND_SELECT_BG; + } else { + cfg_11n->param.htcap_cfg.htcap = data[0]; + PRINTM(MINFO, "SET: htcapinfo:0x%x\n", data[0]); + cfg_11n->param.htcap_cfg.misc_cfg = BAND_SELECT_BOTH; + if (user_data_len == 2) { + if (data[1] != BAND_SELECT_BG && + data[1] != BAND_SELECT_A && + data[1] != BAND_SELECT_BOTH) { + PRINTM(MERROR, "Invalid band selection\n"); + ret = -EINVAL; + goto done; + } + cfg_11n->param.htcap_cfg.misc_cfg = data[1]; + PRINTM(MINFO, "SET: htcapinfo band:0x%x\n", data[1]); + } + /* Update 11n tx parameters in MLAN */ + req->action = MLAN_ACT_SET; + } + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + data[0] = cfg_11n->param.htcap_cfg.htcap; + PRINTM(MINFO, "GET: htcapinfo for 2.4GHz:0x%x\n", data[0]); + + if (req->action == MLAN_ACT_GET) { + cfg_11n->param.htcap_cfg.htcap = 0; + cfg_11n->param.htcap_cfg.misc_cfg = BAND_SELECT_A; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + data[1] = cfg_11n->param.htcap_cfg.htcap; + PRINTM(MINFO, "GET: htcapinfo for 5GHz:0x%x\n", data[1]); + } + + ht_cap = (woal_ht_cap_info *)respbuf; + ht_cap->ht_cap_info_bg = data[0]; + ht_cap->ht_cap_info_a = data[1]; + ret = sizeof(woal_ht_cap_info); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get add BA parameters + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int +woal_setget_priv_addbapara(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int data[5]; + mlan_ioctl_req *req = NULL; + mlan_ds_11n_cfg *cfg_11n = NULL; + woal_addba *addba = NULL; + int ret = 0; + int user_data_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (strlen(respbuf) == + (strlen(CMD_MARVELL) + strlen(PRIV_CMD_ADDBAPARA))) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + memset((char *)data, 0, sizeof(data)); + parse_arguments(respbuf + strlen(CMD_MARVELL) + + strlen(PRIV_CMD_ADDBAPARA), data, + ARRAY_SIZE(data), &user_data_len); + + if (user_data_len != ARRAY_SIZE(data)) { + PRINTM(MERROR, "Invalid number of arguments\n"); + ret = -EINVAL; + goto done; + } + if (data[0] < 0 || data[0] > MLAN_DEFAULT_BLOCK_ACK_TIMEOUT) { + PRINTM(MERROR, "Incorrect addba timeout value.\n"); + ret = -EFAULT; + goto done; + } + if (data[1] <= 0 || data[1] > MLAN_AMPDU_MAX_TXWINSIZE || + data[2] <= 0 || data[2] > MLAN_AMPDU_MAX_RXWINSIZE) { + PRINTM(MERROR, "Incorrect Tx/Rx window size.\n"); + ret = -EFAULT; + goto done; + } + if (data[3] < 0 || data[3] > 1 || data[4] < 0 || data[4] > 1) { + PRINTM(MERROR, "Incorrect Tx/Rx amsdu.\n"); + ret = -EFAULT; + goto done; + } + } + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + cfg_11n = (mlan_ds_11n_cfg *)req->pbuf; + cfg_11n->sub_command = MLAN_OID_11N_CFG_ADDBA_PARAM; + req->req_id = MLAN_IOCTL_11N_CFG; + + if (user_data_len == 0) { + /* Get add BA parameters from MLAN */ + req->action = MLAN_ACT_GET; + } else { + cfg_11n->param.addba_param.timeout = data[0]; + cfg_11n->param.addba_param.txwinsize = data[1]; + cfg_11n->param.addba_param.rxwinsize = data[2]; + cfg_11n->param.addba_param.txamsdu = data[3]; + cfg_11n->param.addba_param.rxamsdu = data[4]; + PRINTM(MINFO, + "SET: timeout:%d txwinsize:%d rxwinsize:%d txamsdu=%d rxamsdu=%d\n", + data[0], data[1], data[2], data[3], data[4]); + /* Update add BA parameters in MLAN */ + req->action = MLAN_ACT_SET; + } + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + addba = (woal_addba *)respbuf; + + addba->time_out = cfg_11n->param.addba_param.timeout; + addba->tx_win_size = cfg_11n->param.addba_param.txwinsize; + addba->rx_win_size = cfg_11n->param.addba_param.rxwinsize; + addba->tx_amsdu = cfg_11n->param.addba_param.txamsdu; + addba->rx_amsdu = cfg_11n->param.addba_param.rxamsdu; + PRINTM(MINFO, + "GET: timeout:%d txwinsize:%d rxwinsize:%d txamsdu=%d, rxamsdu=%d\n", + addba->time_out, addba->tx_win_size, addba->rx_win_size, + addba->tx_amsdu, addba->rx_amsdu); + + ret = sizeof(woal_addba); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; + +} + +/** + * @brief Delete selective BA based on parameters + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int +woal_priv_delba(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + t_u32 data[2] = { 0xFF, 0xFF }; + mlan_ioctl_req *req = NULL; + mlan_ds_11n_cfg *cfg_11n = NULL; + mlan_ds_11n_delba *del_ba = NULL; + int ret = 0; + int user_data_len = 0; + int header_len = 0; + t_u8 *mac_pos = NULL; + t_u8 peer_mac[ETH_ALEN] = { 0 }; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_DELBA); + + if (strlen(respbuf) == header_len) { + /* Incorrect number of arguments */ + PRINTM(MERROR, "%d: Invalid arguments\n", __LINE__); + ret = -EINVAL; + goto done; + } + + mac_pos = strstr(respbuf + header_len, " "); + if (mac_pos) + mac_pos = strstr(mac_pos + 1, " "); + if (mac_pos) { +#define MAC_STRING_LENGTH 17 + if (strlen(mac_pos + 1) != MAC_STRING_LENGTH) { + PRINTM(MERROR, "%d: Invalid arguments\n", __LINE__); + ret = -EINVAL; + goto done; + } + woal_mac2u8(peer_mac, mac_pos + 1); + *mac_pos = '\0'; + } + + parse_arguments(respbuf + header_len, data, ARRAY_SIZE(data), + &user_data_len); + + if (mac_pos) + user_data_len++; + + if (user_data_len > 3 || + (!(data[0] & (DELBA_TX | DELBA_RX))) || + (data[1] != DELBA_ALL_TIDS && !(data[1] <= 7))) { + /* Incorrect number of arguments */ + PRINTM(MERROR, "%d: Invalid arguments\n", __LINE__); + ret = -EINVAL; + goto done; + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + cfg_11n = (mlan_ds_11n_cfg *)req->pbuf; + req->req_id = MLAN_IOCTL_11N_CFG; + cfg_11n->sub_command = MLAN_OID_11N_CFG_DELBA; + + del_ba = &cfg_11n->param.del_ba; + memset(del_ba, 0, sizeof(mlan_ds_11n_delba)); + del_ba->direction = (t_u8)data[0]; + del_ba->tid = DELBA_ALL_TIDS; + if (user_data_len > 1) + del_ba->tid = (t_u8)data[1]; + if (user_data_len > 2) + memcpy(del_ba->peer_mac_addr, peer_mac, ETH_ALEN); + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + ret = sprintf(respbuf, "OK. BA deleted successfully.\n") + 1; + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get the reject addba requst conditions + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int +woal_priv_rejectaddbareq(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + t_u32 data[1]; + mlan_ioctl_req *req = NULL; + mlan_ds_11n_cfg *cfg_11n = NULL; + int ret = 0; + int user_data_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (strlen(respbuf) == + (strlen(CMD_MARVELL) + strlen(PRIV_CMD_REJECTADDBAREQ))) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + memset((char *)data, 0, sizeof(data)); + parse_arguments(respbuf + strlen(CMD_MARVELL) + + strlen(PRIV_CMD_REJECTADDBAREQ), data, + ARRAY_SIZE(data), &user_data_len); + } + + if (user_data_len > 1) { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + cfg_11n = (mlan_ds_11n_cfg *)req->pbuf; + cfg_11n->sub_command = MLAN_OID_11N_CFG_REJECT_ADDBA_REQ; + req->req_id = MLAN_IOCTL_11N_CFG; + + if (user_data_len == 0) { + /* Get the reject addba req conditions */ + req->action = MLAN_ACT_GET; + } else { + /* Set the reject addba req conditions */ + cfg_11n->param.reject_addba_req.conditions = data[0]; + req->action = MLAN_ACT_SET; + } + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + if (req->action == MLAN_ACT_GET) { + sprintf(respbuf, "0x%x", + cfg_11n->param.reject_addba_req.conditions); + ret = strlen(respbuf) + 1; + } else { + ret = sprintf(respbuf, "OK\n") + 1; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; + +} + +/** + * @brief Set/Get the addba reject setting + * + * @param priv A pointer to moal_private structure + * @param action Action set or get + * @param addba_reject A pointer to addba_reject array. + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail + */ +mlan_status +woal_ioctl_addba_reject(moal_private *priv, t_u32 action, t_u8 *addba_reject) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_11n_cfg *cfg_11n = NULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + cfg_11n = (mlan_ds_11n_cfg *)req->pbuf; + cfg_11n->sub_command = MLAN_OID_11N_CFG_ADDBA_REJECT; + req->req_id = MLAN_IOCTL_11N_CFG; + + req->action = action; + if (action == MLAN_ACT_SET) + memcpy(cfg_11n->param.addba_reject, addba_reject, + sizeof(cfg_11n->param.addba_reject)); + /* Send IOCTL request to MLAN */ + ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (ret != MLAN_STATUS_SUCCESS) + goto done; + if (action == MLAN_ACT_GET) + memcpy(addba_reject, cfg_11n->param.addba_reject, + sizeof(cfg_11n->param.addba_reject)); +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get addba prio_tbl + * + * @param priv A pointer to moal_private structure + * @param action Action set or get + * @param aggr_prio_tbl A pointer to mlan_ds_11n_aggr_prio_tbl. + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail + */ +mlan_status +woal_ioctl_aggr_prio_tbl(moal_private *priv, t_u32 action, + mlan_ds_11n_aggr_prio_tbl *aggr_prio_tbl) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_11n_cfg *cfg_11n = NULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + cfg_11n = (mlan_ds_11n_cfg *)req->pbuf; + cfg_11n->sub_command = MLAN_OID_11N_CFG_AGGR_PRIO_TBL; + req->req_id = MLAN_IOCTL_11N_CFG; + + req->action = action; + if (action == MLAN_ACT_SET) + memcpy(&cfg_11n->param.aggr_prio_tbl, aggr_prio_tbl, + sizeof(mlan_ds_11n_aggr_prio_tbl)); + /* Send IOCTL request to MLAN */ + ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (ret != MLAN_STATUS_SUCCESS) + goto done; + if (action == MLAN_ACT_GET) + memcpy(aggr_prio_tbl, &cfg_11n->param.aggr_prio_tbl, + sizeof(mlan_ds_11n_aggr_prio_tbl)); +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; + +} + +/** + * @brief Set/Get addba_param + * + * @param priv A pointer to moal_private structure + * @param action Action set or get + * @param addba_param A pointer to mlan_ds_11n_addba_param. + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail + */ +mlan_status +woal_ioctl_addba_param(moal_private *priv, t_u32 action, + mlan_ds_11n_addba_param *addba_param) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_11n_cfg *cfg_11n = NULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + cfg_11n = (mlan_ds_11n_cfg *)req->pbuf; + cfg_11n->sub_command = MLAN_OID_11N_CFG_ADDBA_PARAM; + req->req_id = MLAN_IOCTL_11N_CFG; + + req->action = action; + if (action == MLAN_ACT_SET) + memcpy(&cfg_11n->param.addba_param, addba_param, + sizeof(mlan_ds_11n_addba_param)); + /* Send IOCTL request to MLAN */ + ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (ret != MLAN_STATUS_SUCCESS) + goto done; + if (action == MLAN_ACT_GET) + memcpy(addba_param, &cfg_11n->param.addba_param, + sizeof(mlan_ds_11n_addba_param)); +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; + +} + +/** + * @brief Configuring rx block-ack window size + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return 0 --success, otherwise failure + */ +int +woal_set_rx_ba_winsize(moal_private *priv, t_u8 *respbuf, int respbuflen) +{ + int data[2]; + t_u8 addba_reject[MAX_NUM_TID]; + mlan_ds_11n_addba_param addba_param; + int ret = 0; + int user_data_len = 0; + + ENTER(); + + memset((char *)data, 0, sizeof(data)); + if (respbuf && strlen(respbuf) > 0) + parse_arguments(respbuf, data, ARRAY_SIZE(data), + &user_data_len); + + if (user_data_len != 2) { + PRINTM(MERROR, "Invalid arguments for ba_winsize command\n"); + ret = -EINVAL; + goto done; + } + if (data[0] > 7 || data[0] < 0) { + PRINTM(MERROR, "Invalid tid %d\n", data[0]); + ret = -EINVAL; + goto done; + } + if (data[1] < 0) { + PRINTM(MERROR, "Invalid winsize %d\n", data[1]); + ret = -EINVAL; + goto done; + } + memset(addba_reject, 0, sizeof(addba_reject)); + if (MLAN_STATUS_SUCCESS != + woal_ioctl_addba_reject(priv, MLAN_ACT_GET, addba_reject)) { + ret = -EFAULT; + goto done; + } + /* disable tx ba */ + if (data[1] == 0) { + addba_reject[data[0]] = MTRUE; + if (MLAN_STATUS_SUCCESS != + woal_ioctl_addba_reject(priv, MLAN_ACT_SET, addba_reject)) + ret = -EFAULT; + } else { + if (addba_reject[data[0]] == MTRUE) { + addba_reject[data[0]] = MFALSE; + if (MLAN_STATUS_SUCCESS != + woal_ioctl_addba_reject(priv, MLAN_ACT_SET, + addba_reject)) { + ret = -EFAULT; + goto done; + } + } + memset(&addba_param, 0, sizeof(addba_param)); + if (MLAN_STATUS_SUCCESS != + woal_ioctl_addba_param(priv, MLAN_ACT_GET, &addba_param)) { + ret = -EFAULT; + goto done; + } + if (data[1] != addba_param.rxwinsize) { + addba_param.rxwinsize = data[1]; + if (MLAN_STATUS_SUCCESS != + woal_ioctl_addba_param(priv, MLAN_ACT_SET, + &addba_param)) + ret = -EFAULT; + } + + } +done: + LEAVE(); + return ret; + +} + +/** + * @brief Configuring trx block-ack window size + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return 0 --success, otherwise failure + */ +int +woal_set_tx_ba_winsize(moal_private *priv, t_u8 *respbuf, int respbuflen) +{ + int data[2]; + mlan_ds_11n_aggr_prio_tbl aggr_prio_tbl; + mlan_ds_11n_addba_param addba_param; + t_u8 tos_to_tid_inv[] = { + 0x02, 0x00, 0x01, 0x03, + 0x04, 0x05, 0x06, 0x07 + }; + int ret = 0; + int user_data_len = 0; + + ENTER(); + + memset((char *)data, 0, sizeof(data)); + if (respbuf && strlen(respbuf) > 0) + parse_arguments(respbuf, data, ARRAY_SIZE(data), + &user_data_len); + + if (user_data_len != 2) { + PRINTM(MERROR, "Invalid arguments for ba_winsize command\n"); + ret = -EINVAL; + goto done; + } + if (data[0] > 7 || data[0] < 0) { + PRINTM(MERROR, "Invalid tid %d\n", data[0]); + ret = -EINVAL; + goto done; + } + if (data[1] < 0) { + PRINTM(MERROR, "Invalid winsize %d\n", data[1]); + ret = -EINVAL; + goto done; + } + memset(&aggr_prio_tbl, 0, sizeof(aggr_prio_tbl)); + if (MLAN_STATUS_SUCCESS != + woal_ioctl_aggr_prio_tbl(priv, MLAN_ACT_GET, &aggr_prio_tbl)) { + ret = -EFAULT; + goto done; + } + /* disable tx ba */ + if (data[1] == 0) { + if (aggr_prio_tbl.ampdu[data[0]] != 0xff) { + aggr_prio_tbl.ampdu[data[0]] = 0xff; + if (MLAN_STATUS_SUCCESS != + woal_ioctl_aggr_prio_tbl(priv, MLAN_ACT_SET, + &aggr_prio_tbl)) + ret = -EFAULT; + } + } else { + if (aggr_prio_tbl.ampdu[data[0]] == 0xff) { + aggr_prio_tbl.ampdu[data[0]] = tos_to_tid_inv[data[0]]; + if (MLAN_STATUS_SUCCESS != + woal_ioctl_aggr_prio_tbl(priv, MLAN_ACT_SET, + &aggr_prio_tbl)) { + ret = -EFAULT; + goto done; + } + } + memset(&addba_param, 0, sizeof(addba_param)); + if (MLAN_STATUS_SUCCESS != + woal_ioctl_addba_param(priv, MLAN_ACT_GET, &addba_param)) { + ret = -EFAULT; + goto done; + } + if (data[1] != addba_param.txwinsize) { + addba_param.txwinsize = data[1]; + if (MLAN_STATUS_SUCCESS != + woal_ioctl_addba_param(priv, MLAN_ACT_SET, + &addba_param)) + ret = -EFAULT; + } + + } +done: + LEAVE(); + return ret; + +} + +/** + * @brief Set/Get aggregation priority table configurations + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int +woal_setget_priv_aggrpriotbl(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int data[MAX_NUM_TID * 2], i, j; + mlan_ioctl_req *req = NULL; + mlan_ds_11n_cfg *cfg_11n = NULL; + int ret = 0; + int user_data_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (strlen(respbuf) == + (strlen(CMD_MARVELL) + strlen(PRIV_CMD_AGGRPRIOTBL))) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + memset((char *)data, 0, sizeof(data)); + parse_arguments(respbuf + strlen(CMD_MARVELL) + + strlen(PRIV_CMD_AGGRPRIOTBL), data, + ARRAY_SIZE(data), &user_data_len); + + if (user_data_len != ARRAY_SIZE(data)) { + PRINTM(MERROR, "Invalid number of arguments\n"); + ret = -EINVAL; + goto done; + } + for (i = 0, j = 0; i < user_data_len; i = i + 2, ++j) { + if ((data[i] > 7 && data[i] != 0xff) || + (data[i + 1] > 7 && data[i + 1] != 0xff)) { + PRINTM(MERROR, + "Invalid priority, valid value 0-7 or 0xff.\n"); + ret = -EFAULT; + goto done; + } + } + } + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + cfg_11n = (mlan_ds_11n_cfg *)req->pbuf; + cfg_11n->sub_command = MLAN_OID_11N_CFG_AGGR_PRIO_TBL; + req->req_id = MLAN_IOCTL_11N_CFG; + + if (user_data_len == 0) { + /* Get aggr priority table from MLAN */ + req->action = MLAN_ACT_GET; + } else { + for (i = 0, j = 0; i < user_data_len; i = i + 2, ++j) { + cfg_11n->param.aggr_prio_tbl.ampdu[j] = data[i]; + cfg_11n->param.aggr_prio_tbl.amsdu[j] = data[i + 1]; + } + /* Update aggr priority table in MLAN */ + req->action = MLAN_ACT_SET; + } + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + for (i = 0, j = 0; i < (MAX_NUM_TID * 2); i = i + 2, ++j) { + respbuf[i] = cfg_11n->param.aggr_prio_tbl.ampdu[j]; + respbuf[i + 1] = cfg_11n->param.aggr_prio_tbl.amsdu[j]; + } + + ret = sizeof(data); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; + +} + +/** + * @brief Set/Get Add BA reject configurations + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int +woal_setget_priv_addbareject(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int data[MAX_NUM_TID], i; + mlan_ioctl_req *req = NULL; + mlan_ds_11n_cfg *cfg_11n = NULL; + int ret = 0; + int user_data_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (strlen(respbuf) == + (strlen(CMD_MARVELL) + strlen(PRIV_CMD_ADDBAREJECT))) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + memset((char *)data, 0, sizeof(data)); + parse_arguments(respbuf + strlen(CMD_MARVELL) + + strlen(PRIV_CMD_ADDBAREJECT), data, + ARRAY_SIZE(data), &user_data_len); + + if (user_data_len != ARRAY_SIZE(data)) { + PRINTM(MERROR, "Invalid number of arguments\n"); + ret = -EINVAL; + goto done; + } + for (i = 0; i < user_data_len; i++) { + if (data[i] != 0 && data[i] != 1) { + PRINTM(MERROR, + "addba reject only takes argument as 0 or 1\n"); + ret = -EFAULT; + goto done; + } + } + } + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + cfg_11n = (mlan_ds_11n_cfg *)req->pbuf; + cfg_11n->sub_command = MLAN_OID_11N_CFG_ADDBA_REJECT; + req->req_id = MLAN_IOCTL_11N_CFG; + + if (user_data_len == 0) { + /* Get add BA reject configuration from MLAN */ + req->action = MLAN_ACT_GET; + } else { + for (i = 0; i < user_data_len; i++) + cfg_11n->param.addba_reject[i] = data[i]; + /* Update add BA reject configuration in MLAN */ + req->action = MLAN_ACT_SET; + } + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + for (i = 0; i < MAX_NUM_TID; i++) + respbuf[i] = cfg_11n->param.addba_reject[i]; + + ret = sizeof(data); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; + +} + +/** + * @brief Set/Get 11AC configurations + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int +woal_get_priv_datarate(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_rate *rate = NULL; + mlan_data_rate *data_rate = NULL; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_rate)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + rate = (mlan_ds_rate *)req->pbuf; + rate->sub_command = MLAN_OID_GET_DATA_RATE; + req->req_id = MLAN_IOCTL_RATE; + req->action = MLAN_ACT_GET; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + data_rate = (mlan_data_rate *)respbuf; + + memcpy(data_rate, &rate->param.data_rate, sizeof(mlan_data_rate)); + + ret = sizeof(mlan_data_rate); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; + +} + +/** + * @brief Set/Get tx rate configurations + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int +woal_setget_priv_txratecfg(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + t_u32 data[3]; + mlan_ioctl_req *req = NULL; + mlan_ds_rate *rate = NULL; + woal_tx_rate_cfg *ratecfg = NULL; + int ret = 0; + int user_data_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (strlen(respbuf) == + (strlen(CMD_MARVELL) + strlen(PRIV_CMD_TXRATECFG))) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + memset((char *)data, 0, sizeof(data)); + parse_arguments(respbuf + strlen(CMD_MARVELL) + + strlen(PRIV_CMD_TXRATECFG), data, + ARRAY_SIZE(data), &user_data_len); + } + + if (user_data_len >= 4) { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_rate)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + req->req_id = MLAN_IOCTL_RATE; + rate = (mlan_ds_rate *)req->pbuf; + rate->sub_command = MLAN_OID_RATE_CFG; + rate->param.rate_cfg.rate_type = MLAN_RATE_INDEX; + + if (user_data_len == 0) { + /* Get operation */ + req->action = MLAN_ACT_GET; + } else { + /* Set operation */ + req->action = MLAN_ACT_SET; + /* format */ + if ((data[0] != AUTO_RATE) && (data[0] >= 3)) { + PRINTM(MERROR, "Invalid format selection\n"); + ret = -EINVAL; + goto done; + } + if (data[0] == AUTO_RATE) { + /* auto */ + rate->param.rate_cfg.is_rate_auto = 1; + } else { + /* fixed rate */ + PRINTM(MINFO, "SET: txratefg format: 0x%x\n", data[0]); + if ((data[0] != AUTO_RATE) && + (data[0] > MLAN_RATE_FORMAT_HT) + ) { + PRINTM(MERROR, "Invalid format selection\n"); + ret = -EINVAL; + goto done; + } + rate->param.rate_cfg.rate_format = data[0]; + } + + if ((user_data_len >= 2) && (data[0] != AUTO_RATE)) { + PRINTM(MINFO, "SET: txratefg index: 0x%x\n", data[1]); + /* sanity check */ + if (((data[0] == MLAN_RATE_FORMAT_LG) && + (data[1] > MLAN_RATE_INDEX_OFDM7)) + || ((data[0] == MLAN_RATE_FORMAT_HT) && + (data[1] != 32) && (data[1] > 7)) + ) { + PRINTM(MERROR, "Invalid index selection\n"); + ret = -EINVAL; + goto done; + } + + PRINTM(MINFO, "SET: txratefg index: 0x%x\n", data[1]); + rate->param.rate_cfg.rate = data[1]; + + } + + } + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + ratecfg = (woal_tx_rate_cfg *)respbuf; + if (rate->param.rate_cfg.is_rate_auto == MTRUE) { + ratecfg->rate_format = 0xFF; + } else { + /* fixed rate */ + ratecfg->rate_format = rate->param.rate_cfg.rate_format; + ratecfg->rate_index = rate->param.rate_cfg.rate; + } + + ret = sizeof(woal_tx_rate_cfg); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; + +} + +#if defined(STA_SUPPORT) || defined(UAP_SUPPORT) +/** + * @brief Get statistics information + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * @param stats A pointer to mlan_ds_get_stats structure + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail + */ +mlan_status +woal_get_stats_info(moal_private *priv, t_u8 wait_option, + mlan_ds_get_stats *stats) +{ + int ret = 0; + mlan_ds_get_info *info = NULL; + mlan_ioctl_req *req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + ENTER(); + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_get_info)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + info = (mlan_ds_get_info *)req->pbuf; + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) + info->sub_command = MLAN_OID_GET_STATS; + else if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) + info->sub_command = MLAN_OID_GET_UAP_STATS_LOG; + req->req_id = MLAN_IOCTL_GET_INFO; + req->action = MLAN_ACT_GET; + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, wait_option); + if (status == MLAN_STATUS_SUCCESS) { + if (stats) + memcpy(stats, &info->param.stats, + sizeof(mlan_ds_get_stats)); +#if defined(STA_WEXT) || defined(UAP_WEXT) + priv->w_stats.discard.fragment = info->param.stats.fcs_error; + priv->w_stats.discard.retries = info->param.stats.retry; + priv->w_stats.discard.misc = info->param.stats.ack_failure; +#endif + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return status; +} + +/** + * @brief Get wireless stats information + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int +woal_get_priv_getlog(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int ret = 0; + mlan_ds_get_stats *stats; + ENTER(); + + if (respbuflen < sizeof(*stats)) { + PRINTM(MERROR, "Get log: respbuflen (%d) too small!", + (int)respbuflen); + ret = -EFAULT; + goto done; + } + stats = (mlan_ds_get_stats *)respbuf; + if (MLAN_STATUS_SUCCESS != + woal_get_stats_info(priv, MOAL_IOCTL_WAIT, stats)) { + PRINTM(MERROR, "Get log: Failed to get stats info!"); + ret = -EFAULT; + goto done; + } + + if (priv->phandle->fw_getlog_enable) + ret = sizeof(mlan_ds_get_stats); + else + ret = sizeof(mlan_ds_get_stats_org); + +done: + LEAVE(); + return ret; +} +#endif + +/** + * @brief Set/Get esupplicant mode configurations + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int +woal_setget_priv_esuppmode(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + t_u32 data[3]; + mlan_ioctl_req *req = NULL; + mlan_ds_sec_cfg *sec = NULL; + woal_esuppmode_cfg *esupp_mode = NULL; + int ret = 0; + int user_data_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (strlen(respbuf) == + (strlen(CMD_MARVELL) + strlen(PRIV_CMD_ESUPPMODE))) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + memset((char *)data, 0, sizeof(data)); + parse_arguments(respbuf + strlen(CMD_MARVELL) + + strlen(PRIV_CMD_ESUPPMODE), data, + ARRAY_SIZE(data), &user_data_len); + } + + if (user_data_len >= 4 || user_data_len == 1 || user_data_len == 2) { + PRINTM(MERROR, "Invalid number of arguments\n"); + ret = -EINVAL; + goto done; + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + req->req_id = MLAN_IOCTL_SEC_CFG; + sec = (mlan_ds_sec_cfg *)req->pbuf; + sec->sub_command = MLAN_OID_SEC_CFG_ESUPP_MODE; + + if (user_data_len == 0) { + /* Get operation */ + req->action = MLAN_ACT_GET; + } else { + /* Set operation */ + req->action = MLAN_ACT_SET; + /* RSN mode */ + sec->param.esupp_mode.rsn_mode = data[0]; + /* Pairwise cipher */ + sec->param.esupp_mode.act_paircipher = (data[1] & 0xFF); + /* Group cipher */ + sec->param.esupp_mode.act_groupcipher = (data[2] & 0xFF); + } + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + esupp_mode = (woal_esuppmode_cfg *)respbuf; + esupp_mode->rsn_mode = + (t_u16)((sec->param.esupp_mode.rsn_mode) & 0xFFFF); + esupp_mode->pairwise_cipher = + (t_u8)((sec->param.esupp_mode.act_paircipher) & 0xFF); + esupp_mode->group_cipher = + (t_u8)((sec->param.esupp_mode.act_groupcipher) & 0xFF); + + ret = sizeof(woal_esuppmode_cfg); +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; + +} + +/** + * @brief Set/Get esupplicant passphrase configurations + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int +woal_setget_priv_passphrase(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_sec_cfg *sec = NULL; + int ret = 0, action = -1, i = 0; + char *begin, *end, *opt; + t_u16 len = 0; + t_u8 zero_mac[] = { 0, 0, 0, 0, 0, 0 }; + t_u8 *mac = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (strlen(respbuf) == + (strlen(CMD_MARVELL) + strlen(PRIV_CMD_PASSPHRASE))) { + PRINTM(MERROR, "No arguments provided\n"); + ret = -EINVAL; + goto done; + } + + /* Parse the buf to get the cmd_action */ + begin = respbuf + strlen(CMD_MARVELL) + strlen(PRIV_CMD_PASSPHRASE); + end = woal_strsep(&begin, ';', '/'); + if (end) + action = woal_atox(end); + if (action < 0 || action > 2 || end[1] != '\0') { + PRINTM(MERROR, "Invalid action argument %s\n", end); + ret = -EINVAL; + goto done; + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + req->req_id = MLAN_IOCTL_SEC_CFG; + sec = (mlan_ds_sec_cfg *)req->pbuf; + sec->sub_command = MLAN_OID_SEC_CFG_PASSPHRASE; + if (action == 0) + req->action = MLAN_ACT_GET; + else + req->action = MLAN_ACT_SET; + + while (begin) { + end = woal_strsep(&begin, ';', '/'); + opt = woal_strsep(&end, '=', '/'); + if (!opt || !end || !end[0]) { + PRINTM(MERROR, "Invalid option\n"); + ret = -EINVAL; + break; + } else if (!strnicmp(opt, "ssid", strlen(opt))) { + if (strlen(end) > MLAN_MAX_SSID_LENGTH) { + PRINTM(MERROR, + "SSID length exceeds max length\n"); + ret = -EFAULT; + break; + } + sec->param.passphrase.ssid.ssid_len = strlen(end); + strncpy((char *)sec->param.passphrase.ssid.ssid, end, + MIN(strlen(end), MLAN_MAX_SSID_LENGTH)); + PRINTM(MINFO, "ssid=%s, len=%d\n", + sec->param.passphrase.ssid.ssid, + (int)sec->param.passphrase.ssid.ssid_len); + } else if (!strnicmp(opt, "bssid", strlen(opt))) { + woal_mac2u8((t_u8 *)&sec->param.passphrase.bssid, end); + } else if (!strnicmp(opt, "psk", strlen(opt)) && + req->action == MLAN_ACT_SET) { + if (strlen(end) != MLAN_PMK_HEXSTR_LENGTH) { + PRINTM(MERROR, "Invalid PMK length\n"); + ret = -EINVAL; + break; + } + woal_ascii2hex((t_u8 *)(sec->param.passphrase.psk.pmk. + pmk), end, + MLAN_PMK_HEXSTR_LENGTH / 2); + sec->param.passphrase.psk_type = MLAN_PSK_PMK; + } else if (!strnicmp(opt, "passphrase", strlen(opt)) && + req->action == MLAN_ACT_SET) { + if (strlen(end) < MLAN_MIN_PASSPHRASE_LENGTH || + strlen(end) > MLAN_MAX_PASSPHRASE_LENGTH) { + PRINTM(MERROR, + "Invalid length for passphrase\n"); + ret = -EINVAL; + break; + } + sec->param.passphrase.psk_type = MLAN_PSK_PASSPHRASE; + memcpy(sec->param.passphrase.psk.passphrase.passphrase, + end, + sizeof(sec->param.passphrase.psk.passphrase. + passphrase)); + sec->param.passphrase.psk.passphrase.passphrase_len = + strlen(end); + PRINTM(MINFO, "passphrase=%s, len=%d\n", + sec->param.passphrase.psk.passphrase.passphrase, + (int)sec->param.passphrase.psk.passphrase. + passphrase_len); + } else { + PRINTM(MERROR, "Invalid option %s\n", opt); + ret = -EINVAL; + break; + } + } + if (ret) + goto done; + + if (action == 2) + sec->param.passphrase.psk_type = MLAN_PSK_CLEAR; + else if (action == 0) + sec->param.passphrase.psk_type = MLAN_PSK_QUERY; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + memset(respbuf, 0, respbuflen); + if (sec->param.passphrase.ssid.ssid_len) { + len += sprintf(respbuf + len, "ssid:"); + memcpy(respbuf + len, sec->param.passphrase.ssid.ssid, + sec->param.passphrase.ssid.ssid_len); + len += sec->param.passphrase.ssid.ssid_len; + len += sprintf(respbuf + len, " "); + } + if (memcmp(&sec->param.passphrase.bssid, zero_mac, sizeof(zero_mac))) { + mac = (t_u8 *)&sec->param.passphrase.bssid; + len += sprintf(respbuf + len, "bssid:"); + for (i = 0; i < ETH_ALEN - 1; ++i) + len += sprintf(respbuf + len, "%02x:", mac[i]); + len += sprintf(respbuf + len, "%02x ", mac[i]); + } + if (sec->param.passphrase.psk_type == MLAN_PSK_PMK) { + len += sprintf(respbuf + len, "psk:"); + for (i = 0; i < MLAN_MAX_KEY_LENGTH; ++i) + len += sprintf(respbuf + len, "%02x", + sec->param.passphrase.psk.pmk.pmk[i]); + len += sprintf(respbuf + len, "\n"); + } + if (sec->param.passphrase.psk_type == MLAN_PSK_PASSPHRASE) + len += sprintf(respbuf + len, "passphrase:%s\n", + sec->param.passphrase.psk.passphrase.passphrase); + + ret = len; +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; + +} + +/** + * @brief Deauthenticate + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int +woal_priv_deauth(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int ret = 0; + t_u8 mac[ETH_ALEN]; + + ENTER(); + + if (strlen(respbuf) > (strlen(CMD_MARVELL) + strlen(PRIV_CMD_DEAUTH))) { + /* Deauth mentioned BSSID */ + woal_mac2u8(mac, + respbuf + strlen(CMD_MARVELL) + + strlen(PRIV_CMD_DEAUTH)); + if (MLAN_STATUS_SUCCESS != + woal_disconnect(priv, MOAL_IOCTL_WAIT, mac, + DEF_DEAUTH_REASON_CODE)) { + ret = -EFAULT; + goto done; + } + } else { + if (MLAN_STATUS_SUCCESS != + woal_disconnect(priv, MOAL_IOCTL_WAIT, NULL, + DEF_DEAUTH_REASON_CODE)) + ret = -EFAULT; + } + +done: + LEAVE(); + return ret; +} + +#ifdef UAP_SUPPORT +/** + * @brief uap station deauth ioctl handler + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +static int +woal_priv_ap_deauth(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + t_u8 *data_ptr; + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_bss *bss = NULL; + mlan_deauth_param deauth_param; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + data_ptr = respbuf + (strlen(CMD_MARVELL) + strlen(PRIV_CMD_AP_DEAUTH)); + memset(&deauth_param, 0, sizeof(mlan_deauth_param)); + memcpy(&deauth_param, data_ptr, sizeof(mlan_deauth_param)); + + PRINTM(MIOCTL, "ioctl deauth station: " MACSTR ", reason=%d\n", + MAC2STR(deauth_param.mac_addr), deauth_param.reason_code); + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (ioctl_req == NULL) { + ret = -ENOMEM; + goto done; + } + bss = (mlan_ds_bss *)ioctl_req->pbuf; + + bss->sub_command = MLAN_OID_UAP_DEAUTH_STA; + ioctl_req->req_id = MLAN_IOCTL_BSS; + ioctl_req->action = MLAN_ACT_SET; + + memcpy(&bss->param.deauth_param, &deauth_param, + sizeof(mlan_deauth_param)); + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + memcpy(data_ptr, &ioctl_req->status_code, sizeof(t_u32)); + ret = sizeof(t_u32); +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + LEAVE(); + return ret; +} + +/** + * @brief uap get station list handler + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ + +static int +woal_priv_get_sta_list(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int ret = 0; + mlan_ds_get_info *info = NULL; + mlan_ds_sta_list *sta_list = NULL; + mlan_ioctl_req *ioctl_req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + /* Allocate an IOCTL request buffer */ + ioctl_req = + (mlan_ioctl_req *) + woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_get_info)); + if (ioctl_req == NULL) { + ret = -ENOMEM; + goto done; + } + + info = (mlan_ds_get_info *)ioctl_req->pbuf; + info->sub_command = MLAN_OID_UAP_STA_LIST; + ioctl_req->req_id = MLAN_IOCTL_GET_INFO; + ioctl_req->action = MLAN_ACT_GET; + + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + sta_list = + (mlan_ds_sta_list *)(respbuf + strlen(CMD_MARVELL) + + strlen(PRIV_CMD_GET_STA_LIST)); + memcpy(sta_list, &info->param.sta_list, sizeof(mlan_ds_sta_list)); + ret = sizeof(mlan_ds_sta_list); +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + LEAVE(); + return ret; +} + +/** + * @brief uap bss_config handler + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +static int +woal_priv_bss_config(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int ret = 0; + mlan_ds_bss *bss = NULL; + mlan_ioctl_req *ioctl_req = NULL; + t_u32 action = 0; + int offset = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + offset = strlen(CMD_MARVELL) + strlen(PRIV_CMD_BSS_CONFIG); + memcpy((u8 *)&action, respbuf + offset, sizeof(action)); + offset += sizeof(action); + + /* Allocate an IOCTL request buffer */ + ioctl_req = + (mlan_ioctl_req *) + woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (ioctl_req == NULL) { + ret = -ENOMEM; + goto done; + } + + bss = (mlan_ds_bss *)ioctl_req->pbuf; + bss->sub_command = MLAN_OID_UAP_BSS_CONFIG; + ioctl_req->req_id = MLAN_IOCTL_BSS; + if (action == 1) { + ioctl_req->action = MLAN_ACT_SET; + /* Get the BSS config from user */ + memcpy(&bss->param.bss_config, respbuf + offset, + sizeof(mlan_uap_bss_param)); + } else { + ioctl_req->action = MLAN_ACT_GET; + } + + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (ioctl_req->action == MLAN_ACT_GET) { + memcpy(respbuf + offset, &bss->param.bss_config, + sizeof(mlan_uap_bss_param)); + } + ret = sizeof(mlan_uap_bss_param); +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + LEAVE(); + return ret; +} +#endif + +#if defined(WIFI_DIRECT_SUPPORT) +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) +/** + * @brief Set/Get BSS role + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int +woal_priv_bssrole(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + t_u32 data[1]; + int ret = 0; + int user_data_len = 0; + t_u8 action = MLAN_ACT_GET; + + ENTER(); + + memset((char *)data, 0, sizeof(data)); + if (strlen(respbuf) == (strlen(CMD_MARVELL) + strlen(PRIV_CMD_BSSROLE))) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + parse_arguments(respbuf + strlen(CMD_MARVELL) + + strlen(PRIV_CMD_BSSROLE), data, + ARRAY_SIZE(data), &user_data_len); + } + + if (user_data_len >= 2) { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto error; + } + + if (user_data_len == 0) { + action = MLAN_ACT_GET; + } else { + if ((data[0] != MLAN_BSS_ROLE_STA && + data[0] != MLAN_BSS_ROLE_UAP) || + priv->bss_type != MLAN_BSS_TYPE_WIFIDIRECT) { + PRINTM(MWARN, "Invalid BSS role\n"); + ret = -EINVAL; + goto error; + } + if (data[0] == GET_BSS_ROLE(priv)) { + PRINTM(MWARN, "Already BSS is in desired role\n"); + goto done; + } + action = MLAN_ACT_SET; + /* Reset interface */ + woal_reset_intf(priv, MOAL_IOCTL_WAIT, MFALSE); + } + + if (MLAN_STATUS_SUCCESS != woal_bss_role_cfg(priv, + action, MOAL_IOCTL_WAIT, + (t_u8 *)data)) { + ret = -EFAULT; + goto error; + } + + if (user_data_len) { + /* Initialize private structures */ + woal_init_priv(priv, MOAL_IOCTL_WAIT); + /* Enable interfaces */ + netif_device_attach(priv->netdev); + woal_start_queue(priv->netdev); + } + +done: + memset(respbuf, 0, respbuflen); + respbuf[0] = (t_u8)data[0]; + ret = 1; + +error: + LEAVE(); + return ret; +} +#endif /* STA_SUPPORT && UAP_SUPPORT */ +#endif /* WIFI_DIRECT_SUPPORT && V14_FEATURE */ + +#ifdef STA_SUPPORT +/** + * @brief Set user scan + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int +woal_priv_setuserscan(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + wlan_user_scan_cfg scan_cfg; + int ret = 0; + + ENTER(); + + /* Create the scan_cfg structure */ + memset(&scan_cfg, 0, sizeof(scan_cfg)); + + /* We expect the scan_cfg structure to be passed in respbuf */ + memcpy((char *)&scan_cfg, + respbuf + strlen(CMD_MARVELL) + strlen(PRIV_CMD_SETUSERSCAN), + sizeof(wlan_user_scan_cfg)); + /* Call for scan */ + if (MLAN_STATUS_FAILURE == woal_do_scan(priv, &scan_cfg)) + ret = -EFAULT; + + LEAVE(); + return ret; +} + +/** + * @brief Retrieve the scan response/beacon table + * + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * @param scan_resp A pointer to mlan_scan_resp structure + * @param scan_start Argument + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +int +moal_ret_get_scan_table_ioctl(t_u8 *respbuf, t_u32 respbuflen, + mlan_scan_resp *scan_resp, t_u32 scan_start) +{ + pBSSDescriptor_t pbss_desc, scan_table; + wlan_ioctl_get_scan_table_info *prsp_info; + int ret_code; + int ret_len; + int space_left; + t_u8 *pcurrent; + t_u8 *pbuffer_end; + t_u32 num_scans_done; + + ENTER(); + + num_scans_done = 0; + ret_code = MLAN_STATUS_SUCCESS; + + prsp_info = (wlan_ioctl_get_scan_table_info *)respbuf; + pcurrent = (t_u8 *)prsp_info->scan_table_entry_buf; + + pbuffer_end = respbuf + respbuflen - 1; + space_left = pbuffer_end - pcurrent; + scan_table = (BSSDescriptor_t *)(scan_resp->pscan_table); + + PRINTM(MINFO, "GetScanTable: scan_start req = %d\n", scan_start); + PRINTM(MINFO, "GetScanTable: length avail = %d\n", respbuflen); + + if (!scan_start) { + PRINTM(MINFO, "GetScanTable: get current BSS Descriptor\n"); + + /* Use to get current association saved descriptor */ + pbss_desc = scan_table; + + ret_code = wlan_get_scan_table_ret_entry(pbss_desc, + &pcurrent, + &space_left); + + if (ret_code == MLAN_STATUS_SUCCESS) + num_scans_done = 1; + + } else { + scan_start--; + + while (space_left + && (scan_start + num_scans_done < + scan_resp->num_in_scan_table) + && (ret_code == MLAN_STATUS_SUCCESS)) { + + pbss_desc = + (scan_table + (scan_start + num_scans_done)); + + PRINTM(MINFO, + "GetScanTable: get current BSS Descriptor [%d]\n", + scan_start + num_scans_done); + + ret_code = wlan_get_scan_table_ret_entry(pbss_desc, + &pcurrent, + &space_left); + + if (ret_code == MLAN_STATUS_SUCCESS) + num_scans_done++; + + } + } + + prsp_info->scan_number = num_scans_done; + ret_len = pcurrent - respbuf; + + LEAVE(); + return ret_len; +} + +/** + * @brief Get scan table + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int +woal_priv_getscantable(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_scan *scan = NULL; + t_u32 scan_start; + mlan_status status = MLAN_STATUS_SUCCESS; + moal_handle *handle = priv->phandle; + + ENTER(); + + /* First make sure scanning is not in progress */ + if (handle->scan_pending_on_block == MTRUE) { + ret = -EAGAIN; + goto done; + } + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_scan)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + scan = (mlan_ds_scan *)req->pbuf; + req->req_id = MLAN_IOCTL_SCAN; + req->action = MLAN_ACT_GET; + + /* Get the whole command from user */ + memcpy(&scan_start, + respbuf + strlen(CMD_MARVELL) + strlen(PRIV_CMD_GETSCANTABLE), + sizeof(scan_start)); + if (scan_start) + scan->sub_command = MLAN_OID_SCAN_NORMAL; + else + scan->sub_command = MLAN_OID_SCAN_GET_CURRENT_BSS; + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status == MLAN_STATUS_SUCCESS) { + ret = moal_ret_get_scan_table_ioctl(respbuf, respbuflen, + &scan->param.scan_resp, + scan_start); + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Extended capabilities configuration + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int +woal_priv_extcapcfg(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int ret, header; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *cfg = NULL; + IEEEtypes_Header_t *ie; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (!respbuf) { + LEAVE(); + return 0; + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + cfg = (mlan_ds_misc_cfg *)req->pbuf; + cfg->sub_command = MLAN_OID_MISC_EXT_CAP_CFG; + req->req_id = MLAN_IOCTL_MISC_CFG; + header = strlen(CMD_MARVELL) + strlen(PRIV_CMD_EXTCAPCFG); + if (strlen(respbuf) == header) + /* GET operation */ + req->action = MLAN_ACT_GET; + else { + /* SET operation */ + ie = (IEEEtypes_Header_t *)(respbuf + header); + if (ie->len > sizeof(ExtCap_t)) { + PRINTM(MERROR, + "Extended Capability lenth is invalid\n"); + ret = -EFAULT; + goto done; + } + req->action = MLAN_ACT_SET; + memset(&cfg->param.ext_cap, 0, sizeof(ExtCap_t)); + memcpy(&cfg->param.ext_cap, ie + 1, ie->len); + } + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + memset(respbuf, 0, respbuflen); + ie = (IEEEtypes_Header_t *)respbuf; + ie->element_id = EXT_CAPABILITY; + ie->len = sizeof(ExtCap_t); + memcpy(ie + 1, &cfg->param.ext_cap, sizeof(ExtCap_t)); + + ret = sizeof(IEEEtypes_Header_t) + ie->len; + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} +#endif + +/** + * @brief Set/Get deep sleep mode configurations + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int +woal_priv_setgetdeepsleep(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + t_u32 data[2]; + int ret = 0; + int user_data_len = 0; + + ENTER(); + + if (strlen(respbuf) == + (strlen(CMD_MARVELL) + strlen(PRIV_CMD_DEEPSLEEP))) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + memset((char *)data, 0, sizeof(data)); + parse_arguments(respbuf + strlen(CMD_MARVELL) + + strlen(PRIV_CMD_DEEPSLEEP), data, + ARRAY_SIZE(data), &user_data_len); + } + + if (user_data_len >= 3) { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + + if (user_data_len == 0) { + if (MLAN_STATUS_SUCCESS != woal_get_deep_sleep(priv, data)) { + ret = -EFAULT; + goto done; + } + sprintf(respbuf, "%d %d", data[0], data[1]); + ret = strlen(respbuf) + 1; + } else { + if (data[0] == DEEP_SLEEP_OFF) { + PRINTM(MINFO, "Exit Deep Sleep Mode\n"); + ret = woal_set_deep_sleep(priv, MOAL_IOCTL_WAIT, MFALSE, + 0); + if (ret != MLAN_STATUS_SUCCESS) { + ret = -EINVAL; + goto done; + } + } else if (data[0] == DEEP_SLEEP_ON) { + PRINTM(MINFO, "Enter Deep Sleep Mode\n"); + if (user_data_len != 2) + data[1] = 0; + ret = woal_set_deep_sleep(priv, MOAL_IOCTL_WAIT, MTRUE, + data[1]); + if (ret != MLAN_STATUS_SUCCESS) { + ret = -EINVAL; + goto done; + } + } else { + PRINTM(MERROR, "Unknown option = %u\n", data[0]); + ret = -EINVAL; + goto done; + } + ret = sprintf(respbuf, "OK\n") + 1; + } + +done: + LEAVE(); + return ret; + +} + +/** + * @brief Set/Get IP address configurations + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int +woal_priv_setgetipaddr(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *misc = NULL; + int ret = 0, op_code = 0, data_length = 0, header = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (priv->bss_type != MLAN_BSS_TYPE_STA) { + PRINTM(MIOCTL, "Bss type[%d]: Not STA, ignore it\n", + priv->bss_type); + ret = sprintf(respbuf, "OK\n") + 1; + goto done; + } + + header = strlen(CMD_MARVELL) + strlen(PRIV_CMD_IPADDR); + data_length = strlen(respbuf) - header; + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + misc = (mlan_ds_misc_cfg *)req->pbuf; + + if (data_length < 1) { /* GET */ + req->action = MLAN_ACT_GET; + } else { + /* Make sure we have the operation argument */ + if (data_length > 2 && respbuf[header + 1] != ';') { + PRINTM(MERROR, + "No operation argument. Separate with ';'\n"); + ret = -EINVAL; + goto done; + } else { + respbuf[header + 1] = '\0'; + } + req->action = MLAN_ACT_SET; + + /* Only one IP is supported in current firmware */ + memset(misc->param.ipaddr_cfg.ip_addr[0], 0, IPADDR_LEN); + in4_pton(&respbuf[header + 2], + MIN((IPADDR_MAX_BUF - 3), (data_length - 2)), + misc->param.ipaddr_cfg.ip_addr[0], ' ', NULL); + misc->param.ipaddr_cfg.ip_addr_num = 1; + misc->param.ipaddr_cfg.ip_addr_type = IPADDR_TYPE_IPV4; + + if (woal_atoi(&op_code, &respbuf[header]) != + MLAN_STATUS_SUCCESS) { + ret = -EINVAL; + goto done; + } + misc->param.ipaddr_cfg.op_code = (t_u32)op_code; + } + + req->req_id = MLAN_IOCTL_MISC_CFG; + misc->sub_command = MLAN_OID_MISC_IP_ADDR; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (req->action == MLAN_ACT_GET) { + snprintf(respbuf, IPADDR_MAX_BUF, "%d;%d.%d.%d.%d", + misc->param.ipaddr_cfg.op_code, + misc->param.ipaddr_cfg.ip_addr[0][0], + misc->param.ipaddr_cfg.ip_addr[0][1], + misc->param.ipaddr_cfg.ip_addr[0][2], + misc->param.ipaddr_cfg.ip_addr[0][3]); + ret = IPADDR_MAX_BUF + 1; + } else { + ret = sprintf(respbuf, "OK\n") + 1; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get WPS session configurations + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int +woal_priv_setwpssession(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_wps_cfg *pwps = NULL; + t_u32 data[1]; + int ret = 0; + int user_data_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + memset((char *)data, 0, sizeof(data)); + if (strlen(respbuf) == + (strlen(CMD_MARVELL) + strlen(PRIV_CMD_WPSSESSION))) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + parse_arguments(respbuf + strlen(CMD_MARVELL) + + strlen(PRIV_CMD_WPSSESSION), data, + ARRAY_SIZE(data), &user_data_len); + } + + if (user_data_len > 1) { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_wps_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + pwps = (mlan_ds_wps_cfg *)req->pbuf; + + req->req_id = MLAN_IOCTL_WPS_CFG; + req->action = MLAN_ACT_SET; + pwps->sub_command = MLAN_OID_WPS_CFG_SESSION; + + if (data[0] == 1) + pwps->param.wps_session = MLAN_WPS_CFG_SESSION_START; + else + pwps->param.wps_session = MLAN_WPS_CFG_SESSION_END; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + ret = sprintf(respbuf, "OK\n") + 1; +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Get OTP user data + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int +woal_priv_otpuserdata(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int data[1]; + int user_data_len = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *misc = NULL; + mlan_ds_misc_otp_user_data *otp = NULL; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (strlen(respbuf) == + (strlen(CMD_MARVELL) + strlen(PRIV_CMD_OTPUSERDATA))) { + PRINTM(MERROR, "Invalid number of arguments\n"); + ret = -EINVAL; + goto done; + } + memset((char *)data, 0, sizeof(data)); + parse_arguments(respbuf + strlen(CMD_MARVELL) + + strlen(PRIV_CMD_OTPUSERDATA), data, ARRAY_SIZE(data), + &user_data_len); + + if (user_data_len != 1) { + PRINTM(MERROR, "Invalid number of arguments\n"); + ret = -EINVAL; + goto done; + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + req->action = MLAN_ACT_GET; + req->req_id = MLAN_IOCTL_MISC_CFG; + + misc = (mlan_ds_misc_cfg *)req->pbuf; + misc->sub_command = MLAN_OID_MISC_OTP_USER_DATA; + misc->param.otp_user_data.user_data_length = data[0]; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + otp = (mlan_ds_misc_otp_user_data *)req->pbuf; + + if (req->action == MLAN_ACT_GET) { + ret = MIN(otp->user_data_length, data[0]); + memcpy(respbuf, otp->user_data, ret); + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set / Get country code + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int +woal_priv_set_get_countrycode(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int ret = 0; + /* char data[COUNTRY_CODE_LEN] = {0, 0, 0}; */ + int header = 0, data_length = 0; /* wrq->u.data.length; */ + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *pcfg_misc = NULL; + mlan_ds_misc_country_code *country_code = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + header = strlen(CMD_MARVELL) + strlen(PRIV_CMD_COUNTRYCODE); + data_length = strlen(respbuf) - header; + + if (data_length > COUNTRY_CODE_LEN) { + PRINTM(MERROR, "Invalid argument!\n"); + ret = -EINVAL; + goto done; + } + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + pcfg_misc = (mlan_ds_misc_cfg *)req->pbuf; + country_code = &pcfg_misc->param.country_code; + pcfg_misc->sub_command = MLAN_OID_MISC_COUNTRY_CODE; + req->req_id = MLAN_IOCTL_MISC_CFG; + + if (data_length <= 1) { + req->action = MLAN_ACT_GET; + } else { + memset(country_code->country_code, 0, COUNTRY_CODE_LEN); + memcpy(country_code->country_code, respbuf + header, + COUNTRY_CODE_LEN); + req->action = MLAN_ACT_SET; + } + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (req->action == MLAN_ACT_GET) { + ret = data_length = COUNTRY_CODE_LEN; + memset(respbuf + header, 0, COUNTRY_CODE_LEN); + memcpy(respbuf, country_code->country_code, COUNTRY_CODE_LEN); + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + + LEAVE(); + return ret; +} + +/** + * @brief Get cfp information + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int +woal_priv_get_cfpinfo(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *cfp_misc = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + int header = 0, data_length = 0; + + ENTER(); + + if (!respbuf) { + PRINTM(MERROR, "response buffer is not available!\n"); + ret = -EINVAL; + goto done; + } + header = strlen(CMD_MARVELL) + strlen(PRIV_CMD_CFPINFO); + data_length = strlen(respbuf) - header; + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + cfp_misc = (mlan_ds_misc_cfg *)req->pbuf; + cfp_misc->sub_command = MLAN_OID_MISC_CFP_INFO; + req->req_id = MLAN_IOCTL_MISC_CFG; + req->action = MLAN_ACT_GET; + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + if (respbuflen < req->data_read_written) { + PRINTM(MERROR, "response buffer length is too short!\n"); + ret = -EINVAL; + goto done; + } + memcpy(respbuf, (t_u8 *)req->pbuf, req->data_read_written); + ret = req->data_read_written; +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + + LEAVE(); + return ret; +} + +/** + * @brief Set/Get TCP Ack enhancement configurations + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int +woal_priv_setgettcpackenh(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + t_u32 data[1]; + int ret = 0; + int user_data_len = 0; + + ENTER(); + + if (strlen(respbuf) == + (strlen(CMD_MARVELL) + strlen(PRIV_CMD_TCPACKENH))) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + memset((char *)data, 0, sizeof(data)); + parse_arguments(respbuf + strlen(CMD_MARVELL) + + strlen(PRIV_CMD_TCPACKENH), data, + ARRAY_SIZE(data), &user_data_len); + } + + if (user_data_len >= 2) { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + + if (user_data_len == 0) { + /* get operation */ + respbuf[0] = priv->enable_tcp_ack_enh; + } else { + /* set operation */ + if (data[0] == MTRUE) { + PRINTM(MINFO, "Enabling TCP Ack enhancement\n"); + priv->enable_tcp_ack_enh = MTRUE; + } else if (data[0] == MFALSE) { + PRINTM(MINFO, "Disabling TCP Ack enhancement\n"); + priv->enable_tcp_ack_enh = MFALSE; + /* release the tcp sessions if any */ + woal_flush_tcp_sess_queue(priv); + } else { + PRINTM(MERROR, "Unknown option = %u\n", data[0]); + ret = -EINVAL; + goto done; + } + respbuf[0] = priv->enable_tcp_ack_enh; + } + ret = 1; + +done: + LEAVE(); + return ret; + +} + +#ifdef REASSOCIATION +/** + * @brief Set Asynced ESSID + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * @param bBSSID A variable that bssid is set or not + * + * @return Number of bytes written, negative for failure. + */ +int +woal_priv_assocessid(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen, + t_u8 bBSSID) +{ + mlan_ssid_bssid ssid_bssid; + moal_handle *handle = priv->phandle; + int ret = 0; + int header_len = 0; + int copy_len = 0; + char buf[64]; + t_u8 buflen = 0; + t_u8 i = 0; + t_u8 mac_idx = 0; + + ENTER(); + + if (bBSSID) + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_ASSOCBSSID); + else + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_ASSOCESSID); + + if (strlen(respbuf) == header_len) { + PRINTM(MERROR, "No argument, invalid operation!\n"); + ret = -EINVAL; + LEAVE(); + return ret; + } + copy_len = strlen(respbuf) - header_len; + buflen = MIN(copy_len, (sizeof(buf) - 1)); + memset(buf, 0, sizeof(buf)); + memset(&ssid_bssid, 0, sizeof(mlan_ssid_bssid)); + memcpy(buf, respbuf + header_len, buflen); + priv->assoc_with_mac = MFALSE; + + /* check if has parameter BSSID */ + if (bBSSID) { + if (buflen < (3 * ETH_ALEN) + 2) { + PRINTM(MERROR, + "Associate: Insufficient length in IOCTL input\n"); + /* buffer should be at least 3 characters per BSSID octet "00:" + ** plus a space separater and at least 1 char in the SSID + */ + ret = -EINVAL; + goto setessid_ret; + } + for (; (i < buflen) && (mac_idx < ETH_ALEN) && (buf[i] != ' '); + i++) { + if (buf[i] == ':') { + mac_idx++; + } else { + ssid_bssid.bssid[mac_idx] = + (t_u8)woal_atox(buf + i); + while ((i < buflen) && isxdigit(buf[i + 1])) + /* Skip entire hex value */ + i++; + } + } + /* Skip one space between the BSSID and start of the SSID */ + i++; + PRINTM(MMSG, "Trying to associate AP BSSID = [" MACSTR "]\n", + MAC2STR(ssid_bssid.bssid)); + priv->assoc_with_mac = MTRUE; + } + + ssid_bssid.ssid.ssid_len = buflen - i; + /* Check the size of the ssid_len */ + if (ssid_bssid.ssid.ssid_len > MLAN_MAX_SSID_LENGTH + 1) { + PRINTM(MERROR, "ssid_bssid.ssid.ssid_len = %d\n", + ssid_bssid.ssid.ssid_len); + ret = -E2BIG; + goto setessid_ret; + } + + /* Copy the SSID */ + memcpy(ssid_bssid.ssid.ssid, buf + i, + MIN(ssid_bssid.ssid.ssid_len, MLAN_MAX_SSID_LENGTH)); + + if (!ssid_bssid.ssid.ssid_len || + (MFALSE == woal_ssid_valid(&ssid_bssid.ssid))) { + PRINTM(MERROR, "Invalid SSID - aborting set_essid\n"); + ret = -EINVAL; + goto setessid_ret; + } + + PRINTM(MMSG, "Trying to associate AP SSID = %s\n", + (char *)ssid_bssid.ssid.ssid); + + /* Cancel re-association */ + priv->reassoc_required = MFALSE; + + if (MOAL_ACQ_SEMAPHORE_BLOCK(&handle->reassoc_sem)) { + PRINTM(MERROR, "Acquire semaphore error, woal_set_essid\n"); + ret = -EBUSY; + LEAVE(); + return ret; + } + + if (priv->scan_type == MLAN_SCAN_TYPE_PASSIVE) + woal_set_scan_type(priv, MLAN_SCAN_TYPE_ACTIVE); + + if (MTRUE == woal_is_connected(priv, &ssid_bssid)) { + PRINTM(MIOCTL, "Already connect to the network\n"); + ret = sprintf(respbuf, + "Has already connected to this ESSID!\n") + 1; + goto setessid_ret; + } + memcpy(&priv->prev_ssid_bssid, &ssid_bssid, sizeof(mlan_ssid_bssid)); + /* disconnect before driver assoc */ + woal_disconnect(priv, MOAL_IOCTL_WAIT, NULL, DEF_DEAUTH_REASON_CODE); + priv->set_asynced_essid_flag = MTRUE; + priv->reassoc_required = MTRUE; + priv->phandle->is_reassoc_timer_set = MTRUE; + woal_mod_timer(&priv->phandle->reassoc_timer, 0); + ret = sprintf(respbuf, "%s\n", buf) + 1; + +setessid_ret: + if (priv->scan_type == MLAN_SCAN_TYPE_PASSIVE) + woal_set_scan_type(priv, MLAN_SCAN_TYPE_PASSIVE); + MOAL_REL_SEMAPHORE(&handle->reassoc_sem); + LEAVE(); + return ret; +} +#endif + +/** + * @brief Get wakeup reason + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int +woal_priv_getwakeupreason(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_pm_cfg *pm_cfg = NULL; + t_u32 data; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (strlen(respbuf) == + (strlen(CMD_MARVELL) + strlen(PRIV_CMD_WAKEUPREASON))) { + /* GET operation */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg)); + if (req == NULL) { + LEAVE(); + return -ENOMEM; + } + + pm_cfg = (mlan_ds_pm_cfg *)req->pbuf; + pm_cfg->sub_command = MLAN_OID_PM_HS_WAKEUP_REASON; + req->req_id = MLAN_IOCTL_PM_CFG; + req->action = MLAN_ACT_GET; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + if (status != MLAN_STATUS_PENDING) + kfree(req); + goto done; + } else { + data = pm_cfg->param.wakeup_reason.hs_wakeup_reason; + sprintf(respbuf, " %d", data); + ret = strlen(respbuf) + 1; + kfree(req); + } + } else { + PRINTM(MERROR, "Not need argument, invalid operation!\n"); + ret = -EINVAL; + goto done; + } + +done: + LEAVE(); + return ret; + +} + +#ifdef STA_SUPPORT +/** + * @brief Set / Get listen interval + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int +woal_priv_set_get_listeninterval(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int data[1]; + int user_data_len = 0; + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_bss *pcfg_bss = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (strlen(respbuf) == + (strlen(CMD_MARVELL) + strlen(PRIV_CMD_LISTENINTERVAL))) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + memset((char *)data, 0, sizeof(data)); + parse_arguments(respbuf + strlen(CMD_MARVELL) + + strlen(PRIV_CMD_LISTENINTERVAL), data, + ARRAY_SIZE(data), &user_data_len); + } + + if (user_data_len > 1) { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + pcfg_bss = (mlan_ds_bss *)req->pbuf; + pcfg_bss->sub_command = MLAN_OID_BSS_LISTEN_INTERVAL; + req->req_id = MLAN_IOCTL_BSS; + + if (user_data_len) { + pcfg_bss->param.listen_interval = (t_u16)data[0]; + req->action = MLAN_ACT_SET; + } else { + req->action = MLAN_ACT_GET; + } + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + if (req->action == MLAN_ACT_GET) { + sprintf(respbuf, "%d", pcfg_bss->param.listen_interval); + ret = strlen(respbuf) + 1; + } else { + ret = sprintf(respbuf, "OK\n") + 1; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + + LEAVE(); + return ret; +} +#endif + +#ifdef DEBUG_LEVEL1 +/** + * @brief Set / Get driver debug level + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int +woal_priv_set_get_drvdbg(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int data[4]; + int user_data_len = 0; + int ret = 0; + + ENTER(); + + if (strlen(respbuf) == (strlen(CMD_MARVELL) + strlen(PRIV_CMD_DRVDBG))) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + memset((char *)data, 0, sizeof(data)); + parse_arguments(respbuf + strlen(CMD_MARVELL) + + strlen(PRIV_CMD_DRVDBG), data, ARRAY_SIZE(data), + &user_data_len); + } + + if (user_data_len > 1) { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + + if (user_data_len) { + /* Get the driver debug bit masks from user */ + drvdbg = data[0]; + /* Set the driver debug bit masks into mlan */ + if (woal_set_drvdbg(priv, drvdbg)) { + PRINTM(MERROR, "Set drvdbg failed!\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + } + + ret = sizeof(drvdbg); + + memcpy(respbuf, &drvdbg, sizeof(drvdbg)); + + printk(KERN_ALERT "drvdbg = 0x%08x\n", drvdbg); +#ifdef DEBUG_LEVEL2 + printk(KERN_ALERT "MINFO (%08x) %s\n", MINFO, + (drvdbg & MINFO) ? "X" : ""); + printk(KERN_ALERT "MWARN (%08x) %s\n", MWARN, + (drvdbg & MWARN) ? "X" : ""); + printk(KERN_ALERT "MENTRY (%08x) %s\n", MENTRY, + (drvdbg & MENTRY) ? "X" : ""); +#endif + printk(KERN_ALERT "MMPA_D (%08x) %s\n", MMPA_D, + (drvdbg & MMPA_D) ? "X" : ""); + printk(KERN_ALERT "MIF_D (%08x) %s\n", MIF_D, + (drvdbg & MIF_D) ? "X" : ""); + printk(KERN_ALERT "MFW_D (%08x) %s\n", MFW_D, + (drvdbg & MFW_D) ? "X" : ""); + printk(KERN_ALERT "MEVT_D (%08x) %s\n", MEVT_D, + (drvdbg & MEVT_D) ? "X" : ""); + printk(KERN_ALERT "MCMD_D (%08x) %s\n", MCMD_D, + (drvdbg & MCMD_D) ? "X" : ""); + printk(KERN_ALERT "MDAT_D (%08x) %s\n", MDAT_D, + (drvdbg & MDAT_D) ? "X" : ""); + printk(KERN_ALERT "MIOCTL (%08x) %s\n", MIOCTL, + (drvdbg & MIOCTL) ? "X" : ""); + printk(KERN_ALERT "MINTR (%08x) %s\n", MINTR, + (drvdbg & MINTR) ? "X" : ""); + printk(KERN_ALERT "MEVENT (%08x) %s\n", MEVENT, + (drvdbg & MEVENT) ? "X" : ""); + printk(KERN_ALERT "MCMND (%08x) %s\n", MCMND, + (drvdbg & MCMND) ? "X" : ""); + printk(KERN_ALERT "MDATA (%08x) %s\n", MDATA, + (drvdbg & MDATA) ? "X" : ""); + printk(KERN_ALERT "MERROR (%08x) %s\n", MERROR, + (drvdbg & MERROR) ? "X" : ""); + printk(KERN_ALERT "MFATAL (%08x) %s\n", MFATAL, + (drvdbg & MFATAL) ? "X" : ""); + printk(KERN_ALERT "MMSG (%08x) %s\n", MMSG, + (drvdbg & MMSG) ? "X" : ""); + +done: + LEAVE(); + return ret; +} + +#endif + +/** + * @brief management frame filter wakeup config + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return 0 --success, otherwise fail + */ +int +woal_priv_mgmt_filter(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_pm_cfg *pm_cfg = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + int header_len = 0, data_len = 0; + int ret = 0; + t_u16 action; + t_u8 *argument; + + ENTER(); + + if (!priv || !priv->phandle) { + PRINTM(MERROR, "priv or handle is null\n"); + ret = -EFAULT; + goto done; + } + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (ioctl_req == NULL) { + ret = -ENOMEM; + goto done; + } + + pm_cfg = (mlan_ds_pm_cfg *)ioctl_req->pbuf; + pm_cfg->sub_command = MLAN_OID_PM_MGMT_FILTER; + ioctl_req->req_id = MLAN_IOCTL_PM_CFG; + + header_len = strlen(PRIV_CMD_MGMT_FILTER) + strlen(CMD_MARVELL); + + if (strlen(respbuf) == header_len) { + /* GET operation */ + action = MLAN_ACT_GET; + } else { + /* SET operation */ + argument = (t_u8 *)(respbuf + header_len); + data_len = respbuflen - header_len; + if (data_len > + MAX_MGMT_FRAME_FILTER * sizeof(mlan_mgmt_frame_wakeup)) { + PRINTM(MERROR, "%d: Invalid arguments\n", __LINE__); + ret = -EINVAL; + goto done; + } + memcpy((t_u8 *)pm_cfg->param.mgmt_filter, argument, data_len); + action = MLAN_ACT_SET; + } + + ioctl_req->action = action; + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + + LEAVE(); + return ret; +} + +#define PARAMETER_GPIO_INDICATION 1 +#define PARAMETER_EXTEND_HSCFG 2 +#define PARAMETER_HS_WAKEUP_INTERVAL 3 +/** + * @brief Set/Get Host Sleep configuration + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * @param invoke_hostcmd MTRUE --invoke HostCmd, otherwise MFALSE + * + * @return 0 --success, otherwise fail + */ +int +woal_priv_hscfg(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen, + BOOLEAN invoke_hostcmd) +{ + int data[13] = { 0 }; + int *temp_data, type; + int user_data_len = 0; + int ret = 0; + mlan_ds_hs_cfg hscfg, hscfg_temp; + t_u16 action; + mlan_bss_info bss_info; + int is_negative = MFALSE; + t_u8 *arguments = NULL; + + ENTER(); + + memset(data, 0, sizeof(data)); + memset(&hscfg, 0, sizeof(mlan_ds_hs_cfg)); + memset(&hscfg_temp, 0, sizeof(mlan_ds_hs_cfg)); + + if (strlen(respbuf) == (strlen(CMD_MARVELL) + strlen(PRIV_CMD_HSCFG))) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + memset((char *)data, 0, sizeof(data)); + arguments = + respbuf + strlen(CMD_MARVELL) + strlen(PRIV_CMD_HSCFG); + if (*arguments == '-') { + is_negative = MTRUE; + arguments += 1; + } + parse_arguments(arguments, data, ARRAY_SIZE(data), + &user_data_len); + + if (is_negative == MTRUE) { + if (data[0] == 1) { + data[0] = -1; + } else { + PRINTM(MERROR, "Invalid arguments\n"); + ret = -EINVAL; + goto done; + } + } + } + + if (sizeof(int) * user_data_len > sizeof(data)) { + PRINTM(MERROR, "Too many arguments\n"); + LEAVE(); + return -EINVAL; + } + + if (user_data_len == 0) { + action = MLAN_ACT_GET; + } else { + if (user_data_len >= 1 && user_data_len <= 13) { + action = MLAN_ACT_SET; + } else { + PRINTM(MERROR, "Invalid arguments\n"); + ret = -EINVAL; + goto done; + } + } + + /* HS config is blocked if HS is already activated */ + if (user_data_len && + (data[0] != HOST_SLEEP_CFG_CANCEL || invoke_hostcmd == MFALSE)) { + memset(&bss_info, 0, sizeof(bss_info)); + woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info); + if (bss_info.is_hs_configured) { + PRINTM(MERROR, "HS already configured\n"); + ret = -EFAULT; + goto done; + } + } + + /* Do a GET first if some arguments are not provided */ + if (user_data_len >= 1 && user_data_len < 11) { + woal_set_get_hs_params(priv, MLAN_ACT_GET, MOAL_IOCTL_WAIT, + &hscfg_temp); + } + hscfg.conditions = hscfg_temp.conditions; + hscfg.gpio = hscfg_temp.gpio; + hscfg.gap = hscfg_temp.gap; + + if (user_data_len) + hscfg.conditions = data[0]; + if (user_data_len >= 2) + hscfg.gpio = data[1]; + if (user_data_len >= 3) + hscfg.gap = data[2]; + user_data_len = user_data_len - 3; + if (user_data_len > 0) { + temp_data = data + 3; + while ((user_data_len > 0) && temp_data) { + type = *temp_data; + switch (type) { + case PARAMETER_GPIO_INDICATION: + if (user_data_len >= 2) + hscfg.ind_gpio = *(++temp_data); + else { + PRINTM(MERROR, + "Invaild number of parameters\n"); + ret = -EINVAL; + goto done; + } + if (user_data_len >= 3) { + hscfg.level = *(++temp_data); + if (hscfg.level != 0 && + hscfg.level != 1) { + PRINTM(MERROR, + "Invalid indication gpio arguments\n"); + ret = -EINVAL; + goto done; + } + } + hscfg.param_type_ind = type; + user_data_len = user_data_len - 3; + temp_data++; + break; + case PARAMETER_EXTEND_HSCFG: + if (user_data_len >= 4) { + hscfg.event_force_ignore = + *(++temp_data); + hscfg.event_use_ext_gap = + *(++temp_data); + hscfg.ext_gap = *(++temp_data); + hscfg.gpio_wave = *(++temp_data); + } else { + PRINTM(MERROR, + "Invaild number of parameters\n"); + ret = -EINVAL; + goto done; + } + /* Force_ignore_gpio and ext_gap_gpio should not set the same bit(s) */ + if ((hscfg.event_force_ignore & hscfg. + event_use_ext_gap) || (hscfg.gpio_wave != 1 + && hscfg. + gpio_wave != 0)) { + PRINTM(MERROR, + "Invalid arguments for extend hscfg\n"); + ret = -EINVAL; + goto done; + } + hscfg.param_type_ext = type; + user_data_len = user_data_len - 5; + temp_data++; + break; + case PARAMETER_HS_WAKEUP_INTERVAL: + if (user_data_len >= 2) + hscfg.hs_wake_interval = *(++temp_data); + else { + PRINTM(MERROR, + "Invaild number of parameters\n"); + ret = -EINVAL; + goto done; + } + user_data_len = user_data_len - 2; + temp_data++; + break; + default: + PRINTM(MERROR, "Unsupported type\n"); + ret = -EINVAL; + goto done; + } + } + } + + if ((invoke_hostcmd == MTRUE) && (action == MLAN_ACT_SET)) { + /* Need to issue an extra IOCTL first to set up parameters */ + hscfg.is_invoke_hostcmd = MFALSE; + if (MLAN_STATUS_SUCCESS != + woal_set_get_hs_params(priv, MLAN_ACT_SET, MOAL_IOCTL_WAIT, + &hscfg)) { + ret = -EFAULT; + goto done; + } + } + hscfg.is_invoke_hostcmd = invoke_hostcmd; + if (MLAN_STATUS_SUCCESS != + woal_set_get_hs_params(priv, action, MOAL_IOCTL_WAIT, &hscfg)) { + ret = -EFAULT; + goto done; + } + + if (action == MLAN_ACT_GET) { + /* Return the current driver host sleep configurations */ + memcpy(respbuf, &hscfg, sizeof(mlan_ds_hs_cfg)); + ret = sizeof(mlan_ds_hs_cfg); + } +done: + LEAVE(); + return ret; +} + +/** + * @brief Set Host Sleep parameters + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return 0 --success, otherwise fail + */ +int +woal_priv_hssetpara(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int data[13] = { 0 }; + int user_data_len = 0; + int ret = 0; + + ENTER(); + + if (strlen(respbuf) == + (strlen(CMD_MARVELL) + strlen(PRIV_CMD_HSSETPARA))) { + PRINTM(MERROR, "Invalid arguments\n"); + ret = -EINVAL; + goto done; + } else { + /* SET operation */ + memset((char *)data, 0, sizeof(data)); + parse_arguments(respbuf + strlen(CMD_MARVELL) + + strlen(PRIV_CMD_HSSETPARA), data, + ARRAY_SIZE(data), &user_data_len); + } + + if (sizeof(int) * user_data_len > sizeof(data)) { + PRINTM(MERROR, "Too many arguments\n"); + LEAVE(); + return -EINVAL; + } + + if (user_data_len >= 1 && user_data_len <= 13) { + sprintf(respbuf, "%s%s%s", CMD_MARVELL, PRIV_CMD_HSCFG, + respbuf + (strlen(CMD_MARVELL) + + strlen(PRIV_CMD_HSSETPARA))); + respbuflen = strlen(respbuf); + ret = woal_priv_hscfg(priv, respbuf, respbuflen, MFALSE); + goto done; + } +done: + LEAVE(); + return ret; +} + +/** + * @brief Set/Get scan configuration parameters + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return 0 --success, otherwise fail + */ +int +woal_priv_set_get_scancfg(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int ret = 0; + int user_data_len = 0; + int arg_len = 7; + int data[arg_len]; + mlan_ds_scan *scan = NULL; + mlan_ioctl_req *req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + memset(data, 0, sizeof(data)); + if (strlen(respbuf) == (strlen(CMD_MARVELL) + strlen(PRIV_CMD_SCANCFG))) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + memset((char *)data, 0, sizeof(data)); + parse_arguments(respbuf + strlen(CMD_MARVELL) + + strlen(PRIV_CMD_SCANCFG), data, + ARRAY_SIZE(data), &user_data_len); + } + + if (sizeof(int) * user_data_len > sizeof(data)) { + PRINTM(MERROR, "Too many arguments\n"); + LEAVE(); + return -EINVAL; + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_scan)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + scan = (mlan_ds_scan *)req->pbuf; + scan->sub_command = MLAN_OID_SCAN_CONFIG; + req->req_id = MLAN_IOCTL_SCAN; + + if (user_data_len) { + if ((data[0] < 0) || (data[0] > MLAN_SCAN_TYPE_PASSIVE)) { + PRINTM(MERROR, "Invalid argument for scan type\n"); + ret = -EINVAL; + goto done; + } + if ((data[1] < 0) || (data[1] > MLAN_SCAN_MODE_ANY)) { + PRINTM(MERROR, "Invalid argument for scan mode\n"); + ret = -EINVAL; + goto done; + } + if ((data[2] < 0) || (data[2] > MAX_PROBES)) { + PRINTM(MERROR, "Invalid argument for scan probes\n"); + ret = -EINVAL; + goto done; + } + if (((data[3] < 0) || + (data[3] > MRVDRV_MAX_ACTIVE_SCAN_CHAN_TIME)) || + ((data[4] < 0) || + (data[4] > MRVDRV_MAX_ACTIVE_SCAN_CHAN_TIME)) || + ((data[5] < 0) || + (data[5] > MRVDRV_MAX_PASSIVE_SCAN_CHAN_TIME))) { + PRINTM(MERROR, "Invalid argument for scan time\n"); + ret = -EINVAL; + goto done; + } + if ((data[6] < 0) || (data[6] > 2)) { + PRINTM(MERROR, "Invalid argument for extended scan\n"); + ret = -EINVAL; + goto done; + } + req->action = MLAN_ACT_SET; + memcpy(&scan->param.scan_cfg, data, sizeof(data)); + } else + req->action = MLAN_ACT_GET; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + if (!user_data_len) { + memcpy(respbuf, &scan->param.scan_cfg, sizeof(mlan_scan_cfg)); + ret = sizeof(mlan_scan_cfg); + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +#ifdef STA_SUPPORT +/** + * @brief Set AP settings + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written if successful, otherwise fail + */ +static int +woal_priv_set_ap(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int ret = 0; + t_u8 *data_ptr; + const t_u8 bcast[MLAN_MAC_ADDR_LENGTH] = + { 255, 255, 255, 255, 255, 255 }; + const t_u8 zero_mac[MLAN_MAC_ADDR_LENGTH] = { 0, 0, 0, 0, 0, 0 }; + mlan_ssid_bssid ssid_bssid; + mlan_bss_info bss_info; + struct mwreq *mwr; + struct sockaddr *awrq; + + ENTER(); + data_ptr = respbuf + (strlen(CMD_MARVELL) + strlen(PRIV_CMD_SET_AP)); + + mwr = (struct mwreq *)data_ptr; + + if (mwr->u.ap_addr.sa_family != ARPHRD_ETHER) { + ret = -EINVAL; + goto done; + } + + awrq = (struct sockaddr *)&(mwr->u.ap_addr); + + PRINTM(MINFO, "ASSOC: WAP: sa_data: " MACSTR "\n", + MAC2STR((t_u8 *)awrq->sa_data)); + + if (MLAN_STATUS_SUCCESS != + woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info)) { + ret = -EFAULT; + goto done; + } +#ifdef REASSOCIATION + /* Cancel re-association */ + priv->reassoc_required = MFALSE; +#endif + + /* zero_mac means disconnect */ + if (!memcmp(zero_mac, awrq->sa_data, MLAN_MAC_ADDR_LENGTH)) { + woal_disconnect(priv, MOAL_IOCTL_WAIT, NULL, + DEF_DEAUTH_REASON_CODE); + goto done; + } + + /* Broadcast MAC means search for best network */ + memset(&ssid_bssid, 0, sizeof(mlan_ssid_bssid)); + + if (memcmp(bcast, awrq->sa_data, MLAN_MAC_ADDR_LENGTH)) { + /* Check if we are already assoicated to the AP */ + if (bss_info.media_connected == MTRUE) { + if (!memcmp(awrq->sa_data, &bss_info.bssid, ETH_ALEN)) + goto done; + } + memcpy(&ssid_bssid.bssid, awrq->sa_data, ETH_ALEN); + } + + if (MLAN_STATUS_SUCCESS != woal_find_best_network(priv, + MOAL_IOCTL_WAIT, + &ssid_bssid)) { + PRINTM(MERROR, + "ASSOC: WAP: MAC address not found in BSSID List\n"); + ret = -ENETUNREACH; + goto done; + } + /* Zero SSID implies use BSSID to connect */ + memset(&ssid_bssid.ssid, 0, sizeof(mlan_802_11_ssid)); + if (MLAN_STATUS_SUCCESS != woal_bss_start(priv, + MOAL_IOCTL_WAIT, + &ssid_bssid)) { + ret = -EFAULT; + goto done; + } +#ifdef REASSOCIATION + memset(&bss_info, 0, sizeof(bss_info)); + if (MLAN_STATUS_SUCCESS != woal_get_bss_info(priv, + MOAL_IOCTL_WAIT, + &bss_info)) { + ret = -EFAULT; + goto done; + } + memcpy(&priv->prev_ssid_bssid.ssid, &bss_info.ssid, + sizeof(mlan_802_11_ssid)); + memcpy(&priv->prev_ssid_bssid.bssid, &bss_info.bssid, + MLAN_MAC_ADDR_LENGTH); +#endif /* REASSOCIATION */ + +done: + LEAVE(); + return ret; + +} +#endif + +/** + * @brief Set BSS mode + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written if successful, otherwise fail + */ +static int +woal_priv_set_bss_mode(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int ret = 0; + mlan_ds_bss *bss = NULL; + mlan_ioctl_req *req = NULL; + struct mwreq *mwr; + t_u8 *data_ptr; + t_u32 mode; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + data_ptr = + respbuf + (strlen(CMD_MARVELL) + strlen(PRIV_CMD_SET_BSS_MODE)); + + mwr = (struct mwreq *)data_ptr; + mode = mwr->u.mode; + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + bss = (mlan_ds_bss *)req->pbuf; + bss->sub_command = MLAN_OID_BSS_MODE; + req->req_id = MLAN_IOCTL_BSS; + req->action = MLAN_ACT_SET; + + switch (mode) { + case MW_MODE_INFRA: + bss->param.bss_mode = MLAN_BSS_MODE_INFRA; + break; + case MW_MODE_ADHOC: + bss->param.bss_mode = MLAN_BSS_MODE_IBSS; + break; + case MW_MODE_AUTO: + bss->param.bss_mode = MLAN_BSS_MODE_AUTO; + break; + default: + ret = -EINVAL; + break; + } + if (ret) + goto done; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +#ifdef STA_SUPPORT +/** + * @brief Set power management + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written if successful, otherwise fail + */ +static int +woal_priv_set_power(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + struct mwreq *mwr; + t_u8 *data_ptr; + int ret = 0, disabled; + + ENTER(); + + if (hw_test) { + PRINTM(MIOCTL, "block set power in hw_test mode\n"); + LEAVE(); + return ret; + } + + data_ptr = respbuf + (strlen(CMD_MARVELL) + strlen(PRIV_CMD_SET_POWER)); + + mwr = (struct mwreq *)data_ptr; + disabled = mwr->u.power.disabled; + + if (MLAN_STATUS_SUCCESS != woal_set_get_power_mgmt(priv, + MLAN_ACT_SET, + &disabled, + mwr->u.power.flags, + MOAL_IOCTL_WAIT)) { + return -EFAULT; + } + LEAVE(); + return ret; +} + +/** + * @brief Set essid + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written if successful, otherwise fail + */ +static int +woal_priv_set_essid(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + mlan_802_11_ssid req_ssid; + mlan_ssid_bssid ssid_bssid; +#ifdef REASSOCIATION + moal_handle *handle = priv->phandle; + mlan_bss_info bss_info; +#endif + int ret = 0; + t_u32 mode = 0; + struct mwreq *mwr; + t_u8 *data_ptr; + + ENTER(); + + data_ptr = respbuf + (strlen(CMD_MARVELL) + strlen(PRIV_CMD_SET_ESSID)); + + mwr = (struct mwreq *)data_ptr; + +#ifdef REASSOCIATION + /* Cancel re-association */ + priv->reassoc_required = MFALSE; + + if (MOAL_ACQ_SEMAPHORE_BLOCK(&handle->reassoc_sem)) { + PRINTM(MERROR, "Acquire semaphore error, woal_set_essid\n"); + LEAVE(); + return -EBUSY; + } +#endif /* REASSOCIATION */ + + /* Check the size of the string */ + if (mwr->u.essid.length > MW_ESSID_MAX_SIZE + 1) { + ret = -E2BIG; + goto setessid_ret; + } + if (priv->scan_type == MLAN_SCAN_TYPE_PASSIVE) + woal_set_scan_type(priv, MLAN_SCAN_TYPE_ACTIVE); + memset(&req_ssid, 0, sizeof(mlan_802_11_ssid)); + memset(&ssid_bssid, 0, sizeof(mlan_ssid_bssid)); + + req_ssid.ssid_len = mwr->u.essid.length; + + /* Check if we asked for 'any' or 'particular' */ + if (!mwr->u.essid.flags) { +#ifdef REASSOCIATION + if (!req_ssid.ssid_len) { + memset(&priv->prev_ssid_bssid.ssid, 0x00, + sizeof(mlan_802_11_ssid)); + memset(&priv->prev_ssid_bssid.bssid, 0x00, + MLAN_MAC_ADDR_LENGTH); + goto setessid_ret; + } +#endif + /* Do normal SSID scanning */ + if (MLAN_STATUS_SUCCESS != + woal_request_scan(priv, MOAL_IOCTL_WAIT, NULL)) { + ret = -EFAULT; + goto setessid_ret; + } + } else { + /* Set the SSID */ + memcpy(req_ssid.ssid, mwr->u.essid.pointer, + MIN(req_ssid.ssid_len, MLAN_MAX_SSID_LENGTH)); + if (!req_ssid.ssid_len || + (MFALSE == woal_ssid_valid(&req_ssid))) { + PRINTM(MERROR, "Invalid SSID - aborting set_essid\n"); + ret = -EINVAL; + goto setessid_ret; + } + + PRINTM(MINFO, "Requested new SSID = %s\n", + (char *)req_ssid.ssid); + memcpy(&ssid_bssid.ssid, &req_ssid, sizeof(mlan_802_11_ssid)); + if (MTRUE == woal_is_connected(priv, &ssid_bssid)) { + PRINTM(MIOCTL, "Already connect to the network\n"); + goto setessid_ret; + } + + if (mwr->u.essid.flags != 0xFFFF) { + if (MLAN_STATUS_SUCCESS != + woal_find_essid(priv, &ssid_bssid, + MOAL_IOCTL_WAIT)) { + /* Do specific SSID scanning */ + if (MLAN_STATUS_SUCCESS != + woal_request_scan(priv, MOAL_IOCTL_WAIT, + &req_ssid)) { + ret = -EFAULT; + goto setessid_ret; + } + } + } + + } + + mode = woal_get_mode(priv, MOAL_IOCTL_WAIT); + + if (mode != MW_MODE_ADHOC) { + if (MLAN_STATUS_SUCCESS != + woal_find_best_network(priv, MOAL_IOCTL_WAIT, + &ssid_bssid)) { + ret = -EFAULT; + goto setessid_ret; + } + } else if (MLAN_STATUS_SUCCESS != + woal_find_best_network(priv, MOAL_IOCTL_WAIT, &ssid_bssid)) + /* Adhoc start, Check the channel command */ + woal_11h_channel_check_ioctl(priv, MOAL_IOCTL_WAIT); + + /* Connect to BSS by ESSID */ + memset(&ssid_bssid.bssid, 0, MLAN_MAC_ADDR_LENGTH); + + if (MLAN_STATUS_SUCCESS != woal_bss_start(priv, + MOAL_IOCTL_WAIT, + &ssid_bssid)) { + ret = -EFAULT; + goto setessid_ret; + } +#ifdef REASSOCIATION + memset(&bss_info, 0, sizeof(bss_info)); + if (MLAN_STATUS_SUCCESS != woal_get_bss_info(priv, + MOAL_IOCTL_WAIT, + &bss_info)) { + ret = -EFAULT; + goto setessid_ret; + } + memcpy(&priv->prev_ssid_bssid.ssid, &bss_info.ssid, + sizeof(mlan_802_11_ssid)); + memcpy(&priv->prev_ssid_bssid.bssid, &bss_info.bssid, + MLAN_MAC_ADDR_LENGTH); +#endif /* REASSOCIATION */ + +setessid_ret: + if (priv->scan_type == MLAN_SCAN_TYPE_PASSIVE) + woal_set_scan_type(priv, MLAN_SCAN_TYPE_PASSIVE); +#ifdef REASSOCIATION + MOAL_REL_SEMAPHORE(&handle->reassoc_sem); +#endif + LEAVE(); + return ret; +} + +/** + * @brief Set authentication mode parameters + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written if successful, otherwise fail + */ +static int +woal_priv_set_auth(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + struct mwreq *mwr; + t_u8 *data_ptr; + int ret = 0; + t_u32 auth_mode = 0; + t_u32 encrypt_mode = 0; + + ENTER(); + + data_ptr = respbuf + (strlen(CMD_MARVELL) + strlen(PRIV_CMD_SET_AUTH)); + + mwr = (struct mwreq *)data_ptr; + + switch (mwr->u.param.flags & MW_AUTH_INDEX) { + case MW_AUTH_CIPHER_PAIRWISE: + case MW_AUTH_CIPHER_GROUP: + if (mwr->u.param.value & MW_AUTH_CIPHER_NONE) + encrypt_mode = MLAN_ENCRYPTION_MODE_NONE; + else if (mwr->u.param.value & MW_AUTH_CIPHER_WEP40) + encrypt_mode = MLAN_ENCRYPTION_MODE_WEP40; + else if (mwr->u.param.value & MW_AUTH_CIPHER_WEP104) + encrypt_mode = MLAN_ENCRYPTION_MODE_WEP104; + else if (mwr->u.param.value & MW_AUTH_CIPHER_TKIP) + encrypt_mode = MLAN_ENCRYPTION_MODE_TKIP; + else if (mwr->u.param.value & MW_AUTH_CIPHER_CCMP) + encrypt_mode = MLAN_ENCRYPTION_MODE_CCMP; + if (MLAN_STATUS_SUCCESS != + woal_set_encrypt_mode(priv, MOAL_IOCTL_WAIT, encrypt_mode)) + ret = -EFAULT; + break; + case MW_AUTH_80211_AUTH_ALG: + switch (mwr->u.param.value) { + case MW_AUTH_ALG_SHARED_KEY: + PRINTM(MINFO, "Auth mode shared key!\n"); + auth_mode = MLAN_AUTH_MODE_SHARED; + break; + case MW_AUTH_ALG_LEAP: + PRINTM(MINFO, "Auth mode LEAP!\n"); + auth_mode = MLAN_AUTH_MODE_NETWORKEAP; + break; + case MW_AUTH_ALG_OPEN_SYSTEM: + PRINTM(MINFO, "Auth mode open!\n"); + auth_mode = MLAN_AUTH_MODE_OPEN; + break; + case MW_AUTH_ALG_SHARED_KEY | MW_AUTH_ALG_OPEN_SYSTEM: + default: + PRINTM(MINFO, "Auth mode auto!\n"); + auth_mode = MLAN_AUTH_MODE_AUTO; + break; + } + if (MLAN_STATUS_SUCCESS != + woal_set_auth_mode(priv, MOAL_IOCTL_WAIT, auth_mode)) + ret = -EFAULT; + break; + case MW_AUTH_WPA_ENABLED: + if (MLAN_STATUS_SUCCESS != + woal_set_wpa_enable(priv, MOAL_IOCTL_WAIT, + mwr->u.param.value)) + ret = -EFAULT; + break; +#define MW_AUTH_WAPI_ENABLED 0x20 + case MW_AUTH_WAPI_ENABLED: + if (MLAN_STATUS_SUCCESS != + woal_set_wapi_enable(priv, MOAL_IOCTL_WAIT, + mwr->u.param.value)) + ret = -EFAULT; + break; + case MW_AUTH_WPA_VERSION: + /* set WPA_VERSION_DISABLED/VERSION_WPA/VERSION_WP2 */ + priv->wpa_version = mwr->u.param.value; + break; + case MW_AUTH_KEY_MGMT: + /* set KEY_MGMT_802_1X/KEY_MGMT_PSK */ + priv->key_mgmt = mwr->u.param.value; + break; + case MW_AUTH_TKIP_COUNTERMEASURES: + case MW_AUTH_DROP_UNENCRYPTED: + case MW_AUTH_RX_UNENCRYPTED_EAPOL: + case MW_AUTH_ROAMING_CONTROL: + case MW_AUTH_PRIVACY_INVOKED: + break; + default: + ret = -EOPNOTSUPP; + break; + } + LEAVE(); + return ret; +} + +/** + * @brief Get current BSSID + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written if successful else negative value + */ +static int +woal_priv_get_ap(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int ret = 0; + mlan_bss_info bss_info; + struct mwreq *mwr; + t_u8 *data_ptr; + + ENTER(); + + data_ptr = respbuf + (strlen(CMD_MARVELL) + strlen(PRIV_CMD_GET_AP)); + mwr = (struct mwreq *)data_ptr; + + memset(&bss_info, 0, sizeof(bss_info)); + + ret = woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info); + if (ret != MLAN_STATUS_SUCCESS) + return -EFAULT; + + if (bss_info.media_connected == MTRUE) { + memcpy(mwr->u.ap_addr.sa_data, &bss_info.bssid, + MLAN_MAC_ADDR_LENGTH); + } else { + memset(mwr->u.ap_addr.sa_data, 0, MLAN_MAC_ADDR_LENGTH); + } + + mwr->u.ap_addr.sa_family = ARPHRD_ETHER; + ret = strlen(CMD_MARVELL) + strlen(PRIV_CMD_GET_AP) + + sizeof(struct mwreq); + + LEAVE(); + return ret; +} + +/** + * @brief Get power management + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written if successful else negative value + */ +static int +woal_priv_get_power(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + struct mwreq *mwr; + t_u8 *data_ptr; + int ret = 0, ps_mode; + + ENTER(); + + data_ptr = respbuf + (strlen(CMD_MARVELL) + strlen(PRIV_CMD_GET_POWER)); + + mwr = (struct mwreq *)data_ptr; + + if (MLAN_STATUS_SUCCESS != woal_set_get_power_mgmt(priv, + MLAN_ACT_GET, + &ps_mode, 0, + MOAL_IOCTL_WAIT)) { + return -EFAULT; + } + + if (ps_mode) + mwr->u.power.disabled = 0; + else + mwr->u.power.disabled = 1; + + mwr->u.power.value = 0; + ret = strlen(CMD_MARVELL) + strlen(PRIV_CMD_GET_POWER) + + sizeof(struct mwreq); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get power save mode + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return 0 --success, otherwise fail + */ +static int +woal_priv_set_get_psmode(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int ret = 0; + int data = 0; + int user_data_len = 0, header_len = 0; + t_u32 action = MLAN_ACT_GET; + + ENTER(); + + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_PSMODE); + + if (strlen(respbuf) == header_len) { + /* GET operation */ + user_data_len = 0; + action = MLAN_ACT_GET; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, &data, + sizeof(data) / sizeof(int), &user_data_len); + action = MLAN_ACT_SET; + } + + if (sizeof(int) * user_data_len > sizeof(data)) { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + + /* Flip the value */ + data = !data; + + if (MLAN_STATUS_SUCCESS != + woal_set_get_power_mgmt(priv, action, &data, 0, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + + if (action == MLAN_ACT_SET) + data = !data; + + memcpy(respbuf, (t_u8 *)&data, sizeof(data)); + ret = sizeof(data); + +done: + LEAVE(); + return ret; +} +#endif /* STA_SUPPORT */ + +/** + * @brief Performs warm reset + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written if successful else negative value + */ +static int +woal_priv_warmreset(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int ret = 0; + int intf_num; + moal_handle *handle = priv->phandle; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *misc = NULL; +#if defined(WIFI_DIRECT_SUPPORT) +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) +#if defined(STA_WEXT) || defined(UAP_WEXT) + t_u8 bss_role = MLAN_BSS_ROLE_STA; +#endif +#endif +#endif /* WIFI_DIRECT_SUPPORT && V14_FEATURE */ + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + woal_cancel_cac_block(priv); + + /* Reset all interfaces */ + ret = woal_reset_intf(priv, MOAL_IOCTL_WAIT, MTRUE); + + /* Initialize private structures */ + for (intf_num = 0; intf_num < handle->priv_num; intf_num++) { + woal_init_priv(handle->priv[intf_num], MOAL_IOCTL_WAIT); +#if defined(WIFI_DIRECT_SUPPORT) +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) +#if defined(STA_WEXT) || defined(UAP_WEXT) + if ((handle->priv[intf_num]->bss_type == + MLAN_BSS_TYPE_WIFIDIRECT) && + (GET_BSS_ROLE(handle->priv[intf_num]) == + MLAN_BSS_ROLE_UAP)) { + if (MLAN_STATUS_SUCCESS != + woal_bss_role_cfg(handle->priv[intf_num], + MLAN_ACT_SET, MOAL_IOCTL_WAIT, + &bss_role)) { + ret = -EFAULT; + goto done; + } + } +#endif /* STA_WEXT || UAP_WEXT */ +#endif /* STA_SUPPORT && UAP_SUPPORT */ +#endif /* WIFI_DIRECT_SUPPORT && V14_FEATURE */ + } + + /* Restart the firmware */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req) { + misc = (mlan_ds_misc_cfg *)req->pbuf; + misc->sub_command = MLAN_OID_MISC_WARM_RESET; + req->req_id = MLAN_IOCTL_MISC_CFG; + req->action = MLAN_ACT_SET; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + if (status != MLAN_STATUS_PENDING) + kfree(req); + goto done; + } + kfree(req); + } + + /* Enable interfaces */ + for (intf_num = 0; intf_num < handle->priv_num; intf_num++) { + netif_device_attach(handle->priv[intf_num]->netdev); + woal_start_queue(handle->priv[intf_num]->netdev); + } + +done: + LEAVE(); + return ret; +} + +/** + * @brief Set/Get TX power configurations + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return 0 --success, otherwise fail + */ +static int +woal_priv_txpowercfg(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int data[5]; + int user_data_len; + int ret = 0; + mlan_bss_info bss_info; + mlan_ds_power_cfg *pcfg = NULL; + mlan_ioctl_req *req = NULL; + t_u8 *arguments = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + ENTER(); + + memset(data, 0, sizeof(data)); + memset(&bss_info, 0, sizeof(bss_info)); + woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info); + + if (strlen(respbuf) == + (strlen(CMD_MARVELL) + strlen(PRIV_CMD_TXPOWERCFG))) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + arguments = + respbuf + strlen(CMD_MARVELL) + + strlen(PRIV_CMD_TXPOWERCFG); + parse_arguments(arguments, data, ARRAY_SIZE(data), + &user_data_len); + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_power_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + pcfg = (mlan_ds_power_cfg *)req->pbuf; + pcfg->sub_command = MLAN_OID_POWER_CFG_EXT; + req->req_id = MLAN_IOCTL_POWER_CFG; + + if (!user_data_len) + req->action = MLAN_ACT_GET; + else { + /* SET operation */ + req->action = MLAN_ACT_SET; + if (sizeof(int) * user_data_len > sizeof(data)) { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + switch (user_data_len) { + case 1: + if (data[0] == 0xFF) + pcfg->param.power_ext.power_group[0]. + rate_format = TX_PWR_CFG_AUTO_CTRL_OFF; + else + ret = -EINVAL; + break; + case 3: + case 5: + switch (data[0]) { + case 0: /* LG */ + pcfg->param.power_ext.power_group[0]. + rate_format = MLAN_RATE_FORMAT_LG; + pcfg->param.power_ext.power_group[0].bandwidth = + MLAN_HT_BW20; + break; + case 1: /* 20 MHz HT */ + pcfg->param.power_ext.power_group[0]. + rate_format = MLAN_RATE_FORMAT_HT; + pcfg->param.power_ext.power_group[0].bandwidth = + MLAN_HT_BW20; + break; + case 2: /* 40 MHz HT */ + pcfg->param.power_ext.power_group[0]. + rate_format = MLAN_RATE_FORMAT_HT; + pcfg->param.power_ext.power_group[0].bandwidth = + MLAN_HT_BW40; + break; + default: + ret = -EINVAL; + break; + } + pcfg->param.power_ext.power_group[0].first_rate_ind = + data[1]; + pcfg->param.power_ext.power_group[0].last_rate_ind = + data[1]; + if (data[2] < bss_info.min_power_level) { + PRINTM(MERROR, + "The set powercfg rate value %d dBm is out of range (%d dBm-%d dBm)!\n", + data[2], (int)bss_info.min_power_level, + (int)bss_info.max_power_level); + ret = -EINVAL; + break; + } + pcfg->param.power_ext.power_group[0].power_min = + data[2]; + pcfg->param.power_ext.power_group[0].power_max = + data[2]; + pcfg->param.power_ext.power_group[0].power_step = 0; + if (user_data_len == 5) { + if (data[2] > data[3]) { + PRINTM(MERROR, + "Min power should be less than maximum!\n"); + ret = -EINVAL; + break; + } + if (data[4] < 0) { + PRINTM(MERROR, + "Step should not less than 0!\n"); + ret = -EINVAL; + break; + } + if (data[3] > bss_info.max_power_level) { + PRINTM(MERROR, + "The set powercfg rate value %d dBm is out of range (%d dBm-%d dBm)!\n", + data[2], + (int)bss_info.min_power_level, + (int)bss_info.max_power_level); + ret = -EINVAL; + break; + } + if (data[4] > data[3] - data[2]) { + PRINTM(MERROR, + "Step should not greater than power difference!\n"); + ret = -EINVAL; + break; + } + pcfg->param.power_ext.power_group[0].power_max = + data[3]; + pcfg->param.power_ext.power_group[0]. + power_step = data[4]; + } + pcfg->param.power_ext.num_pwr_grp = 1; + break; + default: + ret = -EINVAL; + break; + } + if (ret) + goto done; + } + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + if (!user_data_len) { + /* GET operation */ + memcpy(respbuf, (t_u8 *)&pcfg->param.power_ext, + sizeof(pcfg->param.power_ext.num_pwr_grp) + + (pcfg->param.power_ext.num_pwr_grp * + sizeof(mlan_power_group))); + ret = sizeof(pcfg->param.power_ext.num_pwr_grp) + + (pcfg->param.power_ext.num_pwr_grp * + sizeof(mlan_power_group)); + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get PS configuration parameters + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return 0 --success, otherwise fail + */ +static int +woal_priv_pscfg(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int data[7] = { 0 }, ret = 0; + mlan_ds_pm_cfg *pm_cfg = NULL; + mlan_ioctl_req *req = NULL; + int allowed = 3; + int i = 3; + int user_data_len = 0, header_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + allowed++; /* For ad-hoc awake period parameter */ + allowed++; /* For beacon missing timeout parameter */ + allowed += 2; /* For delay to PS and PS mode parameters */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_PSCFG); + + if (strlen(respbuf) == header_len) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + memset((char *)data, 0, sizeof(data)); + parse_arguments(respbuf + header_len, data, ARRAY_SIZE(data), + &user_data_len); + } + + if (user_data_len && user_data_len > allowed) { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + pm_cfg = (mlan_ds_pm_cfg *)req->pbuf; + pm_cfg->sub_command = MLAN_OID_PM_CFG_PS_CFG; + req->req_id = MLAN_IOCTL_PM_CFG; + if (user_data_len) { + if ((data[0] < PS_NULL_DISABLE)) { + PRINTM(MERROR, + "Invalid argument for PS null interval\n"); + ret = -EINVAL; + goto done; + } + if ((data[1] != MRVDRV_IGNORE_MULTIPLE_DTIM) + && (data[1] != MRVDRV_MATCH_CLOSEST_DTIM) + && ((data[1] < MRVDRV_MIN_MULTIPLE_DTIM) + || (data[1] > MRVDRV_MAX_MULTIPLE_DTIM))) { + PRINTM(MERROR, "Invalid argument for multiple DTIM\n"); + ret = -EINVAL; + goto done; + } + + if ((data[2] < MRVDRV_MIN_LISTEN_INTERVAL) + && (data[2] != MRVDRV_LISTEN_INTERVAL_DISABLE)) { + PRINTM(MERROR, + "Invalid argument for listen interval\n"); + ret = -EINVAL; + goto done; + } + + if ((data[i] != SPECIAL_ADHOC_AWAKE_PD) && + ((data[i] < MIN_ADHOC_AWAKE_PD) || + (data[i] > MAX_ADHOC_AWAKE_PD))) { + PRINTM(MERROR, + "Invalid argument for adhoc awake period\n"); + ret = -EINVAL; + goto done; + } + i++; + if ((data[i] != DISABLE_BCN_MISS_TO) && + ((data[i] < MIN_BCN_MISS_TO) || + (data[i] > MAX_BCN_MISS_TO))) { + PRINTM(MERROR, + "Invalid argument for beacon miss timeout\n"); + ret = -EINVAL; + goto done; + } + i++; + if (user_data_len < allowed - 1) + data[i] = DELAY_TO_PS_UNCHANGED; + else if ((data[i] < MIN_DELAY_TO_PS) || + (data[i] > MAX_DELAY_TO_PS)) { + PRINTM(MERROR, "Invalid argument for delay to PS\n"); + ret = -EINVAL; + goto done; + } + i++; + if ((data[i] != PS_MODE_UNCHANGED) && (data[i] != PS_MODE_AUTO) + && (data[i] != PS_MODE_POLL) && (data[i] != PS_MODE_NULL)) { + PRINTM(MERROR, "Invalid argument for PS mode\n"); + ret = -EINVAL; + goto done; + } + i++; + req->action = MLAN_ACT_SET; + memcpy(&pm_cfg->param.ps_cfg, data, + MIN(sizeof(pm_cfg->param.ps_cfg), sizeof(data))); + } else + req->action = MLAN_ACT_GET; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + memcpy(data, &pm_cfg->param.ps_cfg, + MIN((sizeof(int) * allowed), sizeof(pm_cfg->param.ps_cfg))); + memcpy(respbuf, (t_u8 *)data, sizeof(int) * allowed); + ret = sizeof(int) * allowed; + if (req->action == MLAN_ACT_SET) { + pm_cfg = (mlan_ds_pm_cfg *)req->pbuf; + pm_cfg->sub_command = MLAN_OID_PM_CFG_IEEE_PS; + pm_cfg->param.ps_mode = 1; + req->req_id = MLAN_IOCTL_PM_CFG; + req->action = MLAN_ACT_SET; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get PS configuration parameters + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return 0 --success, otherwise fail + */ +static int +woal_priv_bcntimeoutcfg(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int data[4] = { 0 }, ret = 0; + mlan_ds_pm_cfg *pm_cfg = NULL; + mlan_ioctl_req *req = NULL; + int allowed = 4; + int i = 0; + int user_data_len = 0, header_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_BCNTIMEOUTCFG); + + memset((char *)data, 0, sizeof(data)); + parse_arguments(respbuf + header_len, data, ARRAY_SIZE(data), + &user_data_len); + if (user_data_len != allowed) { + PRINTM(MERROR, "Invalid args num: input=%d allowed=%d\n", + user_data_len, allowed); + ret = -EINVAL; + goto done; + } + for (i = 0; i < allowed; i++) { + if (data[i] <= 0) { + PRINTM(MERROR, "Invalid data[%d]=%d\n", i, data[i]); + ret = -EINVAL; + goto done; + } + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + pm_cfg = (mlan_ds_pm_cfg *)req->pbuf; + pm_cfg->sub_command = MLAN_OID_PM_CFG_BCN_TIMEOUT; + req->req_id = MLAN_IOCTL_PM_CFG; + req->action = MLAN_ACT_SET; + pm_cfg->param.bcn_timeout.bcn_miss_tmo_window = (t_u16)data[0]; + pm_cfg->param.bcn_timeout.bcn_miss_tmo_period = (t_u16)data[1]; + pm_cfg->param.bcn_timeout.bcn_rq_tmo_window = (t_u16)data[2]; + pm_cfg->param.bcn_timeout.bcn_rq_tmo_period = (t_u16)data[3]; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get sleep period + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return 0 --success, otherwise fail + */ +static int +woal_priv_sleeppd(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int ret = 0; + mlan_ds_pm_cfg *pm_cfg = NULL; + mlan_ioctl_req *req = NULL; + int data = 0; + int user_data_len = 0, header_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + pm_cfg = (mlan_ds_pm_cfg *)req->pbuf; + pm_cfg->sub_command = MLAN_OID_PM_CFG_SLEEP_PD; + req->req_id = MLAN_IOCTL_PM_CFG; + + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_SLEEPPD); + + if (strlen(respbuf) == header_len) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, &data, + sizeof(data) / sizeof(int), &user_data_len); + } + + if (sizeof(int) * user_data_len > sizeof(data)) { + PRINTM(MERROR, "Too many arguments\n"); + LEAVE(); + return -EINVAL; + } + + if (user_data_len) { + if ((data <= MAX_SLEEP_PERIOD && data >= MIN_SLEEP_PERIOD) || + (data == 0) + || (data == SLEEP_PERIOD_RESERVED_FF) + ) { + req->action = MLAN_ACT_SET; + pm_cfg->param.sleep_period = data; + } else { + ret = -EINVAL; + goto done; + } + } else + req->action = MLAN_ACT_GET; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + if (!user_data_len) { + data = pm_cfg->param.sleep_period; + memcpy(respbuf, (t_u8 *)&data, sizeof(data)); + ret = sizeof(data); + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get Tx control flag + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return 0 --success, otherwise fail + */ +static int +woal_priv_txcontrol(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int ret = 0; + mlan_ds_misc_cfg *misc_cfg = NULL; + mlan_ioctl_req *req = NULL; + int data = 0; + int user_data_len = 0, header_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + misc_cfg = (mlan_ds_misc_cfg *)req->pbuf; + misc_cfg->sub_command = MLAN_OID_MISC_TXCONTROL; + req->req_id = MLAN_IOCTL_MISC_CFG; + + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_TXCONTROL); + + if (strlen(respbuf) == header_len) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, &data, + sizeof(data) / sizeof(int), &user_data_len); + } + + if (sizeof(int) * user_data_len > sizeof(data)) { + PRINTM(MERROR, "Too many arguments\n"); + LEAVE(); + return -EINVAL; + } + + if (user_data_len) { + req->action = MLAN_ACT_SET; + misc_cfg->param.tx_control = (t_u32)data; + } else { + req->action = MLAN_ACT_GET; + } + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + if (!user_data_len) { + data = misc_cfg->param.tx_control; + memcpy(respbuf, (t_u8 *)&data, sizeof(data)); + ret = sizeof(data); + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Read/Write adapter registers value + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return 0 --success, otherwise fail + */ +static int +woal_priv_regrdwr(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int data[3]; + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_reg_mem *reg_mem = NULL; + int user_data_len = 0, header_len = 0; + t_u8 *arguments = NULL, *space_ind = NULL; + t_u32 is_negative_val = MFALSE; + mlan_status status = MLAN_STATUS_SUCCESS; + gfp_t flag; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_reg_mem)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + reg_mem = (mlan_ds_reg_mem *)req->pbuf; + reg_mem->sub_command = MLAN_OID_REG_RW; + req->req_id = MLAN_IOCTL_REG_MEM; + + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_REGRDWR); + + if (strlen(respbuf) == header_len) { + ret = -EINVAL; + goto done; + } + /* SET operation */ + memset((char *)data, 0, sizeof(data)); + flag = (in_atomic() || irqs_disabled())? GFP_ATOMIC : GFP_KERNEL; + arguments = kzalloc(strlen(respbuf) * sizeof(char), flag); + if (arguments == NULL) { + ret = -ENOMEM; + goto done; + } + strcpy(arguments, respbuf + header_len); + space_ind = strstr((char *)arguments, " "); + if (space_ind) + space_ind = strstr(space_ind + 1, " "); + if (space_ind) { + if (*(char *)(space_ind + 1) == '-') { + is_negative_val = MTRUE; + arguments[space_ind + 1 - arguments] = '\0'; + strcat(arguments, space_ind + 2); + } + } + parse_arguments(arguments, data, ARRAY_SIZE(data), &user_data_len); + if (is_negative_val == MTRUE) + data[2] *= -1; + + if (user_data_len == 2) { + req->action = MLAN_ACT_GET; + } else if (user_data_len == 3) { + req->action = MLAN_ACT_SET; + } else { + ret = -EINVAL; + goto done; + } + + reg_mem->param.reg_rw.type = (t_u32)data[0]; + reg_mem->param.reg_rw.offset = (t_u32)data[1]; + if (user_data_len == 3) + reg_mem->param.reg_rw.value = (t_u32)data[2]; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (req->action == MLAN_ACT_GET) { + memcpy(respbuf, ®_mem->param.reg_rw, + sizeof(reg_mem->param.reg_rw)); + ret = sizeof(reg_mem->param.reg_rw); + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + kfree(arguments); + LEAVE(); + return ret; +} + +/** + * @brief Read the EEPROM contents of the card + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return 0 --success, otherwise fail + */ +static int +woal_priv_rdeeprom(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int data[2]; + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_reg_mem *reg_mem = NULL; + int user_data_len = 0, header_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_reg_mem)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + reg_mem = (mlan_ds_reg_mem *)req->pbuf; + reg_mem->sub_command = MLAN_OID_EEPROM_RD; + req->req_id = MLAN_IOCTL_REG_MEM; + + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_RDEEPROM); + + if (strlen(respbuf) == header_len) { + ret = -EINVAL; + goto done; + } + /* SET operation */ + memset((char *)data, 0, sizeof(data)); + parse_arguments(respbuf + header_len, data, ARRAY_SIZE(data), + &user_data_len); + + if (user_data_len == 2) { + req->action = MLAN_ACT_GET; + } else { + ret = -EINVAL; + goto done; + } + + reg_mem->param.rd_eeprom.offset = (t_u16)data[0]; + reg_mem->param.rd_eeprom.byte_count = (t_u16)data[1]; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (req->action == MLAN_ACT_GET) { + memcpy(respbuf, ®_mem->param.rd_eeprom, + sizeof(reg_mem->param.rd_eeprom)); + ret = sizeof(reg_mem->param.rd_eeprom); + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Read/Write device memory value + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return 0 --success, otherwise fail + */ +static int +woal_priv_memrdwr(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int data[2]; + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_reg_mem *reg_mem = NULL; + int user_data_len = 0, header_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_reg_mem)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + reg_mem = (mlan_ds_reg_mem *)req->pbuf; + reg_mem->sub_command = MLAN_OID_MEM_RW; + req->req_id = MLAN_IOCTL_REG_MEM; + + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_MEMRDWR); + + if (strlen(respbuf) == header_len) { + ret = -EINVAL; + goto done; + } + /* SET operation */ + memset((char *)data, 0, sizeof(data)); + parse_arguments(respbuf + header_len, data, ARRAY_SIZE(data), + &user_data_len); + + if (user_data_len == 1) { + PRINTM(MINFO, "MEM_RW: GET\n"); + req->action = MLAN_ACT_GET; + } else if (user_data_len == 2) { + PRINTM(MINFO, "MEM_RW: SET\n"); + req->action = MLAN_ACT_SET; + } else { + ret = -EINVAL; + goto done; + } + + reg_mem->param.mem_rw.addr = (t_u32)data[0]; + if (user_data_len == 2) + reg_mem->param.mem_rw.value = (t_u32)data[1]; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (req->action == MLAN_ACT_GET) { + memcpy(respbuf, ®_mem->param.mem_rw, + sizeof(reg_mem->param.mem_rw)); + ret = sizeof(reg_mem->param.mem_rw); + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Cmd52 read/write register + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +static int +woal_priv_sdcmd52rw(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + t_u8 rw = 0, func, data = 0; + int buf[3], reg, ret = MLAN_STATUS_SUCCESS; + int user_data_len = 0, header_len = 0; + + ENTER(); + + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_SDCMD52RW); + memset((t_u8 *)buf, 0, sizeof(buf)); + + if (strlen(respbuf) == header_len) { + PRINTM(MERROR, "Invalid number of arguments\n"); + ret = -EINVAL; + goto done; + } + parse_arguments(respbuf + header_len, buf, ARRAY_SIZE(buf), + &user_data_len); + + if (user_data_len < 2 || user_data_len > 3) { + PRINTM(MERROR, "Invalid number of arguments\n"); + ret = -EINVAL; + goto done; + } + + func = (t_u8)buf[0]; + if (func > 7) { + PRINTM(MERROR, "Invalid function number!\n"); + ret = -EINVAL; + goto done; + } + reg = (t_u32)buf[1]; + if (user_data_len == 2) { + rw = 0; /* CMD52 read */ + PRINTM(MINFO, "Cmd52 read, func=%d, reg=0x%08X\n", func, reg); + } + if (user_data_len == 3) { + rw = 1; /* CMD52 write */ + data = (t_u8)buf[2]; + PRINTM(MINFO, "Cmd52 write, func=%d, reg=0x%08X, data=0x%02X\n", + func, reg, data); + } + + if (!rw) { + sdio_claim_host(((struct sdio_mmc_card *)priv->phandle->card)-> + func); + if (func) + data = sdio_readb(((struct sdio_mmc_card *)priv-> + phandle->card)->func, reg, &ret); + else + data = sdio_f0_readb(((struct sdio_mmc_card *)priv-> + phandle->card)->func, reg, &ret); + sdio_release_host(((struct sdio_mmc_card *)priv->phandle-> + card)->func); + if (ret) { + PRINTM(MERROR, + "sdio_readb: reading register 0x%X failed\n", + reg); + goto done; + } + } else { + sdio_claim_host(((struct sdio_mmc_card *)priv->phandle->card)-> + func); + if (func) + sdio_writeb(((struct sdio_mmc_card *)priv->phandle-> + card)->func, data, reg, &ret); + else + sdio_f0_writeb(((struct sdio_mmc_card *)priv->phandle-> + card)->func, data, reg, &ret); + sdio_release_host(((struct sdio_mmc_card *)priv->phandle-> + card)->func); + if (ret) { + PRINTM(MERROR, + "sdio_writeb: writing register 0x%X failed\n", + reg); + goto done; + } + } + + /* Action = GET */ + buf[0] = data; + + memcpy(respbuf, &buf, sizeof(int)); + ret = sizeof(int); + +done: + LEAVE(); + return ret; +} + +#ifdef STA_SUPPORT +/** + * @brief arpfilter ioctl function + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return 0 --success, otherwise fail + */ +static int +woal_priv_arpfilter(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int ret = 0; + mlan_ds_misc_cfg *misc = NULL; + mlan_ioctl_req *req = NULL; + t_u8 *data_ptr = NULL; + t_u32 buf_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + misc = (mlan_ds_misc_cfg *)req->pbuf; + misc->sub_command = MLAN_OID_MISC_GEN_IE; + req->req_id = MLAN_IOCTL_MISC_CFG; + req->action = MLAN_ACT_SET; + misc->param.gen_ie.type = MLAN_IE_TYPE_ARP_FILTER; + + data_ptr = respbuf + (strlen(CMD_MARVELL) + strlen(PRIV_CMD_ARPFILTER)); + buf_len = *((t_u16 *)data_ptr); + misc->param.gen_ie.len = buf_len; + memcpy((void *)(misc->param.gen_ie.ie_data), data_ptr + sizeof(buf_len), + buf_len); + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} +#endif /* STA_SUPPORT */ + +#if defined(SDIO_SUSPEND_RESUME) +/** + * @brief Set / Get Auto ARP Response configuration + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int +woal_priv_set_get_auto_arp(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int data[4]; + int user_data_len = 0; + int ret = 0; + moal_handle *handle = NULL; + + ENTER(); + + if (priv == NULL) { + PRINTM(MERROR, "Invalid priv\n"); + goto done; + } + handle = priv->phandle; + + if (strlen(respbuf) == + (strlen(CMD_MARVELL) + strlen(PRIV_CMD_AUTO_ARP))) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + memset((char *)data, 0, sizeof(data)); + parse_arguments(respbuf + strlen(CMD_MARVELL) + + strlen(PRIV_CMD_AUTO_ARP), data, + ARRAY_SIZE(data), &user_data_len); + } + + if (user_data_len > 1) { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + + if (user_data_len) { + /* Get the enable/disable value from user */ + handle->hs_auto_arp = data[0]; + PRINTM(MIOCTL, "Auto ARP : %s\n", + handle->hs_auto_arp ? "enable" : "disable"); + } + + memcpy(respbuf, &handle->hs_auto_arp, sizeof(handle->hs_auto_arp)); + ret = sizeof(handle->hs_auto_arp); + +done: + LEAVE(); + return ret; +} +#endif + +/** + * @brief Get/Set deauth control + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +static int +woal_priv_deauth_ctrl(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + mlan_ds_snmp_mib *cfg = NULL; + mlan_ioctl_req *req = NULL; + int ret = 0, header_len = 0, data = 0, user_data_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_snmp_mib)); + if (req == NULL) { + ret = ENOMEM; + goto done; + } + + cfg = (mlan_ds_snmp_mib *)req->pbuf; + cfg->sub_command = MLAN_OID_SNMP_MIB_CTRL_DEAUTH; + req->req_id = MLAN_IOCTL_SNMP_MIB; + + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_DEAUTH_CTRL); + + if (strlen(respbuf) == header_len) { + /* GET operation */ + req->action = MLAN_ACT_GET; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, &data, + sizeof(data) / sizeof(int), &user_data_len); + if (user_data_len != 1) { + PRINTM(MERROR, "Invalid number of args! %d\n", + user_data_len); + ret = -EINVAL; + goto done; + } + req->action = MLAN_ACT_SET; + cfg->param.deauthctrl = (t_u8)data; + } + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + data = (int)cfg->param.deauthctrl; + memcpy(respbuf, &data, sizeof(data)); + ret = sizeof(data); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +#define MRVL_TLV_HEADER_SIZE 4 +/** + * @brief Get/Set per packet Txctl and Rxinfo configuration + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +static int +woal_priv_per_pkt_cfg(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + mlan_ds_misc_cfg *misc = NULL; + mlan_ioctl_req *req = NULL; + int ret = 0; + t_u8 *pos = NULL; + int left_len, header_len = 0; + mlan_per_pkt_cfg *perpkt = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_PER_PKT_CFG); + pos = respbuf + header_len; + left_len = respbuflen - header_len; + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = ENOMEM; + goto done; + } + + misc = (mlan_ds_misc_cfg *)req->pbuf; + misc->sub_command = MLAN_OID_MISC_PER_PKT_CFG; + req->req_id = MLAN_IOCTL_MISC_CFG; + + if (*pos == 0) { + /* GET operation */ + pos++; + if (priv->tx_protocols.protocol_num) { + perpkt = (mlan_per_pkt_cfg *) pos; + perpkt->type = TLV_TYPE_PER_PKT_CFG; + perpkt->tx_rx_control = TX_PKT_CTRL; + perpkt->proto_type_num = + priv->tx_protocols.protocol_num; + memcpy(perpkt->ether_type, priv->tx_protocols.protocols, + perpkt->proto_type_num * sizeof(t_u16)); + perpkt->len = + (perpkt->proto_type_num + 1) * sizeof(t_u16); + pos += perpkt->len + MRVL_TLV_HEADER_SIZE; + } + if (priv->rx_protocols.protocol_num) { + perpkt = (mlan_per_pkt_cfg *) pos; + perpkt->type = TLV_TYPE_PER_PKT_CFG; + perpkt->tx_rx_control = RX_PKT_INFO; + perpkt->proto_type_num = + priv->rx_protocols.protocol_num; + memcpy(perpkt->ether_type, priv->rx_protocols.protocols, + perpkt->proto_type_num * sizeof(t_u16)); + perpkt->len = + (perpkt->proto_type_num + 1) * sizeof(t_u16); + pos += perpkt->len + MRVL_TLV_HEADER_SIZE; + } + ret = pos - respbuf; + goto done; + } else if (*pos == 1) { + /* SET operation */ + req->action = MLAN_ACT_SET; + pos++; + left_len--; + while (*pos == TLV_TYPE_PER_PKT_CFG && (left_len > 2)) { + perpkt = (mlan_per_pkt_cfg *) pos; + if (perpkt->tx_rx_control & TX_PKT_CTRL) { + priv->tx_protocols.protocol_num = + perpkt->proto_type_num; + if (perpkt->proto_type_num <= + MAX_NUM_ETHER_TYPE) + memcpy(priv->tx_protocols.protocols, + perpkt->ether_type, + perpkt->proto_type_num * + sizeof(t_u16)); + } + if (perpkt->tx_rx_control & RX_PKT_INFO) { + priv->rx_protocols.protocol_num = + perpkt->proto_type_num; + if (perpkt->proto_type_num <= + MAX_NUM_ETHER_TYPE) + memcpy(priv->rx_protocols.protocols, + perpkt->ether_type, + perpkt->proto_type_num * + sizeof(t_u16)); + } + if (!perpkt->tx_rx_control) { + memset(&priv->tx_protocols, 0, + sizeof(dot11_protocol)); + memset(&priv->rx_protocols, 0, + sizeof(dot11_protocol)); + } + pos += perpkt->len + MRVL_TLV_HEADER_SIZE; + left_len -= (perpkt->len + MRVL_TLV_HEADER_SIZE); + } + } + misc->param.txrx_pkt_ctrl = perpkt->tx_rx_control; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get Mgmt Frame passthru mask + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return 0 --success, otherwise fail + */ +int +woal_priv_mgmt_frame_passthru_ctrl(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int ret = 0; + int data = 0; + int user_data_len = 0, header_len = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *mgmt_cfg = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_MGMT_FRAME_CTRL); + if (strlen(respbuf) == header_len) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, &data, 1, &user_data_len); + } + + if (user_data_len >= 2) { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } else { + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + mgmt_cfg = (mlan_ds_misc_cfg *)req->pbuf; + req->req_id = MLAN_IOCTL_MISC_CFG; + mgmt_cfg->sub_command = MLAN_OID_MISC_RX_MGMT_IND; + + if (user_data_len == 0) { /* Get */ + req->action = MLAN_ACT_GET; + } else { /* Set */ + mgmt_cfg->param.mgmt_subtype_mask = data; + req->action = MLAN_ACT_SET; + } + } + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + data = mgmt_cfg->param.mgmt_subtype_mask; + memcpy(respbuf, (t_u8 *)&data, sizeof(data)); + ret = sizeof(data); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; + +} + +/** + * @brief Private IOCTL entry to send an ADDTS TSPEC + * + * Receive a ADDTS command from the application. The command structure + * contains a TSPEC and timeout in milliseconds. The timeout is performed + * in the firmware after the ADDTS command frame is sent. + * + * The TSPEC is received in the API as an opaque block. The firmware will + * send the entire data block, including the bytes after the TSPEC. This + * is done to allow extra IEs to be packaged with the TSPEC in the ADDTS + * action frame. + * + * The IOCTL structure contains two return fields: + * - The firmware command result, which indicates failure and timeouts + * - The IEEE Status code which contains the corresponding value from + * any ADDTS response frame received. + * + * In addition, the opaque TSPEC data block passed in is replaced with the + * TSPEC received in the ADDTS response frame. In case of failure, the + * AP may modify the TSPEC on return and in the case of success, the + * medium time is returned as calculated by the AP. Along with the TSPEC, + * any IEs that are sent in the ADDTS response are also returned and can be + * parsed using the IOCTL length as an indicator of extra elements. + * + * The return value to the application layer indicates a driver execution + * success or failure. A successful return could still indicate a firmware + * failure or AP negotiation failure via the commandResult field copied + * back to the application. + * + * @param priv Pointer to the mlan_private driver data struct + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written if successful else negative value + */ +static int +woal_priv_wmm_addts_req_ioctl(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_wmm_cfg *cfg = NULL; + wlan_ioctl_wmm_addts_req_t addts_ioctl; + int ret = 0, header_len = 0, copy_len = sizeof(addts_ioctl); + t_u8 *data_ptr; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_ADDTS); + data_ptr = respbuf + header_len; + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_wmm_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + req->req_id = MLAN_IOCTL_WMM_CFG; + cfg = (mlan_ds_wmm_cfg *)req->pbuf; + cfg->sub_command = MLAN_OID_WMM_CFG_ADDTS; + + memset(&addts_ioctl, 0x00, sizeof(addts_ioctl)); + + memcpy((t_u8 *)&addts_ioctl, data_ptr, sizeof(addts_ioctl)); + + cfg->param.addts.timeout = addts_ioctl.timeout_ms; + cfg->param.addts.ie_data_len = addts_ioctl.ie_data_len; + + memcpy(cfg->param.addts.ie_data, + addts_ioctl.ie_data, cfg->param.addts.ie_data_len); + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + addts_ioctl.cmd_result = cfg->param.addts.result; + addts_ioctl.ieee_status_code = (t_u8)cfg->param.addts.status_code; + addts_ioctl.ie_data_len = cfg->param.addts.ie_data_len; + + memcpy(addts_ioctl.ie_data, + cfg->param.addts.ie_data, cfg->param.addts.ie_data_len); + + copy_len = (sizeof(addts_ioctl) + - sizeof(addts_ioctl.ie_data) + + cfg->param.addts.ie_data_len); + + memcpy(respbuf, (t_u8 *)&addts_ioctl, copy_len); + ret = copy_len; + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Private IOCTL entry to send a DELTS TSPEC + * + * Receive a DELTS command from the application. The command structure + * contains a TSPEC and reason code along with space for a command result + * to be returned. The information is packaged is sent to the wlan_cmd.c + * firmware command prep and send routines for execution in the firmware. + * + * The reason code is not used for WMM implementations but is indicated in + * the 802.11e specification. + * + * The return value to the application layer indicates a driver execution + * success or failure. A successful return could still indicate a firmware + * failure via the cmd_result field copied back to the application. + * + * @param priv Pointer to the mlan_private driver data struct + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written if successful else negative value + */ +static int +woal_priv_wmm_delts_req_ioctl(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_wmm_cfg *cfg = NULL; + wlan_ioctl_wmm_delts_req_t delts_ioctl; + int ret = 0, header_len = 0, copy_len = 0; + t_u8 *data_ptr; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_DELTS); + data_ptr = respbuf + header_len; + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_wmm_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + req->req_id = MLAN_IOCTL_WMM_CFG; + cfg = (mlan_ds_wmm_cfg *)req->pbuf; + cfg->sub_command = MLAN_OID_WMM_CFG_DELTS; + + memset(&delts_ioctl, 0x00, sizeof(delts_ioctl)); + + if (strlen(respbuf) > header_len) { + copy_len = MIN(strlen(data_ptr), sizeof(delts_ioctl)); + memcpy((t_u8 *)&delts_ioctl, data_ptr, copy_len); + + cfg->param.delts.status_code = + (t_u32)delts_ioctl.ieee_reason_code; + cfg->param.delts.ie_data_len = (t_u8)delts_ioctl.ie_data_len; + + memcpy(cfg->param.delts.ie_data, + delts_ioctl.ie_data, cfg->param.delts.ie_data_len); + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + /* Return the firmware command result back to the application layer */ + delts_ioctl.cmd_result = cfg->param.delts.result; + copy_len = sizeof(delts_ioctl); + memcpy(respbuf, (t_u8 *)&delts_ioctl, copy_len); + ret = copy_len; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Private IOCTL entry to get/set a specified AC Queue's parameters + * + * Receive a AC Queue configuration command which is used to get, set, or + * default the parameters associated with a specific WMM AC Queue. + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return 0 --success, otherwise fail + */ +static int +woal_priv_qconfig(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_wmm_cfg *pwmm = NULL; + mlan_ds_wmm_queue_config *pqcfg = NULL; + wlan_ioctl_wmm_queue_config_t qcfg_ioctl; + t_u8 *data_ptr; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_wmm_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + req->req_id = MLAN_IOCTL_WMM_CFG; + pwmm = (mlan_ds_wmm_cfg *)req->pbuf; + pwmm->sub_command = MLAN_OID_WMM_CFG_QUEUE_CONFIG; + + memset(&qcfg_ioctl, 0x00, sizeof(qcfg_ioctl)); + pqcfg = (mlan_ds_wmm_queue_config *)&pwmm->param.q_cfg; + data_ptr = respbuf + (strlen(CMD_MARVELL) + strlen(PRIV_CMD_QCONFIG)); + + memcpy((t_u8 *)&qcfg_ioctl, data_ptr, sizeof(qcfg_ioctl)); + pqcfg->action = qcfg_ioctl.action; + pqcfg->access_category = qcfg_ioctl.access_category; + pqcfg->msdu_lifetime_expiry = qcfg_ioctl.msdu_lifetime_expiry; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + memset(&qcfg_ioctl, 0x00, sizeof(qcfg_ioctl)); + qcfg_ioctl.action = pqcfg->action; + qcfg_ioctl.access_category = pqcfg->access_category; + qcfg_ioctl.msdu_lifetime_expiry = pqcfg->msdu_lifetime_expiry; + memcpy(data_ptr, (t_u8 *)&qcfg_ioctl, sizeof(qcfg_ioctl)); + ret = strlen(CMD_MARVELL) + strlen(PRIV_CMD_QCONFIG) + + sizeof(qcfg_ioctl); +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Private IOCTL entry to get the status of the WMM queues + * + * Return the following information for each WMM AC: + * - WMM IE Acm Required + * - Firmware Flow Required + * - Firmware Flow Established + * - Firmware Queue Enabled + * - Firmware Delivery Enabled + * - Firmware Trigger Enabled + * + * @param priv Pointer to the moal_private driver data struct + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written if successful else negative value + */ +static int +woal_priv_wmm_queue_status_ioctl(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_wmm_cfg *pwmm = NULL; + wlan_ioctl_wmm_queue_status_t qstatus_ioctl; + int ret = 0, header_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_QSTATUS); + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_wmm_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + req->req_id = MLAN_IOCTL_WMM_CFG; + pwmm = (mlan_ds_wmm_cfg *)req->pbuf; + pwmm->sub_command = MLAN_OID_WMM_CFG_QUEUE_STATUS; + + if (strlen(respbuf) == header_len) { + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + memset(&qstatus_ioctl, 0x00, sizeof(qstatus_ioctl)); + memcpy((void *)&qstatus_ioctl, (void *)&pwmm->param.q_status, + sizeof(qstatus_ioctl)); + memcpy(respbuf, (t_u8 *)&qstatus_ioctl, sizeof(qstatus_ioctl)); + ret = sizeof(qstatus_ioctl); + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Private IOCTL entry to get the status of the WMM Traffic Streams + * + * @param priv Pointer to the moal_private driver data struct + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written if successful else negative value + */ +static int +woal_priv_wmm_ts_status_ioctl(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_wmm_cfg *pwmm = NULL; + wlan_ioctl_wmm_ts_status_t ts_status_ioctl; + int ret = 0, header_len = 0; + t_u8 *data_ptr; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_TS_STATUS); + data_ptr = respbuf + header_len; + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_wmm_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + req->req_id = MLAN_IOCTL_WMM_CFG; + pwmm = (mlan_ds_wmm_cfg *)req->pbuf; + pwmm->sub_command = MLAN_OID_WMM_CFG_TS_STATUS; + + memset(&ts_status_ioctl, 0x00, sizeof(ts_status_ioctl)); + + memcpy((t_u8 *)&ts_status_ioctl, data_ptr, sizeof(ts_status_ioctl)); + + memset(&pwmm->param.ts_status, 0x00, sizeof(ts_status_ioctl)); + pwmm->param.ts_status.tid = ts_status_ioctl.tid; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + memset(&ts_status_ioctl, 0x00, sizeof(ts_status_ioctl)); + memcpy((void *)&ts_status_ioctl, (void *)&pwmm->param.ts_status, + sizeof(ts_status_ioctl)); + memcpy(respbuf, (t_u8 *)&ts_status_ioctl, sizeof(ts_status_ioctl)); + ret = sizeof(ts_status_ioctl); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get MAC control + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int +woal_priv_macctrl(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int data = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *cfg = NULL; + int ret = 0; + int user_data_len = 0, header_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_MAC_CTRL); + if (strlen(respbuf) == header_len) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, &data, 1, &user_data_len); + } + + if (user_data_len > 1) { + PRINTM(MERROR, "Invalid number of arguments\n"); + ret = -EINVAL; + goto done; + } + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + cfg = (mlan_ds_misc_cfg *)req->pbuf; + cfg->sub_command = MLAN_OID_MISC_MAC_CONTROL; + req->req_id = MLAN_IOCTL_MISC_CFG; + + if (user_data_len == 0) + req->action = MLAN_ACT_GET; + else { + cfg->param.mac_ctrl = (t_u32)data; + req->action = MLAN_ACT_SET; + } + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + memcpy(respbuf, (t_u8 *)&cfg->param.mac_ctrl, sizeof(data)); + ret = sizeof(data); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Get connection status + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return 0 --success, otherwise fail + */ +int +woal_priv_getwap(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int ret = 0; +#ifdef STA_SUPPORT + mlan_bss_info bss_info; +#endif + + ENTER(); + +#ifdef STA_SUPPORT + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) { + memset(&bss_info, 0, sizeof(bss_info)); + + woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info); + + if (bss_info.media_connected == MTRUE) { + memcpy(respbuf, (t_u8 *)&bss_info.bssid, + MLAN_MAC_ADDR_LENGTH); + } else { + memset(respbuf, 0, MLAN_MAC_ADDR_LENGTH); + } + } +#endif +#ifdef UAP_SUPPORT + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) { + if (priv->bss_started) { + memcpy(respbuf, priv->current_addr, + MLAN_MAC_ADDR_LENGTH); + } else { + memset(respbuf, 0, MLAN_MAC_ADDR_LENGTH); + } + } +#endif + ret = MLAN_MAC_ADDR_LENGTH; + LEAVE(); + return ret; +} + +/** + * @brief Set/Get Region Code + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int +woal_priv_region_code(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int data = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *cfg = NULL; + int ret = 0; + int user_data_len = 0, header_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_REGION_CODE); + if (strlen(respbuf) == header_len) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, &data, 1, &user_data_len); + } + + if (user_data_len > 1) { + PRINTM(MERROR, "Invalid number of arguments\n"); + ret = -EINVAL; + goto done; + } + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + cfg = (mlan_ds_misc_cfg *)req->pbuf; + cfg->sub_command = MLAN_OID_MISC_REGION; + req->req_id = MLAN_IOCTL_MISC_CFG; + + if (user_data_len == 0) + req->action = MLAN_ACT_GET; + else { + cfg->param.region_code = (t_u32)data; + req->action = MLAN_ACT_SET; + } + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + memcpy(respbuf, (t_u8 *)&cfg->param.region_code, sizeof(data)); + ret = sizeof(data); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get channel time and buffer weight + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int +woal_priv_multi_chan_config(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *cfg = NULL; + t_u8 *data_ptr; + int ret = 0; + int user_data_len = 0, header_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + data_ptr = + respbuf + strlen(CMD_MARVELL) + strlen(PRIV_CMD_MULTI_CHAN_CFG); + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_MULTI_CHAN_CFG); + if (strlen(respbuf) == header_len) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + user_data_len = sizeof(mlan_ds_multi_chan_cfg); + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + cfg = (mlan_ds_misc_cfg *)req->pbuf; + cfg->sub_command = MLAN_OID_MISC_MULTI_CHAN_CFG; + req->req_id = MLAN_IOCTL_MISC_CFG; + + if (user_data_len == 0) { + req->action = MLAN_ACT_GET; + } else { + req->action = MLAN_ACT_SET; + memcpy(&cfg->param.multi_chan_cfg, data_ptr, + sizeof(mlan_ds_multi_chan_cfg)); + } + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + memcpy(respbuf, (mlan_ds_multi_chan_cfg *)&cfg->param.multi_chan_cfg, + req->buf_len); + ret = req->buf_len; + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get multi_channel policy setting + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int +woal_priv_multi_chan_policy(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int ret = 0; + int user_data_len = 0, header_len = 0; + int data = 0; + t_u16 enable; + t_u8 action; + + ENTER(); + + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_MULTI_CHAN_POLICY); + if (strlen(respbuf) == header_len) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, &data, 1, &user_data_len); + } + + if (user_data_len > 1) { + PRINTM(MERROR, "Invalid number of arguments\n"); + ret = -EINVAL; + goto done; + } + + if (user_data_len == 0) { + action = MLAN_ACT_GET; + } else { + action = MLAN_ACT_SET; + enable = (t_u16)data; + } + + if (MLAN_STATUS_SUCCESS != + woal_mc_policy_cfg(priv, &enable, MOAL_IOCTL_WAIT, action)) { + ret = -EFAULT; + goto done; + } + memcpy(respbuf, &enable, sizeof(t_u16)); + ret = sizeof(t_u16); + +done: + LEAVE(); + return ret; +} + +/** + * @brief Set/Get drcs time slicing parameters + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int +woal_priv_drcs_time_slicing_cfg(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *cfg = NULL; + mlan_ds_drcs_cfg *drcs_cfg = NULL; + t_u8 *data_ptr; + int ret = 0; + int user_data_len = 0, header_len = 0; + int data[8]; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + data_ptr = respbuf + strlen(CMD_MARVELL) + strlen(PRIV_CMD_DRCS_CFG); + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_DRCS_CFG); + if (strlen(respbuf) == header_len) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + memset((char *)data, 0, sizeof(data)); + parse_arguments(data_ptr, data, ARRAY_SIZE(data), + &user_data_len); + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + cfg = (mlan_ds_misc_cfg *)req->pbuf; + cfg->sub_command = MLAN_OID_MISC_DRCS_CFG; + req->req_id = MLAN_IOCTL_MISC_CFG; + + if (user_data_len == 0) { + req->action = MLAN_ACT_GET; + } else { + req->action = MLAN_ACT_SET; + drcs_cfg = (mlan_ds_drcs_cfg *) & cfg->param.drcs_cfg[0]; + drcs_cfg->chantime = (t_u8)data[0]; + drcs_cfg->switchtime = (t_u8)data[1]; + drcs_cfg->undozetime = (t_u8)data[2]; + drcs_cfg->mode = (t_u8)data[3]; + /* Set the same parameters for two channels */ + if (user_data_len < ARRAY_SIZE(data)) + drcs_cfg->chan_idx = 0x03; + else { + /* Set the different parameters for two channels */ + drcs_cfg->chan_idx = 0x1; + drcs_cfg = + (mlan_ds_drcs_cfg *) & cfg->param.drcs_cfg[1]; + drcs_cfg->chan_idx = 0x2; + drcs_cfg->chantime = (t_u8)data[4]; + drcs_cfg->switchtime = (t_u8)data[5]; + drcs_cfg->undozetime = (t_u8)data[6]; + drcs_cfg->mode = (t_u8)data[7]; + } + } + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + memcpy(respbuf, (t_u8 *)&cfg->param.drcs_cfg, req->buf_len); + ret = req->buf_len; + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +#ifdef RX_PACKET_COALESCE +/** + * @brief Set/Get RX packet coalesceing setting + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int +woal_priv_rx_pkt_coalesce_cfg(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int ret = 0; + t_u32 data[2]; + int user_data_len = 0, header_len = 0; + mlan_ds_misc_cfg *cfg = NULL; + t_u8 *data_ptr; + mlan_ioctl_req *req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + data_ptr = respbuf + strlen(CMD_MARVELL) + strlen(PRIV_CMD_RX_COAL_CFG); + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_RX_COAL_CFG); + if (strlen(respbuf) == header_len) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, data, ARRAY_SIZE(data), + &user_data_len); + } + + if (sizeof(int) * user_data_len > sizeof(data)) { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + + if ((user_data_len != 0) && (user_data_len != 2)) { + PRINTM(MERROR, "Invalid arguments\n"); + ret = -EINVAL; + goto done; + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + cfg = (mlan_ds_misc_cfg *)req->pbuf; + cfg->sub_command = MLAN_OID_MISC_RX_PACKET_COALESCE; + req->req_id = MLAN_IOCTL_MISC_CFG; + + if (user_data_len == 0) { + req->action = MLAN_ACT_GET; + } else { + req->action = MLAN_ACT_SET; + cfg->param.rx_coalesce.packet_threshold = data[0]; + cfg->param.rx_coalesce.delay = data[1]; + } + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + memcpy(respbuf, + (mlan_ds_misc_rx_packet_coalesce *)&cfg->param.rx_coalesce, + req->buf_len); + ret = req->buf_len; + +done: + LEAVE(); + return ret; +} +#endif +/** + * @brief Set/Get FW side mac address + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return 0 --success, otherwise fail + */ +int +woal_priv_fwmacaddr(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + t_u8 data[ETH_ALEN]; + int ret = 0; + int header_len = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_bss *bss = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_FWMACADDR); + + /* Allocate an IOCTL request buffer */ + req = (mlan_ioctl_req *)woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + /* Fill request buffer */ + bss = (mlan_ds_bss *)req->pbuf; + bss->sub_command = MLAN_OID_BSS_MAC_ADDR; + req->req_id = MLAN_IOCTL_BSS; + + if (strlen(respbuf) == header_len) { + /* GET operation */ + req->action = MLAN_ACT_GET; + } else { + /* SET operation */ + req->action = MLAN_ACT_SET; + memset(data, 0, sizeof(data)); + woal_mac2u8(data, respbuf + header_len); + memcpy(bss->param.mac_addr, data, ETH_ALEN); + } + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + memcpy(respbuf, bss->param.mac_addr, sizeof(data)); + ret = sizeof(data); + HEXDUMP("FW MAC Addr:", respbuf, ETH_ALEN); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +#ifdef STA_CFG80211 +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) +/** + * @brief Set offchannel + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return 0 --success, otherwise fail + */ +int +woal_priv_offchannel(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int data[3]; + int ret = 0; + t_u8 status = 1; + int user_data_len = 0, header_len = 0; + + ENTER(); + + memset(data, 0, sizeof(data)); + + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_OFFCHANNEL); + + if (header_len == strlen(respbuf)) { + /* Query current remain on channel status */ + if (priv->phandle->remain_on_channel) + ret = sprintf(respbuf, + "There is pending remain on channel from bss %d\n", + priv->phandle->remain_bss_index) + 1; + else + ret = sprintf(respbuf, + "There is no pending remain on channel\n") + + 1; + goto done; + } else + parse_arguments(respbuf + header_len, data, ARRAY_SIZE(data), + &user_data_len); + + if (sizeof(int) * user_data_len > sizeof(data)) { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + + if (user_data_len >= 1) { + if ((data[0] != 0) && (data[0] != 1)) { + PRINTM(MERROR, "action (%d) must be either 0 or 1\n", + data[0]); + ret = -EINVAL; + goto done; + } + } + if (user_data_len == 2) { + if (data[0] == 1) { + PRINTM(MERROR, + "channel and duration must both the mentioned\n"); + ret = -EINVAL; + goto done; + } else { + PRINTM(MWARN, + "extra arguments are ignored since action is 'cancel'\n"); + } + } + if (user_data_len == 3) { + if (data[0] == 1) { + if (data[1] < 0) { + PRINTM(MERROR, "channel cannot be negative\n"); + ret = -EINVAL; + goto done; + } + if (data[2] < 0) { + PRINTM(MERROR, "duration cannot be negative\n"); + ret = -EINVAL; + goto done; + } + } + } + + if (data[0] == 0) { + if (!priv->phandle->remain_on_channel) { + ret = sprintf(respbuf, + "There is no pending remain on channel to be canceled\n") + + 1; + goto done; + } + if (woal_cfg80211_remain_on_channel_cfg + (priv, MOAL_IOCTL_WAIT, MTRUE, &status, NULL, 0, 0)) { + PRINTM(MERROR, "remain_on_channel: Failed to cancel\n"); + ret = -EFAULT; + goto done; + } + if (status == MLAN_STATUS_SUCCESS) + priv->phandle->remain_on_channel = MFALSE; + } else if (data[0] == 1) { + if (woal_cfg80211_remain_on_channel_cfg + (priv, MOAL_IOCTL_WAIT, MFALSE, &status, + ieee80211_get_channel(priv->wdev->wiphy, + ieee80211_channel_to_frequency(data + [1] +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + , + (data + [1] + <= + 14 ? + IEEE80211_BAND_2GHZ + : + IEEE80211_BAND_5GHZ) +#endif + )), 0, (t_u32)data[2])) { + PRINTM(MERROR, "remain_on_channel: Failed to start\n"); + ret = -EFAULT; + goto done; + } + if (status == MLAN_STATUS_SUCCESS) { + priv->phandle->remain_on_channel = MTRUE; + priv->phandle->remain_bss_index = priv->bss_index; + } + } + + if (status != MLAN_STATUS_SUCCESS) + ret = -EFAULT; + else + ret = sprintf(respbuf, "OK\n") + 1; + +done: + LEAVE(); + return ret; +} +#endif +#endif + +/** + * @brief Set/Get dscp map + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return 0 --success, otherwise fail + */ +int +woal_priv_set_get_dscp_map(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int ret = MLAN_STATUS_SUCCESS; + t_u8 *pos = NULL; + int copy_size = 0, header_len = 0; + + ENTER(); + + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_DSCP_MAP); + if (strlen(respbuf) != header_len) { + /* SET operation */ + pos = respbuf + header_len; + memcpy(priv->dscp_map, pos, sizeof(priv->dscp_map)); + } + + copy_size = MIN(sizeof(priv->dscp_map), respbuflen); + memcpy(respbuf, priv->dscp_map, copy_size); + ret = copy_size; + + LEAVE(); + return ret; +} + +/** + * @brief Get extended driver version + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int +woal_priv_get_driver_verext(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int data = 0; + mlan_ds_get_info *info = NULL; + mlan_ioctl_req *req = NULL; + int ret = 0; + int copy_size = 0; + int user_data_len = 0, header_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_get_info)); + if (req == NULL) { + ret = ENOMEM; + goto done; + } + + info = (mlan_ds_get_info *)req->pbuf; + info->sub_command = MLAN_OID_GET_VER_EXT; + req->req_id = MLAN_IOCTL_GET_INFO; + req->action = MLAN_ACT_GET; + + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_VEREXT); + + if (strlen(respbuf) == header_len) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, &data, 1, &user_data_len); + } + + if (user_data_len > 1) { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + info->param.ver_ext.version_str_sel = data; + if (((t_s32)(info->param.ver_ext.version_str_sel)) < 0) { + PRINTM(MERROR, "Invalid arguments!\n"); + ret = -EINVAL; + goto done; + } + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + /* + * Set the amount to copy back to the application as the minimum of the + * available assoc resp data or the buffer provided by the application + */ + copy_size = MIN(strlen(info->param.ver_ext.version_str), respbuflen); + memcpy(respbuf, info->param.ver_ext.version_str, copy_size); + ret = copy_size; + PRINTM(MINFO, "MOAL EXTENDED VERSION: %s\n", + info->param.ver_ext.version_str); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +#if defined(STA_SUPPORT) && defined(STA_WEXT) +/** + * @brief SET/Get radio + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +static int +woal_priv_radio_ctrl(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int ret = 0, option = 0; + int user_data_len = 0, header_len = 0; + mlan_bss_info bss_info; + + ENTER(); + + memset(&bss_info, 0, sizeof(bss_info)); + + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_RADIO_CTRL); + + if (strlen(respbuf) == header_len) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, &option, 1, + &user_data_len); + } + + if (user_data_len > 1) { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + if (user_data_len == 1) { + /* Set radio */ + if (option < 0 || option > 1) { + PRINTM(MERROR, "Invalid arguments!\n"); + ret = -EINVAL; + goto done; + } + if (MLAN_STATUS_SUCCESS != woal_set_radio(priv, (t_u8)option)) + ret = -EFAULT; + goto done; + } else { + /* Get radio status */ + woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info); + memcpy(respbuf, &bss_info.radio_on, sizeof(bss_info.radio_on)); + ret = sizeof(bss_info.radio_on); + } +done: + LEAVE(); + return ret; +} +#endif + +/** + * @brief Implement WMM enable command + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +static int +woal_priv_wmm_cfg(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int data = 0; + mlan_ds_wmm_cfg *wmm = NULL; + mlan_ioctl_req *req = NULL; + int ret = 0; + int user_data_len = 0, header_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_wmm_cfg)); + if (req == NULL) { + ret = ENOMEM; + goto done; + } + + wmm = (mlan_ds_wmm_cfg *)req->pbuf; + wmm->sub_command = MLAN_OID_WMM_CFG_ENABLE; + req->req_id = MLAN_IOCTL_WMM_CFG; + + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_WMM_CFG); + + if (strlen(respbuf) == header_len) { + /* GET operation */ + req->action = MLAN_ACT_GET; + user_data_len = 0; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, &data, 1, &user_data_len); + if (user_data_len == 1) { + /* Set wmm */ + if ((data < CMD_DISABLED) || (data > CMD_ENABLED)) { + PRINTM(MERROR, "Invalid arguments!\n"); + ret = -EINVAL; + goto done; + } + req->action = MLAN_ACT_SET; + if (data == CMD_DISABLED) + wmm->param.wmm_enable = MFALSE; + else + wmm->param.wmm_enable = MTRUE; + } else { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + } + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + memcpy(respbuf, &wmm->param.wmm_enable, sizeof(wmm->param.wmm_enable)); + ret = sizeof(wmm->param.wmm_enable); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +#if defined(STA_SUPPORT) +/** + * @brief Implement 802.11D enable command + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +static int +woal_priv_11d_cfg(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int data = 0; + mlan_ds_11d_cfg *pcfg_11d = NULL; + mlan_ioctl_req *req = NULL; + int ret = 0; + int user_data_len = 0, header_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11d_cfg)); + if (req == NULL) { + ret = ENOMEM; + goto done; + } + + pcfg_11d = (mlan_ds_11d_cfg *)req->pbuf; + pcfg_11d->sub_command = MLAN_OID_11D_CFG_ENABLE; + req->req_id = MLAN_IOCTL_11D_CFG; + + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_11D_CFG); + + if (strlen(respbuf) == header_len) { + /* GET operation */ + req->action = MLAN_ACT_GET; + user_data_len = 0; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, &data, 1, &user_data_len); + if (user_data_len == 1) { + if ((data < CMD_DISABLED) || (data > CMD_ENABLED)) { + PRINTM(MERROR, "Invalid arguments!\n"); + ret = -EINVAL; + goto done; + } + req->action = MLAN_ACT_SET; + if (data == CMD_DISABLED) + pcfg_11d->param.enable_11d = MFALSE; + else + pcfg_11d->param.enable_11d = MTRUE; + } else { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + } + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + memcpy(respbuf, &pcfg_11d->param.enable_11d, + sizeof(pcfg_11d->param.enable_11d)); + ret = sizeof(pcfg_11d->param.enable_11d); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Implement 802.11D clear chan table command + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return 0 --success, otherwise fail + */ +static int +woal_priv_11d_clr_chan_tbl(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + mlan_ds_11d_cfg *pcfg_11d = NULL; + mlan_ioctl_req *req = NULL; + int ret = 0; + int header_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11d_cfg)); + if (req == NULL) { + ret = ENOMEM; + goto done; + } + + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_11D_CLR_TBL); + + if (strlen(respbuf) != header_len) { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + pcfg_11d = (mlan_ds_11d_cfg *)req->pbuf; + pcfg_11d->sub_command = MLAN_OID_11D_CLR_CHAN_TABLE; + req->req_id = MLAN_IOCTL_11D_CFG; + req->action = MLAN_ACT_SET; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} +#endif + +/** + * @brief Set/Get WWS mode + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +static int +woal_priv_wws_cfg(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int data = 0; + mlan_ds_misc_cfg *wws = NULL; + mlan_ioctl_req *req = NULL; + int ret = 0; + int user_data_len = 0, header_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = ENOMEM; + goto done; + } + + wws = (mlan_ds_misc_cfg *)req->pbuf; + wws->sub_command = MLAN_OID_MISC_WWS; + req->req_id = MLAN_IOCTL_MISC_CFG; + + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_WWS_CFG); + + if (strlen(respbuf) == header_len) { + /* GET operation */ + req->action = MLAN_ACT_GET; + user_data_len = 0; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, &data, 1, &user_data_len); + if (user_data_len == 1) { + if ((data < CMD_DISABLED) || (data > CMD_ENABLED)) { + PRINTM(MERROR, + "Invalid arguments, WWS config not changed!\n"); + ret = -EINVAL; + goto done; + } + req->action = MLAN_ACT_SET; + wws->param.wws_cfg = (t_u16)data; + } else { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + } + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + memcpy(respbuf, &wws->param.wws_cfg, sizeof(wws->param.wws_cfg)); + ret = sizeof(wws->param.wws_cfg); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +#if defined(REASSOCIATION) +/** + * @brief Set/Get reassociation settings + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +static int +woal_priv_set_get_reassoc(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + moal_handle *handle = priv->phandle; + int data = 0; + int ret = 0; + int user_data_len = 0, header_len = 0; + + ENTER(); + + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_REASSOCTRL); + + if (strlen(respbuf) == header_len) { + /* GET operation */ + user_data_len = 0; + data = (int)(priv->reassoc_on); + memcpy(respbuf, &data, sizeof(data)); + ret = sizeof(data); + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, &data, 1, &user_data_len); + if (user_data_len == 1) { + if (data == 0) { + handle->reassoc_on &= ~MBIT(priv->bss_index); + priv->reassoc_on = MFALSE; + priv->reassoc_required = MFALSE; + if (!handle->reassoc_on && + handle->is_reassoc_timer_set == MTRUE) { + woal_cancel_timer(&handle-> + reassoc_timer); + handle->is_reassoc_timer_set = MFALSE; + } + } else if (data == 1) { + handle->reassoc_on |= MBIT(priv->bss_index); + priv->reassoc_on = MTRUE; + } else { + PRINTM(MERROR, "Invalid arguments!\n"); + ret = -EINVAL; + } + } else { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + } + } + + LEAVE(); + return ret; +} +#endif /* REASSOCIATION */ + +/** + * @brief Get Transmit buffer size + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +static int +woal_priv_txbuf_cfg(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + mlan_ds_11n_cfg *cfg_11n = NULL; + mlan_ioctl_req *req = NULL; + int ret = 0; + int buf_size = 0, header_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); + if (req == NULL) { + ret = ENOMEM; + goto done; + } + + cfg_11n = (mlan_ds_11n_cfg *)req->pbuf; + cfg_11n->sub_command = MLAN_OID_11N_CFG_MAX_TX_BUF_SIZE; + req->req_id = MLAN_IOCTL_11N_CFG; + + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_TXBUF_CFG); + + if (strlen(respbuf) != header_len) { + PRINTM(MERROR, + "Don't support set Tx buffer size after driver loaded!\n"); + ret = -EINVAL; + goto done; + } else { + /* Get Tx buffer size from MLAN */ + req->action = MLAN_ACT_GET; + } + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + buf_size = cfg_11n->param.tx_buf_size; + memcpy(respbuf, &buf_size, sizeof(buf_size)); + ret = sizeof(buf_size); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +#ifdef STA_SUPPORT +/** + * @brief Set/Get auth type + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +static int +woal_priv_auth_type(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int auth_type = 0; + t_u32 auth_mode; + int ret = 0; + int user_data_len = 0, header_len = 0; + + ENTER(); + + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_AUTH_TYPE); + + if (strlen(respbuf) == header_len) { + /* GET operation */ + if (MLAN_STATUS_SUCCESS != + woal_get_auth_mode(priv, MOAL_IOCTL_WAIT, &auth_mode)) { + ret = -EFAULT; + goto done; + } + user_data_len = 0; + auth_type = auth_mode; + memcpy(respbuf, &auth_type, sizeof(auth_type)); + ret = sizeof(auth_type); + goto done; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, &auth_type, 1, + &user_data_len); + if (user_data_len == 1) { + PRINTM(MINFO, "SET: auth_type %d\n", auth_type); + if (((auth_type < MLAN_AUTH_MODE_OPEN) || + (auth_type > MLAN_AUTH_MODE_SHARED)) + && (auth_type != MLAN_AUTH_MODE_AUTO)) { + ret = -EINVAL; + goto done; + } + auth_mode = auth_type; + if (MLAN_STATUS_SUCCESS != + woal_set_auth_mode(priv, MOAL_IOCTL_WAIT, + auth_mode)) { + ret = -EFAULT; + goto done; + } + } else { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + } + } + +done: + LEAVE(); + return ret; +} +#endif + +/** + * @brief Set/get user provisioned local power constraint + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +static int +woal_priv_11h_local_pwr_constraint(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int data = 0; + mlan_ds_11h_cfg *ds_11hcfg = NULL; + mlan_ioctl_req *req = NULL; + int ret = 0; + int user_data_len = 0, header_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11h_cfg)); + if (req == NULL) { + ret = ENOMEM; + goto done; + } + + ds_11hcfg = (mlan_ds_11h_cfg *)req->pbuf; + ds_11hcfg->sub_command = MLAN_OID_11H_LOCAL_POWER_CONSTRAINT; + req->req_id = MLAN_IOCTL_11H_CFG; + + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_POWER_CONS); + + if (strlen(respbuf) == header_len) { + /* GET operation */ + req->action = MLAN_ACT_GET; + user_data_len = 0; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, &data, 1, &user_data_len); + if (user_data_len == 1) { + req->action = MLAN_ACT_SET; + ds_11hcfg->param.usr_local_power_constraint = + (t_s8)data; + } else { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + } + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (req->action == MLAN_ACT_GET) { + data = (int)ds_11hcfg->param.usr_local_power_constraint; + memcpy(respbuf, &data, sizeof(data)); + ret = sizeof(data); + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Get thermal reading + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +static int +woal_priv_thermal(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + mlan_ds_misc_cfg *cfg = NULL; + mlan_ioctl_req *req = NULL; + int ret = 0, header_len = 0, data = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = ENOMEM; + goto done; + } + + cfg = (mlan_ds_misc_cfg *)req->pbuf; + cfg->sub_command = MLAN_OID_MISC_THERMAL; + req->req_id = MLAN_IOCTL_MISC_CFG; + + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_THERMAL); + + if (strlen(respbuf) != header_len) { + PRINTM(MERROR, "Set is not supported for this command\n"); + ret = -EINVAL; + goto done; + } + req->action = MLAN_ACT_GET; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + data = (int)cfg->param.thermal; + memcpy(respbuf, &data, sizeof(data)); + ret = sizeof(data); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get beacon interval + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +static int +woal_priv_beacon_interval(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int data = 0; + mlan_ds_bss *bss = NULL; + mlan_ioctl_req *req = NULL; + int ret = 0; + int user_data_len = 0, header_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + ret = ENOMEM; + goto done; + } + + bss = (mlan_ds_bss *)req->pbuf; + bss->sub_command = MLAN_OID_IBSS_BCN_INTERVAL; + req->req_id = MLAN_IOCTL_BSS; + + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_BCN_INTERVAL); + + if (strlen(respbuf) == header_len) { + /* GET operation */ + req->action = MLAN_ACT_GET; + user_data_len = 0; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, &data, 1, &user_data_len); + if (user_data_len == 1) { + if ((data < MLAN_MIN_BEACON_INTERVAL) || + (data > MLAN_MAX_BEACON_INTERVAL)) { + PRINTM(MERROR, "Invalid arguments!\n"); + ret = -EINVAL; + goto done; + } + req->action = MLAN_ACT_SET; + bss->param.bcn_interval = data; + } else { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + } + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + data = ((mlan_ds_bss *)req->pbuf)->param.bcn_interval; + memcpy(respbuf, &data, sizeof(data)); + ret = sizeof(data); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +#ifdef STA_SUPPORT +/** + * @brief Get signal + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +static int +woal_priv_get_signal(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ +/** Input data size */ +#define IN_DATA_SIZE 2 +/** Output data size */ +#define OUT_DATA_SIZE 12 + int ret = 0; + int in_data[IN_DATA_SIZE]; + int out_data[OUT_DATA_SIZE]; + mlan_ds_get_signal signal; + int data_length = 0; + int buflen = 0; + int user_data_len = 0, header_len = 0; + + ENTER(); + + memset(in_data, 0, sizeof(in_data)); + memset(out_data, 0, sizeof(out_data)); + + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_GET_SIGNAL); + + if (strlen(respbuf) != header_len) + parse_arguments(respbuf + header_len, in_data, IN_DATA_SIZE, + &user_data_len); + buflen = MIN(user_data_len, IN_DATA_SIZE); + + if (priv->media_connected == MFALSE) { + PRINTM(MERROR, "Can not get RSSI in disconnected state\n"); + ret = -ENOTSUPP; + goto done; + } + + if (user_data_len) { + if (user_data_len > IN_DATA_SIZE) { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + } + + switch (user_data_len) { + case 0: /* No checking, get everything */ + break; + case 2: /* Check subtype range */ + if (in_data[1] < 1 || in_data[1] > 4) { + ret = -EINVAL; + goto done; + } + /* Fall through */ + case 1: /* Check type range */ + if (in_data[0] < 1 || in_data[0] > 3) { + ret = -EINVAL; + goto done; + } + break; + default: + ret = -EINVAL; + goto done; + } + + memset(&signal, 0, sizeof(mlan_ds_get_signal)); + if (MLAN_STATUS_SUCCESS != + woal_get_signal_info(priv, MOAL_IOCTL_WAIT, &signal)) { + ret = -EFAULT; + goto done; + } + PRINTM(MINFO, "RSSI Beacon Last : %d\n", (int)signal.bcn_rssi_last); + PRINTM(MINFO, "RSSI Beacon Average: %d\n", (int)signal.bcn_rssi_avg); + PRINTM(MINFO, "RSSI Data Last : %d\n", (int)signal.data_rssi_last); + PRINTM(MINFO, "RSSI Data Average : %d\n", (int)signal.data_rssi_avg); + PRINTM(MINFO, "SNR Beacon Last : %d\n", (int)signal.bcn_snr_last); + PRINTM(MINFO, "SNR Beacon Average : %d\n", (int)signal.bcn_snr_avg); + PRINTM(MINFO, "SNR Data Last : %d\n", (int)signal.data_snr_last); + PRINTM(MINFO, "SNR Data Average : %d\n", (int)signal.data_snr_avg); + PRINTM(MINFO, "NF Beacon Last : %d\n", (int)signal.bcn_nf_last); + PRINTM(MINFO, "NF Beacon Average : %d\n", (int)signal.bcn_nf_avg); + PRINTM(MINFO, "NF Data Last : %d\n", (int)signal.data_nf_last); + PRINTM(MINFO, "NF Data Average : %d\n", (int)signal.data_nf_avg); + + /* Check type */ + switch (in_data[0]) { + case 0: /* Send everything */ + out_data[data_length++] = signal.bcn_rssi_last; + out_data[data_length++] = signal.bcn_rssi_avg; + out_data[data_length++] = signal.data_rssi_last; + out_data[data_length++] = signal.data_rssi_avg; + out_data[data_length++] = signal.bcn_snr_last; + out_data[data_length++] = signal.bcn_snr_avg; + out_data[data_length++] = signal.data_snr_last; + out_data[data_length++] = signal.data_snr_avg; + out_data[data_length++] = signal.bcn_nf_last; + out_data[data_length++] = signal.bcn_nf_avg; + out_data[data_length++] = signal.data_nf_last; + out_data[data_length++] = signal.data_nf_avg; + break; + case 1: /* RSSI */ + /* Check subtype */ + switch (in_data[1]) { + case 0: /* Everything */ + out_data[data_length++] = signal.bcn_rssi_last; + out_data[data_length++] = signal.bcn_rssi_avg; + out_data[data_length++] = signal.data_rssi_last; + out_data[data_length++] = signal.data_rssi_avg; + break; + case 1: /* bcn last */ + out_data[data_length++] = signal.bcn_rssi_last; + break; + case 2: /* bcn avg */ + out_data[data_length++] = signal.bcn_rssi_avg; + break; + case 3: /* data last */ + out_data[data_length++] = signal.data_rssi_last; + break; + case 4: /* data avg */ + out_data[data_length++] = signal.data_rssi_avg; + break; + default: + break; + } + break; + case 2: /* SNR */ + /* Check subtype */ + switch (in_data[1]) { + case 0: /* Everything */ + out_data[data_length++] = signal.bcn_snr_last; + out_data[data_length++] = signal.bcn_snr_avg; + out_data[data_length++] = signal.data_snr_last; + out_data[data_length++] = signal.data_snr_avg; + break; + case 1: /* bcn last */ + out_data[data_length++] = signal.bcn_snr_last; + break; + case 2: /* bcn avg */ + out_data[data_length++] = signal.bcn_snr_avg; + break; + case 3: /* data last */ + out_data[data_length++] = signal.data_snr_last; + break; + case 4: /* data avg */ + out_data[data_length++] = signal.data_snr_avg; + break; + default: + break; + } + break; + case 3: /* NF */ + /* Check subtype */ + switch (in_data[1]) { + case 0: /* Everything */ + out_data[data_length++] = signal.bcn_nf_last; + out_data[data_length++] = signal.bcn_nf_avg; + out_data[data_length++] = signal.data_nf_last; + out_data[data_length++] = signal.data_nf_avg; + break; + case 1: /* bcn last */ + out_data[data_length++] = signal.bcn_nf_last; + break; + case 2: /* bcn avg */ + out_data[data_length++] = signal.bcn_nf_avg; + break; + case 3: /* data last */ + out_data[data_length++] = signal.data_nf_last; + break; + case 4: /* data avg */ + out_data[data_length++] = signal.data_nf_avg; + break; + default: + break; + } + break; + default: + break; + } + + memcpy(respbuf, out_data, (data_length * sizeof(int))); + ret = data_length * sizeof(int); + +done: + LEAVE(); + return ret; +} + +#endif /* #ifdef STA_SUPPORT */ + +#if defined(STA_SUPPORT) +/** + * @brief Make PMF bit required/optional + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + * + * @return 0 -- success, otherwise fail + */ +int +woal_priv_set_get_pmfcfg(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int data[2] = { 0, 0 }; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *cfg = NULL; + mlan_ds_misc_pmfcfg *pmfcfg; + int ret = 0; + int user_data_len = 0, header_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_PMFCFG); + if (strlen(respbuf) == header_len) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, data, ARRAY_SIZE(data), + &user_data_len); + } + + if (user_data_len > 2) { + PRINTM(MERROR, "Invalid number of arguments\n"); + ret = -EINVAL; + goto done; + } + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + cfg = (mlan_ds_misc_cfg *)req->pbuf; + pmfcfg = (mlan_ds_misc_pmfcfg *)&cfg->param.pmfcfg; + cfg->sub_command = MLAN_OID_MISC_PMFCFG; + req->req_id = MLAN_IOCTL_MISC_CFG; + + if (user_data_len == 0) + req->action = MLAN_ACT_GET; + else { + pmfcfg->mfpc = (t_u8)data[0]; + pmfcfg->mfpr = (t_u8)data[1]; + req->action = MLAN_ACT_SET; + } + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + memcpy(respbuf, (t_u8 *)&cfg->param.pmfcfg, + sizeof(mlan_ds_misc_pmfcfg)); + ret = sizeof(mlan_ds_misc_pmfcfg); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} +#endif + +/** + * @brief Get/Set inactivity timeout extend + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + * + * @return Number of bytes written, negative for failure. + */ +static int +woal_priv_inactivity_timeout_ext(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int data[4]; + mlan_ioctl_req *req = NULL; + mlan_ds_pm_cfg *pmcfg = NULL; + pmlan_ds_inactivity_to inac_to = NULL; + int ret = 0; + int user_data_len = 0, header_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_INACTIVITYTO); + memset(data, 0, sizeof(data)); + if (strlen(respbuf) == header_len) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, data, ARRAY_SIZE(data), + &user_data_len); + } + + if (user_data_len != 0 && user_data_len != 3 && user_data_len != 4) { + PRINTM(MERROR, "Invalid number of parameters\n"); + ret = -EINVAL; + goto done; + } + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + pmcfg = (mlan_ds_pm_cfg *)req->pbuf; + inac_to = &pmcfg->param.inactivity_to; + pmcfg->sub_command = MLAN_OID_PM_CFG_INACTIVITY_TO; + req->req_id = MLAN_IOCTL_PM_CFG; + req->action = MLAN_ACT_GET; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (user_data_len) { + inac_to->timeout_unit = data[0]; + inac_to->unicast_timeout = data[1]; + inac_to->mcast_timeout = data[2]; + if (user_data_len == 4) + inac_to->ps_entry_timeout = data[3]; + req->action = MLAN_ACT_SET; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + } else { + data[0] = inac_to->timeout_unit; + data[1] = inac_to->unicast_timeout; + data[2] = inac_to->mcast_timeout; + data[3] = inac_to->ps_entry_timeout; + + memcpy(respbuf, (t_u8 *)data, sizeof(data)); + ret = sizeof(data); + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get ATIM window + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + * + * @return Number of bytes written, negative for failure. + */ +static int +woal_priv_atim_window(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_bss *bss = NULL; + int ret = 0, atim = 0; + int user_data_len = 0, header_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_ATIM_WINDOW); + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + bss = (mlan_ds_bss *)req->pbuf; + bss->sub_command = MLAN_OID_IBSS_ATIM_WINDOW; + req->req_id = MLAN_IOCTL_BSS; + + if (strlen(respbuf) == header_len) { + /* GET operation */ + user_data_len = 0; + req->action = MLAN_ACT_GET; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, &atim, 1, &user_data_len); + + if (user_data_len != 1) { + PRINTM(MERROR, "Invalid number of parameters\n"); + ret = -EINVAL; + goto done; + } + req->action = MLAN_ACT_SET; + bss->param.atim_window = atim; + } + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + memcpy(respbuf, (t_u8 *)&bss->param.atim_window, sizeof(int)); + ret = sizeof(int); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Enable/Disable amsdu_aggr_ctrl + * + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + * + * @return Number of bytes written, negative for failure. + */ +static int +woal_priv_11n_amsdu_aggr_ctrl(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_11n_cfg *cfg_11n = NULL; + int ret = 0, data[2]; + int user_data_len = 0, header_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_AMSDU_AGGR_CTRL); + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + cfg_11n = (mlan_ds_11n_cfg *)req->pbuf; + cfg_11n->sub_command = MLAN_OID_11N_CFG_AMSDU_AGGR_CTRL; + req->req_id = MLAN_IOCTL_11N_CFG; + + if (strlen(respbuf) == header_len) { + /* GET operation */ + user_data_len = 0; + req->action = MLAN_ACT_GET; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, data, 1, &user_data_len); + + if (user_data_len != 1) { + PRINTM(MERROR, "Invalid number of parameters\n"); + ret = -EINVAL; + goto done; + } + req->action = MLAN_ACT_SET; + cfg_11n->param.amsdu_aggr_ctrl.enable = data[0]; + } + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + data[0] = cfg_11n->param.amsdu_aggr_ctrl.enable; + data[1] = cfg_11n->param.amsdu_aggr_ctrl.curr_buf_size; + + memcpy(respbuf, (t_u8 *)data, sizeof(data)); + ret = sizeof(data); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get Transmit beamforming capabilities + * + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + * + * @return Number of bytes written, negative for failure. + */ +static int +woal_priv_tx_bf_cap_ioctl(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_11n_cfg *bf_cfg = NULL; + int ret = 0, bf_cap = 0; + int user_data_len = 0, header_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_TX_BF_CAP); + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + bf_cfg = (mlan_ds_11n_cfg *)req->pbuf; + bf_cfg->sub_command = MLAN_OID_11N_CFG_TX_BF_CAP; + req->req_id = MLAN_IOCTL_11N_CFG; + + if (strlen(respbuf) == header_len) { + /* GET operation */ + user_data_len = 0; + req->action = MLAN_ACT_GET; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, &bf_cap, 1, + &user_data_len); + + if (user_data_len != 1) { + PRINTM(MERROR, "Invalid number of parameters\n"); + ret = -EINVAL; + goto done; + } + req->action = MLAN_ACT_SET; + bf_cfg->param.tx_bf_cap = bf_cap; + } + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + bf_cap = bf_cfg->param.tx_bf_cap; + + memcpy(respbuf, (t_u8 *)&bf_cap, sizeof(bf_cap)); + ret = sizeof(bf_cap); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Turn on/off the sdio clock + * + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + * + * @return Number of bytes written, negative for failure. + */ +static int +woal_priv_sdio_clock_ioctl(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int ret = 0; + int data = 2; + int user_data_len = 0, header_len = 0; + /* Initialize the clock state as on */ + static int clock_state = 1; + + ENTER(); + + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_SDIO_CLOCK); + if (strlen(respbuf) == header_len) { + /* GET operation */ + memcpy(respbuf, (t_u8 *)&clock_state, sizeof(clock_state)); + ret = sizeof(clock_state); + goto done; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, &data, 1, &user_data_len); + + if (user_data_len != 1) { + PRINTM(MERROR, "Invalid number of parameters\n"); + ret = -EINVAL; + goto done; + } + } + switch (data) { + case CMD_DISABLED: + PRINTM(MINFO, "SDIO clock is turned off\n"); + ret = woal_sdio_set_bus_clock(priv->phandle, MFALSE); + clock_state = data; + break; + case CMD_ENABLED: + PRINTM(MINFO, "SDIO clock is turned on\n"); + ret = woal_sdio_set_bus_clock(priv->phandle, MTRUE); + clock_state = data; + break; + default: + ret = -EINVAL; + PRINTM(MINFO, "sdioclock: wrong parameter\n"); + break; + } +done: + LEAVE(); + return ret; +} + +#if defined(SDIO_MULTI_PORT_TX_AGGR) || defined(SDIO_MULTI_PORT_RX_AGGR) +/** + * @brief Set SDIO Multi-point aggregation control parameters + * + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + * + * @return Number of bytes written, negative for failure. + */ +static int +woal_priv_sdio_mpa_ctrl(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *misc = NULL; + int ret = 0, data[6]; + int user_data_len = 0, header_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + memset(data, 0, sizeof(data)); + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_MPA_CTRL); + if (strlen(respbuf) == header_len) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, data, ARRAY_SIZE(data), + &user_data_len); + + if (user_data_len > 6) { + PRINTM(MERROR, "Invalid number of parameters\n"); + ret = -EINVAL; + goto done; + } + } + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + misc = (mlan_ds_misc_cfg *)req->pbuf; + misc->sub_command = MLAN_OID_MISC_SDIO_MPA_CTRL; + req->req_id = MLAN_IOCTL_MISC_CFG; + req->action = MLAN_ACT_GET; + /* Get the values first, then modify these values if + * user had modified them */ + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "woal_request_ioctl returned %d\n", ret); + ret = -EFAULT; + goto done; + } + + if (user_data_len == 0) { + data[0] = misc->param.mpa_ctrl.tx_enable; + data[1] = misc->param.mpa_ctrl.rx_enable; + data[2] = misc->param.mpa_ctrl.tx_buf_size; + data[3] = misc->param.mpa_ctrl.rx_buf_size; + data[4] = misc->param.mpa_ctrl.tx_max_ports; + data[5] = misc->param.mpa_ctrl.rx_max_ports; + + PRINTM(MINFO, "Get Param: %d %d %d %d %d %d\n", data[0], + data[1], data[2], data[3], data[4], data[5]); + + memcpy(respbuf, (t_u8 *)data, sizeof(data)); + ret = sizeof(data); + goto done; + } + + switch (user_data_len) { + case 6: + misc->param.mpa_ctrl.rx_max_ports = data[5]; + case 5: + misc->param.mpa_ctrl.tx_max_ports = data[4]; + case 4: + misc->param.mpa_ctrl.rx_buf_size = data[3]; + case 3: + misc->param.mpa_ctrl.tx_buf_size = data[2]; + case 2: + misc->param.mpa_ctrl.rx_enable = data[1]; + case 1: + /* Set cmd */ + req->action = MLAN_ACT_SET; + + PRINTM(MINFO, "Set Param: %d %d %d %d %d %d\n", data[0], + data[1], data[2], data[3], data[4], data[5]); + + misc->param.mpa_ctrl.tx_enable = data[0]; + break; + default: + PRINTM(MERROR, "Default case error\n"); + ret = -EINVAL; + goto done; + } + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} +#endif + +/** + * @brief Configure sleep parameters + * + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + * + * @return Number of bytes written, negative for failure. + */ +static int +woal_priv_sleep_params_ioctl(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_pm_cfg *pm = NULL; + mlan_ds_sleep_params *psleep_params = NULL; + int data[6] = { 0 }, i; + int user_data_len = 0, header_len = 0; +#ifdef DEBUG_LEVEL1 + char err_str[][35] = { {"sleep clock error in ppm"}, + {"wakeup offset in usec"}, + {"clock stabilization time in usec"}, + {"value of reserved for debug"} + }; +#endif + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + memset(data, 0, sizeof(data)); + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + pm = (mlan_ds_pm_cfg *)req->pbuf; + pm->sub_command = MLAN_OID_PM_CFG_SLEEP_PARAMS; + req->req_id = MLAN_IOCTL_PM_CFG; + psleep_params = (pmlan_ds_sleep_params)&pm->param.sleep_params; + + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_SLEEP_PARAMS); + if (strlen(respbuf) == header_len) { + /* GET operation */ + user_data_len = 0; + req->action = MLAN_ACT_GET; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, data, ARRAY_SIZE(data), + &user_data_len); + if (user_data_len != 6) { + PRINTM(MERROR, "Invalid number of parameters\n"); + ret = -EINVAL; + goto done; + } +#define MIN_VAL 0x0000 +#define MAX_VAL 0xFFFF + for (i = 0; i < 6; i++) { + if ((i == 3) || (i == 4)) { + /* These two cases are handled below the loop */ + continue; + } + if (data[i] < MIN_VAL || data[i] > MAX_VAL) { + PRINTM(MERROR, "Invalid %s (0-65535)!\n", + err_str[i]); + ret = -EINVAL; + goto done; + } + } + if (data[3] < 0 || data[3] > 2) { + PRINTM(MERROR, + "Invalid control periodic calibration (0-2)!\n"); + ret = -EINVAL; + goto done; + } + if (data[4] < 0 || data[4] > 2) { + PRINTM(MERROR, + "Invalid control of external sleep clock (0-2)!\n"); + ret = -EINVAL; + goto done; + } + req->action = MLAN_ACT_SET; + psleep_params->error = data[0]; + psleep_params->offset = data[1]; + psleep_params->stable_time = data[2]; + psleep_params->cal_control = data[3]; + psleep_params->ext_sleep_clk = data[4]; + psleep_params->reserved = data[5]; + } + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + data[0] = psleep_params->error; + data[1] = psleep_params->offset; + data[2] = psleep_params->stable_time; + data[3] = psleep_params->cal_control; + data[4] = psleep_params->ext_sleep_clk; + data[5] = psleep_params->reserved; + + memcpy(respbuf, (t_u8 *)data, sizeof(data)); + ret = sizeof(data); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + + LEAVE(); + return ret; +} + +/** + * @brief Set/Get network monitor configurations + * + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + * + * @return Number of bytes written, negative for failure. + */ +static int +woal_priv_net_monitor_ioctl(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int user_data_len = 0, header_len = 0; + int data_length = 0; + int data[5] = { 0 }; + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *misc = NULL; + mlan_ds_misc_net_monitor *net_mon = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + moal_handle *handle = priv->phandle; + monitor_iface *mon_if = NULL; + struct net_device *ndev = NULL; + + ENTER(); + + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_NET_MON); + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + LEAVE(); + return -ENOMEM; + } + misc = (mlan_ds_misc_cfg *)req->pbuf; + net_mon = (mlan_ds_misc_net_monitor *)&misc->param.net_mon; + misc->sub_command = MLAN_OID_MISC_NET_MONITOR; + req->req_id = MLAN_IOCTL_MISC_CFG; + if (strlen(respbuf) == header_len) { + /* GET operation */ + user_data_len = 0; + req->action = MLAN_ACT_GET; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, data, ARRAY_SIZE(data), + &user_data_len); + if (user_data_len == 1 || user_data_len == 4 || + user_data_len == 5) { + if (data[0] != MFALSE && + data[0] != CHANNEL_SPEC_SNIFFER_MODE) { + PRINTM(MERROR, + "NET_MON: Activity should be enable(=1/2)/disable(=0)\n"); + ret = -EINVAL; + goto done; + } + if ((data[0] == MFALSE && user_data_len != 1) + || (data[0] == CHANNEL_SPEC_SNIFFER_MODE && + (user_data_len != 4 && user_data_len != 5)) + ) { + PRINTM(MERROR, + "NET_MON: Sniffer activity not match with user_data_len\n"); + ret = -EINVAL; + goto done; + } + net_mon->enable_net_mon = data[0]; + if (data[0] == CHANNEL_SPEC_SNIFFER_MODE) { + int i; + if (user_data_len != 4 && user_data_len != 5) { + PRINTM(MERROR, + "NET_MON: Invalid number of args!\n"); + ret = -EINVAL; + goto done; + } + /* Supported filter flags */ + if (!data[1] || data[1] & + ~(MLAN_NETMON_DATA_FRAME | + MLAN_NETMON_MANAGEMENT_FRAME | + MLAN_NETMON_CONTROL_FRAME)) { + PRINTM(MERROR, + "NET_MON: Invalid filter flag\n"); + ret = -EINVAL; + goto done; + } + + if (user_data_len > 2) { + /* Supported bands */ + for (i = 0; + i < sizeof(SupportedAdhocBand); + i++) + if (data[2] == + SupportedAdhocBand[i]) + break; + if (i == sizeof(SupportedAdhocBand)) { + PRINTM(MERROR, + "NET_MON: Invalid band\n"); + ret = -EINVAL; + goto done; + } + } + /* Supported channel */ + if (user_data_len > 3 && + (data[3] < 1 || + data[3] > MLAN_MAX_CHANNEL)) { + PRINTM(MERROR, + "NET_MON: Invalid channel number\n"); + ret = -EINVAL; + goto done; + } + if (user_data_len == 5) { + /* Secondary channel offset */ + if (!(data[2] & (BAND_GN | BAND_AN))) { + PRINTM(MERROR, + "No 11n in band, can not set " + "secondary channel offset\n"); + ret = -EINVAL; + goto done; + } + if ((data[4] != CHANNEL_BW_20MHZ) && + (data[4] != CHANNEL_BW_40MHZ_ABOVE) + && (data[4] != + CHANNEL_BW_40MHZ_BELOW) + ) { + PRINTM(MERROR, + "Invalid secondary channel bandwidth, " + "only allowed 0, 1, 3 or 4\n"); + ret = -EINVAL; + goto done; + } + net_mon->chan_bandwidth = data[4]; + } + + net_mon->filter_flag = data[1]; + net_mon->band = data[2]; + net_mon->channel = data[3]; + } + req->action = MLAN_ACT_SET; + } else { + PRINTM(MERROR, "NET_MON: Invalid number of args!\n"); + ret = -EINVAL; + goto done; + } + } + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "NET_MON: woal_request_ioctl fail\n"); + ret = -EFAULT; + goto done; + } + + if (req->action == MLAN_ACT_SET) { + if (data[0]) { + /** Enable sniffer mode: 1/2 */ + if (!handle->mon_if) { + mon_if = woal_prepare_mon_if(priv, "rtap", 0, + data[0]); + if (!mon_if) { + PRINTM(MFATAL, + "Prepare mon_if fail.\n"); + ret = -EFAULT; + goto done; + } + ndev = mon_if->mon_ndev; + ret = register_netdevice(ndev); + if (ret) { + PRINTM(MFATAL, + "register net_device failed, ret=%d\n", + ret); + free_netdev(ndev); + ret = -EFAULT; + goto done; + } + handle->mon_if = mon_if; + } + /* Save band channel config */ + handle->mon_if->band_chan_cfg.band = net_mon->band; + handle->mon_if->band_chan_cfg.channel = + net_mon->channel; + handle->mon_if->band_chan_cfg.chan_bandwidth = + net_mon->chan_bandwidth; + } else { + /** Disable sniffer mode: 0 */ + if (handle->mon_if) { + ndev = handle->mon_if->mon_ndev; + handle->mon_if = NULL; + unregister_netdevice(ndev); + } + } + } + + data[0] = net_mon->enable_net_mon; + data[1] = net_mon->filter_flag; + data[2] = net_mon->band; + data[3] = net_mon->channel; + data[4] = net_mon->chan_bandwidth; + data_length = 5; + memcpy(respbuf, (t_u8 *)data, sizeof(int) * data_length); + ret = sizeof(int) * data_length; + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + + LEAVE(); + return ret; +} + +#if defined(STA_CFG80211) && defined(UAP_CFG80211) +/** + * @brief Set/Get monitor mode + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return 0 --success, otherwise fail + */ +static int +woal_priv_set_get_monitor_mode(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int ret = 0; + int data = 0; + int user_data_len = 0, header_len = 0; + t_u32 action = MLAN_ACT_GET; + + ENTER(); + + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_MONITOR_MODE); + + if (strlen(respbuf) == header_len) { + /* GET operation */ + user_data_len = 0; + action = MLAN_ACT_GET; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, &data, + sizeof(data) / sizeof(int), &user_data_len); + action = MLAN_ACT_SET; + } + + if (sizeof(int) * user_data_len > sizeof(data)) { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + + if (action == MLAN_ACT_SET) { + if (data == 1) { + priv->phandle->wiphy->interface_modes |= + MBIT(NL80211_IFTYPE_MONITOR); + } else if (data == 0) { + priv->phandle->wiphy->interface_modes &= + ~(MBIT(NL80211_IFTYPE_MONITOR)); + } else { + PRINTM(MERROR, "Invalid input arguments\n"); + ret = -EINVAL; + goto done; + } + } + data = ! !(priv->phandle->wiphy-> + interface_modes & MBIT(NL80211_IFTYPE_MONITOR)); + + ret = sprintf(respbuf, "Monitor mode: %d\n", data) + 1; + +done: + LEAVE(); + return ret; +} +#endif + +#if defined(DFS_TESTING_SUPPORT) +/** + * @brief Set/Get DFS Testing settings + * + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + * + * @return Number of bytes written, negative for failure. + */ +static int +woal_priv_dfs_testing(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_11h_cfg *ds_11hcfg = NULL; + int ret = 0; + int data[4]; + int user_data_len = 0, header_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_DFS_TESTING); + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11h_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + ds_11hcfg = (mlan_ds_11h_cfg *)req->pbuf; + ds_11hcfg->sub_command = MLAN_OID_11H_DFS_TESTING; + req->req_id = MLAN_IOCTL_11H_CFG; + + if (strlen(respbuf) == header_len) { + /* GET operation */ + user_data_len = 0; + req->action = MLAN_ACT_GET; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, data, ARRAY_SIZE(data), + &user_data_len); + if (user_data_len != 4) { + PRINTM(MERROR, "Invalid number of args!\n"); + ret = -EINVAL; + goto done; + } + if ((unsigned)data[0] > 0xFFFFF) { + PRINTM(MERROR, + "The maximum user CAC is 1048575 msec (17 mins approx).\n"); + ret = -EINVAL; + goto done; + } + if ((unsigned)data[1] > 0xFFFFF) { + PRINTM(MERROR, + "The maximum user NOP is 1048575 msec (17 mins approx).\n"); + ret = -EINVAL; + goto done; + } + if ((unsigned)data[3] > 0xFF) { + PRINTM(MERROR, + "The maximum user fixed channel is 255.\n"); + ret = -EINVAL; + goto done; + } + ds_11hcfg->param.dfs_testing.usr_cac_period_msec = + (t_u32)data[0]; + ds_11hcfg->param.dfs_testing.usr_nop_period_sec = + (t_u32)data[1]; + ds_11hcfg->param.dfs_testing.usr_no_chan_change = + data[2] ? 1 : 0; + ds_11hcfg->param.dfs_testing.usr_fixed_new_chan = (t_u8)data[3]; + priv->phandle->cac_period_jiffies = (t_u16)data[0] * HZ / 1000; + req->action = MLAN_ACT_SET; +#ifdef UAP_SUPPORT + priv->user_cac_period_msec = + ds_11hcfg->param.dfs_testing.usr_cac_period_msec; +#endif + } + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (!user_data_len) { + data[0] = ds_11hcfg->param.dfs_testing.usr_cac_period_msec; + data[1] = ds_11hcfg->param.dfs_testing.usr_nop_period_sec; + data[2] = ds_11hcfg->param.dfs_testing.usr_no_chan_change; + data[3] = ds_11hcfg->param.dfs_testing.usr_fixed_new_chan; + memcpy(respbuf, (t_u8 *)data, sizeof(data)); + ret = sizeof(data); + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + + LEAVE(); + return ret; +} +#endif /* DFS_SUPPORT && DFS_TESTING_SUPPORT */ + +/** + * @brief Set/Get CFP table codes + * + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + * + * @return Number of bytes written, negative for failure. + */ +static int +woal_priv_cfp_code(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int ret = 0; + int user_data_len = 0, header_len = 0; + int data[2]; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *misc_cfg = NULL; + mlan_ds_misc_cfp_code *cfp_code = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_CFP_CODE); + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + misc_cfg = (mlan_ds_misc_cfg *)req->pbuf; + cfp_code = &misc_cfg->param.cfp_code; + misc_cfg->sub_command = MLAN_OID_MISC_CFP_CODE; + req->req_id = MLAN_IOCTL_MISC_CFG; + + if (strlen(respbuf) == header_len) { + /* GET operation */ + user_data_len = 0; + req->action = MLAN_ACT_GET; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, data, ARRAY_SIZE(data), + &user_data_len); + if (user_data_len > 2) { + PRINTM(MERROR, "Invalid number of args!\n"); + ret = -EINVAL; + goto done; + } + cfp_code->cfp_code_bg = data[0]; + if (user_data_len == 2) + cfp_code->cfp_code_a = data[1]; + req->action = MLAN_ACT_SET; + } + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (!user_data_len) { + data[0] = cfp_code->cfp_code_bg; + data[1] = cfp_code->cfp_code_a; + memcpy(respbuf, (t_u8 *)data, sizeof(data)); + ret = sizeof(data); + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + + LEAVE(); + return ret; +} + +/** + * @brief Set/Get Tx/Rx antenna + * + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + * + * @return Number of bytes written, negative for failure. + */ +static int +woal_priv_set_get_tx_rx_ant(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int ret = 0; + int user_data_len = 0, header_len = 0; + mlan_ds_radio_cfg *radio = NULL; + mlan_ioctl_req *req = NULL; + int data[3] = { 0 }; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_ANT_CFG); + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_radio_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + radio = (mlan_ds_radio_cfg *)req->pbuf; + radio->sub_command = MLAN_OID_ANT_CFG; + req->req_id = MLAN_IOCTL_RADIO_CFG; + + if (strlen(respbuf) == header_len) { + /* GET operation */ + user_data_len = 0; + req->action = MLAN_ACT_GET; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, data, ARRAY_SIZE(data), + &user_data_len); + if (user_data_len > 2) { + PRINTM(MERROR, "Invalid number of args!\n"); + ret = -EINVAL; + goto done; + } + radio->param.ant_cfg_1x1.antenna = data[0]; + if (user_data_len == 2) + radio->param.ant_cfg_1x1.evaluate_time = data[1]; + req->action = MLAN_ACT_SET; + } + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + if (!user_data_len) { + data[0] = (int)radio->param.ant_cfg_1x1.antenna; + data[1] = (int)radio->param.ant_cfg_1x1.evaluate_time; + data[2] = (int)radio->param.ant_cfg_1x1.current_antenna; + memcpy(respbuf, (t_u8 *)&data, sizeof(data)); + ret = sizeof(data); + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/* + * @brief Set/Get CWMode + * + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + * + * @return Number of bytes written, negative for failure. + */ +static int +woal_priv_set_get_cwmode(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_misc_cfg *misc = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + mlan_ds_cw_mode_ctrl *cwmode; + int ret = 0; + int header_len = 0; + + ENTER(); + + if (!priv || !priv->phandle) { + PRINTM(MERROR, "priv or handle is null\n"); + ret = -EFAULT; + goto done; + } + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (ioctl_req == NULL) { + ret = -ENOMEM; + goto done; + } + + misc = (mlan_ds_misc_cfg *)ioctl_req->pbuf; + misc->sub_command = MLAN_OID_MISC_CWMODE_CTRL; + ioctl_req->req_id = MLAN_IOCTL_MISC_CFG; + + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_CWMODE); + if (strlen(respbuf) == header_len) { + /* GET operation */ + ioctl_req->action = MLAN_ACT_GET; + } else { + /* SET operation */ + ioctl_req->action = MLAN_ACT_SET; + + cwmode = (mlan_ds_cw_mode_ctrl *) (respbuf + header_len + + sizeof(t_u8)); + misc->param.cwmode.mode = cwmode->mode; + misc->param.cwmode.txPower = cwmode->txPower; + misc->param.cwmode.rateInfo = cwmode->rateInfo; + misc->param.cwmode.channel = cwmode->channel; + misc->param.cwmode.chanInfo = cwmode->chanInfo; + misc->param.cwmode.pktLength = cwmode->pktLength; + } + + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + memcpy(respbuf, (t_u8 *)&misc->param.cwmode, + sizeof(misc->param.cwmode)); + ret = sizeof(misc->param.cwmode); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + + LEAVE(); + return ret; +} + +/** + * @brief Set/Get out band independent reset + * + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + * + * @return Number of bytes written, negative for failure. + */ +static int +woal_priv_ind_rst_cfg(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int ret = 0; + int user_data_len = 0, header_len = 0; + mlan_ds_misc_cfg *misc = NULL; + mlan_ioctl_req *req = NULL; + int data[2] = { 0 }; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_IND_RST_CFG); + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + misc = (mlan_ds_misc_cfg *)req->pbuf; + memset(misc, 0, sizeof(mlan_ds_misc_cfg)); + misc->sub_command = MLAN_OID_MISC_IND_RST_CFG; + req->req_id = MLAN_IOCTL_MISC_CFG; + + if (strlen(respbuf) == header_len) { + /* GET operation */ + user_data_len = 0; + req->action = MLAN_ACT_GET; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, data, ARRAY_SIZE(data), + &user_data_len); + if (user_data_len > 2) { + PRINTM(MERROR, "Invalid number of args!\n"); + ret = -EINVAL; + goto done; + } + + if ((user_data_len == 1) || (user_data_len == 2)) { + req->action = MLAN_ACT_SET; + + /* ir_mode */ + if (data[0] < 0 || data[0] > 2) { + PRINTM(MERROR, "Invalid ir mode parameter!\n"); + ret = -EINVAL; + goto done; + } + misc->param.ind_rst_cfg.ir_mode = data[0]; + + /* gpio_pin */ + if (user_data_len == 2) { + if ((data[1] != 0xFF) && + (data[1] < 0 || data[1] > 15)) { + PRINTM(MERROR, + "Invalid gpio pin no!\n"); + ret = -EINVAL; + goto done; + } + misc->param.ind_rst_cfg.gpio_pin = data[1]; + } + } + } + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + data[0] = (int)misc->param.ind_rst_cfg.ir_mode; + data[1] = (int)misc->param.ind_rst_cfg.gpio_pin; + memcpy(respbuf, (t_u8 *)data, sizeof(data)); + ret = sizeof(data); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Get/Set system clock + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + * + * @return Number of bytes written, negative for failure. + */ +static int +woal_priv_sysclock(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int data[65]; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *cfg = NULL; + int ret = 0, i = 0; + int user_data_len = 0, header_len = 0; + int data_length = 0, length_index = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_SYSCLOCK); + memset(data, 0, sizeof(data)); + if (strlen(respbuf) == header_len) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, data, ARRAY_SIZE(data), + &user_data_len); + } + + if (user_data_len > MLAN_MAX_CLK_NUM) { + PRINTM(MERROR, "Invalid number of parameters\n"); + ret = -EINVAL; + goto done; + } + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + cfg = (mlan_ds_misc_cfg *)req->pbuf; + cfg->sub_command = MLAN_OID_MISC_SYS_CLOCK; + req->req_id = MLAN_IOCTL_MISC_CFG; + + if (user_data_len) { + /* SET operation */ + req->action = MLAN_ACT_SET; + + /* Set configurable clocks */ + cfg->param.sys_clock.sys_clk_type = MLAN_CLK_CONFIGURABLE; + cfg->param.sys_clock.sys_clk_num = + MIN(MLAN_MAX_CLK_NUM, user_data_len); + for (i = 0; i < cfg->param.sys_clock.sys_clk_num; i++) + cfg->param.sys_clock.sys_clk[i] = (t_u16)data[i]; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + } else { + /* GET operation */ + req->action = MLAN_ACT_GET; + + /* Get configurable clocks */ + cfg->param.sys_clock.sys_clk_type = MLAN_CLK_CONFIGURABLE; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + /* Current system clock */ + data[1] = (int)cfg->param.sys_clock.cur_sys_clk; + data_length = 1; + + length_index = + MIN(cfg->param.sys_clock.sys_clk_num, MLAN_MAX_CLK_NUM); + + /* Configurable clocks */ + for (i = 1; i <= length_index; i++) + data[i + data_length] = + (int)cfg->param.sys_clock.sys_clk[i - 1]; + + data_length += length_index; + + /* Get supported clocks */ + cfg->param.sys_clock.sys_clk_type = MLAN_CLK_SUPPORTED; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + length_index = + MIN(cfg->param.sys_clock.sys_clk_num, MLAN_MAX_CLK_NUM); + + /* Supported clocks */ + for (i = 1; i <= length_index; i++) + data[i + data_length] = + (int)cfg->param.sys_clock.sys_clk[i - 1]; + + data_length += length_index; + + /* Send length as first element */ + data[0] = data_length; + data_length++; + + memcpy(respbuf, data, sizeof(int) * data_length); + ret = data_length * sizeof(int); + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** AES key length */ +#define AES_KEY_LEN 16 +/** + * @brief Adhoc AES control + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + * + * @return Number of bytes written, negative for failure. + */ +static int +woal_priv_adhoc_aes(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + static char buf[256]; + int ret = 0, action = -1; + unsigned int i; + t_u8 key_ascii[32]; + t_u8 key_hex[16]; + t_u8 *tmp; + mlan_bss_info bss_info; + mlan_ds_sec_cfg *sec = NULL; + mlan_ioctl_req *req = NULL; + t_u8 bcast_addr[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + int header_len = 0; + int copy_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_ADHOC_AES); + memset(key_ascii, 0x00, sizeof(key_ascii)); + memset(key_hex, 0x00, sizeof(key_hex)); + memset(buf, 0x00, sizeof(buf)); + + /* Get current BSS information */ + memset(&bss_info, 0, sizeof(bss_info)); + woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info); + if (bss_info.bss_mode != MLAN_BSS_MODE_IBSS || + bss_info.media_connected == MTRUE) { + PRINTM(MERROR, "STA is connected or not in IBSS mode.\n"); + ret = -EOPNOTSUPP; + goto done; + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + if (strlen(respbuf) == header_len) { + /* Get Adhoc AES Key */ + req->req_id = MLAN_IOCTL_SEC_CFG; + req->action = MLAN_ACT_GET; + sec = (mlan_ds_sec_cfg *)req->pbuf; + sec->sub_command = MLAN_OID_SEC_CFG_ENCRYPT_KEY; + sec->param.encrypt_key.key_len = AES_KEY_LEN; + sec->param.encrypt_key.key_index = MLAN_KEY_INDEX_UNICAST; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + memcpy(key_hex, sec->param.encrypt_key.key_material, + sizeof(key_hex)); + HEXDUMP("Adhoc AES Key (HEX)", key_hex, sizeof(key_hex)); + + tmp = key_ascii; + for (i = 0; i < sizeof(key_hex); i++) + tmp += sprintf((char *)tmp, "%02x", key_hex[i]); + } else { + /* SET operation */ + copy_len = (strlen(respbuf) - header_len); + if (copy_len >= sizeof(buf)) { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + memcpy(buf, respbuf + header_len, copy_len); + + /* Parse the buf to get the cmd_action */ + action = woal_atox(&buf[0]); + if (action < 1 || action > 2) { + PRINTM(MERROR, "Invalid action argument %d\n", action); + ret = -EINVAL; + goto done; + } + + req->req_id = MLAN_IOCTL_SEC_CFG; + req->action = MLAN_ACT_SET; + sec = (mlan_ds_sec_cfg *)req->pbuf; + sec->sub_command = MLAN_OID_SEC_CFG_ENCRYPT_KEY; + + if (action == 1) { + /* Set Adhoc AES Key */ + memcpy(key_ascii, &buf[2], sizeof(key_ascii)); + woal_ascii2hex(key_hex, (char *)key_ascii, + sizeof(key_hex)); + HEXDUMP("Adhoc AES Key (HEX)", key_hex, + sizeof(key_hex)); + + sec->param.encrypt_key.key_len = AES_KEY_LEN; + sec->param.encrypt_key.key_index = + MLAN_KEY_INDEX_UNICAST; + sec->param.encrypt_key.key_flags = + KEY_FLAG_SET_TX_KEY | KEY_FLAG_GROUP_KEY; + memcpy(sec->param.encrypt_key.mac_addr, + (u8 *)bcast_addr, ETH_ALEN); + memcpy(sec->param.encrypt_key.key_material, key_hex, + sec->param.encrypt_key.key_len); + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + } else { + /* Clear Adhoc AES Key */ + sec->param.encrypt_key.key_len = AES_KEY_LEN; + sec->param.encrypt_key.key_index = + MLAN_KEY_INDEX_UNICAST; + sec->param.encrypt_key.key_flags = KEY_FLAG_REMOVE_KEY; + memcpy(sec->param.encrypt_key.mac_addr, + (u8 *)bcast_addr, ETH_ALEN); + memset(sec->param.encrypt_key.key_material, 0, + sizeof(sec->param.encrypt_key.key_material)); + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + } + } + + HEXDUMP("Adhoc AES Key (ASCII)", key_ascii, sizeof(key_ascii)); + copy_len = sizeof(key_ascii); + memcpy(respbuf, &key_ascii, copy_len); + ret = copy_len; + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + + LEAVE(); + return ret; +} + +/** + * @brief Associate to a specific indexed entry in the ScanTable + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + * + * @return Number of bytes written, negative for failure. + */ +static int +woal_priv_associate_ssid_bssid(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int ret = 0, copy_len = 0; + int header_len = 0; + mlan_ssid_bssid ssid_bssid; +#ifdef REASSOCIATION + mlan_bss_info bss_info; +#endif + char buf[64]; + t_u8 buflen; + t_u8 mac_idx; + t_u8 i; + + ENTER(); + + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_ASSOCIATE); + if (strlen(respbuf) == header_len) { + PRINTM(MERROR, "Invalid number of parameters\n"); + ret = -EINVAL; + goto done; + } + copy_len = strlen(respbuf) - header_len; + mac_idx = 0; + buflen = MIN(copy_len, (sizeof(buf) - 1)); + memset(buf, 0, sizeof(buf)); + memset(&ssid_bssid, 0, sizeof(ssid_bssid)); + + if (buflen < (3 * ETH_ALEN) + 2) { + PRINTM(MERROR, + "Associate: Insufficient length in IOCTL input\n"); + + /* buffer should be at least 3 characters per BSSID octet "00:" + ** plus a space separater and at least 1 char in the SSID + */ + ret = -EINVAL; + goto done; + } + + memcpy(buf, respbuf + header_len, buflen); + + /* Skip white space */ + for (i = 0; (i < buflen) && (buf[i] == ' '); i++) ; + + /* Copy/Convert the BSSID */ + for (; (i < buflen) && (mac_idx < ETH_ALEN) && (buf[i] != ' '); i++) { + if (buf[i] == ':') { + mac_idx++; + } else { + ssid_bssid.bssid[mac_idx] = (t_u8)woal_atox(buf + i); + + while (((i < buflen) && isxdigit(buf[i + 1]))) + /* Skip entire hex value */ + i++; + } + } + + /* Skip one space between the BSSID and start of the SSID */ + i++; + + /* Copy the SSID */ + ssid_bssid.ssid.ssid_len = buflen - i; + memcpy(ssid_bssid.ssid.ssid, buf + i, sizeof(ssid_bssid.ssid.ssid)); + + PRINTM(MCMND, "iwpriv assoc: AP=[" MACSTR "], ssid(%d)=[%s]\n", + MAC2STR(ssid_bssid.bssid), + (int)ssid_bssid.ssid.ssid_len, ssid_bssid.ssid.ssid); + + if (MLAN_STATUS_SUCCESS != woal_bss_start(priv, + MOAL_IOCTL_WAIT, + &ssid_bssid)) { + ret = -EFAULT; + goto done; + } +#ifdef REASSOCIATION + memset(&bss_info, 0x00, sizeof(bss_info)); + if (MLAN_STATUS_SUCCESS == woal_get_bss_info(priv, + MOAL_IOCTL_WAIT, + &bss_info)) { + memcpy(&priv->prev_ssid_bssid.ssid, + &bss_info.ssid, sizeof(mlan_802_11_ssid)); + memcpy(&priv->prev_ssid_bssid.bssid, + &bss_info.bssid, MLAN_MAC_ADDR_LENGTH); + } +#endif /* REASSOCIATION */ + +done: + LEAVE(); + return 0; +} + +/* Maximum input output characters in group WOAL_SET_GET_256_CHAR */ +#define MAX_IN_OUT_CHAR 256 +/** Tx BF Global conf argument index */ +#define BF_ENABLE_PARAM 1 +#define SOUND_ENABLE_PARAM 2 +#define FB_TYPE_PARAM 3 +#define SNR_THRESHOLD_PARAM 4 +#define SOUND_INTVL_PARAM 5 +#define BF_MODE_PARAM 6 +#define MAX_TX_BF_GLOBAL_ARGS 6 +#define BF_CFG_ACT_GET 0 +#define BF_CFG_ACT_SET 1 + +/** + * @brief Set/Get Transmit beamforming configuration + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + * + * @return Number of bytes written, negative for failure. + */ +static int +woal_priv_tx_bf_cfg(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int header_len = 0; + int ret = 0, copy_len = 0; + int bf_action = 0, interval = 0; + int snr = 0, i, tmp_val; + t_u8 buf[MAX_IN_OUT_CHAR], char_count = 0; + t_u8 *str, *token, *pos; + t_u16 action = 0; + + mlan_ds_11n_tx_bf_cfg bf_cfg; + mlan_trigger_sound_args *bf_sound = NULL; + mlan_tx_bf_peer_args *tx_bf_peer = NULL; + mlan_snr_thr_args *bf_snr = NULL; + mlan_bf_periodicity_args *bf_periodicity = NULL; + mlan_bf_global_cfg_args *bf_global = NULL; + + ENTER(); + + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_TX_BF_CFG); + if (strlen(respbuf) == header_len) { + PRINTM(MERROR, "Invalid number of parameters\n"); + ret = -EINVAL; + goto done; + } + memset(&bf_cfg, 0, sizeof(bf_cfg)); + /* Pointer to corresponding buffer */ + bf_sound = bf_cfg.body.bf_sound; + tx_bf_peer = bf_cfg.body.tx_bf_peer; + bf_snr = bf_cfg.body.bf_snr; + bf_periodicity = bf_cfg.body.bf_periodicity; + bf_global = &bf_cfg.body.bf_global_cfg; + /* Total characters in buffer */ + char_count = strlen(respbuf) - header_len; + copy_len = char_count; + memset(buf, 0, sizeof(buf)); + if (char_count) { + if (copy_len > sizeof(buf)) { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + memcpy(buf, respbuf + header_len, copy_len); + + if (char_count > 1 && buf[1] != ';') { + PRINTM(MERROR, + "No action argument. Separate with ';'\n"); + ret = -EINVAL; + goto done; + } + /* Replace ';' with NULL in the string to separate args */ + for (i = 0; i < char_count; i++) { + if (buf[i] == ';') + buf[i] = '\0'; + } + /* The first byte represents the beamforming action */ + if (woal_atoi(&bf_action, &buf[0]) != MLAN_STATUS_SUCCESS) { + ret = -EINVAL; + goto done; + } + switch (bf_action) { + case BF_GLOBAL_CONFIGURATION: + if (char_count == 1) { + action = MLAN_ACT_GET; + bf_cfg.action = BF_CFG_ACT_GET; + } else { + action = MLAN_ACT_SET; + bf_cfg.action = BF_CFG_ACT_SET; + /* Eliminate action field */ + token = &buf[2]; + for (i = 1, str = &buf[2]; token != NULL; i++) { + token = strstr(str, " "); + pos = str; + if (token != NULL) { + *token = '\0'; + str = token + 1; + } + woal_atoi(&tmp_val, pos); + switch (i) { + case BF_ENABLE_PARAM: + bf_global->bf_enbl = + (t_u8)tmp_val; + break; + case SOUND_ENABLE_PARAM: + bf_global->sounding_enbl = + (t_u8)tmp_val; + break; + case FB_TYPE_PARAM: + bf_global->fb_type = + (t_u8)tmp_val; + break; + case SNR_THRESHOLD_PARAM: + bf_global->snr_threshold = + (t_u8)tmp_val; + break; + case SOUND_INTVL_PARAM: + bf_global->sounding_interval = + (t_u16)tmp_val; + break; + case BF_MODE_PARAM: + bf_global->bf_mode = + (t_u8)tmp_val; + break; + default: + PRINTM(MERROR, + "Invalid Argument\n"); + ret = -EINVAL; + goto done; + } + } + } + break; + case TRIGGER_SOUNDING_FOR_PEER: + /* First arg = 2 BfAction + * Second arg = 17 MAC "00:50:43:20:BF:64" */ + if (char_count != 19) { + PRINTM(MERROR, "Invalid argument\n"); + ret = -EINVAL; + goto done; + } + woal_mac2u8(bf_sound->peer_mac, &buf[2]); + action = MLAN_ACT_SET; + bf_cfg.action = BF_CFG_ACT_SET; + break; + case SET_GET_BF_PERIODICITY: + /* First arg = 2 BfAction + * Second arg = 18 MAC "00:50:43:20:BF:64;" + * Third arg = 1 (min char) TX BF interval + * 10 (max char) u32 maximum value 4294967295 */ + if (char_count < 19 || char_count > 30) { + PRINTM(MERROR, "Invalid argument\n"); + ret = -EINVAL; + goto done; + } + + woal_mac2u8(bf_periodicity->peer_mac, &buf[2]); + if (char_count == 19) { + action = MLAN_ACT_GET; + bf_cfg.action = BF_CFG_ACT_GET; + } else { + action = MLAN_ACT_SET; + bf_cfg.action = BF_CFG_ACT_SET; + if (woal_atoi(&interval, &buf[20]) != + MLAN_STATUS_SUCCESS) { + ret = -EINVAL; + goto done; + } + bf_periodicity->interval = interval; + } + break; + case TX_BF_FOR_PEER_ENBL: + /* Handle only SET operation here + * First arg = 2 BfAction + * Second arg = 18 MAC "00:50:43:20:BF:64;" + * Third arg = 2 enable/disable bf + * Fourth arg = 2 enable/disable sounding + * Fifth arg = 1 FB Type */ + if (char_count != 25 && char_count != 1) { + PRINTM(MERROR, "Invalid argument\n"); + ret = -EINVAL; + goto done; + } + if (char_count == 1) { + action = MLAN_ACT_GET; + bf_cfg.action = BF_CFG_ACT_GET; + } else { + woal_mac2u8(tx_bf_peer->peer_mac, &buf[2]); + woal_atoi(&tmp_val, &buf[20]); + tx_bf_peer->bf_enbl = (t_u8)tmp_val; + woal_atoi(&tmp_val, &buf[22]); + tx_bf_peer->sounding_enbl = (t_u8)tmp_val; + woal_atoi(&tmp_val, &buf[24]); + tx_bf_peer->fb_type = (t_u8)tmp_val; + action = MLAN_ACT_SET; + bf_cfg.action = BF_CFG_ACT_SET; + } + break; + case SET_SNR_THR_PEER: + /* First arg = 2 BfAction + * Second arg = 18 MAC "00:50:43:20:BF:64;" + * Third arg = 1/2 SNR u8 - can be 1/2 charerters */ + if (char_count != 1 && + !(char_count == 21 || char_count == 22)) { + PRINTM(MERROR, "Invalid argument\n"); + ret = -EINVAL; + goto done; + } + if (char_count == 1) { + action = MLAN_ACT_GET; + bf_cfg.action = BF_CFG_ACT_GET; + } else { + woal_mac2u8(bf_snr->peer_mac, &buf[2]); + if (woal_atoi(&snr, &buf[20]) != + MLAN_STATUS_SUCCESS) { + ret = -EINVAL; + goto done; + } + bf_snr->snr = snr; + action = MLAN_ACT_SET; + bf_cfg.action = BF_CFG_ACT_SET; + } + break; + default: + ret = -EINVAL; + goto done; + } + + /* Save the value */ + bf_cfg.bf_action = bf_action; + if (MLAN_STATUS_SUCCESS != + woal_set_get_tx_bf_cfg(priv, action, &bf_cfg)) { + ret = -EFAULT; + goto done; + } + } else { + ret = -EINVAL; + goto done; + } + + switch (bf_action) { + case BF_GLOBAL_CONFIGURATION: + memcpy(respbuf, bf_global, sizeof(mlan_bf_global_cfg_args)); + ret = sizeof(mlan_bf_global_cfg_args); + break; + case TRIGGER_SOUNDING_FOR_PEER: + memcpy(respbuf, bf_sound, sizeof(mlan_bf_global_cfg_args)); + ret = sizeof(mlan_bf_global_cfg_args); + break; + case SET_GET_BF_PERIODICITY: + memcpy(respbuf, bf_periodicity, + sizeof(mlan_bf_periodicity_args)); + ret = sizeof(mlan_bf_periodicity_args); + break; + case TX_BF_FOR_PEER_ENBL: + memcpy(respbuf, tx_bf_peer, sizeof(mlan_tx_bf_peer_args)); + ret = sizeof(mlan_tx_bf_peer_args); + break; + case SET_SNR_THR_PEER: + memcpy(respbuf, bf_snr, sizeof(mlan_snr_thr_args)); + ret = sizeof(mlan_snr_thr_args); + break; + default: + ret = 0; + } +done: + LEAVE(); + return ret; +} + +/** + * @brief Cmd53 read/write register + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + * + * @return Number of bytes written, negative for failure. + */ +static int +woal_priv_cmd53rdwr(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int header_len = 0; + int ret = 0; + t_u8 *buf = NULL; + t_u8 *data = NULL; + t_u8 rw, func, mode; + t_u16 blklen = 0, blknum = 0; + int reg = 0; + t_u32 pattern_len = 0, total_len = 0; + t_u16 cmd_len; + gfp_t flag; + + ENTER(); + + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_SD_CMD53_RW); + + flag = (in_atomic() || irqs_disabled())? GFP_ATOMIC : GFP_KERNEL; + data = kzalloc(WOAL_2K_BYTES, flag); + if (!data) { + PRINTM(MERROR, "Cannot allocate buffer for command!\n"); + ret = -EFAULT; + goto done; + } + memcpy(&cmd_len, respbuf + header_len, sizeof(cmd_len)); + buf = respbuf + header_len + sizeof(cmd_len); + + rw = buf[0]; /* read/write (0/1) */ + func = buf[1]; /* func (0/1/2) */ + reg = buf[5]; /* address */ + reg = (reg << 8) | buf[4]; + reg = (reg << 8) | buf[3]; + reg = (reg << 8) | buf[2]; + mode = buf[6]; /* byte mode/block mode (0/1) */ + blklen = buf[8]; /* block size */ + blklen = (blklen << 8) | buf[7]; + blknum = buf[10]; /* block number or byte number */ + blknum = (blknum << 8) | buf[9]; + + if (mode == BYTE_MODE) + blklen = 1; + else + mode = BLOCK_MODE; + + total_len = (mode == BLOCK_MODE) ? blknum * blklen : blknum; + if (total_len > WOAL_2K_BYTES) { + PRINTM(MERROR, "Total data length is too large!\n"); + ret = -EINVAL; + goto done; + } + PRINTM(MINFO, "CMD53 read/write, func = %d, addr = %#x, mode = %d, " + "block size = %d, block(byte) number = %d\n", + func, reg, mode, blklen, blknum); + + if (!rw) { + sdio_claim_host(((struct sdio_mmc_card *)priv->phandle->card)-> + func); + if (sdio_readsb + (((struct sdio_mmc_card *)priv->phandle->card)->func, + respbuf, reg, total_len)) { + PRINTM(MERROR, + "sdio_readsb: reading memory 0x%x failed\n", + reg); + goto done; + } + sdio_release_host(((struct sdio_mmc_card *)priv->phandle-> + card)->func); + ret = total_len; + } else { + int pos = 0; + pattern_len = cmd_len - 11; + if (pattern_len > total_len) + pattern_len = total_len; + + /* Copy/duplicate the pattern to data buffer */ + for (pos = 0; pos < total_len; pos++) + data[pos] = buf[11 + (pos % pattern_len)]; + sdio_claim_host(((struct sdio_mmc_card *)priv->phandle->card)-> + func); + if (sdio_writesb + (((struct sdio_mmc_card *)priv->phandle->card)->func, reg, + data, total_len)) + PRINTM(MERROR, + "sdio_writesb: writing memory 0x%x failed\n", + reg); + sdio_release_host(((struct sdio_mmc_card *)priv->phandle-> + card)->func); + } + +done: + kfree(data); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get Port Control mode + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + * + * @return Number of bytes written, negative for failure. + */ +static int +woal_priv_port_ctrl(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int header_len = 0, user_data_len = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_sec_cfg *sec = NULL; + int ret = 0, data = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + ENTER(); + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + sec = (mlan_ds_sec_cfg *)req->pbuf; + sec->sub_command = MLAN_OID_SEC_CFG_PORT_CTRL_ENABLED; + req->req_id = MLAN_IOCTL_SEC_CFG; + + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_PORT_CTRL); + if (strlen(respbuf) == header_len) { + /* GET operation */ + user_data_len = 0; + req->action = MLAN_ACT_GET; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, &data, + sizeof(data) / sizeof(int), &user_data_len); + if (user_data_len == 1) { + sec->param.port_ctrl_enabled = data; + req->action = MLAN_ACT_SET; + } else { + PRINTM(MERROR, "Invalid number of parameters\n"); + ret = -EINVAL; + goto done; + } + } + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (!user_data_len) { + memcpy(respbuf, &sec->param.port_ctrl_enabled, sizeof(int)); + ret = sizeof(int); + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + + LEAVE(); + return ret; +} + +/** + * @brief Private IOCTL entry to get the By-passed TX packet from upper layer + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + * + * @return Number of bytes written, negative for failure. + */ +static int +woal_priv_bypassed_packet(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int header_len = 0; + int ret = 0; + struct sk_buff *skb = NULL; + struct ethhdr *eth; + t_u16 moreLen = 0, copyLen = 0; + ENTER(); + +#define MLAN_BYPASS_PKT_EXTRA_OFFSET (4) + + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_PB_BYPASS); + copyLen = strlen(respbuf) - header_len; + moreLen = MLAN_MIN_DATA_HEADER_LEN + MLAN_BYPASS_PKT_EXTRA_OFFSET + + sizeof(mlan_buffer); + + skb = alloc_skb(copyLen + moreLen, GFP_KERNEL); + if (skb == NULL) { + PRINTM(MERROR, "kmalloc no memory !!\n"); + LEAVE(); + return -ENOMEM; + } + + skb_reserve(skb, moreLen); + + memcpy(skb_put(skb, copyLen), respbuf + header_len, copyLen); + + eth = (struct ethhdr *)skb->data; + eth->h_proto = __constant_htons(eth->h_proto); + skb->dev = priv->netdev; + + HEXDUMP("Bypass TX Data", skb->data, MIN(skb->len, 100)); + + woal_hard_start_xmit(skb, priv->netdev); + + LEAVE(); + return ret; +} + +/** + * @brief Control Coalescing status Enable/Disable + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + * + * @return Number of bytes written, negative for failure. + */ +static int +woal_priv_coalescing_status(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int header_len = 0, user_data_len = 0; + int ret = 0, data = 0; + mlan_ds_misc_cfg *pcoal = NULL; + mlan_ioctl_req *req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + pcoal = (mlan_ds_misc_cfg *)req->pbuf; + + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_COALESCE_STATUS); + if (strlen(respbuf) == header_len) { + /* GET operation */ + user_data_len = 0; + req->action = MLAN_ACT_GET; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, &data, + sizeof(data) / sizeof(int), &user_data_len); + if (user_data_len == 1) { + if (data == 1) + pcoal->param.coalescing_status = + MLAN_MISC_COALESCING_ENABLE; + else + pcoal->param.coalescing_status = + MLAN_MISC_COALESCING_DISABLE; + req->action = MLAN_ACT_SET; + } else { + PRINTM(MERROR, "Invalid number of parameters\n"); + ret = -EINVAL; + goto done; + } + } + + req->req_id = MLAN_IOCTL_MISC_CFG; + pcoal->sub_command = MLAN_OID_MISC_COALESCING_STATUS; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + data = (int)(((mlan_ds_misc_cfg *)req->pbuf)->param.coalescing_status); + + memcpy(respbuf, &data, sizeof(int)); + ret = sizeof(int); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get module configuration + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + * + * @return Number of bytes written, negative for failure. + */ +static int +woal_priv_fw_wakeup_method(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int header_len = 0, user_data_len = 0; + int ret = 0, data[2]; + mlan_ds_pm_cfg *pm_cfg = NULL; + mlan_ioctl_req *req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + pm_cfg = (mlan_ds_pm_cfg *)req->pbuf; + + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_FW_WAKEUP_METHOD); + if (strlen(respbuf) == header_len) { + /* GET operation */ + user_data_len = 0; + req->action = MLAN_ACT_GET; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, data, + sizeof(data) / sizeof(int), &user_data_len); + if (user_data_len > 2) { + PRINTM(MERROR, "Invalid parameter number\n"); + ret = -EINVAL; + goto done; + } + if (data[0] != FW_WAKEUP_METHOD_INTERFACE && + data[0] != FW_WAKEUP_METHOD_GPIO) { + PRINTM(MERROR, "Invalid FW wake up method:%d\n", + data[0]); + ret = -EINVAL; + goto done; + } + if (data[0] == FW_WAKEUP_METHOD_GPIO) { + if (user_data_len == 1) { + PRINTM(MERROR, + "Please provide gpio pin number for FW_WAKEUP_METHOD gpio\n"); + ret = -EINVAL; + goto done; + } + pm_cfg->param.fw_wakeup_params.gpio_pin = data[1]; + } + + req->action = MLAN_ACT_SET; + pm_cfg->param.fw_wakeup_params.method = data[0]; + } + + pm_cfg->sub_command = MLAN_OID_PM_CFG_FW_WAKEUP_METHOD; + req->req_id = MLAN_IOCTL_PM_CFG; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + data[0] = ((mlan_ds_pm_cfg *)req->pbuf)->param.fw_wakeup_params.method; + data[1] = + ((mlan_ds_pm_cfg *)req->pbuf)->param.fw_wakeup_params.gpio_pin; + memcpy(respbuf, &data, sizeof(data)); + ret = sizeof(int) * 2; +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + + LEAVE(); + return ret; +} + +/** + * @brief Set Robustcoex gpiocfg + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + * + * @return Number of bytes written, negative for failure. + */ +static int +woal_priv_robustcoex(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int header_len = 0, user_data_len = 0; + int ret = 0, data[3]; + mlan_ds_misc_cfg *robust_coex_cfg = NULL; + mlan_ioctl_req *req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + robust_coex_cfg = (mlan_ds_misc_cfg *)req->pbuf; + while (respbuf[0] == ' ') { + /** skip space */ + respbuf++; + } + + if (strncmp(respbuf, "gpiocfg", strlen("gpiocfg")) == 0) { + header_len = strlen("gpiocfg") + 1; + parse_arguments(respbuf + header_len, data, + sizeof(data) / sizeof(int), &user_data_len); + if (user_data_len > 3) { + PRINTM(MERROR, "Invalid parameter number\n"); + ret = -EINVAL; + goto done; + } + if (data[0] != ROBUSTCOEX_GPIOCFG_ENABLE && + data[0] != ROBUSTCOEX_GPIOCFG_DISABLE) { + PRINTM(MERROR, "Invalid parameter number\n"); + ret = -EINVAL; + goto done; + } + if (data[0] == ROBUSTCOEX_GPIOCFG_ENABLE) { + if (user_data_len != 3) { + PRINTM(MMSG, + "Please provide gpio num and gpio polarity for ROBUSTCOEX_GPIOCFG_ENABLE\n"); + ret = -EINVAL; + goto done; + } + robust_coex_cfg->param.robustcoexparams.method = + ROBUSTCOEX_GPIO_CFG; + robust_coex_cfg->param.robustcoexparams.enable = + ROBUSTCOEX_GPIOCFG_ENABLE; + robust_coex_cfg->param.robustcoexparams.gpio_num = + data[1]; + robust_coex_cfg->param.robustcoexparams.gpio_polarity = + data[2]; + } else { + robust_coex_cfg->param.robustcoexparams.method = + ROBUSTCOEX_GPIO_CFG; + robust_coex_cfg->param.robustcoexparams.enable = + ROBUSTCOEX_GPIOCFG_DISABLE; + robust_coex_cfg->param.robustcoexparams.gpio_num = 0; + robust_coex_cfg->param.robustcoexparams.gpio_polarity = + 0; + } + req->action = MLAN_ACT_SET; + req->req_id = MLAN_IOCTL_MISC_CFG; + robust_coex_cfg->sub_command = MLAN_OID_MISC_ROBUSTCOEX; + } else { + PRINTM(MERROR, "Invalid parameter\n"); + ret = -EFAULT; + goto done; + } + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; + +} + +/** + * @brief Set and get boot sleep configure + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + * + * @return Number of bytes written, negative for failure. + */ +static int +woal_priv_bootsleep(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int ret = MLAN_STATUS_SUCCESS; + int user_data_len = 0; + int header_len = 0; + int allowed = 1; + int data[1] = { 0 }; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *misc = NULL; + + ENTER(); + + if (!priv || !priv->phandle) { + PRINTM(MERROR, "priv or handle is null\n"); + ret = -EFAULT; + goto done; + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + LEAVE(); + return -ENOMEM; + } + + misc = (mlan_ds_misc_cfg *)req->pbuf; + misc->sub_command = MLAN_OID_MISC_BOOT_SLEEP; + req->req_id = MLAN_IOCTL_MISC_CFG; + + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_BOOTSLEEP); + + if (strlen(respbuf) == header_len) { + req->action = MLAN_ACT_GET; + } else { + req->action = MLAN_ACT_SET; + parse_arguments(respbuf + header_len, data, ARRAY_SIZE(data), + &user_data_len); + if (user_data_len != allowed) { + PRINTM(MERROR, "Invalid number of args! %d\n", + user_data_len); + ret = -EINVAL; + goto done; + } + misc->param.boot_sleep = data[0] ? 1 : 0; + PRINTM(MIOCTL, "boot sleep cfg:%u\n", misc->param.boot_sleep); + } + + ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT_TIMEOUT); + if (ret != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + memcpy(respbuf, &misc->param.boot_sleep, + sizeof(misc->param.boot_sleep)); + ret = sizeof(misc->param.boot_sleep); + + PRINTM(MIOCTL, "boot sleep cfg: 0x%u\n", misc->param.boot_sleep); + +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +#if defined(WIFI_DIRECT_SUPPORT) +#if defined(UAP_CFG80211) +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION +/** + * @brief Set/Get P2P NoA (Notice of Absence) parameters + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + * + * @return Number of bytes written, negative for failure. + */ +static int +woal_priv_cfg_noa(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int header_len = 0, user_data_len = 0; + int ret = 0, data[7]; + mlan_ds_wifi_direct_config noa_cfg; + + ENTER(); + + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_CFG_NOA); + memset(&noa_cfg, 0, sizeof(noa_cfg)); + + memset(data, 0, sizeof(data)); + parse_arguments(respbuf + header_len, data, ARRAY_SIZE(data), + &user_data_len); + + if (user_data_len > 5) { + PRINTM(MERROR, "invalid parameters\n"); + ret = -EINVAL; + goto done; + } + + noa_cfg.flags |= WIFI_DIRECT_NOA; + + if (woal_p2p_config(priv, MLAN_ACT_GET, &noa_cfg) != + MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "Could not get P2P noa config\n"); + ret = -EINVAL; + goto done; + } + + if (strlen(respbuf) == header_len) { + /* GET operation */ + memcpy(respbuf, &noa_cfg, sizeof(noa_cfg)); + ret = sizeof(noa_cfg); + } else { + switch (user_data_len) { + case 5: + noa_cfg.noa_interval = (t_u32)data[4]; + case 4: + noa_cfg.noa_duration = (t_u32)data[3]; + case 3: + if (data[2] < 1 || data[2] > 255) { + PRINTM(MERROR, + "Invalid number of absence intervals\n"); + ret = -EINVAL; + goto done; + } + noa_cfg.noa_count = (t_u8)data[2]; + case 2: + if (data[1] < 0 || data[1] > 255) { + PRINTM(MERROR, "Invalid Index\n"); + ret = -EINVAL; + goto done; + } + noa_cfg.index = (t_u16)data[1]; + case 1: + if (data[0] < 0 || data[0] > 1) { + PRINTM(MERROR, "Invalid noa enable\n"); + ret = -EINVAL; + goto done; + } + noa_cfg.noa_enable = (t_u8)data[0]; + noa_cfg.flags |= WIFI_DIRECT_NOA; + break; + default: + break; + } + woal_p2p_config(priv, MLAN_ACT_SET, &noa_cfg); + } + +done: + + LEAVE(); + return ret; +} + +/** + * @brief Set/Get P2P OPP-PS parameters + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + * + * @return Number of bytes written, negative for failure. + */ +static int +woal_priv_cfg_opp_ps(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int header_len = 0, user_data_len = 0; + int ret = 0, data[7]; + mlan_ds_wifi_direct_config opp_ps_cfg; + + ENTER(); + + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_CFG_OPP_PS); + memset(&opp_ps_cfg, 0, sizeof(opp_ps_cfg)); + + memset(data, 0, sizeof(data)); + parse_arguments(respbuf + header_len, data, ARRAY_SIZE(data), + &user_data_len); + + if (user_data_len > 2) { + PRINTM(MERROR, "invalid parameters\n"); + ret = -EINVAL; + goto done; + } + + opp_ps_cfg.flags |= WIFI_DIRECT_OPP_PS; + + if (woal_p2p_config(priv, MLAN_ACT_GET, &opp_ps_cfg) != + MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "Could not get P2P opp ps config\n"); + ret = -EINVAL; + goto done; + } + + if (strlen(respbuf) == header_len) { + /* GET operation */ + memcpy(respbuf, &opp_ps_cfg, sizeof(opp_ps_cfg)); + ret = sizeof(opp_ps_cfg); + } else { + switch (user_data_len) { + case 2: + opp_ps_cfg.ct_window = (t_u8)data[1]; + case 1: + if (data[0] < 0 || data[0] > 1) { + PRINTM(MERROR, "Invalid ps enable\n"); + ret = -EINVAL; + goto done; + } + opp_ps_cfg.opp_ps_enable = (t_u8)data[0]; + opp_ps_cfg.flags |= WIFI_DIRECT_OPP_PS; + default: + break; + } + woal_p2p_config(priv, MLAN_ACT_SET, &opp_ps_cfg); + } + +done: + + LEAVE(); + return ret; +} +#endif +#endif +#endif + +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#ifdef WIFI_DIRECT_SUPPORT +#define DEF_NOA_INTERVAL 100 +/** + ** @brief Set/Get P2P NoA (Notice of Absence) parameters + ** @param priv Pointer to moal_private structure + ** @param respbuf Pointer to response buffer + ** @param resplen Response buffer length + ** + ** @return Number of bytes written, negative for failure. + **/ +static int +woal_p2p_ps_cfg(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int user_data_len = 0; + int ret = 0, data[2]; + u32 duration = priv->phandle->noa_duration; + u32 interval = 0; + + ENTER(); + if (strlen(respbuf) > strlen("P2P_PERIODIC_SLEEP")) { + memset((char *)data, 0, sizeof(data)); + parse_arguments(respbuf + strlen("P2P_PERIODIC_SLEEP") + 1, + data, ARRAY_SIZE(data), &user_data_len); + } + if ((user_data_len != 1) && (user_data_len != 2)) { + PRINTM(MERROR, + " Invalid parameter number for P2P_PERIODIC_SLEEP"); + ret = -EINVAL; + goto done; + } + if (data[0] < DEF_NOA_INTERVAL) + interval = DEF_NOA_INTERVAL; + else + interval = + (data[0] + DEF_NOA_INTERVAL - + 1) / DEF_NOA_INTERVAL * DEF_NOA_INTERVAL; + + if (user_data_len == 2) + duration = data[1]; + if (duration >= interval) { + PRINTM(MERROR, + " Invalid noa duration/interval! duration=%d interval=%d\n", + duration, interval); + ret = -EINVAL; + goto done; + } + priv->phandle->noa_interval = interval; + priv->phandle->noa_duration = duration; + PRINTM(MIOCTL, "configure noa interval=%d, duration=%d\n", + priv->phandle->noa_interval, priv->phandle->noa_duration); +done: + LEAVE(); + return ret; +} +#endif +#endif + +/** + * @brief Set/Get DFS repeater mode + * + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + * + * @return Number of bytes written, negative for failure. + */ +static int +woal_priv_dfs_repeater_cfg(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int ret = 0; + int user_data_len = 0, header_len = 0, data[1]; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *misc_cfg = NULL; + mlan_ds_misc_dfs_repeater *dfs_repeater = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_DFS_REPEATER_CFG); + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + req->req_id = MLAN_IOCTL_MISC_CFG; + misc_cfg = (mlan_ds_misc_cfg *)req->pbuf; + misc_cfg->sub_command = MLAN_OID_MISC_DFS_REAPTER_MODE; + dfs_repeater = + (mlan_ds_misc_dfs_repeater *)&misc_cfg->param.dfs_repeater; + + if (strlen(respbuf) == header_len) { + /* GET operation */ + user_data_len = 0; + req->action = MLAN_ACT_GET; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, data, ARRAY_SIZE(data), + &user_data_len); + if (user_data_len != 1) { + PRINTM(MERROR, "Invalid number of args! %d\n", + user_data_len); + ret = -EINVAL; + goto done; + } + if ((data[0] != MTRUE) && (data[0] != MFALSE)) { + PRINTM(MERROR, "Invalid DFS repeater mode %d\n", + data[0]); + ret = -EINVAL; + goto done; + } + dfs_repeater->mode = (t_u16)data[0]; + + req->action = MLAN_ACT_SET; + } + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (!user_data_len) { + memcpy(respbuf, (t_u8 *)dfs_repeater, + sizeof(mlan_ds_misc_dfs_repeater)); + ret = sizeof(mlan_ds_misc_dfs_repeater); + } + + /* Store current value of DFS repeater mode for futher references. eg., for + * avoiding CAC timers + */ + priv->phandle->dfs_repeater_mode = dfs_repeater->mode; + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + + LEAVE(); + return ret; +} + +#ifdef WIFI_DIRECT_SUPPORT +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +/** + * @brief Set/Get MIRACAST configuration parameters + * + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + * + * @return Number of bytes written, negative for failure. + */ +static int +woal_priv_miracast_cfg(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int ret = 0; + int user_data_len = 0, header_len = 0, data[3] = { 0, 0, 0 }; + + ENTER(); + + if (!priv || !priv->phandle) { + PRINTM(MERROR, "priv or handle is null\n"); + ret = -EFAULT; + goto done; + } + + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_MIRACAST_CFG); + + if (strlen(respbuf) == header_len) { + /* GET operation */ + data[0] = priv->phandle->miracast_mode; + data[1] = priv->phandle->miracast_scan_time; + data[2] = priv->phandle->scan_chan_gap; + + memcpy(respbuf, (t_u8 *)data, sizeof(data)); + ret = sizeof(data); + } else { + /* SET operation */ + memset(data, 0, sizeof(data)); + parse_arguments(respbuf + header_len, data, ARRAY_SIZE(data), + &user_data_len); + + if (user_data_len > 3) { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + if (data[0] < 0 || data[0] > 2 || data[1] < 0 || data[2] < 0) { + PRINTM(MERROR, "Invalid argument\n"); + ret = -EINVAL; + goto done; + } + } + + if (user_data_len >= 1) + priv->phandle->miracast_mode = (t_u8)data[0]; + if (user_data_len >= 2) + priv->phandle->miracast_scan_time = (t_u16)data[1]; + if (user_data_len == 3) + priv->phandle->scan_chan_gap = (t_u16)data[2]; + +done: + LEAVE(); + return ret; +} + +/** + * @brief Configuring scan gap for miracast mode + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return 0 --success, otherwise failure + */ +int +woal_set_scan_chan_gap(moal_private *priv, t_u8 *respbuf, int respbuflen) +{ + t_u32 data[2]; + int ret = 0; + int user_data_len = 0; + + ENTER(); + + if (strlen(respbuf) > strlen("SCAN_TIMING")) { + memset((char *)data, 0, sizeof(data)); + parse_arguments(respbuf + strlen("SCAN_TIMING") + 1, data, + ARRAY_SIZE(data), &user_data_len); + } + + if (user_data_len != 2) { + PRINTM(MERROR, "Invalid arguments for scan timing\n"); + ret = -EINVAL; + goto done; + } + priv->phandle->miracast_scan_time = (t_u16)data[0]; + priv->phandle->scan_chan_gap = (t_u16)data[1]; +done: + LEAVE(); + return ret; + +} +#endif +#endif + +/** + * @brief Set/Get control to coex RX window size configuration + * + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + * + * @return Number of bytes written, negative for failure. + */ +static int +woal_priv_coex_rx_winsize(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int ret = 0; + int user_data_len = 0, header_len = 0, data = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_11n_cfg *cfg_11n = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (!priv || !priv->phandle) { + PRINTM(MERROR, "priv or handle is null\n"); + ret = -EFAULT; + goto done; + } + + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_COEX_RX_WINSIZE); + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + req->req_id = MLAN_IOCTL_11N_CFG; + cfg_11n = (mlan_ds_11n_cfg *)req->pbuf; + cfg_11n->sub_command = MLAN_OID_11N_CFG_COEX_RX_WINSIZE; + + if (strlen(respbuf) == header_len) { + /* GET operation */ + user_data_len = 0; + req->action = MLAN_ACT_GET; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, &data, + sizeof(data) / sizeof(int), &user_data_len); + if (user_data_len != 1) { + PRINTM(MERROR, "Invalid number of args! %d\n", + user_data_len); + ret = -EINVAL; + goto done; + } + if ((data != MTRUE) && (data != MFALSE)) { + PRINTM(MERROR, + "Invalid coex RX window size parameter %d\n", + data); + ret = -EINVAL; + goto done; + } + cfg_11n->param.coex_rx_winsize = data; + req->action = MLAN_ACT_SET; + } + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (!user_data_len) { + memcpy(respbuf, (t_u8 *)&cfg_11n->param.coex_rx_winsize, + sizeof(t_u32)); + ret = sizeof(t_u32); + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + + LEAVE(); + return ret; +} + +/** + * @brief Set/Get control to TX AMPDU configuration on infra link + * + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + * + * @return Number of bytes written, negative for failure. + */ +static int +woal_priv_txaggrctrl(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int ret = 0; + int user_data_len = 0, header_len = 0, data = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_11n_cfg *cfg_11n = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (!priv || !priv->phandle) { + PRINTM(MERROR, "priv or handle is null\n"); + ret = -EFAULT; + goto done; + } + + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_TX_AGGR_CTRL); + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + req->req_id = MLAN_IOCTL_11N_CFG; + cfg_11n = (mlan_ds_11n_cfg *)req->pbuf; + cfg_11n->sub_command = MLAN_OID_11N_CFG_TX_AGGR_CTRL; + + if (strlen(respbuf) == header_len) { + /* GET operation */ + user_data_len = 0; + req->action = MLAN_ACT_GET; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, &data, + sizeof(data) / sizeof(int), &user_data_len); + if (user_data_len != 1) { + PRINTM(MERROR, "Invalid number of args! %d\n", + user_data_len); + ret = -EINVAL; + goto done; + } + if ((data != MTRUE) && (data != MFALSE)) { + PRINTM(MERROR, "Invalid txaggrctrl parameter %d\n", + data); + ret = -EINVAL; + goto done; + } + cfg_11n->param.txaggrctrl = data; + req->action = MLAN_ACT_SET; + } + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (!user_data_len) { + memcpy(respbuf, (t_u8 *)&cfg_11n->param.txaggrctrl, + sizeof(t_u32)); + ret = sizeof(t_u32); + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + + LEAVE(); + return ret; +} + +/** + * @brief Set/Get control to enable/disable auto TDLS + * + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + * + * @return Number of bytes written, negative for failure. + */ +static int +woal_priv_auto_tdls(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int ret = 0; + int user_data_len = 0, header_len = 0, data = 0; + + ENTER(); + + if (!priv || !priv->phandle) { + PRINTM(MERROR, "priv or handle is null\n"); + ret = -EFAULT; + goto done; + } + + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_AUTO_TDLS); + + if (strlen(respbuf) == header_len) { + /* GET operation */ + data = priv->enable_auto_tdls; + memcpy(respbuf, (t_u8 *)&data, sizeof(data)); + ret = sizeof(data); + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, &data, + sizeof(data) / sizeof(int), &user_data_len); + if (user_data_len != 1) { + PRINTM(MERROR, "Invalid number of args! %d\n", + user_data_len); + ret = -EINVAL; + goto done; + } + if ((data != MTRUE) && (data != MFALSE)) { + PRINTM(MERROR, "Invalid autotdls parameter %d\n", data); + ret = -EINVAL; + goto done; + } + priv->enable_auto_tdls = (t_u8)data; + } + +done: + LEAVE(); + return ret; +} + +/** + * @brief Get SOC temperature + * + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + * + * @return Number of bytes written, negative for failure. + */ +static int +woal_priv_get_sensor_temp(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *pcfg = NULL; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + pcfg = (mlan_ds_misc_cfg *)req->pbuf; + pcfg->sub_command = MLAN_OID_MISC_GET_SENSOR_TEMP; + req->req_id = MLAN_IOCTL_MISC_CFG; + req->action = MLAN_ACT_GET; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + memset(respbuf, 0, respbuflen); + memcpy(respbuf, &pcfg->param.sensor_temp.temperature, sizeof(t_u32)); + + ret = sizeof(t_u32); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +/** + * @brief Enable/disable DFS offload + * + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + * + * @return Number of bytes written, negative for failure. + */ +static int +woal_priv_dfs_offload_enable(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + struct wiphy *wiphy = NULL; + int ret = 0, dfs_offload_en = 0, user_data_len = 0, header_len = 0; + + ENTER(); + + if (priv && priv->wdev) + wiphy = priv->wdev->wiphy; + if (!wiphy) { + PRINTM(MERROR, "wiphy is NULL\n"); + ret = -EFAULT; + goto done; + } + if (woal_is_any_interface_active(priv->phandle)) { + PRINTM(MERROR, + "DFS offload enable/disable do not allowed after BSS started!\n"); + ret = -EFAULT; + goto done; + } + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_DFS_OFFLOAD); + parse_arguments(respbuf + header_len, &dfs_offload_en, + sizeof(dfs_offload_en) / sizeof(int), &user_data_len); + if (user_data_len != 1) { + PRINTM(MERROR, "Invalid number of args! %d\n", user_data_len); + ret = -EINVAL; + goto done; + } + + if (dfs_offload_en != 0 && dfs_offload_en != 1) { + PRINTM(MERROR, "Invalid args!\n"); + ret = -EINVAL; + goto done; + } + if (dfs_offload != dfs_offload_en) { + dfs_offload = dfs_offload_en; + woal_update_radar_chans_dfs_state(wiphy); + } +done: + LEAVE(); + return ret; +} +#endif +#endif + +/** + * @brief Set/Get TDLS CS off channel value + * + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + + * @return Number of bytes written, negative for failure. + */ +static int +woal_priv_tdls_cs_chan(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_misc_cfg *misc = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + int ret = 0; + int user_data_len = 0, header_len = 0, data = 0; + + ENTER(); + + if (!priv || !priv->phandle) { + PRINTM(MERROR, "priv or handle is null\n"); + ret = -EFAULT; + goto done; + } + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (ioctl_req == NULL) { + ret = -ENOMEM; + goto done; + } + + misc = (mlan_ds_misc_cfg *)ioctl_req->pbuf; + misc->sub_command = MLAN_OID_MISC_TDLS_CS_CHANNEL; + ioctl_req->req_id = MLAN_IOCTL_MISC_CFG; + + header_len = strlen("TDLS_CS_CHAN"); + if (strlen(respbuf) == header_len) { + /* GET operation */ + ioctl_req->action = MLAN_ACT_GET; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len + 1, &data, + sizeof(data) / sizeof(int), &user_data_len); + if (user_data_len != 1) { + PRINTM(MERROR, "Invalid number of args! %d\n", + user_data_len); + ret = -EINVAL; + goto done; + } + ioctl_req->action = MLAN_ACT_SET; + misc->param.tdls_cs_channel = (t_u8)data; + } + + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + ret = sprintf(respbuf, "off channel %d\n", + misc->param.tdls_cs_channel) + 1; + + PRINTM(MIOCTL, "tdls CS channel %d\n", misc->param.tdls_cs_channel); +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + + LEAVE(); + return ret; +} + +/** + * @brief Set/Get TDLS idle timeout value + * + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + + * @return Number of bytes written, negative for failure. + */ +static int +woal_priv_tdls_idle_time(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_misc_cfg *misc = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + int ret = 0; + int user_data_len = 0, header_len = 0, data = 0; + + ENTER(); + + if (!priv || !priv->phandle) { + PRINTM(MERROR, "priv or handle is null\n"); + ret = -EFAULT; + goto done; + } + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (ioctl_req == NULL) { + ret = -ENOMEM; + goto done; + } + + misc = (mlan_ds_misc_cfg *)ioctl_req->pbuf; + misc->sub_command = MLAN_OID_MISC_TDLS_IDLE_TIME; + ioctl_req->req_id = MLAN_IOCTL_MISC_CFG; + + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_TDLS_IDLE_TIME); + if (strlen(respbuf) == header_len) { + /* GET operation */ + ioctl_req->action = MLAN_ACT_GET; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, &data, + sizeof(data) / sizeof(int), &user_data_len); + if (user_data_len != 1) { + PRINTM(MERROR, "Invalid number of args! %d\n", + user_data_len); + ret = -EINVAL; + goto done; + } + ioctl_req->action = MLAN_ACT_SET; + misc->param.tdls_idle_time = (t_u16)data; + } + + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + memcpy(respbuf, (t_u8 *)&misc->param.tdls_idle_time, sizeof(t_u16)); + ret = sizeof(t_u16); + + PRINTM(MIOCTL, "tdls idle time %d\n", misc->param.tdls_idle_time); +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + + LEAVE(); + return ret; +} + +#if defined(UAP_SUPPORT) +/** + * @brief Check validation of channel and oper class + * + * @param priv Pointer to moal_private structure + * @param channel channel + * @param oper_class oper_class + + * @return SUCCESS/FAIL + */ +static int +woal_check_valid_channel_operclass(moal_private *priv, int channel, + int oper_class) +{ + int ret = 0; + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_misc_cfg *misc = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (ioctl_req == NULL) { + ret = -ENOMEM; + goto done; + } + + misc = (mlan_ds_misc_cfg *)ioctl_req->pbuf; + misc->sub_command = MLAN_OID_MISC_OPER_CLASS_CHECK; + ioctl_req->req_id = MLAN_IOCTL_MISC_CFG; + ioctl_req->action = MLAN_ACT_GET; + misc->param.bw_chan_oper.oper_class = (t_u8)oper_class; + misc->param.bw_chan_oper.channel = (t_u8)channel; + + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + + LEAVE(); + return ret; +} + +/** + ** @brief Set extended channel switch ie + ** + ** @param priv Pointer to moal_private structure + ** @param respbuf Pointer to response buffer + ** @param resplen Response buffer length + ** + ** @return Number of bytes written, negative for failure. + **/ +static int +woal_priv_extend_channel_switch(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int ret = 0; + int user_data_len = 0; + int data[4] = { 0 }; + woal_extend_chan_switch *ext_chan_switch = NULL; + custom_ie *pcust_chansw_ie = NULL; + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_misc_cfg *misc = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (!priv || !priv->phandle) { + PRINTM(MERROR, "priv or handle is null\n"); + ret = -EFAULT; + goto done; + } + + if (priv->bss_role != MLAN_BSS_ROLE_UAP) { + PRINTM(MERROR, + "Extended Channel Switch is only allowed for AP/GO mode\n"); + ret = -EFAULT; + goto done; + } + + if (priv->bss_started != MTRUE) { + PRINTM(MERROR, "AP is not started!\n"); + ret = -EFAULT; + goto done; + } + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (ioctl_req == NULL) { + ret = -ENOMEM; + goto done; + } + + misc = (mlan_ds_misc_cfg *)ioctl_req->pbuf; + misc->sub_command = MLAN_OID_MISC_CUSTOM_IE; + ioctl_req->req_id = MLAN_IOCTL_MISC_CFG; + ioctl_req->action = MLAN_ACT_SET; + misc->param.cust_ie.type = TLV_TYPE_MGMT_IE; + misc->param.cust_ie.len = (sizeof(custom_ie) - MAX_IE_SIZE); + + pcust_chansw_ie = (custom_ie *)&misc->param.cust_ie.ie_data_list[0]; + pcust_chansw_ie->ie_index = 0xffff; /*Auto index */ + pcust_chansw_ie->ie_length = sizeof(woal_extend_chan_switch); + pcust_chansw_ie->mgmt_subtype_mask = MGMT_MASK_BEACON | MGMT_MASK_PROBE_RESP; /*Add IE for BEACON/probe resp */ + ext_chan_switch = + (woal_extend_chan_switch *) pcust_chansw_ie->ie_buffer; + + parse_arguments(respbuf + strlen(CMD_MARVELL) + + strlen(PRIV_CMD_EXTEND_CHAN_SWITCH), data, + ARRAY_SIZE(data), &user_data_len); + + if (sizeof(int) * user_data_len > sizeof(data)) { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + + ext_chan_switch->element_id = EXTEND_CHANNEL_SWITCH_ANN; + ext_chan_switch->len = 4; + ext_chan_switch->chan_switch_mode = data[0]; + ext_chan_switch->new_oper_class = data[1]; + ext_chan_switch->new_channel_num = data[2]; + ext_chan_switch->chan_switch_count = data[3]; + if (ext_chan_switch->new_channel_num >= 52 && + ext_chan_switch->new_channel_num <= 144) { + PRINTM(MERROR, "Switch to DFS channel is not allowed!\n"); + ret = -EINVAL; + goto done; + } + if (woal_check_valid_channel_operclass(priv, data[2], data[1])) { + PRINTM(MERROR, "Wrong channel switch parameters!\n"); + ret = -EINVAL; + goto done; + } + if (ext_chan_switch->chan_switch_mode) { + if (netif_carrier_ok(priv->netdev)) + netif_carrier_off(priv->netdev); + woal_stop_queue(priv->netdev); + priv->uap_tx_blocked = MTRUE; + } + + DBG_HEXDUMP(MCMD_D, "ECSA IE", (t_u8 *)pcust_chansw_ie->ie_buffer, + pcust_chansw_ie->ie_length); + + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "Failed to set ECSA IE\n"); + ret = -EFAULT; + goto done; + } + + priv->phandle->chsw_wait_q_woken = MFALSE; + /* wait for channel switch to complete */ + wait_event_interruptible_timeout(priv->phandle->chsw_wait_q, + priv->phandle->chsw_wait_q_woken, + (u32)HZ * + (ext_chan_switch->chan_switch_count + + 2) * 110 / 1000); + + pcust_chansw_ie->ie_index = 0xffff; /*Auto index */ + pcust_chansw_ie->mgmt_subtype_mask = 0; + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "Failed to clear ECSA IE\n"); + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + + LEAVE(); + return ret; +} + +/** + * @brief Get non-global oper class + * + * @param priv Pointer to moal_private structure + * @param bw bandwidth + * @param channel channel + * @param oper_class pointer to oper_class + + * @return non-global operclass + */ +static int +woal_priv_get_nonglobal_operclass_by_bw_channel(moal_private *priv, + t_u8 bandwidth, t_u8 channel, + t_u8 *oper_class) +{ + int ret = 0; + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_misc_cfg *misc = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (ioctl_req == NULL) { + ret = -ENOMEM; + goto done; + } + + misc = (mlan_ds_misc_cfg *)ioctl_req->pbuf; + misc->sub_command = MLAN_OID_MISC_OPER_CLASS; + ioctl_req->req_id = MLAN_IOCTL_MISC_CFG; + ioctl_req->action = MLAN_ACT_GET; + misc->param.bw_chan_oper.bandwidth = bandwidth; + misc->param.bw_chan_oper.channel = channel; + + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + *oper_class = misc->param.bw_chan_oper.oper_class; + +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + + LEAVE(); + return ret; +} + +/** + * @brief P2P extended channel switch + * + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + + * @return Number of bytes written, negative for failure. + */ +static int +woal_priv_p2p_ecsa(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int ret = 0; + int user_data_len = 0, header_len = 0; + int data[2] = { 0 }; + t_u8 bw = 0, oper_class = 0, channel = 0; + woal_extend_chan_switch *ext_chan_switch = NULL; + custom_ie *pcust_chansw_ie = NULL; + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_misc_cfg *misc = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (!priv || !priv->phandle) { + PRINTM(MERROR, "priv or handle is null\n"); + ret = -EFAULT; + goto done; + } + + if (priv->bss_role != MLAN_BSS_ROLE_UAP) { + PRINTM(MERROR, + "Extended Channel Switch is only allowed for AP/GO mode\n"); + ret = -EFAULT; + goto done; + } + + if (priv->bss_started != MTRUE) { + PRINTM(MERROR, "AP is not started!\n"); + ret = -EFAULT; + goto done; + } + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (ioctl_req == NULL) { + ret = -ENOMEM; + goto done; + } + + misc = (mlan_ds_misc_cfg *)ioctl_req->pbuf; + misc->sub_command = MLAN_OID_MISC_CUSTOM_IE; + ioctl_req->req_id = MLAN_IOCTL_MISC_CFG; + ioctl_req->action = MLAN_ACT_SET; + misc->param.cust_ie.type = TLV_TYPE_MGMT_IE; + misc->param.cust_ie.len = (sizeof(custom_ie) - MAX_IE_SIZE); + + pcust_chansw_ie = (custom_ie *)&misc->param.cust_ie.ie_data_list[0]; + pcust_chansw_ie->ie_index = 0xffff; /*Auto index */ + pcust_chansw_ie->ie_length = sizeof(woal_extend_chan_switch); + pcust_chansw_ie->mgmt_subtype_mask = MGMT_MASK_BEACON | MGMT_MASK_PROBE_RESP; /*Add IE for BEACON/probe resp */ + ext_chan_switch = + (woal_extend_chan_switch *) pcust_chansw_ie->ie_buffer; + + header_len = strlen("P2P_ECSA"); + parse_arguments(respbuf + header_len + 1, data, ARRAY_SIZE(data), + &user_data_len); + + if (user_data_len != 2) { + PRINTM(MERROR, "Invalid parameters\n"); + ret = -EFAULT; + goto done; + } + + channel = data[0]; + /* bandwidth 20:20M 40:40M 80:80M */ + bw = data[1]; + if (bw != 20 && bw != 40 && bw != 80) { + PRINTM(MERROR, "Unsupported bandwidth\n"); + ret = -EINVAL; + goto done; + } + if (channel >= 52 && channel <= 144) { + PRINTM(MERROR, "Switch to DFS channel is not allowed!\n"); + ret = -EINVAL; + goto done; + } + + woal_priv_get_nonglobal_operclass_by_bw_channel(priv, bw, channel, + &oper_class); + if (oper_class == 0) { + PRINTM(MERROR, "Wrong parameters!\n"); + ret = -EFAULT; + goto done; + } + ext_chan_switch->element_id = EXTEND_CHANNEL_SWITCH_ANN; + ext_chan_switch->len = 4; + ext_chan_switch->chan_switch_mode = 1; + ext_chan_switch->new_oper_class = oper_class; + ext_chan_switch->new_channel_num = channel; + ext_chan_switch->chan_switch_count = 5; + + if (ext_chan_switch->chan_switch_mode) { + if (netif_carrier_ok(priv->netdev)) + netif_carrier_off(priv->netdev); + woal_stop_queue(priv->netdev); + priv->uap_tx_blocked = MTRUE; + } + + DBG_HEXDUMP(MCMD_D, "ECSA IE", (t_u8 *)pcust_chansw_ie->ie_buffer, + pcust_chansw_ie->ie_length); + + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + priv->phandle->chsw_wait_q_woken = MFALSE; + /* wait for channel switch to complete */ + wait_event_interruptible_timeout(priv->phandle->chsw_wait_q, + priv->phandle->chsw_wait_q_woken, + (u32)HZ * + (ext_chan_switch->chan_switch_count + + 2) * 110 / 1000); + + pcust_chansw_ie->ie_index = 0xffff; /*Auto index */ + pcust_chansw_ie->mgmt_subtype_mask = 0; + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "Failed to clear ECSA IE\n"); + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + + LEAVE(); + return ret; +} +#endif + +/** + ** @brief set/get 11k + ** + ** @param priv Pointer to moal_private structure + ** @param respbuf Pointer to response buffer + ** @param resplen Response buffer length + ** + ** @return Number of bytes written, negative for failure. + **/ +static int +woal_priv_11k_cfg(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_11k_cfg *pcfg_11k = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + int ret = 0; + int user_data_len = 0, header_len = 0, data = 0; + ENTER(); + + if (!priv || !priv->phandle) { + PRINTM(MERROR, "priv or handle is null\n"); + ret = -EFAULT; + goto done; + } + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11k_cfg)); + if (ioctl_req == NULL) { + ret = -ENOMEM; + goto done; + } + + pcfg_11k = (mlan_ds_11k_cfg *) ioctl_req->pbuf; + pcfg_11k->sub_command = MLAN_OID_11K_CFG_ENABLE; + ioctl_req->req_id = MLAN_IOCTL_11K_CFG; + + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_11K_CFG); + if (strlen(respbuf) == header_len) { + ioctl_req->action = MLAN_ACT_GET; + } else { + parse_arguments(respbuf + header_len, &data, 1, &user_data_len); + if (user_data_len == 1) { + if ((data < CMD_DISABLED) || (data > CMD_ENABLED)) { + PRINTM(MERROR, "Invalid arguments!\n"); + ret = -EINVAL; + goto done; + } + ioctl_req->action = MLAN_ACT_SET; + if (data == CMD_DISABLED) + pcfg_11k->param.enable_11k = MFALSE; + else + pcfg_11k->param.enable_11k = MTRUE; + } else { + PRINTM(MERROR, "Too many arguments %d \n", + user_data_len); + ret = -EINVAL; + goto done; + } + } + + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + memcpy(respbuf, &pcfg_11k->param.enable_11k, + sizeof(pcfg_11k->param.enable_11k)); + ret = sizeof(pcfg_11k->param.enable_11k); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + + LEAVE(); + return ret; +} + +/** + ** @brief neighbor_report + ** + ** @param priv Pointer to moal_private structure + ** @param respbuf Pointer to response buffer + ** @param resplen Response buffer length + ** + ** @return Number of bytes written, negative for failure. + **/ +static int +woal_priv_11k_neighbor_report(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_11k_cfg *pcfg_11k = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + int ret = 0; + int header_len = 0; + + ENTER(); + + if (!priv || !priv->phandle) { + PRINTM(MERROR, "priv or handle is null\n"); + ret = -EFAULT; + goto done; + } + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11k_cfg)); + if (ioctl_req == NULL) { + ret = -ENOMEM; + goto done; + } + + pcfg_11k = (mlan_ds_11k_cfg *) ioctl_req->pbuf; + pcfg_11k->sub_command = MLAN_OID_11K_GET_NLIST; + ioctl_req->req_id = MLAN_IOCTL_11K_CFG; + ioctl_req->action = MLAN_ACT_GET; + + header_len = strlen(CMD_MARVELL) + strlen(PRIV_CMD_11K_NEIGHBOR_REPORT); + if (strlen(respbuf) != header_len) { + PRINTM(MERROR, "argument error\n"); + ret = -EINVAL; + goto done; + } + + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + + LEAVE(); + return ret; +} + +/** + * @brief enable/disable roaming offload to firmware + * + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + + * @return Number of bytes written, negative for failure. + */ +static int +woal_priv_set_roam_offload(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int user_data_len = 0, header_len = 0, ret = 0; + int data = 0; + + ENTER(); + + if (!priv || !priv->phandle) { + PRINTM(MERROR, "priv or handle is null\n"); + ret = -EFAULT; + goto done; + } + + header_len = strlen("SETROAMOFFLOAD"); + parse_arguments(respbuf + header_len + 1, &data, 1, &user_data_len); + + if (data < 0 || data > 5) { + PRINTM(MERROR, "Invalid parameters\n"); + ret = -EFAULT; + goto done; + } + + ret = woal_enable_fw_roaming(priv, data); +done: + + LEAVE(); + return ret; +} + +/** + * @brief set roaming offload aplist to firmware + * + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + + * @return Number of bytes written, negative for failure. + */ +static int +woal_priv_set_roam_offload_aplist(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_misc_cfg *misc = NULL; + mlan_ds_misc_roam_offload *roam = NULL; + mlan_ds_misc_roam_offload_aplist *aplist = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + int ret = 0, i = 0; + int user_data_len = 0, header_len = 0; + int ap_count = 0; + char *begin, *end; + t_u8 mac_addr[6]; + + ENTER(); + + if (!priv || !priv->phandle) { + PRINTM(MERROR, "priv or handle is null\n"); + ret = -EFAULT; + goto done; + } + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (ioctl_req == NULL) { + ret = -ENOMEM; + goto done; + } + + misc = (mlan_ds_misc_cfg *)ioctl_req->pbuf; + misc->sub_command = MLAN_OID_MISC_ROAM_OFFLOAD_APLIST; + ioctl_req->req_id = MLAN_IOCTL_MISC_CFG; + + roam = (mlan_ds_misc_roam_offload *) & misc->param.roam_offload; + /*Set enable to invalid value(valid: 0, 1, 2) */ + roam->enable = 3; + aplist = &roam->aplist; + + header_len = strlen("SETROAMOFFLAPLIST"); + user_data_len = strlen(respbuf) - header_len; + if (!user_data_len) { + /* GET operation */ + ioctl_req->action = MLAN_ACT_GET; + } else { + begin = &respbuf[header_len + 1]; + end = begin; + while (begin && *begin == ' ') { + begin++; + end++; + } + while (end && *end != ' ') + end++; + *end = '\0'; + end++; + if (woal_atoi(&ap_count, begin) != MLAN_STATUS_SUCCESS) { + ret = -EINVAL; + goto done; + } + aplist->ap_num = ap_count; + if (ap_count > 0 && ap_count <= MAX_AP_LIST) { + /* SET operation */ + ioctl_req->action = MLAN_ACT_SET; + for (i = 0; i < ap_count; i++) { + begin = end; + while (begin && *begin == ' ') { + begin++; + end++; + } + while (end && *end != ' ' && *end != '\0') + end++; + if (end == begin) { + PRINTM(MERROR, + "AP number %d is wrong\n", + ap_count); + ret = -EINVAL; + goto done; + } + *end = '\0'; + end++; + woal_mac2u8(mac_addr, begin); + memcpy(aplist->ap_mac[i], mac_addr, + MLAN_MAC_ADDR_LENGTH); + } + } else { + PRINTM(MERROR, + "AP number is wrong.Support max 8 APs\n"); + ret = -EINVAL; + goto done; + } + } + + DBG_HEXDUMP(MERROR, "APLIST", (t_u8 *)aplist->ap_mac, + aplist->ap_num * MLAN_MAC_ADDR_LENGTH); + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + + LEAVE(); + return ret; +} + +/** + * @brief Configure roaming offload to firmware + * + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + + * @return Number of bytes written, negative for failure. + */ +static int +woal_priv_roam_offload_cfg(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int ret = 0, user_data_len = 0, header_len = 0, data = 0; + char *begin, *end, *pvariable_name; + t_u8 mac_addr[MLAN_MAC_ADDR_LENGTH]; + woal_roam_offload_cfg roam_offload_cfg; + t_u8 len = 0; + int count = 0, i = 0; + + ENTER(); + + if (!priv || !priv->phandle) { + PRINTM(MERROR, "priv or handle is null\n"); + ret = -EFAULT; + goto done; + } + memset((char *)&roam_offload_cfg, 0, sizeof(roam_offload_cfg)); + header_len = strlen("CFGROAMOFFLOAD"); + user_data_len = strlen(respbuf) - header_len; + if (!user_data_len) { + PRINTM(MERROR, "Invalid number of parameters\n"); + ret = -EINVAL; + goto done; + } + roam_offload_cfg.band_rssi.band_preferred = 0xff; + roam_offload_cfg.trigger_condition = 0xff; + end = &respbuf[header_len]; + while (((t_u8 *)end - &respbuf[header_len]) < user_data_len - 1) { + end++; + begin = end; + while (begin && *begin == ' ') { + begin++; + end++; + } + while (end && *end != ' ' && *end != '\0' && *end != '=') + end++; + if (end == begin) { + PRINTM(MERROR, "Invalid command specified!\n"); + ret = -EINVAL; + goto done; + } + if (end) + *end = '\0'; + pvariable_name = begin; + + if (((t_u8 *)end - &respbuf[header_len]) >= user_data_len) { + PRINTM(MERROR, "Invalid command length!\n"); + ret = -EINVAL; + goto done; + } + end++; + begin = end; + while (begin && (*begin == ' ' || *begin == '=')) { + begin++; + end++; + } + while (end && *end != ' ' && *end != '\0' && *end != '=') + end++; + if (end == begin) { + PRINTM(MERROR, "Invalid command specified!\n"); + ret = -EINVAL; + goto done; + } + *end = '\0'; + + if (strcmp(pvariable_name, "AUTO_RECONNECT") == 0) { + woal_atoi(&data, begin); + } else if (strcmp(pvariable_name, "BSSID") == 0) { + woal_mac2u8(mac_addr, begin); + memcpy(roam_offload_cfg.bssid, mac_addr, + MLAN_MAC_ADDR_LENGTH); + } else if (strcmp(pvariable_name, "BLACKLIST") == 0) { + if (woal_atoi(&count, begin) != MLAN_STATUS_SUCCESS) { + ret = -EINVAL; + goto done; + } + if (count > 0 && count <= MAX_AP_LIST) { + roam_offload_cfg.black_list.ap_num = count; + for (i = 0; i < count; i++) { + end++; + begin = end; + while (begin && *begin == ' ') { + begin++; + end++; + } + while (end && *end != ' ' && + *end != '\0') + end++; + if (end == begin) { + PRINTM(MERROR, + "BSSID %d is wrong\n", + count); + ret = -EINVAL; + goto done; + } + *end = '\0'; + woal_mac2u8(mac_addr, begin); + memcpy(roam_offload_cfg.black_list. + ap_mac[i], mac_addr, + MLAN_MAC_ADDR_LENGTH); + } + } else { + PRINTM(MERROR, + "BSSID number is wrong.Support max %d BSSIDs\n", + MAX_AP_LIST); + ret = -EINVAL; + goto done; + } + } else if (strcmp(pvariable_name, "SSID") == 0) { + if (woal_atoi(&count, begin) != MLAN_STATUS_SUCCESS) { + ret = -EINVAL; + goto done; + } + if (count > 0 && count <= MAX_SSID_NUM) { + roam_offload_cfg.ssid_list.ssid_num = count; + for (i = 0; i < count; i++) { + end++; + begin = end; + while (begin && *begin == ' ') { + begin++; + end++; + } + while (end && *end != ' ' && + *end != '\0') { + end++; + len++; + } + if ((end == begin) || + len >= MLAN_MAX_SSID_LENGTH) { + PRINTM(MERROR, + "SSID %d is wrong\n", + count); + ret = -EINVAL; + goto done; + } + *end = '\0'; + roam_offload_cfg.ssid_list.ssids[i]. + ssid_len = len + 1; + memcpy((t_u8 *)&roam_offload_cfg. + ssid_list.ssids[i].ssid, begin, + len + 1); + len = 0; + } + } else { + PRINTM(MERROR, + "SSID number is wrong.Support max %d SSIDs\n", + MAX_SSID_NUM); + ret = -EINVAL; + goto done; + } + } else if (strcmp(pvariable_name, "RETRY_COUNT") == 0) { + woal_atoi(&data, begin); + roam_offload_cfg.retry_count = (t_u8)data; + } + + else if (strcmp(pvariable_name, "TRIGGER_CONDITION") == 0) { + woal_atoi(&data, begin); + roam_offload_cfg.trigger_condition = (t_u16)data; + } + + else if (strcmp(pvariable_name, "MAX_RSSI") == 0) { + woal_atoi(&data, begin); + roam_offload_cfg.max_rssi = (t_u8)data; + roam_offload_cfg.rssi_param_set_flag = 1; + } else if (strcmp(pvariable_name, "MIN_RSSI") == 0) { + woal_atoi(&data, begin); + roam_offload_cfg.min_rssi = (t_u8)data; + roam_offload_cfg.rssi_param_set_flag = 1; + } else if (strcmp(pvariable_name, "STEP_RSSI") == 0) { + woal_atoi(&data, begin); + roam_offload_cfg.step_rssi = (t_u8)data; + roam_offload_cfg.rssi_param_set_flag = 1; + } + + else if (strcmp(pvariable_name, "BAND_PREFER") == 0) { + woal_atoi(&data, begin); + roam_offload_cfg.band_rssi.band_preferred = (t_u8)data; + roam_offload_cfg.band_rssi_flag = 1; + } else if (strcmp(pvariable_name, "RSSI_HYSTERESIS") == 0) { + woal_atoi(&data, begin); + roam_offload_cfg.band_rssi.rssi_hysteresis = (t_u8)data; + roam_offload_cfg.band_rssi_flag = 1; + } + + else if (strcmp(pvariable_name, "BSSTYPE") == 0) { + woal_atoi(&data, begin); + roam_offload_cfg.bgscan_cfg.bss_type = (t_u8)data; + roam_offload_cfg.bgscan_set_flag++; + } else if (strcmp(pvariable_name, "CHANSPERSCAN") == 0) { + woal_atoi(&data, begin); + roam_offload_cfg.bgscan_cfg.channels_per_scan = + (t_u8)data; + roam_offload_cfg.bgscan_set_flag++; + } else if (strcmp(pvariable_name, "BGRPTCONDITION") == 0) { + woal_atoi(&data, begin); + roam_offload_cfg.bgscan_cfg.bg_rpt_condition = + (t_u32)data; + roam_offload_cfg.bgscan_set_flag++; + } else if (strcmp(pvariable_name, "SCANINTERVAL") == 0) { + woal_atoi(&data, begin); + roam_offload_cfg.bgscan_cfg.scan_interval = (t_u32)data; + roam_offload_cfg.bgscan_set_flag++; + } + + else if (strcmp(pvariable_name, "EESMODE") == 0) { + woal_atoi(&data, begin); + roam_offload_cfg.ees_cfg.ees_mode = (t_u16)data; + roam_offload_cfg.ees_param_set_flag++; + } else if (strcmp(pvariable_name, "EESRPTCONDITION") == 0) { + woal_atoi(&data, begin); + roam_offload_cfg.ees_cfg.ees_rpt_condition = + (t_u16)data; + roam_offload_cfg.ees_param_set_flag++; + } else if (strcmp(pvariable_name, "HIGHSCANPERIOD") == 0) { + woal_atoi(&data, begin); + roam_offload_cfg.ees_cfg.high_scan_period = (t_u16)data; + roam_offload_cfg.ees_param_set_flag++; + } else if (strcmp(pvariable_name, "HIGHSCANCOUNT") == 0) { + woal_atoi(&data, begin); + roam_offload_cfg.ees_cfg.high_scan_count = (t_u16)data; + roam_offload_cfg.ees_param_set_flag++; + } else if (strcmp(pvariable_name, "MIDSCANPERIOD") == 0) { + woal_atoi(&data, begin); + roam_offload_cfg.ees_cfg.mid_scan_period = (t_u16)data; + roam_offload_cfg.ees_param_set_flag++; + } else if (strcmp(pvariable_name, "MIDSCANCOUNT") == 0) { + woal_atoi(&data, begin); + roam_offload_cfg.ees_cfg.mid_scan_count = (t_u16)data; + roam_offload_cfg.ees_param_set_flag++; + } else if (strcmp(pvariable_name, "LOWSCANPERIOD") == 0) { + woal_atoi(&data, begin); + roam_offload_cfg.ees_cfg.low_scan_period = (t_u16)data; + roam_offload_cfg.ees_param_set_flag++; + } else if (strcmp(pvariable_name, "LOWSCANCOUNT") == 0) { + woal_atoi(&data, begin); + roam_offload_cfg.ees_cfg.low_scan_count = (t_u16)data; + roam_offload_cfg.ees_param_set_flag++; + } + + else if (strcmp(pvariable_name, "BCNMISSTHRESHOLD") == 0) { + woal_atoi(&data, begin); + roam_offload_cfg.bcn_miss_threshold = (t_u8)data; + } + + else if (strcmp(pvariable_name, "PREBCNMISSTHRESHOLD") == 0) { + woal_atoi(&data, begin); + roam_offload_cfg.pre_bcn_miss_threshold = (t_u8)data; + } else if (strcmp(pvariable_name, "REPEATCOUNT") == 0) { + woal_atoi(&data, begin); + roam_offload_cfg.repeat_count = (t_u16)data; + } else { + PRINTM(MERROR, "Un-support parameter: %s\n", + pvariable_name); + ret = -EINVAL; + goto done; + } + } + if (priv->phandle->fw_roam_enable == AUTO_RECONNECT) { + memcpy(priv->phandle->auto_reconnect_bssid, + roam_offload_cfg.bssid, MLAN_MAC_ADDR_LENGTH); + memcpy(priv->phandle->auto_reconnect_ssid.ssid, + roam_offload_cfg.ssid_list.ssids[0].ssid, + roam_offload_cfg.ssid_list.ssids[0].ssid_len); + priv->phandle->auto_reconnect_retry_count = (t_u8)data; + } else { + if (roamoffload_in_hs) + memcpy((void *)&priv->phandle->fw_roam_params, + (void *)&roam_offload_cfg, + sizeof(roam_offload_cfg)); + else + woal_config_fw_roaming(priv, ROAM_OFFLOAD_PARAM_CFG, + &roam_offload_cfg); + } + +done: + LEAVE(); + return ret; +} + +/** + * @brief Configure roaming SSID passphrase + * + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + + * @return Number of bytes written, negative for failure. + */ +static int +woal_priv_set_roam_passphrase(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_sec_cfg *sec = NULL; + int ret = 0, action = -1; + int user_data_len = 0, header_len = 0; + char *begin, *end, *opt, *item; + mlan_status status = MLAN_STATUS_SUCCESS; + woal_roam_offload_cfg roam_offload_cfg; + mlan_ds_passphrase *ssid_passphrase = NULL; + + ENTER(); + + if (!priv || !priv->phandle) { + PRINTM(MERROR, "priv or handle is null\n"); + ret = -EFAULT; + goto done; + } + + memset((char *)&roam_offload_cfg, 0, sizeof(roam_offload_cfg)); + header_len = strlen("SETROAMPASSPHRASE"); + user_data_len = strlen(respbuf) - header_len; + if (!user_data_len) { + PRINTM(MERROR, "Invalid number of parameters\n"); + ret = -EINVAL; + goto done; + } + + /* Parse the buf to get the cmd_action */ + begin = respbuf + header_len; + while (begin && *begin == ' ') + begin++; + end = woal_strsep(&begin, ';', '/'); + if (end) + action = woal_atox(end); + PRINTM(MMSG, "action= %d\n", action); + if (action != 1 || end[1] != '\0') { + PRINTM(MERROR, "Invalid action argument %s\n", end); + ret = -EINVAL; + goto done; + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + req->req_id = MLAN_IOCTL_SEC_CFG; + sec = (mlan_ds_sec_cfg *)req->pbuf; + sec->sub_command = MLAN_OID_SEC_CFG_PASSPHRASE; + sec->multi_passphrase = 1; + req->action = MLAN_ACT_SET; + + /*Parse the buffer like "ssid=xxx passphrase=xxxx;ssid=xxx passphrase=xxx" */ + while (begin) { + while (begin && *begin == ' ') + begin++; + end = woal_strsep(&begin, ';', '/'); + item = woal_strsep(&end, ' ', '/'); + opt = woal_strsep(&item, '=', '/'); + while (opt) { + if (roam_offload_cfg.userset_passphrase >= + MAX_SEC_SSID_NUM - 1) { + PRINTM(MERROR, + "Support max %d security SSIDs!\n", + MAX_SEC_SSID_NUM); + break; + } + ssid_passphrase = + &sec->param.roam_passphrase[roam_offload_cfg. + userset_passphrase]; + if (!opt || !item) { + PRINTM(MERROR, "Invalid option\n"); + ret = -EINVAL; + goto done; + } else if (!strnicmp(opt, "ssid", strlen(opt))) { + if (strlen(end) > MLAN_MAX_SSID_LENGTH) { + PRINTM(MERROR, + "SSID length exceeds max length\n"); + ret = -EFAULT; + goto done; + } + ssid_passphrase->ssid.ssid_len = strlen(item); + strncpy((char *)ssid_passphrase->ssid.ssid, + item, MIN(strlen(item), + MLAN_MAX_SSID_LENGTH)); + PRINTM(MINFO, "ssid=%s, len=%d\n", + ssid_passphrase->ssid.ssid, + (int)ssid_passphrase->ssid.ssid_len); + } else if (!strnicmp(opt, "passphrase", strlen(opt)) && + req->action == MLAN_ACT_SET) { + if (strlen(item) < MLAN_MIN_PASSPHRASE_LENGTH || + strlen(item) > MLAN_MAX_PASSPHRASE_LENGTH) { + PRINTM(MERROR, + "Invalid length for passphrase\n"); + ret = -EINVAL; + goto done; + } + ssid_passphrase->psk_type = MLAN_PSK_PASSPHRASE; + memcpy(ssid_passphrase->psk.passphrase. + passphrase, item, MIN(strlen(item), + MLAN_MAX_PASSPHRASE_LENGTH)); + ssid_passphrase->psk.passphrase.passphrase_len = + strlen(item); + PRINTM(MINFO, "passphrase=%s, len=%d\n", + ssid_passphrase->psk.passphrase. + passphrase, + (int)ssid_passphrase->psk.passphrase. + passphrase_len); + } else { + PRINTM(MERROR, "Invalid option %s\n", opt); + ret = -EINVAL; + goto done; + } + if (!end || *end == '\0') + break; + while (end && *end == ' ') + end++; + item = woal_strsep(&end, ' ', '/'); + opt = woal_strsep(&item, '=', '/'); + } + roam_offload_cfg.userset_passphrase++; + } + + if (roamoffload_in_hs) { + memcpy((char *)priv->phandle->ssid_passphrase, + (char *)sec->param.roam_passphrase, + MAX_SEC_SSID_NUM * sizeof(mlan_ds_passphrase)); + priv->phandle->fw_roam_params.userset_passphrase = + roam_offload_cfg.userset_passphrase; + goto done; + } + + woal_config_fw_roaming(priv, ROAM_OFFLOAD_ENABLE, &roam_offload_cfg); + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + if (ret) + goto done; + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +static int +woal_get_correlated_time(moal_private *priv, t_u8 *buf, t_u32 respbuflen) +{ + mlan_ioctl_req *req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + mlan_ds_misc_cfg *rate = NULL; + int ret = 1; + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + rate = (mlan_ds_misc_cfg *)req->pbuf; + rate->sub_command = MLAN_OID_MISC_GET_CORRELATED_TIME;; + req->req_id = MLAN_IOCTL_MISC_CFG; + req->action = MLAN_ACT_GET; + + status = woal_request_ioctl(priv, req, MOAL_NO_WAIT); + if (status == MLAN_STATUS_FAILURE) { + ret = -EFAULT; + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; + +} + +/** + * @brief Set priv command for Android + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * + * @return 0 --success, otherwise fail + */ +int +woal_android_priv_cmd(struct net_device *dev, struct ifreq *req) +{ + int ret = 0; + android_wifi_priv_cmd priv_cmd; + moal_private *priv = (moal_private *)netdev_priv(dev); + char *buf = NULL; + char *pdata; +#ifdef STA_SUPPORT + int power_mode = 0; + int band = 0; + char *pband = NULL; + mlan_bss_info bss_info; + mlan_ds_get_signal signal; + mlan_rate_cfg_t rate; + t_u8 country_code[COUNTRY_CODE_LEN]; + int copy_len = 0; +#endif + int len = 0; + gfp_t flag; + char *cmd_buf = NULL; + + ENTER(); + if (!priv || !priv->phandle) { + PRINTM(MERROR, "priv or handle is NULL\n"); + ret = -EFAULT; + goto done; + } + if (copy_from_user(&priv_cmd, req->ifr_data, + sizeof(android_wifi_priv_cmd))) { + ret = -EFAULT; + goto done; + } +#define CMD_BUF_LEN 2048 + if (priv_cmd.used_len < 0 || priv_cmd.total_len <= 0 || + priv_cmd.used_len > priv_cmd.total_len) { + PRINTM(MERROR, + "Invalid Android priv cmd len. used_len: %d, total_len: %d\n", + priv_cmd.used_len, priv_cmd.total_len); + ret = -EINVAL; + goto done; + } + if (priv_cmd.total_len + 1 > CMD_BUF_LEN) + priv_cmd.total_len = CMD_BUF_LEN - 1; + + flag = (in_atomic() || irqs_disabled())? GFP_ATOMIC : GFP_KERNEL; + buf = kzalloc(CMD_BUF_LEN, flag); + if (!buf) { + PRINTM(MERROR, "%s: failed to allocate memory\n", __FUNCTION__); + ret = -ENOMEM; + goto done; + } +#ifdef USERSPACE_32BIT_OVER_KERNEL_64BIT + memcpy(&cmd_buf, &priv_cmd.buf, sizeof(cmd_buf)); +#else + cmd_buf = priv_cmd.buf; +#endif + if (copy_from_user(buf, cmd_buf, priv_cmd.total_len)) { + ret = -EFAULT; + goto done; + } + buf[CMD_BUF_LEN - 1] = '\0'; + + PRINTM(MIOCTL, "Android priv cmd: [%s] on [%s]\n", buf, req->ifr_name); + + if (strncmp(buf, CMD_MARVELL, strlen(CMD_MARVELL)) && + woal_check_driver_status(priv->phandle)) { + PRINTM(MERROR, "%s fail when driver hang\n", buf); + ret = -EFAULT; + goto done; + } + + if (strncmp(buf, CMD_MARVELL, strlen(CMD_MARVELL)) == 0) { + /* This command has come from mlanutl app */ + + /* Check command */ + if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_VERSION, + strlen(PRIV_CMD_VERSION)) == 0) { + /* Get version */ + len = woal_get_priv_driver_version(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_BANDCFG, + strlen(PRIV_CMD_BANDCFG)) == 0) { + /* Set/Get band configuration */ + len = woal_setget_priv_bandcfg(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_HOSTCMD, + strlen(PRIV_CMD_HOSTCMD)) == 0) { + /* hostcmd configuration */ + len = woal_priv_hostcmd(priv, buf, priv_cmd.total_len, + MOAL_IOCTL_WAIT); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_HTTXCFG, + strlen(PRIV_CMD_HTTXCFG)) == 0) { + /* Set/Get HT Tx configuration */ + len = woal_setget_priv_httxcfg(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_HTCAPINFO, + strlen(PRIV_CMD_HTCAPINFO)) == 0) { + /* Set/Get HT Capability information */ + len = woal_setget_priv_htcapinfo(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_ADDBAPARA, + strlen(PRIV_CMD_ADDBAPARA)) == 0) { + /* Set/Get Add BA parameters */ + len = woal_setget_priv_addbapara(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_AGGRPRIOTBL, + strlen(PRIV_CMD_AGGRPRIOTBL)) == 0) { + /* Set/Get Aggregation priority table parameters */ + len = woal_setget_priv_aggrpriotbl(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_ADDBAREJECT, + strlen(PRIV_CMD_ADDBAREJECT)) == 0) { + /* Set/Get Add BA reject parameters */ + len = woal_setget_priv_addbareject(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_DELBA, + strlen(PRIV_CMD_DELBA)) == 0) { + /* Delete selective BA based on parameters */ + len = woal_priv_delba(priv, buf, priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_REJECTADDBAREQ, + strlen(PRIV_CMD_REJECTADDBAREQ)) == 0) { + /* Set/Get the reject addba requst conditions */ + len = woal_priv_rejectaddbareq(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_DATARATE, + strlen(PRIV_CMD_DATARATE)) == 0) { + /* Get data rate */ + len = woal_get_priv_datarate(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_TXRATECFG, + strlen(PRIV_CMD_TXRATECFG)) == 0) { + /* Set/Get tx rate cfg */ + len = woal_setget_priv_txratecfg(priv, buf, + priv_cmd.total_len); + goto handled; +#if defined(STA_SUPPORT) || defined(UAP_SUPPORT) + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_GETLOG, + strlen(PRIV_CMD_GETLOG)) == 0) { + /* Get wireless stats information */ + len = woal_get_priv_getlog(priv, buf, + priv_cmd.total_len); + goto handled; +#endif + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_CUSTOMIE, + strlen(PRIV_CMD_CUSTOMIE)) == 0) { + /* Custom IE configuration */ + len = woal_priv_customie(priv, buf, priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_ESUPPMODE, + strlen(PRIV_CMD_ESUPPMODE)) == 0) { + /* Esupplicant mode configuration */ + len = woal_setget_priv_esuppmode(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_PASSPHRASE, + strlen(PRIV_CMD_PASSPHRASE)) == 0) { + /* Esupplicant passphrase configuration */ + len = woal_setget_priv_passphrase(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_DEAUTH, + strlen(PRIV_CMD_DEAUTH)) == 0) { + /* Deauth */ + len = woal_priv_deauth(priv, buf, priv_cmd.total_len); + goto handled; +#ifdef UAP_SUPPORT + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_AP_DEAUTH, + strlen(PRIV_CMD_AP_DEAUTH)) == 0) { + /* AP Deauth */ + len = woal_priv_ap_deauth(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_GET_STA_LIST, + strlen(PRIV_CMD_GET_STA_LIST)) == 0) { + /* Get STA list */ + len = woal_priv_get_sta_list(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_BSS_CONFIG, + strlen(PRIV_CMD_BSS_CONFIG)) == 0) { + /* BSS config */ + len = woal_priv_bss_config(priv, buf, + priv_cmd.total_len); + goto handled; +#endif +#if defined(WIFI_DIRECT_SUPPORT) +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_BSSROLE, + strlen(PRIV_CMD_BSSROLE)) == 0) { + /* BSS Role */ + len = woal_priv_bssrole(priv, buf, + (t_u32)priv_cmd.total_len); + goto handled; +#endif +#endif +#ifdef STA_SUPPORT + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_SETUSERSCAN, + strlen(PRIV_CMD_SETUSERSCAN)) == 0) { + /* Set user scan */ + len = woal_priv_setuserscan(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_GETSCANTABLE, + strlen(PRIV_CMD_GETSCANTABLE)) == 0) { + /* Get scan table */ + len = woal_priv_getscantable(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_EXTCAPCFG, + strlen(PRIV_CMD_EXTCAPCFG)) == 0) { + /* Extended capabilities configure */ + len = woal_priv_extcapcfg(priv, buf, + (t_u32)priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_CANCELSCAN, + strlen(PRIV_CMD_CANCELSCAN)) == 0) { + /* Cancel scan */ + len = woal_cancel_scan(priv, MOAL_IOCTL_WAIT); + goto handled; +#endif + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_DEEPSLEEP, + strlen(PRIV_CMD_DEEPSLEEP)) == 0) { + /* Deep sleep */ + len = woal_priv_setgetdeepsleep(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_IPADDR, + strlen(PRIV_CMD_IPADDR)) == 0) { + /* IP address */ + len = woal_priv_setgetipaddr(priv, buf, + (t_u32)priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_WPSSESSION, + strlen(PRIV_CMD_WPSSESSION)) == 0) { + /* WPS Session */ + len = woal_priv_setwpssession(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_OTPUSERDATA, + strlen(PRIV_CMD_OTPUSERDATA)) == 0) { + /* OTP user data */ + len = woal_priv_otpuserdata(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_COUNTRYCODE, + strlen(PRIV_CMD_COUNTRYCODE)) == 0) { + /* Country code */ + len = woal_priv_set_get_countrycode(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_MARVELL), PRIV_CMD_CFPINFO, + strlen(PRIV_CMD_CFPINFO)) == 0) { + /* CFP info */ + len = woal_priv_get_cfpinfo(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_TCPACKENH, + strlen(PRIV_CMD_TCPACKENH)) == 0) { + /* TCP ack enhancement */ + len = woal_priv_setgettcpackenh(priv, buf, + priv_cmd.total_len); + goto handled; +#ifdef REASSOCIATION + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_ASSOCBSSID, + strlen(PRIV_CMD_ASSOCBSSID)) == 0) { + /* Associate to essid */ + len = woal_priv_assocessid(priv, buf, + priv_cmd.total_len, 1); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_ASSOCESSID, + strlen(PRIV_CMD_ASSOCESSID)) == 0) { + /* Associate to essid */ + len = woal_priv_assocessid(priv, buf, + priv_cmd.total_len, 0); + goto handled; +#endif + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_WAKEUPREASON, + strlen(PRIV_CMD_WAKEUPREASON)) == 0) { + /* wakeup reason */ + len = woal_priv_getwakeupreason(priv, buf, + priv_cmd.total_len); + goto handled; +#ifdef STA_SUPPORT + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_LISTENINTERVAL, + strlen(PRIV_CMD_LISTENINTERVAL)) == 0) { + /* Listen Interval */ + len = woal_priv_set_get_listeninterval(priv, buf, + priv_cmd. + total_len); + goto handled; +#endif +#ifdef DEBUG_LEVEL1 + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_DRVDBG, + strlen(PRIV_CMD_DRVDBG)) == 0) { + /* Driver debug bit mask */ + len = woal_priv_set_get_drvdbg(priv, buf, + priv_cmd.total_len); + goto handled; +#endif + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_HSCFG, + strlen(PRIV_CMD_HSCFG)) == 0) { + /* HS configuration */ + len = woal_priv_hscfg(priv, buf, priv_cmd.total_len, + MTRUE); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_HSSETPARA, + strlen(PRIV_CMD_HSSETPARA)) == 0) { + /* Set HS parameter */ + len = woal_priv_hssetpara(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_MGMT_FILTER, + strlen(PRIV_CMD_MGMT_FILTER)) == 0) { + /* Management frame filter wakeup */ + len = woal_priv_mgmt_filter(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_SCANCFG, + strlen(PRIV_CMD_SCANCFG)) == 0) { + /* Scan configuration */ + len = woal_priv_set_get_scancfg(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_SET_BSS_MODE, + strlen(PRIV_CMD_SET_BSS_MODE)) == 0) { + /* Set bss mode */ + len = woal_priv_set_bss_mode(priv, buf, + priv_cmd.total_len); + goto handled; +#ifdef STA_SUPPORT + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_SET_AP, + strlen(PRIV_CMD_SET_AP)) == 0) { + /* Set AP */ + len = woal_priv_set_ap(priv, buf, priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_SET_POWER, + strlen(PRIV_CMD_SET_POWER)) == 0) { + /* Set power management parameters */ + len = woal_priv_set_power(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_SET_ESSID, + strlen(PRIV_CMD_SET_ESSID)) == 0) { + /* Set essid */ + len = woal_priv_set_essid(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_SET_AUTH, + strlen(PRIV_CMD_SET_AUTH)) == 0) { + /* Set authentication mode parameters */ + len = woal_priv_set_auth(priv, buf, priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_GET_AP, + strlen(PRIV_CMD_GET_AP)) == 0) { + /* Get AP */ + len = woal_priv_get_ap(priv, buf, priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_GET_POWER, + strlen(PRIV_CMD_GET_POWER)) == 0) { + /* Get power management parameters */ + len = woal_priv_get_power(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_PSMODE, + strlen(PRIV_CMD_PSMODE)) == 0) { + /* Set/Get PS mode */ + len = woal_priv_set_get_psmode(priv, buf, + priv_cmd.total_len); + goto handled; +#endif + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_WARMRESET, + strlen(PRIV_CMD_WARMRESET)) == 0) { + /* Performs warm reset */ + len = woal_priv_warmreset(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_TXPOWERCFG, + strlen(PRIV_CMD_TXPOWERCFG)) == 0) { + /* TX power configurations */ + len = woal_priv_txpowercfg(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_PSCFG, + strlen(PRIV_CMD_PSCFG)) == 0) { + /* PS configurations */ + len = woal_priv_pscfg(priv, buf, priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_BCNTIMEOUTCFG, + strlen(PRIV_CMD_BCNTIMEOUTCFG)) == 0) { + /* Beacon timeout configurations */ + len = woal_priv_bcntimeoutcfg(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_SLEEPPD, + strlen(PRIV_CMD_SLEEPPD)) == 0) { + /* Sleep period */ + len = woal_priv_sleeppd(priv, buf, priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_TXCONTROL, + strlen(PRIV_CMD_TXCONTROL)) == 0) { + /* Tx control */ + len = woal_priv_txcontrol(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_REGRDWR, + strlen(PRIV_CMD_REGRDWR)) == 0) { + /* Register Read/Write */ + len = woal_priv_regrdwr(priv, buf, priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_RDEEPROM, + strlen(PRIV_CMD_RDEEPROM)) == 0) { + /* Read the EEPROM contents of the card */ + len = woal_priv_rdeeprom(priv, buf, priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_MEMRDWR, + strlen(PRIV_CMD_MEMRDWR)) == 0) { + /* Memory Read/Write */ + len = woal_priv_memrdwr(priv, buf, priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_SDCMD52RW, + strlen(PRIV_CMD_SDCMD52RW)) == 0) { + /* Cmd52 read/write register */ + len = woal_priv_sdcmd52rw(priv, buf, + priv_cmd.total_len); + goto handled; +#ifdef STA_SUPPORT + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_ARPFILTER, + strlen(PRIV_CMD_ARPFILTER)) == 0) { + /* ARPFilter Configuration */ + len = woal_priv_arpfilter(priv, buf, + priv_cmd.total_len); + goto handled; +#endif +#if defined(SDIO_SUSPEND_RESUME) + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_AUTO_ARP, + strlen(PRIV_CMD_AUTO_ARP)) == 0) { + /* Auto ARP enable/disable */ + len = woal_priv_set_get_auto_arp(priv, buf, + priv_cmd.total_len); + goto handled; +#endif +#ifdef RX_PACKET_COALESCE + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_RX_COAL_CFG, + strlen(PRIV_CMD_RX_COAL_CFG)) == 0) { + /* RX packet coalescing Configuration */ + len = woal_priv_rx_pkt_coalesce_cfg(priv, buf, + priv_cmd.total_len); + goto handled; +#endif + + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_MGMT_FRAME_CTRL, + strlen(PRIV_CMD_MGMT_FRAME_CTRL)) == 0) { + /* Mgmt Frame Passthrough Ctrl */ + len = woal_priv_mgmt_frame_passthru_ctrl(priv, buf, + priv_cmd. + total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_QCONFIG, + strlen(PRIV_CMD_QCONFIG)) == 0) { + /* Queue config */ + len = woal_priv_qconfig(priv, buf, priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_ADDTS, + strlen(PRIV_CMD_ADDTS)) == 0) { + /* Send an ADDTS TSPEC */ + len = woal_priv_wmm_addts_req_ioctl(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_DELTS, + strlen(PRIV_CMD_DELTS)) == 0) { + /* Send a DELTS TSPE */ + len = woal_priv_wmm_delts_req_ioctl(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_QSTATUS, + strlen(PRIV_CMD_QSTATUS)) == 0) { + /* Get the status of the WMM queues */ + len = woal_priv_wmm_queue_status_ioctl(priv, buf, + priv_cmd. + total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_TS_STATUS, + strlen(PRIV_CMD_TS_STATUS)) == 0) { + /* Get the status of the WMM Traffic Streams */ + len = woal_priv_wmm_ts_status_ioctl(priv, buf, + priv_cmd.total_len); + goto handled; +#ifdef STA_SUPPORT + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_QOS_CFG, + strlen(PRIV_CMD_QOS_CFG)) == 0) { + t_u32 action = MLAN_ACT_GET; + if (strlen(buf) == + strlen(CMD_MARVELL) + strlen(PRIV_CMD_QOS_CFG)) { + pdata = buf; /* GET operation */ + } else { + pdata = buf + strlen(CMD_MARVELL) + + strlen(PRIV_CMD_QOS_CFG); + action = MLAN_ACT_SET; /* SET operation */ + } + if (MLAN_STATUS_SUCCESS != + woal_priv_qos_cfg(priv, action, pdata)) { + ret = -EFAULT; + goto done; + } + if (action == MLAN_ACT_GET) + len = sizeof(t_u8); + goto handled; +#endif + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_MAC_CTRL, + strlen(PRIV_CMD_MAC_CTRL)) == 0) { + /* MAC CTRL */ + len = woal_priv_macctrl(priv, buf, priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_GETWAP, + strlen(PRIV_CMD_GETWAP)) == 0) { + /* Get WAP */ + len = woal_priv_getwap(priv, buf, priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_REGION_CODE, + strlen(PRIV_CMD_REGION_CODE)) == 0) { + /* Region Code */ + len = woal_priv_region_code(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_DRCS_CFG, + strlen(PRIV_CMD_DRCS_CFG)) == 0) { + /* DRCS configuration for mc_cfg_ext */ + len = woal_priv_drcs_time_slicing_cfg(priv, buf, + priv_cmd. + total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_MULTI_CHAN_CFG, + strlen(PRIV_CMD_MULTI_CHAN_CFG)) == 0) { + /* Channel time and buffer weight configuration */ + len = woal_priv_multi_chan_config(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), + PRIV_CMD_MULTI_CHAN_POLICY, + strlen(PRIV_CMD_MULTI_CHAN_POLICY)) == 0) { + /* Multi-channel Policy enable/disable */ + len = woal_priv_multi_chan_policy(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_FWMACADDR, + strlen(PRIV_CMD_FWMACADDR)) == 0) { + /* Set FW MAC address */ + len = woal_priv_fwmacaddr(priv, buf, + priv_cmd.total_len); + goto handled; +#ifdef STA_CFG80211 +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_OFFCHANNEL, + strlen(PRIV_CMD_OFFCHANNEL)) == 0) { + if (IS_STA_CFG80211(cfg80211_wext)) { + /* Set offchannel */ + len = woal_priv_offchannel(priv, buf, + priv_cmd.total_len); + } else + len = sprintf(buf, + "CFG80211 is not enabled\n") + 1; + goto handled; +#endif +#endif + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_DSCP_MAP, + strlen(PRIV_CMD_DSCP_MAP)) == 0) { + /* Set/Get DSCP Map */ + len = woal_priv_set_get_dscp_map(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_VEREXT, + strlen(PRIV_CMD_VEREXT)) == 0) { + /* Get Extended version */ + len = woal_priv_get_driver_verext(priv, buf, + priv_cmd.total_len); + goto handled; +#if defined(STA_SUPPORT) && defined(STA_WEXT) + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_RADIO_CTRL, + strlen(PRIV_CMD_RADIO_CTRL)) == 0) { + /* Set/Get radio */ + len = woal_priv_radio_ctrl(priv, buf, + priv_cmd.total_len); + goto handled; +#endif + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_WMM_CFG, + strlen(PRIV_CMD_WMM_CFG)) == 0) { + /* Implement WMM enable command */ + len = woal_priv_wmm_cfg(priv, buf, priv_cmd.total_len); + goto handled; +#if defined(STA_SUPPORT) + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_11D_CFG, + strlen(PRIV_CMD_11D_CFG)) == 0) { + /* Implement 802.11D enable command */ + len = woal_priv_11d_cfg(priv, buf, priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_11D_CLR_TBL, + strlen(PRIV_CMD_11D_CLR_TBL)) == 0) { + /* Implement 802.11D clear chan table command */ + len = woal_priv_11d_clr_chan_tbl(priv, buf, + priv_cmd.total_len); + goto handled; +#endif + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_11K_CFG, + strlen(PRIV_CMD_11K_CFG)) == 0) { + /* Implement 802.11K enable command */ + len = woal_priv_11k_cfg(priv, buf, priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), + PRIV_CMD_11K_NEIGHBOR_REPORT, + strlen(PRIV_CMD_11K_NEIGHBOR_REPORT)) == 0) { + /* Implement 802.11K get neighbor AP list command */ + len = woal_priv_11k_neighbor_report(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_WWS_CFG, + strlen(PRIV_CMD_WWS_CFG)) == 0) { + /* Set/Get WWS configuration */ + len = woal_priv_wws_cfg(priv, buf, priv_cmd.total_len); + goto handled; +#if defined(REASSOCIATION) + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_REASSOCTRL, + strlen(PRIV_CMD_REASSOCTRL)) == 0) { + /* Set/Get reassociation settings */ + len = woal_priv_set_get_reassoc(priv, buf, + priv_cmd.total_len); + goto handled; +#endif + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_TXBUF_CFG, + strlen(PRIV_CMD_TXBUF_CFG)) == 0) { + /* Get Transmit buffer size */ + len = woal_priv_txbuf_cfg(priv, buf, + priv_cmd.total_len); + goto handled; +#ifdef STA_SUPPORT + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_AUTH_TYPE, + strlen(PRIV_CMD_AUTH_TYPE)) == 0) { + /* Set/Get auth type */ + len = woal_priv_auth_type(priv, buf, + priv_cmd.total_len); + goto handled; +#endif + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_POWER_CONS, + strlen(PRIV_CMD_POWER_CONS)) == 0) { + /* Set/get user provisioned local power constraint */ + len = woal_priv_11h_local_pwr_constraint(priv, buf, + priv_cmd. + total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_THERMAL, + strlen(PRIV_CMD_THERMAL)) == 0) { + /* Get thermal reading */ + len = woal_priv_thermal(priv, buf, priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_BCN_INTERVAL, + strlen(PRIV_CMD_BCN_INTERVAL)) == 0) { + /* Set/Get beacon interval */ + len = woal_priv_beacon_interval(priv, buf, + priv_cmd.total_len); + goto handled; +#ifdef STA_SUPPORT + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_GET_SIGNAL, + strlen(PRIV_CMD_GET_SIGNAL)) == 0) { + /* Get signal */ + len = woal_priv_get_signal(priv, buf, + priv_cmd.total_len); + goto handled; +#endif +#if defined(STA_SUPPORT) + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_PMFCFG, + strlen(PRIV_CMD_PMFCFG)) == 0) { + /* Configure PMF */ + len = woal_priv_set_get_pmfcfg(priv, buf, + priv_cmd.total_len); + goto handled; +#endif + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_INACTIVITYTO, + strlen(PRIV_CMD_INACTIVITYTO)) == 0) { + /* Get/Set inactivity timeout extend */ + len = woal_priv_inactivity_timeout_ext(priv, buf, + priv_cmd. + total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_ATIM_WINDOW, + strlen(PRIV_CMD_ATIM_WINDOW)) == 0) { + /* Set/Get ATIM window */ + len = woal_priv_atim_window(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_AMSDU_AGGR_CTRL, + strlen(PRIV_CMD_AMSDU_AGGR_CTRL)) == 0) { + /* Enable/Disable amsdu_aggr_ctrl */ + len = woal_priv_11n_amsdu_aggr_ctrl(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_TX_BF_CAP, + strlen(PRIV_CMD_TX_BF_CAP)) == 0) { + /* Set/Get Transmit beamforming capabilities */ + len = woal_priv_tx_bf_cap_ioctl(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_SDIO_CLOCK, + strlen(PRIV_CMD_SDIO_CLOCK)) == 0) { + /* Turn on/off the sdio clock */ + len = woal_priv_sdio_clock_ioctl(priv, buf, + priv_cmd.total_len); + goto handled; +#if defined(SDIO_MULTI_PORT_TX_AGGR) || defined(SDIO_MULTI_PORT_RX_AGGR) + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_MPA_CTRL, + strlen(PRIV_CMD_MPA_CTRL)) == 0) { + /* Set SDIO Multi-point aggregation + * control parameters */ + len = woal_priv_sdio_mpa_ctrl(priv, buf, + priv_cmd.total_len); + goto handled; +#endif + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_SLEEP_PARAMS, + strlen(PRIV_CMD_SLEEP_PARAMS)) == 0) { + /* Configure sleep parameters */ + len = woal_priv_sleep_params_ioctl(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_NET_MON, + strlen(PRIV_CMD_NET_MON)) == 0) { + /* Set/Get network monitor configurations */ + len = woal_priv_net_monitor_ioctl(priv, buf, + priv_cmd.total_len); + goto handled; +#if defined(STA_CFG80211) && defined(UAP_CFG80211) + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_MONITOR_MODE, + strlen(PRIV_CMD_MONITOR_MODE)) == 0) { + if (IS_STA_CFG80211(cfg80211_wext)) { + /* Set/Get monitor mode */ + len = woal_priv_set_get_monitor_mode(priv, buf, + priv_cmd. + total_len); + } else + len = sprintf(buf, + "CFG80211 is not enabled\n") + 1; + goto handled; +#endif +#if defined(DFS_TESTING_SUPPORT) + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_DFS_TESTING, + strlen(PRIV_CMD_DFS_TESTING)) == 0) { + /* Set/Get DFS Testing settings */ + len = woal_priv_dfs_testing(priv, buf, + priv_cmd.total_len); + goto handled; +#endif + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_CFP_CODE, + strlen(PRIV_CMD_CFP_CODE)) == 0) { + /* Set/Get CFP table codes */ + len = woal_priv_cfp_code(priv, buf, priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_CWMODE, + strlen(PRIV_CMD_CWMODE)) == 0) { + /* Set/Get Tx CWMode */ + len = woal_priv_set_get_cwmode(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_ANT_CFG, + strlen(PRIV_CMD_ANT_CFG)) == 0) { + /* Set/Get Tx/Rx antenna */ + len = woal_priv_set_get_tx_rx_ant(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_SYSCLOCK, + strlen(PRIV_CMD_SYSCLOCK)) == 0) { + /* Get/Set system clock */ + len = woal_priv_sysclock(priv, buf, priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_ADHOC_AES, + strlen(PRIV_CMD_ADHOC_AES)) == 0) { + /* Adhoc AES control */ + len = woal_priv_adhoc_aes(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_ASSOCIATE, + strlen(PRIV_CMD_ASSOCIATE)) == 0) { + /* Associate to a specific indexed entry in the ScanTable */ + len = woal_priv_associate_ssid_bssid(priv, buf, + priv_cmd. + total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_TX_BF_CFG, + strlen(PRIV_CMD_TX_BF_CFG)) == 0) { + /* Set/Get Transmit beamforming configuration */ + len = woal_priv_tx_bf_cfg(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_SD_CMD53_RW, + strlen(PRIV_CMD_SD_CMD53_RW)) == 0) { + /* Cmd53 read/write register */ + len = woal_priv_cmd53rdwr(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_ROBUSTCOEX, + strlen(PRIV_CMD_ROBUSTCOEX)) == 0) { + /* Set Robustcoex GPIOcfg */ + pdata = buf + strlen(CMD_MARVELL) + + strlen(PRIV_CMD_ROBUSTCOEX); + len = priv_cmd.total_len - strlen(PRIV_CMD_ROBUSTCOEX) - + strlen(CMD_MARVELL); + len = woal_priv_robustcoex(priv, pdata, len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_BOOTSLEEP, + strlen(PRIV_CMD_BOOTSLEEP)) == 0) { + len = woal_priv_bootsleep(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_PORT_CTRL, + strlen(PRIV_CMD_PORT_CTRL)) == 0) { + /* Set/Get Port Control mode */ + len = woal_priv_port_ctrl(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_PB_BYPASS, + strlen(PRIV_CMD_PB_BYPASS)) == 0) { + /* Private IOCTL entry to get the By-passed TX packet from upper layer */ + len = woal_priv_bypassed_packet(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_COALESCE_STATUS, + strlen(PRIV_CMD_COALESCE_STATUS)) == 0) { + /* Control Coalescing status Enable/Disable */ + len = woal_priv_coalescing_status(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), + PRIV_CMD_FW_WAKEUP_METHOD, + strlen(PRIV_CMD_FW_WAKEUP_METHOD)) == 0) { + /* Set/Get module configuration */ + len = woal_priv_fw_wakeup_method(priv, buf, + priv_cmd.total_len); + goto handled; +#if defined(WIFI_DIRECT_SUPPORT) +#if defined(UAP_CFG80211) +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_CFG_NOA, + strlen(PRIV_CMD_CFG_NOA)) == 0) { + /* Set/Get P2P NoA (Notice of Absence) parameters */ + len = woal_priv_cfg_noa(priv, buf, priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_CFG_OPP_PS, + strlen(PRIV_CMD_CFG_OPP_PS)) == 0) { + /* Set/Get P2P OPP-PS parameters */ + len = woal_priv_cfg_opp_ps(priv, buf, + priv_cmd.total_len); + goto handled; +#endif +#endif +#endif + } else if (strnicmp + (buf + strlen(CMD_MARVELL), + PRIV_CMD_DFS_REPEATER_CFG, + strlen(PRIV_CMD_DFS_REPEATER_CFG)) == 0) { + /* Set/Get DFS_REPEATER mode */ + len = woal_priv_dfs_repeater_cfg(priv, buf, + priv_cmd.total_len); + goto handled; +#ifdef WIFI_DIRECT_SUPPORT +#if defined(STA_CFG80211) || defined(UAP_CFG80211) + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_MIRACAST_CFG, + strlen(PRIV_CMD_MIRACAST_CFG)) == 0) { + /* Set/Get MIRACAST configuration parameters */ + len = woal_priv_miracast_cfg(priv, buf, + priv_cmd.total_len); + goto handled; +#endif +#endif + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_COEX_RX_WINSIZE, + strlen(PRIV_CMD_COEX_RX_WINSIZE)) == 0) { + /* Set/Get control to coex RX window size */ + len = woal_priv_coex_rx_winsize(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_TX_AGGR_CTRL, + strlen(PRIV_CMD_TX_AGGR_CTRL)) == 0) { + /* Set/Get control to TX AMPDU on infra link */ + len = woal_priv_txaggrctrl(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_AUTO_TDLS, + strlen(PRIV_CMD_AUTO_TDLS)) == 0) { + /* Set/Get control to enable/disable auto TDLS */ + len = woal_priv_auto_tdls(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_TDLS_IDLE_TIME, + strlen(PRIV_CMD_TDLS_IDLE_TIME)) == 0) { + /* Set/Get TDLS idle timeout value */ + len = woal_priv_tdls_idle_time(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_GET_SENSOR_TEMP, + strlen(PRIV_CMD_GET_SENSOR_TEMP)) == 0) { + /* Get SOC temperature */ + len = woal_priv_get_sensor_temp(priv, buf, + priv_cmd.total_len); + goto handled; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) +#if defined(STA_CFG80211) || defined(UAP_CFG80211) + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_DFS_OFFLOAD, + strlen(PRIV_CMD_DFS_OFFLOAD)) == 0) { + /* Enable/disable DFS offload */ + if (IS_STA_OR_UAP_CFG80211(cfg80211_wext)) + len = woal_priv_dfs_offload_enable(priv, buf, + priv_cmd. + total_len); + else + len = sprintf(buf, + "CFG80211 is not enabled\n") + 1; + goto handled; +#endif +#endif +#if defined(UAP_SUPPORT) + } else if (strnicmp + (buf + strlen(CMD_MARVELL), + PRIV_CMD_EXTEND_CHAN_SWITCH, + strlen(PRIV_CMD_EXTEND_CHAN_SWITCH)) == 0) { + /* Extended channel switch */ + len = woal_priv_extend_channel_switch(priv, buf, + priv_cmd. + total_len); + goto handled; +#endif + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_IND_RST_CFG, + strlen(PRIV_CMD_IND_RST_CFG)) == 0) { + /* Set/Get out band independent reset */ + len = woal_priv_ind_rst_cfg(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_PER_PKT_CFG, + strlen(PRIV_CMD_PER_PKT_CFG)) == 0) { + /* Get/Set per packet Txctl and Rxinfo configuration */ + len = woal_priv_per_pkt_cfg(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), PRIV_CMD_DEAUTH_CTRL, + strlen(PRIV_CMD_DEAUTH_CTRL)) == 0) { + len = woal_priv_deauth_ctrl(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp + (buf + strlen(CMD_MARVELL), + PRIV_CMD_GET_CORRELATED_TIME, + strlen(PRIV_CMD_GET_CORRELATED_TIME)) == 0) { + len = woal_get_correlated_time(priv, buf, + priv_cmd.total_len); + goto handled; + } else { + PRINTM(MERROR, + "Unknown MARVELL PRIVATE command %s, ignored\n", + buf); + ret = -EFAULT; + goto done; + } + } +#ifdef STA_SUPPORT + if (strncmp(buf, "RSSILOW-THRESHOLD", strlen("RSSILOW-THRESHOLD")) == 0) { + pdata = buf + strlen("RSSILOW-THRESHOLD") + 1; + if (MLAN_STATUS_SUCCESS != + woal_set_rssi_low_threshold(priv, pdata, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "SCAN-CFG", strlen("SCAN-CFG")) == 0) { + PRINTM(MIOCTL, "Set SCAN CFG\n"); + if (MLAN_STATUS_SUCCESS != + woal_set_scan_cfg(priv, buf, priv_cmd.total_len)) { + ret = -EFAULT; + goto done; + } + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "RSSI", strlen("RSSI")) == 0) { + if (MLAN_STATUS_SUCCESS != woal_get_bss_info(priv, + MOAL_IOCTL_WAIT, + &bss_info)) { + ret = -EFAULT; + goto done; + } + if (bss_info.media_connected) { + if (MLAN_STATUS_SUCCESS != woal_get_signal_info(priv, + MOAL_IOCTL_WAIT, + &signal)) + { + ret = -EFAULT; + goto done; + } + len = sprintf(buf, "%.32s rssi %d\n", + bss_info.ssid.ssid, + signal.bcn_rssi_avg) + 1; + } else { + len = sprintf(buf, "OK\n") + 1; + } + } else if (strncmp(buf, "LINKSPEED", strlen("LINKSPEED")) == 0) { + if (MLAN_STATUS_SUCCESS != + woal_set_get_data_rate(priv, MLAN_ACT_GET, &rate)) { + ret = -EFAULT; + goto done; + } + PRINTM(MIOCTL, "tx rate=%d\n", (int)rate.rate); + len = sprintf(buf, "LinkSpeed %d\n", + (int)(rate.rate * 500000 / 1000000)) + + 1; + } else +#endif + if (strncmp(buf, "MACADDR", strlen("MACADDR")) == 0) { + len = sprintf(buf, "Macaddr = %02X:%02X:%02X:%02X:%02X:%02X\n", + priv->current_addr[0], priv->current_addr[1], + priv->current_addr[2], priv->current_addr[3], + priv->current_addr[4], priv->current_addr[5]) + 1; + } +#ifdef STA_SUPPORT + else if (strncmp(buf, "GETPOWER", strlen("GETPOWER")) == 0) { + if (MLAN_STATUS_SUCCESS != + woal_get_powermode(priv, &power_mode)) { + ret = -EFAULT; + goto done; + } + len = sprintf(buf, "powermode = %d\n", power_mode) + 1; + } else if (strncmp(buf, "SCAN-ACTIVE", strlen("SCAN-ACTIVE")) == 0) { + if (MLAN_STATUS_SUCCESS != woal_set_scan_type(priv, + MLAN_SCAN_TYPE_ACTIVE)) + { + ret = -EFAULT; + goto done; + } + priv->scan_type = MLAN_SCAN_TYPE_ACTIVE; + PRINTM(MIOCTL, "Set Active Scan\n"); + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "SCAN-PASSIVE", strlen("SCAN-PASSIVE")) == 0) { + if (MLAN_STATUS_SUCCESS != woal_set_scan_type(priv, + MLAN_SCAN_TYPE_PASSIVE)) + { + ret = -EFAULT; + goto done; + } + priv->scan_type = MLAN_SCAN_TYPE_PASSIVE; + PRINTM(MIOCTL, "Set Passive Scan\n"); + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "POWERMODE", strlen("POWERMODE")) == 0) { + pdata = buf + strlen("POWERMODE") + 1; + if (!hw_test) { + if (MLAN_STATUS_SUCCESS != + woal_set_powermode(priv, pdata)) { + ret = -EFAULT; + goto done; + } + } + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "SETROAMING", strlen("SETROAMING")) == 0) { + pdata = buf + strlen("SETROAMING") + 1; +#ifdef STA_CFG80211 + if (*pdata == '1') { + priv->roaming_enabled = MTRUE; + PRINTM(MIOCTL, "Roaming enabled\n"); + } else if (*pdata == '0') { + priv->roaming_enabled = MFALSE; + PRINTM(MIOCTL, "Roaming disabled\n"); + } +#endif + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "ROAM", strlen("ROAM")) == 0) { + pdata = buf + strlen("ROAM") + 1; +#ifdef STA_CFG80211 + if (*pdata == '1') { + priv->roaming_enabled = MTRUE; + PRINTM(MIOCTL, "Roaming enabled\n"); + } else if (*pdata == '0') { + priv->roaming_enabled = MFALSE; + PRINTM(MIOCTL, "Roaming disabled\n"); + } +#endif + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "COUNTRY", strlen("COUNTRY")) == 0) { + copy_len = strlen(buf) - strlen("COUNTRY") - 1; + if (copy_len > COUNTRY_CODE_LEN || copy_len <= 0) { + PRINTM(MERROR, "Invalid country length\n"); + ret = -EFAULT; + goto done; + } + memset(country_code, 0, sizeof(country_code)); + memcpy(country_code, buf + strlen("COUNTRY") + 1, copy_len); + PRINTM(MIOCTL, "Set COUNTRY %s\n", country_code); + if (cntry_txpwr) { + if (MLAN_STATUS_SUCCESS != + woal_request_country_power_table(priv, + country_code)) { + ret = -EFAULT; + goto done; + } + } +#ifdef STA_CFG80211 + if (IS_STA_CFG80211(cfg80211_wext)) { + PRINTM(MIOCTL, "Notify country code=%s\n", + country_code); + regulatory_hint(priv->wdev->wiphy, country_code); + len = sprintf(buf, "OK\n") + 1; + goto done; + } +#endif + if (MLAN_STATUS_SUCCESS != + woal_set_region_code(priv, country_code)) { + ret = -EFAULT; + goto done; + } + len = sprintf(buf, "OK\n") + 1; + } else if (memcmp(buf, WEXT_CSCAN_HEADER, WEXT_CSCAN_HEADER_SIZE) == 0) { + PRINTM(MIOCTL, "Set Combo Scan\n"); + if (MLAN_STATUS_SUCCESS != woal_set_combo_scan(priv, buf, + priv_cmd. + total_len)) { + ret = -EFAULT; + goto done; + } + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "GETBAND", strlen("GETBAND")) == 0) { + if (MLAN_STATUS_SUCCESS != woal_get_band(priv, &band)) { + ret = -EFAULT; + goto done; + } + len = sprintf(buf, "Band %d\n", band) + 1; + } else if (strncmp(buf, "SETBAND", strlen("SETBAND")) == 0) { + pband = buf + strlen("SETBAND") + 1; + if (MLAN_STATUS_SUCCESS != woal_set_band(priv, pband)) { + ret = -EFAULT; + goto done; + } + len = sprintf(buf, "OK\n") + 1; + } +#endif + else if (strncmp(buf, "START", strlen("START")) == 0) { + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "STOP", strlen("STOP")) == 0) { + len = sprintf(buf, "OK\n") + 1; + } +#ifdef UAP_SUPPORT + else if (strncmp(buf, "AP_BSS_START", strlen("AP_BSS_START")) == 0) { + ret = woal_uap_bss_ctrl(priv, MOAL_IOCTL_WAIT, UAP_BSS_START); + if (ret) + goto done; + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "AP_BSS_STOP", strlen("AP_BSS_STOP")) == 0) { + ret = woal_uap_bss_ctrl(priv, MOAL_IOCTL_WAIT, UAP_BSS_STOP); + if (ret) + goto done; + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "AP_SET_CFG", strlen("AP_SET_CFG")) == 0) { + if (priv_cmd.total_len <= strlen("AP_SET_CFG") + 1) + goto done; + pdata = buf + strlen("AP_SET_CFG") + 1; + ret = woal_uap_set_ap_cfg(priv, pdata, + priv_cmd.total_len - + strlen("AP_SET_CFG") - 1); + if (ret) + goto done; + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "WL_FW_RELOAD", strlen("WL_FW_RELOAD")) == 0) { + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "AP_GET_STA_LIST", strlen("AP_GET_STA_LIST")) == + 0) { + /* TODO Add STA list support */ + len = sprintf(buf, "OK\n") + 1; + } +#endif + else if (strncmp(buf, "SETSUSPENDOPT", strlen("SETSUSPENDOPT")) == 0) { + /* it will be done by GUI */ + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "SETSUSPENDMODE", strlen("SETSUSPENDMODE")) == + 0) { + /* it will be done by GUI */ + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "BTCOEXMODE", strlen("BTCOEXMODE")) == 0) { + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "BTCOEXSCAN-START", strlen("BTCOEXSCAN-START")) + == 0) { + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "BTCOEXSCAN-STOP", strlen("BTCOEXSCAN-STOP")) == + 0) { + len = sprintf(buf, "OK\n") + 1; + } +#ifdef STA_SUPPORT + else if (strncmp(buf, "BGSCAN-START", strlen("BGSCAN-START")) == 0) { + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "BGSCAN-CONFIG", strlen("BGSCAN-CONFIG")) == 0) { + if (MLAN_STATUS_SUCCESS != + woal_set_bg_scan(priv, buf, priv_cmd.total_len)) { + ret = -EFAULT; + goto done; + } + priv->bg_scan_start = MTRUE; + priv->bg_scan_reported = MFALSE; + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "BGSCAN-STOP", strlen("BGSCAN-STOP")) == 0) { + if (priv->bg_scan_start && !priv->scan_cfg.rssi_threshold) { + if (MLAN_STATUS_SUCCESS != + woal_stop_bg_scan(priv, MOAL_NO_WAIT)) { + ret = -EFAULT; + goto done; + } + priv->bg_scan_start = MFALSE; + priv->bg_scan_reported = MFALSE; + } + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "RXFILTER-START", strlen("RXFILTER-START")) == + 0) { +#ifdef MEF_CFG_RX_FILTER + ret = woal_set_rxfilter(priv, MTRUE); + if (ret) + goto done; +#endif + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "RXFILTER-STOP", strlen("RXFILTER-STOP")) == 0) { +#ifdef MEF_CFG_RX_FILTER + ret = woal_set_rxfilter(priv, MFALSE); + if (ret) + goto done; +#endif + len = sprintf(buf, "OK\n") + 1; + } +#ifdef STA_CFG80211 + else if (strncmp(buf, "GET_EVENT", strlen("GET_EVENT")) == 0) { + if (IS_STA_CFG80211(cfg80211_wext)) { + if (priv->last_event & EVENT_BG_SCAN_REPORT) + woal_inform_bss_from_scan_result(priv, NULL, + MOAL_IOCTL_WAIT); + } + len = sprintf(buf, "EVENT=%d\n", priv->last_event) + 1; + priv->last_event = 0; + } else if (strncmp(buf, "GET_802_11W", strlen("GET_802_11W")) == 0) { + len = sprintf(buf, "802_11W=ENABLED\n") + 1; + } +#endif /* STA_CFG80211 */ + else if (strncmp(buf, "RXFILTER-ADD", strlen("RXFILTER-ADD")) == 0) { + pdata = buf + strlen("RXFILTER-ADD") + 1; + if (MLAN_STATUS_SUCCESS != woal_add_rxfilter(priv, pdata)) { + ret = -EFAULT; + goto done; + } + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "RXFILTER-REMOVE", strlen("RXFILTER-REMOVE")) == + 0) { + pdata = buf + strlen("RXFILTER-REMOVE") + 1; + if (MLAN_STATUS_SUCCESS != woal_remove_rxfilter(priv, pdata)) { + ret = -EFAULT; + goto done; + } + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "QOSINFO", strlen("QOSINFO")) == 0) { + pdata = buf + strlen("QOSINFO") + 1; +#ifdef STA_SUPPORT + if (MLAN_STATUS_SUCCESS != + woal_priv_qos_cfg(priv, MLAN_ACT_SET, pdata)) { + ret = -EFAULT; + goto done; + } +#endif + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "SLEEPPD", strlen("SLEEPPD")) == 0) { + pdata = buf + strlen("SLEEPPD") + 1; + if (MLAN_STATUS_SUCCESS != woal_set_sleeppd(priv, pdata)) { + ret = -EFAULT; + goto done; + } + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "SET_AP_WPS_P2P_IE", + strlen("SET_AP_WPS_P2P_IE")) == 0) { + pdata = buf + strlen("SET_AP_WPS_P2P_IE") + 1; + /* Android cmd format: + * "SET_AP_WPS_P2P_IE 1" -- beacon IE + * "SET_AP_WPS_P2P_IE 2" -- proberesp IE + * "SET_AP_WPS_P2P_IE 4" -- assocresp IE + */ +#if defined(STA_CFG80211) && defined(UAP_CFG80211) +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 2, 0) + if (MLAN_STATUS_SUCCESS != + woal_set_ap_wps_p2p_ie(priv, (t_u8 *)pdata, + priv_cmd.used_len - + strlen("SET_AP_WPS_P2P_IE") - 1)) { + ret = -EFAULT; + goto done; + } +#endif +#endif + len = sprintf(buf, "OK\n") + 1; + } +#endif + else if (strncmp(buf, "P2P_DEV_ADDR", strlen("P2P_DEV_ADDR")) == 0) { + memset(buf, 0x0, (size_t) priv_cmd.total_len); + memcpy(buf, priv->current_addr, ETH_ALEN); + len = ETH_ALEN; + } else if (strncmp(buf, ("P2P_GET_NOA"), strlen("P2P_GET_NOA")) == 0) { + /* TODO + * Just return '\0' + */ + memset(buf, 0x0, (size_t) priv_cmd.total_len); + *buf = 0; + len = 1; + } else if (strnicmp(buf, "MIRACAST", strlen("MIRACAST")) == 0) { + pdata = buf + strlen("MIRACAST"); + /* Android cmd format: + * "MIRACAST 0" -- disabled + * "MIRACAST 1" -- operating as source + * "MIRACAST 2" -- operating as sink + */ +#ifdef WIFI_DIRECT_SUPPORT +#if defined(STA_CFG80211) || defined(UAP_CFG80211) + if (MLAN_STATUS_SUCCESS != + woal_set_miracast_mode(priv, (t_u8 *)pdata, + priv_cmd.used_len - + strlen("MIRACAST"))) { + ret = -EFAULT; + goto done; + } +#endif +#endif + len = sprintf(buf, "OK\n") + 1; + } else if (strnicmp(buf, "SCAN_TIMING", strlen("SCAN_TIMING")) == 0) { +#ifdef WIFI_DIRECT_SUPPORT +#if defined(STA_CFG80211) || defined(UAP_CFG80211) + if (MLAN_STATUS_SUCCESS != + woal_set_scan_chan_gap(priv, buf, priv_cmd.total_len)) { + ret = -EFAULT; + goto done; + } +#endif +#endif + len = sprintf(buf, "OK\n") + 1; + } else if (strnicmp(buf, "BA_WSIZE_RX", strlen("BA_WSIZE_RX")) == 0) { + pdata = buf + strlen("BA_WSIZE_RX") + 1; + len = priv_cmd.total_len - strlen("BA_WSIZE_RX") - 1; + if (MLAN_STATUS_SUCCESS != + woal_set_rx_ba_winsize(priv, pdata, len)) { + ret = -EFAULT; + goto done; + } + len = sprintf(buf, "OK\n") + 1; + } else if (strnicmp(buf, "BA_WSIZE_TX", strlen("BA_WSIZE_TX")) == 0) { + pdata = buf + strlen("BA_WSIZE_TX") + 1; + len = priv_cmd.total_len - strlen("BA_WSIZE_TX") - 1; + if (MLAN_STATUS_SUCCESS != + woal_set_tx_ba_winsize(priv, pdata, len)) { + ret = -EFAULT; + goto done; + } + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp + (buf, "FAKE_SCAN_COMPLETE", + strlen("FAKE_SCAN_COMPLETE")) == 0) { + pdata = buf + strlen("FAKE_SCAN_COMPLETE") + 1; +#ifdef STA_CFG80211 + if (*pdata == '1') { + priv->fake_scan_complete = MTRUE; + PRINTM(MIOCTL, "fake scan complete enabled\n"); + } else if (*pdata == '0') { + priv->fake_scan_complete = MFALSE; + PRINTM(MIOCTL, "fake scan complete disabled\n"); + } +#endif + len = sprintf(buf, "OK\n") + 1; + } +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#ifdef WIFI_DIRECT_SUPPORT + else if (strncmp + (buf, "P2P_PERIODIC_SLEEP", + strlen("P2P_PERIODIC_SLEEP")) == 0) { + if (MLAN_STATUS_SUCCESS != + woal_p2p_ps_cfg(priv, buf, priv_cmd.total_len)) { + ret = -EFAULT; + goto done; + } + len = sprintf(buf, "OK\n") + 1; + } +#endif +#endif + else if (strncmp(buf, "WLS_BATCHING", strlen("WLS_BATCHING")) == 0) { + /* TODO */ + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "TDLS_CS_CHAN", strlen("TDLS_CS_CHAN")) == 0) { + len = woal_priv_tdls_cs_chan(priv, buf, priv_cmd.total_len); + } +#if defined(UAP_SUPPORT) + else if (strncmp(buf, "P2P_ECSA", strlen("P2P_ECSA")) == 0) { + len = woal_priv_p2p_ecsa(priv, buf, priv_cmd.total_len); + } +#endif + else if (strncmp(buf, "SETROAMOFFLOAD", strlen("SETROAMOFFLOAD")) == 0) { + len = woal_priv_set_roam_offload(priv, buf, priv_cmd.total_len); + } else if (strncmp + (buf, "SETROAMOFFLAPLIST", + strlen("SETROAMOFFLAPLIST")) == 0) { + len = woal_priv_set_roam_offload_aplist(priv, buf, + priv_cmd.total_len); + } else if (strncmp(buf, "CFGROAMOFFLOAD", strlen("CFGROAMOFFLOAD")) == + 0) { + len = woal_priv_roam_offload_cfg(priv, buf, priv_cmd.total_len); + } else if (strncmp + (buf, "SETROAMPASSPHRASE", + strlen("SETROAMPASSPHRASE")) == 0) { + len = woal_priv_set_roam_passphrase(priv, buf, + priv_cmd.total_len); + } else { + PRINTM(MIOCTL, "Unknown PRIVATE command: %s, ignored\n", buf); + ret = -EFAULT; + goto done; + } + +handled: + PRINTM(MIOCTL, "PRIV Command return: %s, length=%d\n", buf, len); + + if (len > 0) { + priv_cmd.used_len = len; + if (priv_cmd.used_len <= priv_cmd.total_len) { + memset(buf + priv_cmd.used_len, 0, + (size_t) (CMD_BUF_LEN - priv_cmd.used_len)); + if (copy_to_user(cmd_buf, buf, priv_cmd.total_len)) { + PRINTM(MERROR, + "%s: failed to copy data to user buffer\n", + __FUNCTION__); + ret = -EFAULT; + goto done; + } + if (copy_to_user + (req->ifr_data, &priv_cmd, + sizeof(android_wifi_priv_cmd))) { + PRINTM(MERROR, + "%s: failed to copy command header to user buffer\n", + __FUNCTION__); + ret = -EFAULT; + } + } else { + PRINTM(MERROR, + "%s: the buffer supplied by appl is too small (supplied: %d, used: %d)\n", + __FUNCTION__, priv_cmd.total_len, + priv_cmd.used_len); + ret = -EFAULT; + } + } else { + ret = len; + } + +done: + kfree(buf); + LEAVE(); + return ret; +} + +/******************************************************** + Global Functions +********************************************************/ +/** + * @brief Create a brief scan resp to relay basic BSS info to the app layer + * + * When the beacon/probe response has not been buffered, use the saved BSS + * information available to provide a minimum response for the application + * ioctl retrieval routines. Include: + * - Timestamp + * - Beacon Period + * - Capabilities (including WMM Element if available) + * - SSID + * + * @param ppbuffer Output parameter: Buffer used to create basic scan rsp + * @param pbss_desc Pointer to a BSS entry in the scan table to create + * scan response from for delivery to the application layer + * + * @return N/A + */ +void +wlan_scan_create_brief_table_entry(t_u8 **ppbuffer, BSSDescriptor_t *pbss_desc) +{ + t_u8 *ptmp_buf = *ppbuffer; + t_u8 tmp_ssid_hdr[2]; + t_u8 ie_len = 0; + + ENTER(); + + memcpy(ptmp_buf, pbss_desc->time_stamp, sizeof(pbss_desc->time_stamp)); + ptmp_buf += sizeof(pbss_desc->time_stamp); + + memcpy(ptmp_buf, &pbss_desc->beacon_period, + sizeof(pbss_desc->beacon_period)); + ptmp_buf += sizeof(pbss_desc->beacon_period); + + memcpy(ptmp_buf, &pbss_desc->cap_info, sizeof(pbss_desc->cap_info)); + ptmp_buf += sizeof(pbss_desc->cap_info); + + tmp_ssid_hdr[0] = 0; /* Element ID for SSID is zero */ + tmp_ssid_hdr[1] = pbss_desc->ssid.ssid_len; + memcpy(ptmp_buf, tmp_ssid_hdr, sizeof(tmp_ssid_hdr)); + ptmp_buf += sizeof(tmp_ssid_hdr); + + memcpy(ptmp_buf, pbss_desc->ssid.ssid, pbss_desc->ssid.ssid_len); + ptmp_buf += pbss_desc->ssid.ssid_len; + + if (pbss_desc->wmm_ie.vend_hdr.element_id == WMM_IE) { + ie_len = sizeof(IEEEtypes_Header_t) + + pbss_desc->wmm_ie.vend_hdr.len; + memcpy(ptmp_buf, &pbss_desc->wmm_ie, ie_len); + ptmp_buf += ie_len; + } + + if (pbss_desc->pwpa_ie) { + if ((*(pbss_desc->pwpa_ie)).vend_hdr.element_id == WPA_IE) { + ie_len = sizeof(IEEEtypes_Header_t) + + (*(pbss_desc->pwpa_ie)).vend_hdr.len; + memcpy(ptmp_buf, pbss_desc->pwpa_ie, ie_len); + } + + ptmp_buf += ie_len; + } + + if (pbss_desc->prsn_ie) { + if ((*(pbss_desc->prsn_ie)).ieee_hdr.element_id == RSN_IE) { + ie_len = sizeof(IEEEtypes_Header_t) + + (*(pbss_desc->prsn_ie)).ieee_hdr.len; + memcpy(ptmp_buf, pbss_desc->prsn_ie, ie_len); + } + + ptmp_buf += ie_len; + } + + *ppbuffer = ptmp_buf; + LEAVE(); +} + +/** + * @brief Create a wlan_ioctl_get_scan_table_entry for a given BSS + * Descriptor for inclusion in the ioctl response to the user space + * application. + * + * + * @param pbss_desc Pointer to a BSS entry in the scan table to form + * scan response from for delivery to the application layer + * @param ppbuffer Output parameter: Buffer used to output scan return struct + * @param pspace_left Output parameter: Number of bytes available in the + * response buffer. + * + * @return MLAN_STATUS_SUCCESS, or < 0 with IOCTL error code + */ +int +wlan_get_scan_table_ret_entry(BSSDescriptor_t *pbss_desc, + t_u8 **ppbuffer, int *pspace_left) +{ + wlan_ioctl_get_scan_table_entry *prsp_entry; + wlan_ioctl_get_scan_table_entry tmp_rsp_entry; + int space_needed; + t_u8 *pcurrent; + int variable_size; + + const int fixed_size = sizeof(wlan_ioctl_get_scan_table_entry); + + ENTER(); + + pcurrent = *ppbuffer; + + /* The variable size returned is the stored beacon size */ + variable_size = pbss_desc->beacon_buf_size; + + /* If we stored a beacon and its size was zero, set the variable + * size return value to the size of the brief scan response + * wlan_scan_create_brief_table_entry creates. Also used if + * we are not configured to store beacons in the first place + */ + if (!variable_size) { + variable_size = pbss_desc->ssid.ssid_len + 2; + variable_size += (sizeof(pbss_desc->beacon_period) + + sizeof(pbss_desc->time_stamp) + + sizeof(pbss_desc->cap_info)); + if (pbss_desc->wmm_ie.vend_hdr.element_id == WMM_IE) { + variable_size += (sizeof(IEEEtypes_Header_t) + + pbss_desc->wmm_ie.vend_hdr.len); + } + + if (pbss_desc->pwpa_ie) { + if ((*(pbss_desc->pwpa_ie)).vend_hdr.element_id == + WPA_IE) { + variable_size += (sizeof(IEEEtypes_Header_t) + + + (*(pbss_desc->pwpa_ie)). + vend_hdr.len); + } + } + + if (pbss_desc->prsn_ie) { + if ((*(pbss_desc->prsn_ie)).ieee_hdr.element_id == + RSN_IE) { + variable_size += (sizeof(IEEEtypes_Header_t) + + + (*(pbss_desc->prsn_ie)). + ieee_hdr.len); + } + } + } + + space_needed = fixed_size + variable_size; + + PRINTM(MINFO, "GetScanTable: need(%d), left(%d)\n", + space_needed, *pspace_left); + + if (space_needed >= *pspace_left) { + *pspace_left = 0; + LEAVE(); + return -E2BIG; + } + + *pspace_left -= space_needed; + + tmp_rsp_entry.fixed_field_length = (sizeof(tmp_rsp_entry) + - + sizeof(tmp_rsp_entry. + fixed_field_length) + - + sizeof(tmp_rsp_entry. + bss_info_length)); + + memcpy(tmp_rsp_entry.fixed_fields.bssid, + pbss_desc->mac_address, sizeof(prsp_entry->fixed_fields.bssid)); + + tmp_rsp_entry.fixed_fields.rssi = pbss_desc->rssi; + tmp_rsp_entry.fixed_fields.channel = pbss_desc->channel; + tmp_rsp_entry.fixed_fields.network_tsf = pbss_desc->network_tsf; + tmp_rsp_entry.bss_info_length = variable_size; + + /* + * Copy fixed fields to user space + */ + memcpy(pcurrent, &tmp_rsp_entry, fixed_size); + pcurrent += fixed_size; + + if (pbss_desc->pbeacon_buf) { + /* + * Copy variable length elements to user space + */ + memcpy(pcurrent, pbss_desc->pbeacon_buf, + pbss_desc->beacon_buf_size); + + pcurrent += pbss_desc->beacon_buf_size; + } else { + wlan_scan_create_brief_table_entry(&pcurrent, pbss_desc); + } + + *ppbuffer = pcurrent; + + LEAVE(); + + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief ioctl function - entry point + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @param cmd Command + * + * @return 0 --success, otherwise fail + */ +int +woal_do_ioctl(struct net_device *dev, struct ifreq *req, int cmd) +{ + int ret = 0; + + ENTER(); + + PRINTM(MINFO, "woal_do_ioctl: ioctl cmd = 0x%x\n", cmd); + switch (cmd) { + case WOAL_ANDROID_DEF_CMD: + /** android default ioctl ID is SIOCDEVPRIVATE + 1 */ + ret = woal_android_priv_cmd(dev, req); + break; + case WOAL_CUSTOM_IE_CFG: + ret = woal_custom_ie_ioctl(dev, req); + break; + case WOAL_MGMT_FRAME_TX: + ret = woal_send_host_packet(dev, req); + break; + case WOAL_TDLS_CONFIG: + ret = woal_tdls_config_ioctl(dev, req); + break; + case WOAL_ANDROID_PRIV_CMD: + ret = woal_android_priv_cmd(dev, req); + break; + case WOAL_GET_BSS_TYPE: + ret = woal_get_bss_type(dev, req); + break; + default: +#if defined(STA_WEXT) +#ifdef STA_SUPPORT + ret = woal_wext_do_ioctl(dev, req, cmd); +#else + ret = -EINVAL; +#endif +#else + ret = -EINVAL; +#endif + break; + } + + LEAVE(); + return ret; +} diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_eth_ioctl.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_eth_ioctl.h new file mode 100644 index 000000000000..0731b4b6d3b1 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_eth_ioctl.h @@ -0,0 +1,463 @@ + +/** @file moal_eth_ioctl.h + * + * @brief This file contains definition for private IOCTL call. + * + * Copyright (C) 2014-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +/******************************************************** +Change log: + 01/05/2012: initial version +********************************************************/ +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#include "moal_cfg80211.h" +#endif + +#ifndef _WOAL_ETH_PRIV_H_ +#define _WOAL_ETH_PRIV_H_ + +/** Command disabled */ +#define CMD_DISABLED 0 +/** Command enabled */ +#define CMD_ENABLED 1 +/** Command get */ +#define CMD_GET 2 + +/** 2K bytes */ +#define WOAL_2K_BYTES 2000 + +/** Marvell private command identifier string */ +#define CMD_MARVELL "MRVL_CMD" + +/** Private command: Version */ +#define PRIV_CMD_VERSION "version" +/** Private command: Band cfg */ +#define PRIV_CMD_BANDCFG "bandcfg" +/** Private command: Host cmd */ +#define PRIV_CMD_HOSTCMD "hostcmd" +/** Private command: Custom IE config*/ +#define PRIV_CMD_CUSTOMIE "customie" +/** Private command: HT Tx Cfg */ +#define PRIV_CMD_HTTXCFG "httxcfg" +/** Private command: HT Cap Info */ +#define PRIV_CMD_HTCAPINFO "htcapinfo" +/** Private command: Add BA para */ +#define PRIV_CMD_ADDBAPARA "addbapara" +/** Private command: Aggragation priority table */ +#define PRIV_CMD_AGGRPRIOTBL "aggrpriotbl" +/** Private command: Add BA reject cfg */ +#define PRIV_CMD_ADDBAREJECT "addbareject" +/** Private command: Delete BA */ +#define PRIV_CMD_DELBA "delba" +/** Private command: Reject Addba Req */ +#define PRIV_CMD_REJECTADDBAREQ "rejectaddbareq" +#define PRIV_CMD_DATARATE "getdatarate" +#define PRIV_CMD_TXRATECFG "txratecfg" +#define PRIV_CMD_GETLOG "getlog" +#define PRIV_CMD_ESUPPMODE "esuppmode" +#define PRIV_CMD_PASSPHRASE "passphrase" +#define PRIV_CMD_DEAUTH "deauth" +#ifdef UAP_SUPPORT +#define PRIV_CMD_AP_DEAUTH "apdeauth" +#define PRIV_CMD_GET_STA_LIST "getstalist" +#define PRIV_CMD_BSS_CONFIG "bssconfig" +#endif +#if defined(WIFI_DIRECT_SUPPORT) +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) +#define PRIV_CMD_BSSROLE "bssrole" +#endif +#endif +#ifdef STA_SUPPORT +#define PRIV_CMD_GETSCANTABLE "getscantable" +#define PRIV_CMD_SETUSERSCAN "setuserscan" +#define PRIV_CMD_EXTCAPCFG "extcapcfg" +#define PRIV_CMD_CANCELSCAN "cancelscan" +#endif +#define PRIV_CMD_DEEPSLEEP "deepsleep" +#define PRIV_CMD_IPADDR "ipaddr" +#define PRIV_CMD_WPSSESSION "wpssession" +#define PRIV_CMD_OTPUSERDATA "otpuserdata" +#define PRIV_CMD_COUNTRYCODE "countrycode" +#define PRIV_CMD_TCPACKENH "tcpackenh" +#ifdef REASSOCIATION +#define PRIV_CMD_ASSOCESSID "assocessid" +#define PRIV_CMD_ASSOCBSSID "assocessid_bssid" +#endif +#define PRIV_CMD_WAKEUPREASON "wakeupreason" +#ifdef STA_SUPPORT +#define PRIV_CMD_LISTENINTERVAL "listeninterval" +#endif +#ifdef DEBUG_LEVEL1 +#define PRIV_CMD_DRVDBG "drvdbg" +#endif +#define PRIV_CMD_HSCFG "hscfg" +#define PRIV_CMD_HSSETPARA "hssetpara" +#define PRIV_CMD_MGMT_FILTER "mgmtfilter" +#define PRIV_CMD_SCANCFG "scancfg" +#define PRIV_CMD_SET_BSS_MODE "setbssmode" +#ifdef STA_SUPPORT +#define PRIV_CMD_SET_AP "setap" +#define PRIV_CMD_SET_POWER "setpower" +#define PRIV_CMD_SET_ESSID "setessid" +#define PRIV_CMD_SET_AUTH "setauth" +#define PRIV_CMD_GET_AP "getap" +#define PRIV_CMD_GET_POWER "getpower" +#define PRIV_CMD_PSMODE "psmode" +#endif +#define PRIV_CMD_WARMRESET "warmreset" +#define PRIV_CMD_TXPOWERCFG "txpowercfg" +#define PRIV_CMD_PSCFG "pscfg" +#define PRIV_CMD_BCNTIMEOUTCFG "bcntimeoutcfg" +#define PRIV_CMD_SLEEPPD "sleeppd" +#define PRIV_CMD_TXCONTROL "txcontrol" +#define PRIV_CMD_REGRDWR "regrdwr" +#define PRIV_CMD_RDEEPROM "rdeeprom" +#define PRIV_CMD_MEMRDWR "memrdwr" +#define PRIV_CMD_SDCMD52RW "sdcmd52rw" +#define PRIV_CMD_ARPFILTER "arpfilter" +#define PRIV_CMD_MGMT_FRAME_CTRL "mgmtframectrl" +#define PRIV_CMD_QCONFIG "qconfig" +#define PRIV_CMD_ADDTS "addts" +#define PRIV_CMD_DELTS "delts" +#define PRIV_CMD_QSTATUS "qstatus" +#define PRIV_CMD_TS_STATUS "ts_status" +#define PRIV_CMD_QOS_CFG "qoscfg" +#define PRIV_CMD_MAC_CTRL "macctrl" +#define PRIV_CMD_GETWAP "getwap" +#define PRIV_CMD_REGION_CODE "regioncode" +#define PRIV_CMD_CFPINFO "cfpinfo" +#define PRIV_CMD_FWMACADDR "fwmacaddr" +#define PRIV_CMD_OFFCHANNEL "offchannel" +#define PRIV_CMD_DSCP_MAP "dscpmap" +/** Private command: Verext */ +#define PRIV_CMD_VEREXT "verext" +#if defined(STA_SUPPORT) && defined(STA_WEXT) +#define PRIV_CMD_RADIO_CTRL "radioctrl" +#endif +#define PRIV_CMD_WMM_CFG "wmmcfg" +#if defined(STA_SUPPORT) +#define PRIV_CMD_11D_CFG "11dcfg" +#define PRIV_CMD_11D_CLR_TBL "11dclrtbl" +#endif +#define PRIV_CMD_WWS_CFG "wwscfg" +#if defined(REASSOCIATION) +#define PRIV_CMD_REASSOCTRL "reassoctrl" +#endif +#define PRIV_CMD_TXBUF_CFG "txbufcfg" +#ifdef STA_SUPPORT +#define PRIV_CMD_AUTH_TYPE "authtype" +#endif +#define PRIV_CMD_POWER_CONS "powercons" +#define PRIV_CMD_THERMAL "thermal" +#define PRIV_CMD_BCN_INTERVAL "bcninterval" +#ifdef STA_SUPPORT +#define PRIV_CMD_GET_SIGNAL "getsignal" +#endif +#if defined(STA_SUPPORT) +#define PRIV_CMD_PMFCFG "pmfcfg" +#endif +#define PRIV_CMD_INACTIVITYTO "inactivityto" +#define PRIV_CMD_ATIM_WINDOW "atimwindow" +#define PRIV_CMD_AMSDU_AGGR_CTRL "amsduaggrctrl" +#define PRIV_CMD_TX_BF_CAP "httxbfcap" +#define PRIV_CMD_SDIO_CLOCK "sdioclock" +#if defined(SDIO_MULTI_PORT_TX_AGGR) || defined(SDIO_MULTI_PORT_RX_AGGR) +#define PRIV_CMD_MPA_CTRL "mpactrl" +#endif +#define PRIV_CMD_SLEEP_PARAMS "sleepparams" +#define PRIV_CMD_NET_MON "netmon" +#if defined(STA_CFG80211) && defined(UAP_CFG80211) +#define PRIV_CMD_MONITOR_MODE "monitormode" +#endif +#if defined(DFS_TESTING_SUPPORT) +#define PRIV_CMD_DFS_TESTING "dfstesting" +#endif +#define PRIV_CMD_CFP_CODE "cfpcode" +#define PRIV_CMD_CWMODE "cwmode" +#define PRIV_CMD_ANT_CFG "antcfg" +#define PRIV_CMD_SYSCLOCK "sysclock" +#define PRIV_CMD_ADHOC_AES "adhocaes" +#define PRIV_CMD_ASSOCIATE "associate" +#define PRIV_CMD_TX_BF_CFG "httxbfcfg" +#define PRIV_CMD_PORT_CTRL "port_ctrl" +#define PRIV_CMD_PB_BYPASS "pb_bypass" +#define PRIV_CMD_COALESCE_STATUS "coalesce_status" +#define PRIV_CMD_FW_WAKEUP_METHOD "fwwakeupmethod" +#define PRIV_CMD_SD_CMD53_RW "sdcmd53rw" +#ifdef RX_PACKET_COALESCE +#define PRIV_CMD_RX_COAL_CFG "rxpktcoal_cfg" +#endif +#define PRIV_CMD_MULTI_CHAN_CFG "mc_cfg" +#define PRIV_CMD_MULTI_CHAN_POLICY "mc_policy" +#define PRIV_CMD_DRCS_CFG "mc_cfg_ext" +#if defined(WIFI_DIRECT_SUPPORT) +#if defined(UAP_CFG80211) +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION +#define PRIV_CMD_CFG_NOA "cfg_noa" +#define PRIV_CMD_CFG_OPP_PS "cfg_opp_ps" +#endif +#endif +#endif +#define PRIV_CMD_DFS_REPEATER_CFG "dfs_repeater" +#ifdef WIFI_DIRECT_SUPPORT +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#define PRIV_CMD_MIRACAST_CFG "miracastcfg" +#endif +#endif +#define PRIV_CMD_COEX_RX_WINSIZE "coex_rx_winsize" +#define PRIV_CMD_TX_AGGR_CTRL "txaggrctrl" +#define PRIV_CMD_AUTO_TDLS "autotdls" + +#define PRIV_CMD_GET_SENSOR_TEMP "get_sensor_temp" + +#define PRIV_CMD_11K_CFG "11k_enable" +#define PRIV_CMD_11K_NEIGHBOR_REPORT "neighbor_report" +#if defined(UAP_SUPPORT) +#define PRIV_CMD_EXTEND_CHAN_SWITCH "channel_switch" +#endif + +#define PRIV_CMD_TDLS_IDLE_TIME "tdls_idle_time" + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) +#define PRIV_CMD_DFS_OFFLOAD "dfs_offload" +#endif + +#if defined(SDIO_SUSPEND_RESUME) +#define PRIV_CMD_AUTO_ARP "auto_arp" +#endif + +#define PRIV_CMD_PER_PKT_CFG "per_pkt_cfg" + +#define PRIV_CMD_DEAUTH_CTRL "ctrldeauth" + +/**Private command ID to set/get independent reset*/ +#define PRIV_CMD_IND_RST_CFG "indrstcfg" + +/** Private command ID for Android default commands */ +#define WOAL_ANDROID_DEF_CMD (SIOCDEVPRIVATE + 1) + +/** Private command ID to send TLD configuration */ +#define WOAL_TDLS_CONFIG (SIOCDEVPRIVATE + 5) + +/** Private command ID to pass mgmt frame */ +#define WOAL_MGMT_FRAME_TX WOAL_MGMT_FRAME_TX_IOCTL + +/** Private command ID to pass custom IE list */ +#define WOAL_CUSTOM_IE_CFG (SIOCDEVPRIVATE + 13) + +/** Private command ID for Android ICS priv CMDs */ +#define WOAL_ANDROID_PRIV_CMD (SIOCDEVPRIVATE + 14) + +/** Private command ID to get BSS type */ +#define WOAL_GET_BSS_TYPE (SIOCDEVPRIVATE + 15) + +/** Private command ID for robustcoex */ +#define PRIV_CMD_ROBUSTCOEX "robustcoex" + +#define PRIV_CMD_BOOTSLEEP "bootsleep" + +#define PRIV_CMD_GET_CORRELATED_TIME "GET_CORRELATED_TIME" + +int woal_do_ioctl(struct net_device *dev, struct ifreq *req, int cmd); + +/* + * For android private commands, fixed value of ioctl is used. + * Internally commands are differentiated using strings. + * + * application needs to specify "total_len" of data for copy_from_user + * kernel updates "used_len" during copy_to_user + */ +/** Private command structure from app */ +#ifdef USERSPACE_32BIT_OVER_KERNEL_64BIT +typedef struct _android_wifi_priv_cmd { + /** Buffer pointer */ + t_u64 buf; + /** buffer updated by driver */ + int used_len; + /** buffer sent by application */ + int total_len; +} __attribute__ ((packed)) + android_wifi_priv_cmd; +#else +typedef struct _android_wifi_priv_cmd { + /** Buffer pointer */ + char *buf; + /** buffer updated by driver */ + int used_len; + /** buffer sent by application */ + int total_len; +} android_wifi_priv_cmd; +#endif + +#ifndef IFNAMSIZ +#define IFNAMSIZ 16 +#endif + +/* Maximum size of the ESSID and NICKN strings */ +#define MW_ESSID_MAX_SIZE 32 + +/* Modes of operation */ +#define MW_MODE_AUTO 0 /* Let the driver decides */ +#define MW_MODE_ADHOC 1 /* Single cell network */ +#define MW_MODE_INFRA 2 /* Multi cell network, roaming, ... */ +#define MW_MODE_MASTER 3 /* Synchronisation master or Access Point */ +#define MW_MODE_REPEAT 4 /* Wireless Repeater (forwarder) */ +#define MW_MODE_SECOND 5 /* Secondary master/repeater (backup) */ +#define MW_MODE_MONITOR 6 /* Passive monitor (listen only) */ +#define MW_MODE_MESH 7 /* Mesh (IEEE 802.11s) network */ + +#define MW_POWER_TYPE 0xF000 /* Type of parameter */ +#define MW_POWER_PERIOD 0x1000 /* Value is a period/duration of */ +#define MW_POWER_TIMEOUT 0x2000 /* Value is a timeout (to go asleep) */ + +#define MW_AUTH_INDEX 0x0FFF +#define MW_AUTH_FLAGS 0xF000 +#define MW_AUTH_WPA_VERSION 0 +#define MW_AUTH_CIPHER_PAIRWISE 1 +#define MW_AUTH_CIPHER_GROUP 2 +#define MW_AUTH_KEY_MGMT 3 +#define MW_AUTH_TKIP_COUNTERMEASURES 4 +#define MW_AUTH_DROP_UNENCRYPTED 5 +#define MW_AUTH_80211_AUTH_ALG 6 +#define MW_AUTH_WPA_ENABLED 7 +#define MW_AUTH_RX_UNENCRYPTED_EAPOL 8 +#define MW_AUTH_ROAMING_CONTROL 9 +#define MW_AUTH_PRIVACY_INVOKED 10 +#define MW_AUTH_CIPHER_GROUP_MGMT 11 +#define MW_AUTH_MFP 12 + +#define MW_AUTH_CIPHER_NONE 0x00000001 +#define MW_AUTH_CIPHER_WEP40 0x00000002 +#define MW_AUTH_CIPHER_TKIP 0x00000004 +#define MW_AUTH_CIPHER_CCMP 0x00000008 +#define MW_AUTH_CIPHER_WEP104 0x00000010 +#define MW_AUTH_CIPHER_AES_CMAC 0x00000020 + +#define MW_AUTH_ALG_OPEN_SYSTEM 0x00000001 +#define MW_AUTH_ALG_SHARED_KEY 0x00000002 +#define MW_AUTH_ALG_LEAP 0x00000004 + +/* Generic format for most parameters that fit in an int */ +struct mw_param { + t_s32 value; /* The value of the parameter itself */ + t_u8 fixed; /* Hardware should not use auto select */ + t_u8 disabled; /* Disable the feature */ + t_u16 flags; /* Various specifc flags (if any) */ +}; + +/* + * For all data larger than 16 octets, we need to use a + * pointer to memory allocated in user space. + */ +struct mw_point { + t_u8 *pointer; /* Pointer to the data (in user space) */ + t_u16 length; /* number of fields or size in bytes */ + t_u16 flags; /* Optional params */ +}; + +/* + * This structure defines the payload of an ioctl, and is used + * below. + */ +union mwreq_data { + /* Config - generic */ + char name[IFNAMSIZ]; + + struct mw_point essid; /* Extended network name */ + t_u32 mode; /* Operation mode */ + struct mw_param power; /* PM duration/timeout */ + struct sockaddr ap_addr; /* Access point address */ + struct mw_param param; /* Other small parameters */ + struct mw_point data; /* Other large parameters */ +}; + + /* The structure to exchange data for ioctl */ +struct mwreq { + union { + char ifrn_name[IFNAMSIZ]; /* if name, e.g. "eth0" */ + } ifr_ifrn; + + /* Data part */ + union mwreq_data u; +}; + +typedef struct woal_priv_ht_cap_info { + t_u32 ht_cap_info_bg; + t_u32 ht_cap_info_a; +} woal_ht_cap_info; + +typedef struct woal_priv_addba { + t_u32 time_out; + t_u32 tx_win_size; + t_u32 rx_win_size; + t_u32 tx_amsdu; + t_u32 rx_amsdu; +} woal_addba; + +/** data structure for extended channel switch */ +typedef struct woal_priv_extend_chan_switch { + /* IEEE element ID = 60 */ + t_u8 element_id; + /** Element length after id and len, set to 4 */ + t_u8 len; + /** STA should not transmit any frames if 1 */ + t_u8 chan_switch_mode; + /** Operate class # that AP/IBSS is moving to */ + t_u8 new_oper_class; + /** Channel # that AP/IBSS is moving to */ + t_u8 new_channel_num; + /** of TBTTs before channel switch */ + t_u8 chan_switch_count; +} woal_extend_chan_switch; + +/** data structure for cmd txratecfg */ +typedef struct woal_priv_tx_rate_cfg { + /* LG rate: 0, HT rate: 1, VHT rate: 2 */ + t_u32 rate_format; + /** Rate/MCS index (0xFF: auto) */ + t_u32 rate_index; + /** Data rate */ + t_u32 rate; +} woal_tx_rate_cfg; + +typedef struct woal_priv_esuppmode_cfg { + /* RSN mode */ + t_u16 rsn_mode; + /* Pairwise cipher */ + t_u8 pairwise_cipher; + /* Group cipher */ + t_u8 group_cipher; +} woal_esuppmode_cfg; + +mlan_status woal_set_ap_wps_p2p_ie(moal_private *priv, t_u8 *ie, size_t len); +mlan_status woal_ioctl_aggr_prio_tbl(moal_private *priv, t_u32 action, + mlan_ds_11n_aggr_prio_tbl *aggr_prio_tbl); + +int woal_android_priv_cmd(struct net_device *dev, struct ifreq *req); + +#define TLV_TYPE_PER_PKT_CFG 0x0001 +#define TX_PKT_CTRL MBIT(0) +#define RX_PKT_INFO MBIT(1) + +/* Enum for different CW mode type */ +typedef enum _cw_modes_e { + CWMODE_DISABLE, + CWMODE_TXCONTPKT, + CWMODE_TXCONTWAVE, +} cw_modes_e; +#endif /* _WOAL_ETH_PRIV_H_ */ diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_ioctl.c b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_ioctl.c new file mode 100644 index 000000000000..ba96f3bec762 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_ioctl.c @@ -0,0 +1,6391 @@ +/** @file moal_ioctl.c + * + * @brief This file contains ioctl function to MLAN + * + * Copyright (C) 2008-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +/******************************************************** +Change log: + 10/21/2008: initial version +********************************************************/ + +#include "moal_main.h" +#include "moal_eth_ioctl.h" +#include "moal_sdio.h" +#ifdef UAP_SUPPORT +#include "moal_uap.h" +#endif + +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#include "moal_cfg80211.h" +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) +#include "moal_cfgvendor.h" +#endif +#endif + +/******************************************************** + Local Variables +********************************************************/ +#define MRVL_TLV_HEADER_SIZE 4 +/* Marvell Channel config TLV ID */ +#define MRVL_CHANNELCONFIG_TLV_ID (0x0100 + 0x2a) /* 0x012a */ + +typedef struct _hostcmd_header { + /** Command Header : Command */ + t_u16 command; + /** Command Header : Size */ + t_u16 size; + /** Command Header : Sequence number */ + t_u16 seq_num; + /** Command Header : Result */ + t_u16 result; + /** Command action */ + t_u16 action; +} hostcmd_header, *phostcmd_header; + +/** Region code mapping */ +typedef struct _region_code_mapping_t { + /** Region */ + t_u8 region[COUNTRY_CODE_LEN]; + /** Code */ + t_u8 code; +} region_code_mapping_t; + +#define EU_REGION_CODE 0x30 + +/** Region code mapping table */ +static region_code_mapping_t region_code_mapping[] = { + {"US ", 0x10}, /* US FCC */ + {"CA ", 0x20}, /* IC Canada */ + {"SG ", 0x10}, /* Singapore */ + {"EU ", 0x30}, /* ETSI */ + {"AU ", 0x30}, /* Australia */ + {"KR ", 0x30}, /* Republic Of Korea */ + {"CN ", 0x50}, /* China */ + {"JP ", 0xFF}, /* Japan special */ +}; + +/** EEPROM Region code mapping table */ +static region_code_mapping_t hw_region_code_mapping[] = { + {"US ", 0x10}, /* US FCC */ + {"CA ", 0x20}, /* IC Canada */ + {"KR ", 0x30}, /* Korea */ + {"CN ", 0x50}, /* China */ + {"ES ", 0x31}, /* Spain */ + {"FR ", 0x32}, /* France */ + {"JP ", 0x40}, /* Japan */ + {"JP ", 0x41}, /* Japan */ +}; + +/** Country code for ETSI */ +static t_u8 eu_country_code_table[][COUNTRY_CODE_LEN] = { + "AL", "AD", "AT", "AU", "BY", "BE", "BA", "BG", "HR", "CY", + "CZ", "DK", "EE", "FI", "FR", "MK", "DE", "GR", "HU", "IS", + "IE", "IT", "KR", "LV", "LI", "LT", "LU", "MT", "MD", "MC", + "ME", "NL", "NO", "PL", "RO", "RU", "SM", "RS", "SI", "SK", + "ES", "SE", "CH", "TR", "UA", "UK", "GB" +}; + +/******************************************************** + Global Variables +********************************************************/ + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) +#ifdef UAP_SUPPORT +/** Network device handlers for uAP */ +extern const struct net_device_ops woal_uap_netdev_ops; +#endif +#ifdef STA_SUPPORT +/** Network device handlers for STA */ +extern const struct net_device_ops woal_netdev_ops; +#endif +#endif +extern int cfg80211_wext; +int disconnect_on_suspend; + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +extern int dfs_offload; +#endif +#endif + +extern int roamoffload_in_hs; + +/** gtk rekey offload mode */ +extern int gtk_rekey_offload; + +/******************************************************** + Local Functions +********************************************************/ +/** + * @brief This function converts region string to region code + * + * @param region_string Region string + * + * @return Region code + */ +t_u8 +region_string_2_region_code(char *region_string) +{ + t_u8 i; + + ENTER(); + for (i = 0; i < ARRAY_SIZE(region_code_mapping); i++) { + if (!memcmp(region_string, + region_code_mapping[i].region, + strlen(region_string))) { + LEAVE(); + return region_code_mapping[i].code; + } + } + + /* If still not found, look for code in EU country code table */ + for (i = 0; i < ARRAY_SIZE(eu_country_code_table); i++) { + if (!memcmp(region_string, eu_country_code_table[i], + COUNTRY_CODE_LEN - 1)) { + PRINTM(MIOCTL, "found region code=%d in EU table\n", + EU_REGION_CODE); + LEAVE(); + return EU_REGION_CODE; + } + } + + /* Default is US */ + LEAVE(); + return region_code_mapping[0].code; +} + +/** + * @brief This function converts region string to region code + * + * @param country_code Region string + * + * @return Region code + */ +t_bool +woal_is_etsi_country(t_u8 *country_code) +{ + + t_u8 i; + ENTER(); + + for (i = 0; i < ARRAY_SIZE(eu_country_code_table); i++) { + if (!memcmp(country_code, eu_country_code_table[i], + COUNTRY_CODE_LEN - 1)) { + PRINTM(MIOCTL, "found region code=%d in EU table\n", + EU_REGION_CODE); + LEAVE(); + return MTRUE; + } + } + + LEAVE(); + return MFALSE; +} + +/** + * @brief This function converts region string to region code + * + * @param region_code region code + * + * @return Region string or NULL + */ +char * +region_code_2_string(t_u8 region_code) +{ + t_u8 i; + + ENTER(); + for (i = 0; i < ARRAY_SIZE(hw_region_code_mapping); i++) { + if (hw_region_code_mapping[i].code == region_code) { + LEAVE(); + return hw_region_code_mapping[i].region; + } + } + LEAVE(); + return NULL; +} + +t_u8 +woal_is_valid_alpha2(char *alpha2) +{ + if (!alpha2 || strlen(alpha2) < 2) + return MFALSE; + if (isalpha(alpha2[0]) && isalpha(alpha2[1])) + return MTRUE; + return MFALSE; +} + +/** + * @brief Copy mc address to the mlist + * + * @param mlist A pointer to mlan_multicast_list structure + * @param mac mc address + * + * @return N/A + */ +static inline void +woal_copy_mc_addr(mlan_multicast_list *mlist, mlan_802_11_mac_addr mac) +{ + int i = 0; + for (i = 0; i < mlist->num_multicast_addr; i++) { + if (!memcmp(&mlist->mac_list[i], mac, ETH_ALEN)) + return; + } + if (mlist->num_multicast_addr < MLAN_MAX_MULTICAST_LIST_SIZE) + memcpy(&mlist->mac_list[mlist->num_multicast_addr], mac, + ETH_ALEN); + mlist->num_multicast_addr++; + return; +} + +/** + * @brief Copy multicast table + * + * @param mlist A pointer to mlan_multicast_list structure + * @param dev A pointer to net_device structure + * + * @return Number of multicast addresses + */ +static inline int +woal_copy_mcast_addr(mlan_multicast_list *mlist, struct net_device *dev) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) + struct dev_mc_list *mcptr = dev->mc_list; + int i = 0; +#else + struct netdev_hw_addr *mcptr = NULL; +#endif /* < 2.6.35 */ + + ENTER(); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) + for (i = 0; i < dev->mc_count && mcptr; i++) { + woal_copy_mc_addr(mlist, mcptr->dmi_addr); + mcptr = mcptr->next; + } +#else + netdev_for_each_mc_addr(mcptr, dev) + woal_copy_mc_addr(mlist, mcptr->addr); +#endif /* < 2.6.35 */ + LEAVE(); + return mlist->num_multicast_addr; +} + +/** + * @brief copy mc list from all the active interface + * + * @param handle A pointer to moal_handle + * @param mlist A pointer to multicast list + * + * @return total_mc_count + */ +static int +woal_copy_all_mc_list(moal_handle *handle, mlan_multicast_list *mlist) +{ + int i; + moal_private *priv = NULL; +#ifdef STA_SUPPORT + int mc_count = 0; +#endif + ENTER(); + for (i = 0; i < handle->priv_num && (priv = handle->priv[i]); i++) { +#ifdef STA_SUPPORT + if (GET_BSS_ROLE(handle->priv[i]) == MLAN_BSS_ROLE_STA) { + if (handle->priv[i]->media_connected == MTRUE) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) + mc_count = priv->netdev->mc_count; +#else + mc_count = netdev_mc_count(priv->netdev); +#endif + if (mc_count) + woal_copy_mcast_addr(mlist, + priv->netdev); + } + } +#endif + } + PRINTM(MIOCTL, "total mc_count=%d\n", mlist->num_multicast_addr); + LEAVE(); + return mlist->num_multicast_addr; +} + +/** + * @brief Fill in wait queue + * + * @param priv A pointer to moal_private structure + * @param wait A pointer to wait_queue structure + * @param wait_option Wait option + * + * @return N/A + */ +static inline void +woal_fill_wait_queue(moal_private *priv, wait_queue *wait, t_u8 wait_option) +{ + ENTER(); + wait->start_time = jiffies; + wait->condition = MFALSE; + wait->wait_timeout = MFALSE; + switch (wait_option) { + case MOAL_NO_WAIT: + break; + case MOAL_IOCTL_WAIT: + init_waitqueue_head(&wait->wait); + break; + case MOAL_IOCTL_WAIT_TIMEOUT: + init_waitqueue_head(&wait->wait); + wait->wait_timeout = MTRUE; + break; + } + LEAVE(); + return; +} + +/** + * @brief Wait mlan ioctl complete + * + * @param priv A pointer to moal_private structure + * @param req A pointer to mlan_ioctl_req structure + * @param wait_option Wait option + * + * @return N/A + */ +static inline mlan_status +woal_wait_ioctl_complete(moal_private *priv, mlan_ioctl_req *req, + t_u8 wait_option) +{ + mlan_status status; + wait_queue *wait = (wait_queue *)req->reserved_1; + unsigned long flags; + + ENTER(); + + priv->phandle->ioctl_timeout = MFALSE; + + switch (wait_option) { + case MOAL_NO_WAIT: + break; + case MOAL_IOCTL_WAIT: + while (wait_event_interruptible_exclusive + (wait->wait, wait->condition) + == -ERESTARTSYS && wait->retry < MAX_RETRY_CNT) { + wait->retry++; + } + break; + case MOAL_IOCTL_WAIT_TIMEOUT: + wait_event_timeout(wait->wait, wait->condition, + MOAL_IOCTL_TIMEOUT); + break; + } + spin_lock_irqsave(&priv->phandle->driver_lock, flags); + if (wait->condition == MFALSE) { + if (wait_option == MOAL_IOCTL_WAIT_TIMEOUT) { + priv->phandle->ioctl_timeout = MTRUE; + PRINTM(MMSG, + "wlan: IOCTL timeout %p id=0x%x, sub_id=0x%x, wait_option=%d, action=%d\n", + req, req->req_id, (*(t_u32 *)req->pbuf), + wait_option, (int)req->action); + } else { + PRINTM(MMSG, + "wlan: IOCTL by signal %p id=0x%x, sub_id=0x%x, wait_option=%d, action=%d\n", + req, req->req_id, (*(t_u32 *)req->pbuf), + wait_option, (int)req->action); + } + req->reserved_1 = 0; + status = MLAN_STATUS_PENDING; + } else { + status = wait->status; + } + spin_unlock_irqrestore(&priv->phandle->driver_lock, flags); + + LEAVE(); + return status; +} + +/** + * @brief CAC period block cmd handler + * + * @param priv A pointer to moal_private structure + * @param req A pointer to mlan_ioctl_req buffer + * + * @return MTRUE/MFALSE + */ +static inline t_bool +woal_cac_period_block_cmd(moal_private *priv, pmlan_ioctl_req req) +{ + mlan_status ret = MFALSE; + t_u32 sub_command; + + ENTER(); + if (req == NULL || req->pbuf == NULL) + goto done; + + sub_command = *(t_u32 *)req->pbuf; + + switch (req->req_id) { + case MLAN_IOCTL_SCAN: + if (sub_command == MLAN_OID_SCAN_NORMAL || + sub_command == MLAN_OID_SCAN_SPECIFIC_SSID || + sub_command == MLAN_OID_SCAN_USER_CONFIG) + ret = MTRUE; + break; + case MLAN_IOCTL_BSS: + if (sub_command == MLAN_OID_BSS_STOP || + sub_command == MLAN_OID_BSS_CHANNEL + /* sub_command == MLAN_OID_BSS_ROLE */ ) + ret = MTRUE; +#ifdef UAP_SUPPORT + else if (sub_command == MLAN_OID_UAP_BSS_CONFIG) { + mlan_ds_bss *bss = (mlan_ds_bss *)req->pbuf; + if (bss->param.bss_config.channel) + ret = MTRUE; + else + ret = MFALSE; + } +#endif + break; + case MLAN_IOCTL_RADIO_CFG: + if (sub_command == MLAN_OID_BAND_CFG) + ret = MTRUE; + break; + case MLAN_IOCTL_SNMP_MIB: + if (sub_command == MLAN_OID_SNMP_MIB_DOT11D) + ret = MTRUE; +#if defined(UAP_SUPPORT) + if (sub_command == MLAN_OID_SNMP_MIB_DOT11H) + ret = MTRUE; +#endif + break; + case MLAN_IOCTL_11D_CFG: +#ifdef STA_SUPPORT + if (sub_command == MLAN_OID_11D_CFG_ENABLE) + ret = MTRUE; +#endif + if (sub_command == MLAN_OID_11D_DOMAIN_INFO) + ret = MTRUE; + break; + case MLAN_IOCTL_MISC_CFG: + if (sub_command == MLAN_OID_MISC_REGION) + ret = MTRUE; + if (sub_command == MLAN_OID_MISC_HOST_CMD) { + phostcmd_header phostcmd; + t_u8 *ptlv_buf; + t_u16 tag, length; + + phostcmd = + (phostcmd_header)((pmlan_ds_misc_cfg)req-> + pbuf)->param.hostcmd.cmd; + ptlv_buf = (t_u8 *)phostcmd + sizeof(hostcmd_header); + if (phostcmd->action == MLAN_ACT_SET) { + while (ptlv_buf < + (t_u8 *)phostcmd + phostcmd->size) { + tag = *(t_u16 *)ptlv_buf; + length = *(t_u16 *)(ptlv_buf + 2); + /* Check Blocking TLV here, should add more... */ + if (tag == MRVL_CHANNELCONFIG_TLV_ID) { + ret = MTRUE; + break; + } + ptlv_buf += + (length + MRVL_TLV_HEADER_SIZE); + } + } + } + break; + case MLAN_IOCTL_11H_CFG: + /* Prevent execute more than once */ + if (sub_command == MLAN_OID_11H_CHANNEL_CHECK) + ret = MTRUE; + break; + default: + ret = MFALSE; + break; + } + +done: + LEAVE(); + return ret; +} + +/******************************************************** + Global Functions +********************************************************/ + +/** + * @brief Send ioctl request to MLAN + * + * @param priv A pointer to moal_private structure + * @param req A pointer to mlan_ioctl_req buffer + * @param wait_option Wait option (MOAL_IOCTL_WAIT or MOAL_NO_WAIT) + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING + * -- success, otherwise fail + */ +mlan_status +woal_request_ioctl(moal_private *priv, mlan_ioctl_req *req, t_u8 wait_option) +{ + wait_queue *wait = NULL; + mlan_status status; + unsigned long flags; + t_u32 sub_command = *(t_u32 *)req->pbuf; + + ENTER(); + + if (!priv) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + if (sub_command != MLAN_OID_GET_DEBUG_INFO) { + if (priv->phandle->surprise_removed == MTRUE || + priv->phandle->driver_state) { + PRINTM(MERROR, + "IOCTL is not allowed while the device is not present or hang\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } +#if defined(SDIO_SUSPEND_RESUME) + if (priv->phandle->is_suspended == MTRUE) { + PRINTM(MERROR, + "IOCTL is not allowed while suspended\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } +#endif + } + /* For MLAN_OID_MISC_HOST_CMD, action is 0, "action set" is checked later */ + if ((req->action == MLAN_ACT_SET || req->action == 0) && + priv->phandle->cac_period == MTRUE) { + + /* CAC checking period left to complete jiffies */ + long cac_left_jiffies; + + /* cac_left_jiffies will be negative if and only if + * event MLAN_EVENT_ID_DRV_MEAS_REPORT recieved from FW + * after CAC measure period ends, + * usually this could be considered as a FW bug + */ + cac_left_jiffies = priv->phandle->cac_timer_jiffies - + (jiffies - priv->phandle->meas_start_jiffies); +#ifdef DFS_TESTING_SUPPORT + if (priv->phandle->cac_period_jiffies) { + cac_left_jiffies = priv->phandle->cac_period_jiffies - + (jiffies - priv->phandle->meas_start_jiffies); + } +#endif + if (cac_left_jiffies < 0) { + /* Avoid driver hang in FW died during CAC measure period */ + priv->phandle->cac_period = MFALSE; + PRINTM(MERROR, + "CAC measure period spends longer than scheduled time " + "or meas done event never received\n"); + status = MLAN_STATUS_FAILURE; +#ifdef UAP_SUPPORT +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + if (priv->uap_host_based && dfs_offload) + woal_cfg80211_dfs_vendor_event(priv, + event_dfs_cac_aborted, + &priv->chan); +#endif +#endif +#endif + + goto done; + } + + /* Check BSS START first */ + if (sub_command == MLAN_OID_BSS_START) { + mlan_ds_bss *bss; + bss = (mlan_ds_bss *)req->pbuf; + /* + * Bss delay start after channel report received, + * not block the driver by delay executing. This is + * because a BSS_START cmd is always executed right + * after channel check issued. + */ + if (priv->phandle->delay_bss_start == MFALSE) { + PRINTM(MMSG, + "Received BSS Start command during CAC period, delay executing %ld seconds\n", + cac_left_jiffies / HZ); + priv->phandle->delay_bss_start = MTRUE; + memcpy(&priv->phandle->delay_ssid_bssid, + &bss->param.ssid_bssid, + sizeof(mlan_ssid_bssid)); + /* TODO: return success to allow the half below of routines + * of which calling BSS start to execute + */ + status = MLAN_STATUS_SUCCESS; + goto done; + } else { + /* TODO: not blocking it, just return failure */ + PRINTM(MMSG, + "Only one BSS Start command allowed for delay executing!\n"); + status = MLAN_STATUS_FAILURE; + goto done; + } + } + if (woal_cac_period_block_cmd(priv, req)) { + priv->phandle->meas_wait_q_woken = MFALSE; + PRINTM(MMSG, + "CAC check is on going... Blocking Command %ld seconds\n", + cac_left_jiffies / HZ); + /* blocking timeout set to 1.5 * CAC checking period left time */ + wait_event_interruptible_timeout(priv->phandle-> + meas_wait_q, + priv->phandle-> + meas_wait_q_woken, + cac_left_jiffies * 3 / + 2); + } + } +#ifdef UAP_CFG80211 +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) + else if (priv->phandle->is_cac_timer_set && + (req->action == MLAN_ACT_SET || req->action == 0)) { + if (woal_cac_period_block_cmd(priv, req)) { + PRINTM(MMSG, + "CAC check is on going... Blocking Command\n"); + status = MLAN_STATUS_FAILURE; + goto done; + } + } +#endif +#endif + else if (priv->phandle->cac_period) { + PRINTM(MINFO, "Operation during CAC check period.\n"); + } + wait = (wait_queue *)req->reserved_1; + req->bss_index = priv->bss_index; + if (wait_option) + woal_fill_wait_queue(priv, wait, wait_option); + else + req->reserved_1 = 0; + + /* Call MLAN ioctl handle */ + atomic_inc(&priv->phandle->ioctl_pending); + spin_lock_irqsave(&priv->phandle->ioctl_lock, flags); + status = mlan_ioctl(priv->phandle->pmlan_adapter, req); + spin_unlock_irqrestore(&priv->phandle->ioctl_lock, flags); + switch (status) { + case MLAN_STATUS_PENDING: + if (wait_option == MOAL_NO_WAIT) + PRINTM(MIOCTL, "IOCTL MOAL_NO_WAIT: %p\n", req); + else + PRINTM(MIOCTL, + "IOCTL pending: %p id=0x%x, sub_id=0x%x wait_option=%d, action=%d\n", + req, req->req_id, (*(t_u32 *)req->pbuf), + wait_option, (int)req->action); + /* Status pending, wake up main process */ + queue_work(priv->phandle->workqueue, &priv->phandle->main_work); + + /* Wait for completion */ + if (wait_option) + status = woal_wait_ioctl_complete(priv, req, + wait_option); + break; + case MLAN_STATUS_SUCCESS: + case MLAN_STATUS_FAILURE: + case MLAN_STATUS_RESOURCE: + PRINTM(MIOCTL, + "IOCTL: %p id=0x%x, sub_id=0x%x wait_option=%d, action=%d status=%d\n", + req, req->req_id, (*(t_u32 *)req->pbuf), wait_option, + (int)req->action, status); + atomic_dec(&priv->phandle->ioctl_pending); + break; + default: + atomic_dec(&priv->phandle->ioctl_pending); + break; + } + +done: + LEAVE(); + return status; +} + +/** + * @brief Send set MAC address request to MLAN + * + * @param priv A pointer to moal_private structure + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING + * -- success, otherwise fail + */ +mlan_status +woal_request_set_mac_address(moal_private *priv) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_bss *bss = NULL; + mlan_status status; + ENTER(); + + /* Allocate an IOCTL request buffer */ + req = (mlan_ioctl_req *)woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + status = MLAN_STATUS_FAILURE; + goto done; + } + + /* Fill request buffer */ + bss = (mlan_ds_bss *)req->pbuf; + bss->sub_command = MLAN_OID_BSS_MAC_ADDR; + memcpy(&bss->param.mac_addr, priv->current_addr, + sizeof(mlan_802_11_mac_addr)); + req->req_id = MLAN_IOCTL_BSS; + req->action = MLAN_ACT_SET; + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status == MLAN_STATUS_SUCCESS) { + memcpy(priv->netdev->dev_addr, priv->current_addr, ETH_ALEN); + HEXDUMP("priv->MacAddr:", priv->current_addr, ETH_ALEN); + } else { + PRINTM(MERROR, + "set mac address failed! status=%d, error_code=0x%x\n", + status, req->status_code); + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return status; +} + +/** + * @brief Send multicast list request to MLAN + * + * @param priv A pointer to moal_private structure + * @param dev A pointer to net_device structure + * + * @return N/A + */ +void +woal_request_set_multicast_list(moal_private *priv, struct net_device *dev) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_bss *bss = NULL; + mlan_status status; + int mc_count = 0; + + ENTER(); + + /* Allocate an IOCTL request buffer */ + req = (mlan_ioctl_req *)woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + PRINTM(MERROR, "%s:Fail to allocate ioctl req buffer\n", + __func__); + goto done; + } + + /* Fill request buffer */ + bss = (mlan_ds_bss *)req->pbuf; + bss->sub_command = MLAN_OID_BSS_MULTICAST_LIST; + req->req_id = MLAN_IOCTL_BSS; + req->action = MLAN_ACT_SET; + if (dev->flags & IFF_PROMISC) { + bss->param.multicast_list.mode = MLAN_PROMISC_MODE; + } else if (dev->flags & IFF_ALLMULTI) { + bss->param.multicast_list.mode = MLAN_ALL_MULTI_MODE; + } else { + bss->param.multicast_list.mode = MLAN_MULTICAST_MODE; + mc_count = + woal_copy_all_mc_list(priv->phandle, + &bss->param.multicast_list); + if (mc_count > MLAN_MAX_MULTICAST_LIST_SIZE) + bss->param.multicast_list.mode = MLAN_ALL_MULTI_MODE; + } + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, MOAL_NO_WAIT); + if (status != MLAN_STATUS_PENDING) + kfree(req); +done: + LEAVE(); + return; +} + +/** + * @brief Send deauth command to MLAN + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * @param mac MAC address to deauthenticate + * @param reason_code reason code to deauthenticate + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail + */ +mlan_status +woal_disconnect(moal_private *priv, t_u8 wait_option, t_u8 *mac, + t_u16 reason_code) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_bss *bss = NULL; + mlan_status status; + + ENTER(); + + /* Allocate an IOCTL request buffer */ + req = (mlan_ioctl_req *)woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + status = MLAN_STATUS_FAILURE; + goto done; + } + + /* Fill request buffer */ + bss = (mlan_ds_bss *)req->pbuf; + bss->sub_command = MLAN_OID_BSS_STOP; + if (mac) + memcpy(&bss->param.deauth_param.mac_addr, mac, + sizeof(mlan_802_11_mac_addr)); + bss->param.deauth_param.reason_code = reason_code; + req->req_id = MLAN_IOCTL_BSS; + req->action = MLAN_ACT_SET; + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, wait_option); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); +#ifdef REASSOCIATION + priv->reassoc_required = MFALSE; +#endif /* REASSOCIATION */ + LEAVE(); + return status; +} + +/** + * @brief Send bss_start command to MLAN + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * @param ssid_bssid A point to mlan_ssid_bssid structure + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail + */ +mlan_status +woal_bss_start(moal_private *priv, t_u8 wait_option, + mlan_ssid_bssid *ssid_bssid) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_bss *bss = NULL; + mlan_status status; + + ENTER(); + + /* Stop the O.S. TX queue When we are roaming */ + woal_stop_queue(priv->netdev); + if (priv->media_connected == MFALSE) { + if (netif_carrier_ok(priv->netdev)) + netif_carrier_off(priv->netdev); + } + + /* Allocate an IOCTL request buffer */ + req = (mlan_ioctl_req *)woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + status = MLAN_STATUS_FAILURE; + goto done; + } + + /* Fill request buffer */ + bss = (mlan_ds_bss *)req->pbuf; + bss->sub_command = MLAN_OID_BSS_START; + if (ssid_bssid) + memcpy(&bss->param.ssid_bssid, ssid_bssid, + sizeof(mlan_ssid_bssid)); + req->req_id = MLAN_IOCTL_BSS; + req->action = MLAN_ACT_SET; + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, wait_option); +#ifdef STA_CFG80211 +#ifdef STA_SUPPORT + priv->assoc_status = req->status_code; +#endif +#endif +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return status; +} + +/** + * @brief Get BSS info + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * @param bss_info A pointer to mlan_bss_info structure + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail + */ +mlan_status +woal_get_bss_info(moal_private *priv, t_u8 wait_option, mlan_bss_info *bss_info) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_get_info *info = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + ENTER(); + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_get_info)); + if (req == NULL) { + PRINTM(MERROR, + "Fail to allocate the buffer for get bss_info\n"); + status = MLAN_STATUS_FAILURE; + goto done; + } + + /* Fill request buffer */ + info = (mlan_ds_get_info *)req->pbuf; + info->sub_command = MLAN_OID_GET_BSS_INFO; + req->req_id = MLAN_IOCTL_GET_INFO; + req->action = MLAN_ACT_GET; + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, wait_option); + if (status == MLAN_STATUS_SUCCESS) { + if (bss_info) + memcpy(bss_info, &info->param.bss_info, + sizeof(mlan_bss_info)); + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return status; +} + +/** + * @brief Set/Get generic IE + * + * @param priv A pointer to moal_private structure + * @param action Action set or get + * @param ie Information element + * @param ie_len Length of the IE + * @param wait_option wait option + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status +woal_set_get_gen_ie(moal_private *priv, t_u32 action, + t_u8 *ie, int *ie_len, t_u8 wait_option) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_misc_cfg *misc = NULL; + mlan_ioctl_req *req = NULL; + + ENTER(); + + if ((action == MLAN_ACT_GET) && (ie == NULL || ie_len == NULL)) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + if (action == MLAN_ACT_SET && *ie_len > MAX_IE_SIZE) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + misc = (mlan_ds_misc_cfg *)req->pbuf; + misc->sub_command = MLAN_OID_MISC_GEN_IE; + req->req_id = MLAN_IOCTL_MISC_CFG; + req->action = action; + misc->param.gen_ie.type = MLAN_IE_TYPE_GEN_IE; + + if (action == MLAN_ACT_SET) { + misc->param.gen_ie.len = *ie_len; + if (*ie_len) + memcpy(misc->param.gen_ie.ie_data, ie, *ie_len); + } + + ret = woal_request_ioctl(priv, req, wait_option); + if (ret != MLAN_STATUS_SUCCESS) + goto done; + + if (action == MLAN_ACT_GET) { + *ie_len = misc->param.gen_ie.len; + if (*ie_len) + memcpy(ie, misc->param.gen_ie.ie_data, *ie_len); + } + +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +#ifdef STA_SUPPORT +/** + * @brief Set/Get retry count + * + * @param priv A pointer to moal_private structure + * @param action Action set or get + * @param wait_option Wait option + * @param value Retry value + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail + */ +mlan_status +woal_set_get_retry(moal_private *priv, t_u32 action, + t_u8 wait_option, int *value) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_snmp_mib *mib = NULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + ENTER(); + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_snmp_mib)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* Fill request buffer */ + mib = (mlan_ds_snmp_mib *)req->pbuf; + mib->sub_command = MLAN_OID_SNMP_MIB_RETRY_COUNT; + req->req_id = MLAN_IOCTL_SNMP_MIB; + req->action = action; + + if (action == MLAN_ACT_SET) { + if (*value < MLAN_TX_RETRY_MIN || *value > MLAN_TX_RETRY_MAX) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + mib->param.retry_count = *value; + } + + /* Send IOCTL request to MLAN */ + ret = woal_request_ioctl(priv, req, wait_option); + if (ret == MLAN_STATUS_SUCCESS && action == MLAN_ACT_GET) + *value = mib->param.retry_count; +#ifdef STA_CFG80211 + /* If set is invoked from other than iw i.e iwconfig, + * wiphy retry count should be updated as well */ + if (IS_STA_CFG80211(cfg80211_wext) && priv->wdev && priv->wdev->wiphy && + (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) && + (action == MLAN_ACT_SET)) { + priv->wdev->wiphy->retry_long = (t_u8)*value; + priv->wdev->wiphy->retry_short = (t_u8)*value; + } +#endif + +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get RTS threshold + * + * @param priv A pointer to moal_private structure + * @param action Action set or get + * @param wait_option Wait option + * @param value RTS threshold value + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail + */ +mlan_status +woal_set_get_rts(moal_private *priv, t_u32 action, t_u8 wait_option, int *value) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_snmp_mib *mib = NULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + ENTER(); + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_snmp_mib)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* Fill request buffer */ + mib = (mlan_ds_snmp_mib *)req->pbuf; + mib->sub_command = MLAN_OID_SNMP_MIB_RTS_THRESHOLD; + req->req_id = MLAN_IOCTL_SNMP_MIB; + req->action = action; + + if (action == MLAN_ACT_SET) { + if (*value < MLAN_RTS_MIN_VALUE || *value > MLAN_RTS_MAX_VALUE) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + mib->param.rts_threshold = *value; + } + + /* Send IOCTL request to MLAN */ + ret = woal_request_ioctl(priv, req, wait_option); + if (ret == MLAN_STATUS_SUCCESS && action == MLAN_ACT_GET) + *value = mib->param.rts_threshold; +#ifdef STA_CFG80211 + /* If set is invoked from other than iw i.e iwconfig, + * wiphy RTS threshold should be updated as well */ + if (IS_STA_CFG80211(cfg80211_wext) && priv->wdev && priv->wdev->wiphy && + (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) && + (action == MLAN_ACT_SET)) + priv->wdev->wiphy->rts_threshold = *value; +#endif + +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get Fragment threshold + * + * @param priv A pointer to moal_private structure + * @param action Action set or get + * @param wait_option Wait option + * @param value Fragment threshold value + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail + */ +mlan_status +woal_set_get_frag(moal_private *priv, t_u32 action, + t_u8 wait_option, int *value) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_snmp_mib *mib = NULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + ENTER(); + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_snmp_mib)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* Fill request buffer */ + mib = (mlan_ds_snmp_mib *)req->pbuf; + mib->sub_command = MLAN_OID_SNMP_MIB_FRAG_THRESHOLD; + req->req_id = MLAN_IOCTL_SNMP_MIB; + req->action = action; + + if (action == MLAN_ACT_SET) { + if (*value < MLAN_FRAG_MIN_VALUE || + *value > MLAN_FRAG_MAX_VALUE) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + mib->param.frag_threshold = *value; + } + + /* Send IOCTL request to MLAN */ + ret = woal_request_ioctl(priv, req, wait_option); + if (ret == MLAN_STATUS_SUCCESS && action == MLAN_ACT_GET) + *value = mib->param.frag_threshold; +#ifdef STA_CFG80211 + /* If set is invoked from other than iw i.e iwconfig, + * wiphy fragment threshold should be updated as well */ + if (IS_STA_CFG80211(cfg80211_wext) && priv->wdev && priv->wdev->wiphy && + (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) && + (action == MLAN_ACT_SET)) + priv->wdev->wiphy->frag_threshold = *value; +#endif + +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get TX power + * + * @param priv A pointer to moal_private structure + * @param action Action set or get + * @param power_cfg A pinter to mlan_power_cfg_t structure + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail + */ +mlan_status +woal_set_get_tx_power(moal_private *priv, + t_u32 action, mlan_power_cfg_t *power_cfg) +{ + mlan_ds_power_cfg *pcfg = NULL; + mlan_ioctl_req *req = NULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_power_cfg)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + pcfg = (mlan_ds_power_cfg *)req->pbuf; + pcfg->sub_command = MLAN_OID_POWER_CFG; + req->req_id = MLAN_IOCTL_POWER_CFG; + req->action = action; + if (action == MLAN_ACT_SET && power_cfg) + memcpy(&pcfg->param.power_cfg, power_cfg, + sizeof(mlan_power_cfg_t)); + + ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (ret != MLAN_STATUS_SUCCESS) + goto done; + + if (ret == MLAN_STATUS_SUCCESS && power_cfg) + memcpy(power_cfg, &pcfg->param.power_cfg, + sizeof(mlan_power_cfg_t)); + +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get IEEE power management + * + * @param priv A pointer to moal_private structure + * @param action Action set or get + * @param disabled A pointer to disabled flag + * @param power_type IEEE power type + * @param wait_option wait option + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status +woal_set_get_power_mgmt(moal_private *priv, + t_u32 action, int *disabled, int power_type, + t_u8 wait_option) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ioctl_req *req = NULL; + mlan_ds_pm_cfg *pm_cfg = NULL; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + pm_cfg = (mlan_ds_pm_cfg *)req->pbuf; + pm_cfg->sub_command = MLAN_OID_PM_CFG_IEEE_PS; + req->req_id = MLAN_IOCTL_PM_CFG; + req->action = action; + + if (action == MLAN_ACT_SET) { + PRINTM(MINFO, "PS_MODE set power disabled=%d power type=%#x\n", + *disabled, power_type); + if (*disabled) + pm_cfg->param.ps_mode = 0; + else { + /* Check not support case only (vwrq->disabled == FALSE) */ + if ((power_type & MW_POWER_TYPE) == MW_POWER_TIMEOUT) { + PRINTM(MERROR, + "Setting power timeout is not supported\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } else if ((power_type & MW_POWER_TYPE) == + MW_POWER_PERIOD) { + PRINTM(MERROR, + "Setting power period is not supported\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + pm_cfg->param.ps_mode = 1; + } + } + + ret = woal_request_ioctl(priv, req, wait_option); + if (ret != MLAN_STATUS_SUCCESS) + goto done; + + if (ret == MLAN_STATUS_SUCCESS && action == MLAN_ACT_GET) + *disabled = pm_cfg->param.ps_mode; + +#ifdef STA_CFG80211 + /* If set is invoked from other than iw i.e iwconfig, + * wiphy IEEE power save mode should be updated */ + if (IS_STA_CFG80211(cfg80211_wext) && priv->wdev && + (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) && + (action == MLAN_ACT_SET)) { + if (*disabled) + priv->wdev->ps = MFALSE; + else + priv->wdev->ps = MTRUE; + } +#endif + +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set region code + * + * @param priv A pointer to moal_private structure + * @param region A pointer to region string + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING --success, otherwise fail + */ +mlan_status +woal_set_region_code(moal_private *priv, char *region) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_misc_cfg *cfg = NULL; + mlan_ioctl_req *req = NULL; + + ENTER(); + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + cfg = (mlan_ds_misc_cfg *)req->pbuf; + cfg->sub_command = MLAN_OID_MISC_REGION; + req->req_id = MLAN_IOCTL_MISC_CFG; + req->action = MLAN_ACT_SET; + cfg->param.region_code = region_string_2_region_code(region); + ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get data rate + * + * @param priv A pointer to moal_private structure + * @param action Action set or get + * @param datarate A pointer to mlan_rate_cfg_t structure + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status +woal_set_get_data_rate(moal_private *priv, + t_u8 action, mlan_rate_cfg_t *datarate) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_rate *rate = NULL; + mlan_ioctl_req *req = NULL; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_rate)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + rate = (mlan_ds_rate *)req->pbuf; + rate->param.rate_cfg.rate_type = MLAN_RATE_VALUE; + rate->sub_command = MLAN_OID_RATE_CFG; + req->req_id = MLAN_IOCTL_RATE; + req->action = action; + + if (datarate && (action == MLAN_ACT_SET)) + memcpy(&rate->param.rate_cfg, datarate, + sizeof(mlan_rate_cfg_t)); + + ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (ret == MLAN_STATUS_SUCCESS && datarate && action == MLAN_ACT_GET) + memcpy(datarate, &rate->param.rate_cfg, + sizeof(mlan_rate_cfg_t)); + +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Get assoc_resp buffer + * + * @param priv A pointer to moal_private structure + * @param assoc_rsp A pointer to mlan_ds_misc_assoc_rsp structure + * @param wait_option wait option + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status +woal_get_assoc_rsp(moal_private *priv, mlan_ds_misc_assoc_rsp *assoc_rsp, + t_u8 wait_option) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_misc_cfg *misc = NULL; + mlan_ioctl_req *req = NULL; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + PRINTM(MERROR, "Fail to allocate buffer for get assoc resp\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + req->req_id = MLAN_IOCTL_MISC_CFG; + misc = (pmlan_ds_misc_cfg)req->pbuf; + misc->sub_command = MLAN_OID_MISC_ASSOC_RSP; + req->action = MLAN_ACT_GET; + + ret = woal_request_ioctl(priv, req, wait_option); + if (ret == MLAN_STATUS_SUCCESS && assoc_rsp) + memcpy(assoc_rsp, &misc->param.assoc_resp, + sizeof(mlan_ds_misc_assoc_rsp)); + +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} +#endif + +/** + * @brief Send get FW info request to MLAN + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * @param fw_info FW information + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail + */ +mlan_status +woal_request_get_fw_info(moal_private *priv, t_u8 wait_option, + mlan_fw_info *fw_info) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_get_info *info; + mlan_status status; + ENTER(); + memset(priv->current_addr, 0xff, ETH_ALEN); + + /* Allocate an IOCTL request buffer */ + req = (mlan_ioctl_req *) + woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_get_info)); + if (req == NULL) { + status = MLAN_STATUS_FAILURE; + goto done; + } + + /* Fill request buffer */ + info = (mlan_ds_get_info *)req->pbuf; + req->req_id = MLAN_IOCTL_GET_INFO; + req->action = MLAN_ACT_GET; + info->sub_command = MLAN_OID_GET_FW_INFO; + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, wait_option); + if (status == MLAN_STATUS_SUCCESS) { + priv->phandle->fw_release_number = info->param.fw_info.fw_ver; + priv->phandle->fw_ecsa_enable = info->param.fw_info.ecsa_enable; + priv->phandle->fw_getlog_enable = + info->param.fw_info.getlog_enable; + priv->phandle->fw_roaming_support = + info->param.fw_info.fw_roaming_support; + if (priv->current_addr[0] == 0xff) + memcpy(priv->current_addr, + &info->param.fw_info.mac_addr, + sizeof(mlan_802_11_mac_addr)); + memcpy(priv->netdev->dev_addr, priv->current_addr, ETH_ALEN); + if (fw_info) + memcpy(fw_info, &info->param.fw_info, + sizeof(mlan_fw_info)); + DBG_HEXDUMP(MCMD_D, "mac", priv->current_addr, 6); + } else + PRINTM(MERROR, + "get fw info failed! status=%d, error_code=0x%x\n", + status, req->status_code); +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return status; +} + +#ifdef STA_SUPPORT +/** + * @brief Send get ext cap info request to MLAN + * + * @param priv A pointer to moal_private structure + * @param buf data buffer + * @param len data buffer length + * + * @return number of bytes of extended capability -- success, otherwise error + */ +int +woal_request_extcap(moal_private *priv, t_u8 *buf, t_u8 len) +{ + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *cfg = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + if (buf == NULL || len <= 0 || len < sizeof(ExtCap_t)) { + ret = -EINVAL; + goto out; + } + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto out; + } + cfg = (mlan_ds_misc_cfg *)req->pbuf; + cfg->sub_command = MLAN_OID_MISC_EXT_CAP_CFG; + req->req_id = MLAN_IOCTL_MISC_CFG; + req->action = MLAN_ACT_GET; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto out; + } + memset(buf, 0, len); + memcpy(buf, &cfg->param.ext_cap, sizeof(ExtCap_t)); + ret = sizeof(ExtCap_t); +out: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} +#endif + +#ifdef PROC_DEBUG +/** + * @brief Get debug info + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * @param debug_info A pointer to mlan_debug_info structure + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail + */ +mlan_status +woal_get_debug_info(moal_private *priv, t_u8 wait_option, + mlan_debug_info *debug_info) +{ + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_get_info *info = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + ENTER(); + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(t_u32) + + sizeof(mlan_debug_info)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + info = (mlan_ds_get_info *)req->pbuf; + info->sub_command = MLAN_OID_GET_DEBUG_INFO; + req->req_id = MLAN_IOCTL_GET_INFO; + req->action = MLAN_ACT_GET; + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, wait_option); + if (status == MLAN_STATUS_SUCCESS) { + if (debug_info) { + memcpy(debug_info, &info->param.debug_info, + sizeof(mlan_debug_info)); + } + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return status; +} +#endif /* PROC_DEBUG */ + +#if defined(STA_WEXT) || defined(UAP_WEXT) +/** + * @brief host command ioctl function + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * @return 0 --success, otherwise fail + */ +int +woal_host_command(moal_private *priv, struct iwreq *wrq) +{ + HostCmd_Header cmd_header; + int ret = 0; + mlan_ds_misc_cfg *misc = NULL; + mlan_ioctl_req *req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + /* Sanity check */ + if (wrq->u.data.pointer == NULL) { + PRINTM(MERROR, "hostcmd IOCTL corrupt data\n"); + ret = -EINVAL; + goto done; + } + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + misc = (mlan_ds_misc_cfg *)req->pbuf; + memset(&cmd_header, 0, sizeof(cmd_header)); + + /* get command header */ + if (copy_from_user + (&cmd_header, wrq->u.data.pointer, sizeof(HostCmd_Header))) { + PRINTM(MERROR, "copy from user failed: Host command header\n"); + ret = -EFAULT; + goto done; + } + misc->param.hostcmd.len = woal_le16_to_cpu(cmd_header.size); + + PRINTM(MINFO, "Host command len = %u\n", misc->param.hostcmd.len); + + if (!misc->param.hostcmd.len || + misc->param.hostcmd.len > MLAN_SIZE_OF_CMD_BUFFER) { + PRINTM(MERROR, "Invalid data buffer length\n"); + ret = -EINVAL; + goto done; + } + + /* get the whole command from user */ + if (copy_from_user + (misc->param.hostcmd.cmd, wrq->u.data.pointer, + woal_le16_to_cpu(cmd_header.size))) { + PRINTM(MERROR, "copy from user failed\n"); + ret = -EFAULT; + goto done; + } + misc->sub_command = MLAN_OID_MISC_HOST_CMD; + req->req_id = MLAN_IOCTL_MISC_CFG; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + if (copy_to_user + (wrq->u.data.pointer, (t_u8 *)misc->param.hostcmd.cmd, + misc->param.hostcmd.len)) { + ret = -EFAULT; + goto done; + } + wrq->u.data.length = misc->param.hostcmd.len; +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} +#endif + +#if defined(WIFI_DIRECT_SUPPORT) || defined(UAP_SUPPORT) +/** + * @brief host command ioctl function + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +/********* format of ifr_data *************/ +/* buf_len + Hostcmd_body */ +/* buf_len: 4 bytes */ +/* the length of the buf which */ +/* can be used to return data */ +/* to application */ +/* Hostcmd_body */ +/*******************************************/ +int +woal_hostcmd_ioctl(struct net_device *dev, struct ifreq *req) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + t_u32 buf_len = 0; + HostCmd_Header cmd_header; + int ret = 0; + mlan_ds_misc_cfg *misc = NULL; + mlan_ioctl_req *ioctl_req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "uap_hostcmd_ioctl() corrupt data\n"); + ret = -EFAULT; + goto done; + } + + if (copy_from_user(&buf_len, req->ifr_data, sizeof(buf_len))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + + memset(&cmd_header, 0, sizeof(cmd_header)); + + /* get command header */ + if (copy_from_user + (&cmd_header, req->ifr_data + sizeof(buf_len), + sizeof(HostCmd_Header))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + + PRINTM(MINFO, "Host command len = %d\n", + woal_le16_to_cpu(cmd_header.size)); + + if (woal_le16_to_cpu(cmd_header.size) > MLAN_SIZE_OF_CMD_BUFFER) { + ret = -EINVAL; + goto done; + } + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (ioctl_req == NULL) { + ret = -ENOMEM; + goto done; + } + misc = (mlan_ds_misc_cfg *)ioctl_req->pbuf; + + misc->param.hostcmd.len = woal_le16_to_cpu(cmd_header.size); + + /* get the whole command from user */ + if (copy_from_user + (misc->param.hostcmd.cmd, req->ifr_data + sizeof(buf_len), + misc->param.hostcmd.len)) { + PRINTM(MERROR, "copy from user failed\n"); + ret = -EFAULT; + goto done; + } + misc->sub_command = MLAN_OID_MISC_HOST_CMD; + ioctl_req->req_id = MLAN_IOCTL_MISC_CFG; + + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + if (misc->param.hostcmd.len > buf_len) { + PRINTM(MERROR, + "buf_len is too small, resp_len=%d, buf_len=%d\n", + (int)misc->param.hostcmd.len, (int)buf_len); + ret = -EFAULT; + goto done; + } + if (copy_to_user + (req->ifr_data + sizeof(buf_len), (t_u8 *)misc->param.hostcmd.cmd, + misc->param.hostcmd.len)) { + ret = -EFAULT; + goto done; + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + LEAVE(); + return ret; +} +#endif + +/** + * @brief CUSTOM_IE ioctl handler + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +int +woal_custom_ie_ioctl(struct net_device *dev, struct ifreq *req) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_misc_cfg *misc = NULL; + mlan_ds_misc_custom_ie *custom_ie = NULL; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + gfp_t flag; + + ENTER(); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "woal_custom_ie_ioctl() corrupt data\n"); + ret = -EFAULT; + goto done; + } + + flag = (in_atomic() || irqs_disabled())? GFP_ATOMIC : GFP_KERNEL; + custom_ie = kzalloc(sizeof(mlan_ds_misc_custom_ie), flag); + if (!custom_ie) { + ret = -ENOMEM; + goto done; + } + + if (copy_from_user + (custom_ie, req->ifr_data, sizeof(mlan_ds_misc_custom_ie))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (ioctl_req == NULL) { + ret = -ENOMEM; + goto done; + } + + misc = (mlan_ds_misc_cfg *)ioctl_req->pbuf; + misc->sub_command = MLAN_OID_MISC_CUSTOM_IE; + ioctl_req->req_id = MLAN_IOCTL_MISC_CFG; + if ((custom_ie->len == 0)||(custom_ie->len == + sizeof(custom_ie->ie_data_list[0]. + ie_index))) + ioctl_req->action = MLAN_ACT_GET; + else + ioctl_req->action = MLAN_ACT_SET; + + memcpy(&misc->param.cust_ie, custom_ie, sizeof(mlan_ds_misc_custom_ie)); + + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (ioctl_req->action == MLAN_ACT_GET) { + if (copy_to_user + (req->ifr_data, &misc->param.cust_ie, + sizeof(mlan_ds_misc_custom_ie))) { + PRINTM(MERROR, "Copy to user failed!\n"); + ret = -EFAULT; + goto done; + } + } else if (ioctl_req->status_code == MLAN_ERROR_IOCTL_FAIL) { + /* send a separate error code to indicate error from driver */ + ret = EFAULT; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + kfree(custom_ie); + LEAVE(); + return ret; +} + +/** + * @brief send raw data packet ioctl function + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +int +woal_send_host_packet(struct net_device *dev, struct ifreq *req) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + t_u32 packet_len = 0; + int ret = 0; + pmlan_buffer pmbuf = NULL; + mlan_status status; + IEEEtypes_ActionCategory_e *action_cat; + t_u8 *action; + struct tx_status_info *tx_info = NULL; + struct sk_buff *skb = NULL; + unsigned long flags; + struct ieee80211_mgmt *mgmt_frame; + + ENTER(); + + if (!priv || !priv->phandle) { + PRINTM(MERROR, "priv or handle is NULL\n"); + ret = -EFAULT; + goto done; + } + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "woal_send_host_packet() corrupt data\n"); + ret = -EFAULT; + goto done; + } + + if (copy_from_user(&packet_len, req->ifr_data, sizeof(packet_len))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } +#define PACKET_HEADER_LEN 8 +#define MV_ETH_FRAME_LEN 1514 + if (packet_len > MV_ETH_FRAME_LEN) { + PRINTM(MERROR, "Invalid packet length %d\n", packet_len); + ret = -EFAULT; + goto done; + } + pmbuf = woal_alloc_mlan_buffer(priv->phandle, + (int)(MLAN_MIN_DATA_HEADER_LEN + + (int)packet_len + + PACKET_HEADER_LEN)); + if (!pmbuf) { + PRINTM(MERROR, "Fail to allocate mlan_buffer\n"); + ret = -ENOMEM; + goto done; + } + pmbuf->data_offset = MLAN_MIN_DATA_HEADER_LEN; + + /* get whole packet and header */ + if (copy_from_user + (pmbuf->pbuf + pmbuf->data_offset, + req->ifr_data + sizeof(packet_len), + PACKET_HEADER_LEN + packet_len)) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + woal_free_mlan_buffer(priv->phandle, pmbuf); + goto done; + } + pmbuf->data_len = PACKET_HEADER_LEN + packet_len; + pmbuf->buf_type = MLAN_BUF_TYPE_RAW_DATA; + pmbuf->bss_index = priv->bss_index; + action_cat = + (IEEEtypes_ActionCategory_e *)&pmbuf->pbuf[pmbuf->data_offset + + MLAN_ACTION_FRAME_CATEGORY_OFFSET]; + action = &pmbuf->pbuf[pmbuf->data_offset + + MLAN_ACTION_FRAME_ACTION_OFFSET]; + + if (*action_cat == IEEE_MGMT_ACTION_CATEGORY_UNPROTECT_WNM && + *action == 0x1) { + pmbuf->flags |= MLAN_BUF_FLAG_TX_STATUS; + if (!priv->tx_seq_num) + priv->tx_seq_num++; + pmbuf->tx_seq_num = priv->tx_seq_num++; + tx_info = kzalloc(sizeof(struct tx_status_info), GFP_ATOMIC); + if (tx_info) { + skb = alloc_skb(pmbuf->data_len, GFP_ATOMIC); + if (skb) { + mgmt_frame = (struct ieee80211_mgmt *)skb->data; + //Copy DA,SA and BSSID feilds. + memcpy(mgmt_frame->da, + &pmbuf->pbuf[pmbuf->data_offset + 14], + 3 * MLAN_MAC_ADDR_LENGTH); + //Copy packet data starting from category feild + memcpy(&mgmt_frame->u.action.category, + action_cat, + packet_len - + MLAN_ACTION_FRAME_CATEGORY_OFFSET); + /* frame_ctrl+duration+DA+SA+BSSID+seq_ctrl = 24 Bytes */ + /*category+action+dt+status = 4Bytes */ + skb_put(skb, + 24 + 4 + packet_len - + MLAN_ACTION_FRAME_CATEGORY_OFFSET); + spin_lock_irqsave(&priv->tx_stat_lock, flags); + tx_info->tx_skb = skb; + tx_info->tx_seq_num = pmbuf->tx_seq_num; + tx_info->tx_cookie = 0; + INIT_LIST_HEAD(&tx_info->link); + list_add_tail(&tx_info->link, + &priv->tx_stat_queue); + spin_unlock_irqrestore(&priv->tx_stat_lock, + flags); + } else { + kfree(tx_info); + tx_info = NULL; + } + } + } + + status = mlan_send_packet(priv->phandle->pmlan_adapter, pmbuf); + switch (status) { + case MLAN_STATUS_PENDING: + atomic_inc(&priv->phandle->tx_pending); + queue_work(priv->phandle->workqueue, &priv->phandle->main_work); + break; + case MLAN_STATUS_SUCCESS: + woal_free_mlan_buffer(priv->phandle, pmbuf); + break; + case MLAN_STATUS_FAILURE: + default: + woal_free_mlan_buffer(priv->phandle, pmbuf); + ret = -EFAULT; + break; + } +done: + LEAVE(); + return ret; +} + +#if defined(UAP_WEXT) +/** + * @brief Set/Get CUSTOM_IE ioctl handler + * + * @param priv A pointer to moal_private structure + * @param mask Mask to set or clear from caller + * @param ie IE buffer to set for beacon + * @param ie_len Length of the IE + * + * @return 0 --success, otherwise fail + */ +int +woal_set_get_custom_ie(moal_private *priv, t_u16 mask, t_u8 *ie, int ie_len) +{ + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_misc_cfg *misc = NULL; + mlan_ds_misc_custom_ie *misc_ie = NULL; + int ret = 0; + custom_ie *pcust_bcn_ie = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (ioctl_req == NULL) { + LEAVE(); + return -ENOMEM; + } + + misc = (mlan_ds_misc_cfg *)ioctl_req->pbuf; + misc->sub_command = MLAN_OID_MISC_CUSTOM_IE; + ioctl_req->req_id = MLAN_IOCTL_MISC_CFG; + ioctl_req->action = MLAN_ACT_SET; + misc_ie = &misc->param.cust_ie; + + misc_ie->type = TLV_TYPE_MGMT_IE; + misc_ie->len = (sizeof(custom_ie) - MAX_IE_SIZE) + ie_len; + pcust_bcn_ie = misc_ie->ie_data_list; + pcust_bcn_ie->ie_index = 0xffff; + pcust_bcn_ie->mgmt_subtype_mask = mask; + pcust_bcn_ie->ie_length = ie_len; + memcpy(pcust_bcn_ie->ie_buffer, ie, ie_len); + + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) + ret = -EFAULT; + + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + LEAVE(); + return ret; +} +#endif /* defined(HOST_TXRX_MGMT_FRAME) && defined(UAP_WEXT) */ + +/** + * @brief TDLS configuration ioctl handler + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +int +woal_tdls_config_ioctl(struct net_device *dev, struct ifreq *req) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_misc_cfg *misc = NULL; + mlan_ds_misc_tdls_config *tdls_data = NULL; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + gfp_t flag; + + ENTER(); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "woal_tdls_config_ioctl() corrupt data\n"); + ret = -EFAULT; + goto done; + } + flag = (in_atomic() || irqs_disabled())? GFP_ATOMIC : GFP_KERNEL; + tdls_data = kzalloc(sizeof(mlan_ds_misc_tdls_config), flag); + if (!tdls_data) { + ret = -ENOMEM; + goto done; + } + + if (copy_from_user + (tdls_data, req->ifr_data, sizeof(mlan_ds_misc_tdls_config))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (ioctl_req == NULL) { + ret = -ENOMEM; + goto done; + } + + misc = (mlan_ds_misc_cfg *)ioctl_req->pbuf; + misc->sub_command = MLAN_OID_MISC_TDLS_CONFIG; + ioctl_req->req_id = MLAN_IOCTL_MISC_CFG; + if (tdls_data->tdls_action == WLAN_TDLS_DISCOVERY_REQ + || tdls_data->tdls_action == WLAN_TDLS_LINK_STATUS) + ioctl_req->action = MLAN_ACT_GET; + else + ioctl_req->action = MLAN_ACT_SET; + + memcpy(&misc->param.tdls_config, tdls_data, + sizeof(mlan_ds_misc_tdls_config)); + + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (tdls_data->tdls_action == WLAN_TDLS_DISCOVERY_REQ + || tdls_data->tdls_action == WLAN_TDLS_LINK_STATUS) { + if (copy_to_user(req->ifr_data, &misc->param.tdls_config, + sizeof(mlan_ds_misc_tdls_config))) { + PRINTM(MERROR, "Copy to user failed!\n"); + ret = -EFAULT; + goto done; + } + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + kfree(tdls_data); + LEAVE(); + return ret; +} + +/** + * @brief ioctl function get BSS type + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +int +woal_get_bss_type(struct net_device *dev, struct ifreq *req) +{ + int ret = 0; + moal_private *priv = (moal_private *)netdev_priv(dev); + int bss_type; + + ENTER(); + + bss_type = (int)priv->bss_type; + if (copy_to_user(req->ifr_data, &bss_type, sizeof(int))) { + PRINTM(MINFO, "Copy to user failed!\n"); + ret = -EFAULT; + } + + LEAVE(); + return ret; +} + +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) +/** + * @brief Swithces BSS role of interface + * + * @param priv A pointer to moal_private structure + * @param action Action: set or get + * @param wait_option Wait option (MOAL_IOCTL_WAIT or MOAL_NO_WAIT) + * @param bss_role A pointer to bss role + * + * @return 0 --success, otherwise fail + */ +mlan_status +woal_bss_role_cfg(moal_private *priv, t_u8 action, + t_u8 wait_option, t_u8 *bss_role) +{ + int ret = 0; + mlan_ds_bss *bss = NULL; + mlan_ioctl_req *req = NULL; + struct net_device *dev = priv->netdev; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + bss = (mlan_ds_bss *)req->pbuf; + bss->sub_command = MLAN_OID_BSS_ROLE; + req->req_id = MLAN_IOCTL_BSS; + req->action = action; + if (action == MLAN_ACT_SET) { + if (priv->bss_role == *bss_role) { + PRINTM(MWARN, "BSS is in desired role already\n"); + goto done; + } else { + bss->param.bss_role = *bss_role; + } + } + status = woal_request_ioctl(priv, req, wait_option); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (action == MLAN_ACT_GET) { + *bss_role = bss->param.bss_role; + } else { + /* Update moal_private */ + priv->bss_role = *bss_role; + if (priv->bss_type == MLAN_BSS_TYPE_UAP) + priv->bss_type = MLAN_BSS_TYPE_STA; + else if (priv->bss_type == MLAN_BSS_TYPE_STA) + priv->bss_type = MLAN_BSS_TYPE_UAP; + + if (*bss_role == MLAN_BSS_ROLE_UAP) { + /* Switch: STA -> uAP */ + /* Setup the OS Interface to our functions */ +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 29) + dev->do_ioctl = woal_uap_do_ioctl; + dev->set_multicast_list = woal_uap_set_multicast_list; +#else + dev->netdev_ops = &woal_uap_netdev_ops; +#endif +#ifdef UAP_WEXT + if (IS_UAP_WEXT(cfg80211_wext)) { +#if WIRELESS_EXT < 21 + dev->get_wireless_stats = + woal_get_uap_wireless_stats; +#endif + dev->wireless_handlers = + (struct iw_handler_def *) + &woal_uap_handler_def; + } +#endif /* UAP_WEXT */ + } else if (*bss_role == MLAN_BSS_ROLE_STA) { + /* Switch: uAP -> STA */ + /* Setup the OS Interface to our functions */ +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 29) + dev->do_ioctl = woal_do_ioctl; + dev->set_multicast_list = woal_set_multicast_list; +#else + dev->netdev_ops = &woal_netdev_ops; +#endif +#ifdef STA_WEXT + if (IS_STA_WEXT(cfg80211_wext)) { +#if WIRELESS_EXT < 21 + dev->get_wireless_stats = + woal_get_wireless_stats; +#endif + dev->wireless_handlers = + (struct iw_handler_def *) + &woal_handler_def; + } +#endif /* STA_WEXT */ + } + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +#if defined(STA_WEXT) || defined(UAP_WEXT) +/** + * @brief Set/Get BSS role + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +int +woal_set_get_bss_role(moal_private *priv, struct iwreq *wrq) +{ + int ret = 0; + int bss_role = 0; + t_u8 action = MLAN_ACT_GET; + + ENTER(); + + if (wrq->u.data.length) { + if (copy_from_user(&bss_role, wrq->u.data.pointer, sizeof(int))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + if ((bss_role != MLAN_BSS_ROLE_STA && + bss_role != MLAN_BSS_ROLE_UAP) +#if defined(WIFI_DIRECT_SUPPORT) + || (priv->bss_type != MLAN_BSS_TYPE_WIFIDIRECT) +#endif + ) { + PRINTM(MWARN, "Invalid BSS role\n"); + ret = -EINVAL; + goto done; + } + if (bss_role == GET_BSS_ROLE(priv)) { + PRINTM(MWARN, "Already BSS is in desired role\n"); + ret = -EINVAL; + goto done; + } + action = MLAN_ACT_SET; + /* Reset interface */ + woal_reset_intf(priv, MOAL_IOCTL_WAIT, MFALSE); + } + + if (MLAN_STATUS_SUCCESS != woal_bss_role_cfg(priv, + action, MOAL_IOCTL_WAIT, + (t_u8 *)&bss_role)) { + ret = -EFAULT; + goto done; + } + + if (!wrq->u.data.length) { + if (copy_to_user(wrq->u.data.pointer, &bss_role, sizeof(int))) { + ret = -EFAULT; + goto done; + } + wrq->u.data.length = 1; + } else { +#if defined(STA_CFG80211) || defined(UAP_CFG80211) + if (IS_STA_OR_UAP_CFG80211(cfg80211_wext)) + woal_clear_all_mgmt_ies(priv, MOAL_IOCTL_WAIT); +#endif + /* Initialize private structures */ + woal_init_priv(priv, MOAL_IOCTL_WAIT); + + /* Enable interfaces */ + netif_device_attach(priv->netdev); + woal_start_queue(priv->netdev); + } + +done: + LEAVE(); + return ret; +} +#endif /* STA_WEXT || UAP_WEXT */ +#endif /* STA_SUPPORT && UAP_SUPPORT */ + +#if defined(SDIO_SUSPEND_RESUME) +/** + * @brief Set auto arp resp + * + * @param handle A pointer to moal_handle structure + * @param enable enable/disable + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail + */ +static mlan_status +woal_set_auto_arp(moal_handle *handle, t_u8 enable) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + int i = 0; + moal_private *priv = NULL; + mlan_ds_misc_cfg *misc = NULL; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_ipaddr_cfg ipaddr_cfg; + + ENTER(); + + memset(&ipaddr_cfg, 0, sizeof(ipaddr_cfg)); + for (i = 0; i < handle->priv_num && (priv = handle->priv[i]); i++) { + if (priv && priv->ip_addr_type != IPADDR_TYPE_NONE) { + memcpy(ipaddr_cfg.ip_addr[ipaddr_cfg.ip_addr_num], + priv->ip_addr, IPADDR_LEN); + ipaddr_cfg.ip_addr_num++; + } + } + if (ipaddr_cfg.ip_addr_num == 0) { + PRINTM(MIOCTL, "No IP addr configured.\n"); + goto done; + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + PRINTM(MIOCTL, "IOCTL req allocated failed!\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + misc = (mlan_ds_misc_cfg *)req->pbuf; + misc->sub_command = MLAN_OID_MISC_IP_ADDR; + req->req_id = MLAN_IOCTL_MISC_CFG; + req->action = MLAN_ACT_SET; + memcpy(&misc->param.ipaddr_cfg, &ipaddr_cfg, sizeof(ipaddr_cfg)); + if (enable) { + misc->param.ipaddr_cfg.op_code = MLAN_IPADDR_OP_ARP_FILTER | + MLAN_IPADDR_OP_AUTO_ARP_RESP; + misc->param.ipaddr_cfg.ip_addr_type = IPADDR_TYPE_IPV4; + } else { + /** remove ip */ + misc->param.ipaddr_cfg.op_code = MLAN_IPADDR_OP_IP_REMOVE; + } + ret = woal_request_ioctl(woal_get_priv(handle, MLAN_BSS_ROLE_ANY), req, + MOAL_NO_WAIT); + if (ret != MLAN_STATUS_SUCCESS && ret != MLAN_STATUS_PENDING) + PRINTM(MIOCTL, "Set auto arp IOCTL failed!\n"); +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +#endif + +/** + * @brief Set/Get DTIM period + * + * @param priv A pointer to moal_private structure + * @param action Action set or get + * @param wait_option Wait option + * @param value DTIM period + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail + */ +mlan_status +woal_set_get_dtim_period(moal_private *priv, + t_u32 action, t_u8 wait_option, t_u8 *value) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_snmp_mib *mib = NULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_snmp_mib)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* Fill request buffer */ + mib = (mlan_ds_snmp_mib *)req->pbuf; + mib->sub_command = MLAN_OID_SNMP_MIB_DTIM_PERIOD; + req->req_id = MLAN_IOCTL_SNMP_MIB; + req->action = action; + + if (action == MLAN_ACT_SET) + mib->param.dtim_period = *value; + + /* Send IOCTL request to MLAN */ + ret = woal_request_ioctl(priv, req, wait_option); + if (ret == MLAN_STATUS_SUCCESS && action == MLAN_ACT_GET) + *value = (t_u8)mib->param.dtim_period; + +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Get Host Sleep parameters + * + * @param priv A pointer to moal_private structure + * @param action Action: set or get + * @param wait_option Wait option (MOAL_IOCTL_WAIT or MOAL_NO_WAIT) + * @param hscfg A pointer to mlan_ds_hs_cfg structure + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail + */ +mlan_status +woal_set_get_hs_params(moal_private *priv, t_u16 action, t_u8 wait_option, + mlan_ds_hs_cfg *hscfg) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_pm_cfg *pmcfg = NULL; + mlan_ioctl_req *req = NULL; + + ENTER(); + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + pmcfg = (mlan_ds_pm_cfg *)req->pbuf; + pmcfg->sub_command = MLAN_OID_PM_CFG_HS_CFG; + req->req_id = MLAN_IOCTL_PM_CFG; + req->action = action; + if (action == MLAN_ACT_SET) + memcpy(&pmcfg->param.hs_cfg, hscfg, sizeof(mlan_ds_hs_cfg)); + + /* Send IOCTL request to MLAN */ + ret = woal_request_ioctl(priv, req, wait_option); + if (ret == MLAN_STATUS_SUCCESS) { + if (hscfg && action == MLAN_ACT_GET) { + memcpy(hscfg, &pmcfg->param.hs_cfg, + sizeof(mlan_ds_hs_cfg)); + } + } +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Get wakeup reason + * + * @param priv A pointer to moal_private structure + * @param wakeup_reason A pointer to mlan_ds_hs_wakeup_reason structure + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail + */ +mlan_status +woal_get_wakeup_reason(moal_private *priv, + mlan_ds_hs_wakeup_reason *wakeup_reason) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_pm_cfg *pmcfg = NULL; + mlan_ioctl_req *req = NULL; + + ENTER(); + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + pmcfg = (mlan_ds_pm_cfg *)req->pbuf; + pmcfg->sub_command = MLAN_OID_PM_HS_WAKEUP_REASON; + req->req_id = MLAN_IOCTL_PM_CFG; + req->action = MLAN_ACT_GET; + + /* Send IOCTL request to MLAN */ + ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (ret == MLAN_STATUS_SUCCESS) { + wakeup_reason->hs_wakeup_reason = + pmcfg->param.wakeup_reason.hs_wakeup_reason; + } +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Cancel Host Sleep configuration + * + * @param priv A pointer to moal_private structure + * @param wait_option wait option + * + * @return MLAN_STATUS_SUCCESS, MLAN_STATUS_PENDING, + * or MLAN_STATUS_FAILURE + */ +mlan_status +woal_cancel_hs(moal_private *priv, t_u8 wait_option) +{ + moal_handle *handle = NULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_hs_cfg hscfg; + int i; + ENTER(); + + if (!priv) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + handle = priv->phandle; + /* Cancel Host Sleep */ + + hscfg.conditions = HOST_SLEEP_CFG_CANCEL; + hscfg.is_invoke_hostcmd = MTRUE; + ret = woal_set_get_hs_params(priv, MLAN_ACT_SET, wait_option, &hscfg); + + if (roamoffload_in_hs) { + /*Disable firmware roaming */ + woal_enable_fw_roaming(priv, 0); + } + if (handle->fw_roam_enable == ROAM_OFFLOAD_WITH_BSSID || + handle->fw_roam_enable == ROAM_OFFLOAD_WITH_SSID || + handle->fw_roam_enable == AUTO_RECONNECT) + woal_config_fw_roaming(priv, ROAM_OFFLOAD_RESUME_CFG, NULL); + if (handle->fw_roam_enable == AUTO_RECONNECT) + woal_set_clear_pmk(priv, MLAN_ACT_CLEAR); + +#if defined(SDIO_SUSPEND_RESUME) + if (priv->phandle->hs_auto_arp) { + PRINTM(MIOCTL, "Cancel Host Sleep... remove FW auto arp\n"); + /* remove auto arp from FW */ + woal_set_auto_arp(priv->phandle, MFALSE); + } +#endif + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 1, 0) + if (GTK_REKEY_OFFLOAD_SUSPEND == gtk_rekey_offload) { + PRINTM(MIOCTL, + "Cancel Host Sleep... clear gtk rekey offload of FW\n"); + for (i = 0; i < handle->priv_num; i++) { + if (handle->priv[i] && handle->priv[i]->gtk_data_ready) { + PRINTM(MCMND, "clear GTK in resume\n"); + woal_set_rekey_data(handle->priv[i], NULL, + MLAN_ACT_CLEAR); + } + } + } +#endif + + LEAVE(); + return ret; +} + +/** @brief This function config fw roaming parameters + * + * @param priv A Pointer to the moal_private structure + * @return MTRUE or MFALSE + */ +int +woal_set_fw_roaming_params(moal_private *priv) +{ + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + mlan_ioctl_req *req = NULL; + mlan_ds_sec_cfg *sec = NULL; + woal_roam_offload_cfg roam_offload_cfg; + t_u8 zero[MLAN_MAX_KEY_LENGTH] = { 0 }; + + /*Enable fw roaming */ + woal_config_fw_roaming(priv, ROAM_OFFLOAD_ENABLE, NULL); + /*Download fw roaming parameters */ + woal_config_fw_roaming(priv, ROAM_OFFLOAD_PARAM_CFG, + &priv->phandle->fw_roam_params); + + /*Download userset passphrase key and current connection's PMK */ + if (!priv->phandle->fw_roam_params.userset_passphrase) { + woal_set_clear_pmk(priv, MLAN_ACT_SET); + goto done; + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + req->req_id = MLAN_IOCTL_SEC_CFG; + sec = (mlan_ds_sec_cfg *)req->pbuf; + sec->sub_command = MLAN_OID_SEC_CFG_PASSPHRASE; + sec->multi_passphrase = 1; + req->action = MLAN_ACT_SET; + + /*Copy user set passphrase */ + memcpy((char *)sec->param.roam_passphrase, + (char *)priv->phandle->ssid_passphrase, + MAX_SEC_SSID_NUM * sizeof(mlan_ds_passphrase)); + roam_offload_cfg.userset_passphrase = + priv->phandle->fw_roam_params.userset_passphrase; + + if (memcmp(priv->pmk.pmk, zero, MLAN_MAX_KEY_LENGTH)) { + /*Download current connection PMK */ + if (priv->pmk_saved) { + woal_set_clear_pmk(priv, MLAN_ACT_SET); + priv->pmk_saved = false; + } + } + + /*Set userset to mlan adapter */ + woal_config_fw_roaming(priv, ROAM_OFFLOAD_ENABLE, &roam_offload_cfg); + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + if (ret) + goto done; +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** @brief This function enable/disable fw roaming + * + * @param priv A Pointer to the moal_private structure + * @param enable Enable/disable fw roaming + * @return MTRUE or MFALSE + */ +int +woal_enable_fw_roaming(moal_private *priv, int data) +{ + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_misc_cfg *misc = NULL; + mlan_ds_misc_roam_offload *roam = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + int ret = 0; + + ENTER(); + + if (!priv || !priv->phandle) { + PRINTM(MERROR, "priv or handle is null\n"); + ret = -EFAULT; + goto done; + } + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (ioctl_req == NULL) { + ret = -ENOMEM; + goto done; + } + + misc = (mlan_ds_misc_cfg *)ioctl_req->pbuf; + misc->sub_command = MLAN_OID_MISC_ROAM_OFFLOAD; + ioctl_req->req_id = MLAN_IOCTL_MISC_CFG; + + roam = (mlan_ds_misc_roam_offload *) & misc->param.roam_offload; + roam->aplist.ap_num = 0; + /* SET operation */ + ioctl_req->action = MLAN_ACT_SET; + roam->enable = data; + roam->config_mode = ROAM_OFFLOAD_ENABLE; + + if (roamoffload_in_hs && data) { + priv->phandle->fw_roam_enable = data; + goto done; + } + + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + priv->phandle->fw_roam_enable = data; + if (!data) { + memset((char *)&priv->phandle->fw_roam_params, 0, + sizeof(woal_roam_offload_cfg)); + memset((char *)&priv->phandle->ssid_passphrase, 0, + MAX_SEC_SSID_NUM * sizeof(mlan_ds_passphrase)); + } else if (priv->media_connected && priv->pmk_saved) { + woal_set_clear_pmk(priv, MLAN_ACT_SET); + priv->pmk_saved = false; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + + LEAVE(); + return ret; +} + +#if defined(SDIO_SUSPEND_RESUME) +/** @brief This function enables the host sleep + * + * @param priv A Pointer to the moal_private structure + * @return MTRUE or MFALSE + */ +int +woal_enable_hs(moal_private *priv) +{ + mlan_ds_hs_cfg hscfg; + moal_handle *handle = NULL; + int hs_actived = MFALSE; + int timeout = 0; + int i; +#ifdef SDIO_SUSPEND_RESUME + mlan_ds_ps_info pm_info; +#endif + + ENTER(); + + if (priv == NULL) { + PRINTM(MERROR, "Invalid priv\n"); + goto done; + } + handle = priv->phandle; + if (handle->hs_activated == MTRUE) { + PRINTM(MIOCTL, "HS Already actived\n"); + hs_actived = MTRUE; + goto done; + } + for (i = 0; i < MIN(handle->priv_num, MLAN_MAX_BSS_NUM); i++) { + if (handle->priv[i] && + (GET_BSS_ROLE(handle->priv[i]) == MLAN_BSS_ROLE_STA)) { + if (disconnect_on_suspend && + handle->priv[i]->media_connected == MTRUE) { + PRINTM(MIOCTL, "disconnect on suspend\n"); + woal_disconnect(handle->priv[i], MOAL_NO_WAIT, + NULL, DEF_DEAUTH_REASON_CODE); + } + } + if (handle->priv[i]) { + PRINTM(MIOCTL, "woal_delba_all on priv[%d]\n", i); + woal_delba_all(handle->priv[i], MOAL_IOCTL_WAIT); + } + } + +#if defined(STA_CFG80211) && defined(UAP_CFG80211) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + + if (priv->phandle->is_remain_timer_set) { + woal_cancel_timer(&priv->phandle->remain_timer); + woal_remain_timer_func(priv->phandle); + } + /* cancel pending remain on channel */ + if (priv->phandle->remain_on_channel) { + t_u8 channel_status; + moal_private *remain_priv = + priv->phandle->priv[priv->phandle->remain_bss_index]; + if (remain_priv) { + woal_cfg80211_remain_on_channel_cfg(remain_priv, + MOAL_NO_WAIT, MTRUE, + &channel_status, + NULL, 0, 0); + if (priv->phandle->cookie) { + cfg80211_remain_on_channel_expired( +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 6, 0) + remain_priv-> + netdev, +#else + remain_priv-> + wdev, +#endif + priv-> + phandle-> + cookie, + &priv-> + phandle-> + chan, +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 8, 0) + priv-> + phandle-> + channel_type, +#endif + GFP_ATOMIC); + priv->phandle->cookie = 0; + } + } + priv->phandle->remain_on_channel = MFALSE; + } +#endif +#endif + +#ifdef STA_SUPPORT + woal_reconfig_bgscan(priv->phandle); +#endif + + if (roamoffload_in_hs) { + woal_set_fw_roaming_params(priv); + } + if (handle->fw_roam_enable == ROAM_OFFLOAD_WITH_BSSID || + handle->fw_roam_enable == ROAM_OFFLOAD_WITH_SSID || + handle->fw_roam_enable == AUTO_RECONNECT) { + woal_config_fw_roaming(priv, ROAM_OFFLOAD_SUSPEND_CFG, NULL); + if (priv->phandle->fw_roam_enable == AUTO_RECONNECT) + woal_set_clear_pmk(priv, MLAN_ACT_SET); + } + + if (handle->hs_auto_arp) { + PRINTM(MIOCTL, "Host Sleep enabled... set FW auto arp\n"); + /* Set auto arp response configuration to Fw */ + woal_set_auto_arp(handle, MTRUE); + } +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 1, 0) + if (GTK_REKEY_OFFLOAD_SUSPEND == gtk_rekey_offload) { + PRINTM(MIOCTL, + "Host Sleep enabled... set gtk rekey offload to FW\n"); + for (i = 0; i < handle->priv_num; i++) { + if (handle->priv[i] && handle->priv[i]->gtk_data_ready) { + PRINTM(MCMND, "set GTK before suspend\n"); + woal_set_rekey_data(handle->priv[i], + &handle->priv[i]-> + gtk_rekey_data, + MLAN_ACT_SET); + } + } + } +#endif + + /* Enable Host Sleep */ + handle->hs_activate_wait_q_woken = MFALSE; + memset(&hscfg, 0, sizeof(mlan_ds_hs_cfg)); + hscfg.is_invoke_hostcmd = MTRUE; + if (woal_set_get_hs_params(priv, MLAN_ACT_SET, MOAL_NO_WAIT, &hscfg) == + MLAN_STATUS_FAILURE) { + PRINTM(MIOCTL, "IOCTL request HS enable failed\n"); + goto done; + } + timeout = wait_event_interruptible_timeout(handle->hs_activate_wait_q, + handle-> + hs_activate_wait_q_woken, + HS_ACTIVE_TIMEOUT); + sdio_claim_host(((struct sdio_mmc_card *)handle->card)->func); + if ((handle->hs_activated == MTRUE) || (handle->is_suspended == MTRUE)) { + PRINTM(MCMND, "suspend success! force=%u skip=%u\n", + handle->hs_force_count, handle->hs_skip_count); + hs_actived = MTRUE; + } +#ifdef SDIO_SUSPEND_RESUME + else { + handle->suspend_fail = MTRUE; + woal_get_pm_info(priv, &pm_info); + if (pm_info.is_suspend_allowed == MTRUE) { +#ifdef MMC_PM_FUNC_SUSPENDED + woal_wlan_is_suspended(priv->phandle); +#endif + handle->hs_force_count++; + PRINTM(MCMND, "suspend allowed! force=%u skip=%u\n", + handle->hs_force_count, handle->hs_skip_count); + hs_actived = MTRUE; + } + } +#endif /* SDIO_SUSPEND_RESUME */ + sdio_release_host(((struct sdio_mmc_card *)handle->card)->func); + if (hs_actived != MTRUE) { + handle->hs_skip_count++; +#ifdef SDIO_SUSPEND_RESUME + PRINTM(MCMND, + "suspend skipped! timeout=%d allow=%d force=%u skip=%u\n", + timeout, (int)pm_info.is_suspend_allowed, + handle->hs_force_count, handle->hs_skip_count); +#else + PRINTM(MCMND, "suspend skipped! timeout=%d skip=%u\n", + timeout, handle->hs_skip_count); +#endif + woal_cancel_hs(priv, MOAL_NO_WAIT); + } +done: + LEAVE(); + return hs_actived; +} +#endif + +#ifdef CONFIG_PROC_FS +/** + * @brief This function send soft_reset command to firmware + * + * @param handle A pointer to moal_handle structure + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING on success, + * otherwise failure code + */ +mlan_status +woal_request_soft_reset(moal_handle *handle) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *misc = NULL; + + ENTER(); + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req) { + misc = (mlan_ds_misc_cfg *)req->pbuf; + misc->sub_command = MLAN_OID_MISC_SOFT_RESET; + req->req_id = MLAN_IOCTL_MISC_CFG; + req->action = MLAN_ACT_SET; + ret = woal_request_ioctl(woal_get_priv + (handle, MLAN_BSS_ROLE_ANY), req, + MOAL_IOCTL_WAIT); + } + + handle->surprise_removed = MTRUE; + woal_sched_timeout(5); + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} +#endif /* CONFIG_PROC_FS */ + +/** + * @brief Set wapi enable + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * @param enable MTRUE or MFALSE + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail + */ +mlan_status +woal_set_wapi_enable(moal_private *priv, t_u8 wait_option, t_u32 enable) +{ + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_sec_cfg *sec = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + ENTER(); + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + sec = (mlan_ds_sec_cfg *)req->pbuf; + sec->sub_command = MLAN_OID_SEC_CFG_WAPI_ENABLED; + req->req_id = MLAN_IOCTL_SEC_CFG; + req->action = MLAN_ACT_SET; + sec->param.wapi_enabled = enable; + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, wait_option); +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return status; +} + +/** + * @brief Get version + * + * @param handle A pointer to moal_handle structure + * @param version A pointer to version buffer + * @param max_len max length of version buffer + * + * @return N/A + */ +void +woal_get_version(moal_handle *handle, char *version, int max_len) +{ + union { + t_u32 l; + t_u8 c[4]; + } ver; + char fw_ver[32]; + + ENTER(); + + ver.l = handle->fw_release_number; + snprintf(fw_ver, sizeof(fw_ver), "%u.%u.%u.p%u", + ver.c[2], ver.c[1], ver.c[0], ver.c[3]); + + snprintf(version, max_len, handle->driver_version, fw_ver); + + LEAVE(); +} + +#if defined(STA_WEXT) || defined(UAP_WEXT) +/** + * @brief Get Driver Version + * + * @param priv A pointer to moal_private structure + * @param req A pointer to ifreq structure + * + * @return 0 --success, otherwise fail + */ +int +woal_get_driver_version(moal_private *priv, struct ifreq *req) +{ + struct iwreq *wrq = (struct iwreq *)req; + int len; + char buf[MLAN_MAX_VER_STR_LEN]; + ENTER(); + + woal_get_version(priv->phandle, buf, sizeof(buf) - 1); + + len = strlen(buf); + if (wrq->u.data.pointer) { + if (copy_to_user(wrq->u.data.pointer, buf, len)) { + PRINTM(MERROR, "Copy to user failed\n"); + LEAVE(); + return -EFAULT; + } + wrq->u.data.length = len; + } + PRINTM(MINFO, "MOAL VERSION: %s\n", buf); + LEAVE(); + return 0; +} + +/** + * @brief Get extended driver version + * + * @param priv A pointer to moal_private structure + * @param ireq A pointer to ifreq structure + * + * @return 0 --success, otherwise fail + */ +int +woal_get_driver_verext(moal_private *priv, struct ifreq *ireq) +{ + struct iwreq *wrq = (struct iwreq *)ireq; + mlan_ds_get_info *info = NULL; + mlan_ioctl_req *req = NULL; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_get_info)); + if (req == NULL) { + LEAVE(); + return -ENOMEM; + } + + info = (mlan_ds_get_info *)req->pbuf; + info->sub_command = MLAN_OID_GET_VER_EXT; + req->req_id = MLAN_IOCTL_GET_INFO; + req->action = MLAN_ACT_GET; + + if (!wrq->u.data.flags) { + info->param.ver_ext.version_str_sel = + *((int *)(wrq->u.name + SUBCMD_OFFSET)); + } else { + if (copy_from_user + (&info->param.ver_ext.version_str_sel, wrq->u.data.pointer, + sizeof(info->param.ver_ext.version_str_sel))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } else { + if (((t_s32)(info->param.ver_ext.version_str_sel)) < 0) { + PRINTM(MERROR, "Invalid arguments!\n"); + ret = -EINVAL; + goto done; + } + } + } + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (wrq->u.data.pointer) { + if (copy_to_user + (wrq->u.data.pointer, info->param.ver_ext.version_str, + strlen(info->param.ver_ext.version_str))) { + PRINTM(MERROR, "Copy to user failed\n"); + ret = -EFAULT; + goto done; + } + wrq->u.data.length = strlen(info->param.ver_ext.version_str); + } + + PRINTM(MINFO, "MOAL EXTENDED VERSION: %s\n", + info->param.ver_ext.version_str); +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + + LEAVE(); + return ret; +} +#endif + +#ifdef DEBUG_LEVEL1 +/** + * @brief Set driver debug bit masks to mlan in order to enhance performance + * + * @param priv A pointer to moal_private structure + * @param drvdbg Driver debug level + * + * @return 0 --success, otherwise fail + */ +int +woal_set_drvdbg(moal_private *priv, t_u32 drvdbg) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *misc = NULL; + int ret = 0; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + LEAVE(); + return -ENOMEM; + } + + misc = (mlan_ds_misc_cfg *)req->pbuf; + misc->sub_command = MLAN_OID_MISC_DRVDBG; + req->req_id = MLAN_IOCTL_MISC_CFG; + req->action = MLAN_ACT_SET; + misc->param.drvdbg = drvdbg; + + ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + + if (ret != MLAN_STATUS_PENDING) + kfree(req); + + LEAVE(); + return ret; +} +#endif + +/** + * @brief Mgmt frame forward registration + * + * @param priv A pointer to moal_private structure + * @param action Action: set or get + * @param pmgmt_subtype_mask A Pointer to mgmt frame subtype mask + * @param wait_option wait option (MOAL_IOCTL_WAIT or MOAL_NO_WAIT) + * + * @return 0 --success, otherwise fail + */ +int +woal_reg_rx_mgmt_ind(moal_private *priv, t_u16 action, + t_u32 *pmgmt_subtype_mask, t_u8 wait_option) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *misc = NULL; + int ret = 0; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + LEAVE(); + return -ENOMEM; + } + + misc = (mlan_ds_misc_cfg *)req->pbuf; + misc->sub_command = MLAN_OID_MISC_RX_MGMT_IND; + req->req_id = MLAN_IOCTL_MISC_CFG; + req->action = action; + misc->param.mgmt_subtype_mask = *pmgmt_subtype_mask; + if (req->action == MLAN_ACT_SET) + memcpy(&misc->param.mgmt_subtype_mask, + pmgmt_subtype_mask, + sizeof(misc->param.mgmt_subtype_mask)); + + ret = woal_request_ioctl(priv, req, wait_option); + + if (req->action == MLAN_ACT_GET) + memcpy(pmgmt_subtype_mask, &misc->param.mgmt_subtype_mask, + sizeof(misc->param.mgmt_subtype_mask)); + + if (ret != MLAN_STATUS_PENDING) + kfree(req); + + LEAVE(); + return ret; +} + +/** + * @brief Set/Get Transmit beamforming capabilities + * + * @param priv A pointer to moal_private structure + * @param action Action: set or get + * @param tx_bf_cap A pointer to tx_buf_cap buffer + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status +woal_set_get_tx_bf_cap(moal_private *priv, t_u16 action, t_u32 *tx_bf_cap) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_11n_cfg *bf_cfg = NULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (!tx_bf_cap) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* Fill request buffer */ + bf_cfg = (mlan_ds_11n_cfg *)req->pbuf; + req->req_id = MLAN_IOCTL_11N_CFG; + bf_cfg->sub_command = MLAN_OID_11N_CFG_TX_BF_CAP; + req->action = action; + if (action == MLAN_ACT_SET) + bf_cfg->param.tx_bf_cap = *tx_bf_cap; + + /* Send IOCTL request to MLAN */ + ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (ret != MLAN_STATUS_SUCCESS) { + goto done; + } + + if (action == MLAN_ACT_GET) + *tx_bf_cap = bf_cfg->param.tx_bf_cap; + +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get Transmit beamforming configuration + * + * @param priv A pointer to moal_private structure + * @param action Action: set or get + * @param tx_bf_cfg A pointer to tx_bf_cfg structure + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status +woal_set_get_tx_bf_cfg(moal_private *priv, t_u16 action, + mlan_ds_11n_tx_bf_cfg *tx_bf_cfg) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ioctl_req *req = NULL; + mlan_ds_11n_cfg *bf_cfg = NULL; + + ENTER(); + + /* Sanity test */ + if (tx_bf_cfg == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* Fill request buffer */ + bf_cfg = (mlan_ds_11n_cfg *)req->pbuf; + req->req_id = MLAN_IOCTL_11N_CFG; + bf_cfg->sub_command = MLAN_OID_11N_CFG_TX_BF_CFG; + + req->action = action; + memcpy(&bf_cfg->param.tx_bf, tx_bf_cfg, sizeof(mlan_ds_11n_tx_bf_cfg)); + + /* Send IOCTL request to MLAN */ + ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (ret != MLAN_STATUS_SUCCESS) + goto done; + + if (action == MLAN_ACT_GET) + memcpy(tx_bf_cfg, &bf_cfg->param.tx_bf, + sizeof(mlan_ds_11n_tx_bf_cfg)); + +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Handle ioctl resp + * + * @param priv Pointer to moal_private structure + * @param req Pointer to mlan_ioctl_req structure + * + * @return N/A + */ +void +woal_process_ioctl_resp(moal_private *priv, mlan_ioctl_req *req) +{ + ENTER(); + + if (priv == NULL) { + LEAVE(); + return; + } + switch (req->req_id) { + case MLAN_IOCTL_GET_INFO: +#ifdef STA_WEXT +#ifdef STA_SUPPORT + if (IS_STA_WEXT(cfg80211_wext) && + GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) + woal_ioctl_get_info_resp(priv, + (mlan_ds_get_info *)req->pbuf); +#endif +#endif +#ifdef UAP_WEXT +#ifdef UAP_SUPPORT + if (IS_UAP_WEXT(cfg80211_wext) && + GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) + woal_ioctl_get_uap_info_resp(priv, + (mlan_ds_get_info *)req-> + pbuf); +#endif +#endif + break; +#ifdef STA_WEXT +#ifdef STA_SUPPORT + case MLAN_IOCTL_BSS: + if (IS_STA_WEXT(cfg80211_wext) && + GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) + woal_ioctl_get_bss_resp(priv, (mlan_ds_bss *)req->pbuf); + break; +#endif +#endif + case MLAN_IOCTL_MISC_CFG: + woal_ioctl_get_misc_conf(priv, (mlan_ds_misc_cfg *)req->pbuf); + default: + break; + } + + LEAVE(); + return; +} + +/** + * @brief Get PM info + * + * @param priv A pointer to moal_private structure + * @param pm_info A pointer to mlan_ds_ps_info structure + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail + */ +mlan_status +woal_get_pm_info(moal_private *priv, mlan_ds_ps_info *pm_info) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_pm_cfg *pmcfg = NULL; + mlan_ioctl_req *req = NULL; + + ENTER(); + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg)); + if (req == NULL) { + PRINTM(MERROR, "Fail to alloc mlan_ds_pm_cfg buffer\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* Fill request buffer */ + pmcfg = (mlan_ds_pm_cfg *)req->pbuf; + pmcfg->sub_command = MLAN_OID_PM_INFO; + req->req_id = MLAN_IOCTL_PM_CFG; + req->action = MLAN_ACT_GET; + + /* Send IOCTL request to MLAN */ + ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (ret == MLAN_STATUS_SUCCESS) { + if (pm_info) { + memcpy(pm_info, &pmcfg->param.ps_info, + sizeof(mlan_ds_ps_info)); + } + } +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Get Deep Sleep + * + * @param priv Pointer to the moal_private driver data struct + * @param data Pointer to return deep_sleep setting + * + * @return 0 --success, otherwise fail + */ +int +woal_get_deep_sleep(moal_private *priv, t_u32 *data) +{ + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_pm_cfg *pm = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg)); + if (req == NULL) { + LEAVE(); + return -ENOMEM; + } + pm = (mlan_ds_pm_cfg *)req->pbuf; + pm->sub_command = MLAN_OID_PM_CFG_DEEP_SLEEP; + req->req_id = MLAN_IOCTL_PM_CFG; + + req->action = MLAN_ACT_GET; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + *data = pm->param.auto_deep_sleep.auto_ds; + *(data + 1) = pm->param.auto_deep_sleep.idletime; + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set Deep Sleep + * + * @param priv Pointer to the moal_private driver data struct + * @param wait_option wait option + * @param bdeep_sleep TRUE--enalbe deepsleep, FALSE--disable deepsleep + * @param idletime Idle time for optimized PS API + * + * @return 0 --success, otherwise fail + */ +int +woal_set_deep_sleep(moal_private *priv, t_u8 wait_option, BOOLEAN bdeep_sleep, + t_u16 idletime) +{ + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_pm_cfg *pm = NULL; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg)); + if (req == NULL) { + LEAVE(); + return -ENOMEM; + } + pm = (mlan_ds_pm_cfg *)req->pbuf; + pm->sub_command = MLAN_OID_PM_CFG_DEEP_SLEEP; + req->req_id = MLAN_IOCTL_PM_CFG; + + req->action = MLAN_ACT_SET; + if (bdeep_sleep == MTRUE) { + PRINTM(MIOCTL, "Deep Sleep: sleep\n"); + pm->param.auto_deep_sleep.auto_ds = DEEP_SLEEP_ON; + if (idletime) + pm->param.auto_deep_sleep.idletime = idletime; + ret = woal_request_ioctl(priv, req, wait_option); + if (ret != MLAN_STATUS_SUCCESS && ret != MLAN_STATUS_PENDING) { + ret = -EFAULT; + goto done; + } + } else { + PRINTM(MIOCTL, "%lu : Deep Sleep: wakeup\n", jiffies); + pm->param.auto_deep_sleep.auto_ds = DEEP_SLEEP_OFF; + ret = woal_request_ioctl(priv, req, wait_option); + if (ret != MLAN_STATUS_SUCCESS && ret != MLAN_STATUS_PENDING) { + ret = -EFAULT; + goto done; + } + } +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Cancel CAC period block + * + * @param priv A pointer to moal_private structure + * + * @return N/A + */ +void +woal_cancel_cac_block(moal_private *priv) +{ + ENTER(); + /* if during CAC period, wake up wait queue */ + if (priv->phandle->cac_period == MTRUE) { + priv->phandle->cac_period = MFALSE; + /* Make sure Chan Report is cancelled */ + woal_11h_cancel_chan_report_ioctl(priv, MOAL_IOCTL_WAIT); + priv->phandle->meas_start_jiffies = 0; + if (priv->phandle->delay_bss_start == MTRUE) + priv->phandle->delay_bss_start = MFALSE; + if (priv->phandle->meas_wait_q_woken == MFALSE) { + priv->phandle->meas_wait_q_woken = MTRUE; + wake_up_interruptible(&priv->phandle->meas_wait_q); + } +#ifdef UAP_SUPPORT +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + if (priv->uap_host_based && dfs_offload) + woal_cfg80211_dfs_vendor_event(priv, + event_dfs_cac_aborted, + &priv->chan); +#endif +#endif +#endif + } + LEAVE(); +} + +/** MEAS report timeout value in seconds */ + +/** + * @brief Issue MLAN_OID_11H_CHANNEL_CHECK ioctl + * + * @param priv Pointer to the moal_private driver data struct + * @param wait_option wait option + * + * @return 0 --success, otherwise fail + */ +int +woal_11h_channel_check_ioctl(moal_private *priv, t_u8 wait_option) +{ + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_11h_cfg *ds_11hcfg = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (priv->skip_cac) { + LEAVE(); + return status; + } + + /* Skip sending request/report query when DFS_REPEATER_MODE is on. This + * would get rid of CAC timers before starting BSSes in DFS_REPEATER_MODE + */ + if (priv->phandle->dfs_repeater_mode) { + LEAVE(); + return status; + } + + if (woal_is_any_interface_active(priv->phandle)) { + /* When any other interface is active + * Get rid of CAC timer when drcs is disabled */ + t_u16 enable = 0; + ret = woal_mc_policy_cfg(priv, &enable, wait_option, + MLAN_ACT_GET); + if (!enable) { + LEAVE(); + return status; + } + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11h_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + ds_11hcfg = (mlan_ds_11h_cfg *)req->pbuf; + + ds_11hcfg->sub_command = MLAN_OID_11H_CHANNEL_CHECK; + ds_11hcfg->param.chan_rpt_req.host_based = MFALSE; + req->req_id = MLAN_IOCTL_11H_CFG; + req->action = MLAN_ACT_SET; + /* Send Channel Check command and wait until the report is ready */ + status = woal_request_ioctl(priv, req, wait_option); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + /* set flag from here */ + priv->phandle->cac_period = MTRUE; + priv->phandle->meas_start_jiffies = jiffies; + priv->phandle->cac_timer_jiffies = + ds_11hcfg->param.chan_rpt_req.millisec_dwell_time * HZ / 1000; +#ifdef UAP_SUPPORT +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + if (priv->uap_host_based && dfs_offload) + woal_cfg80211_dfs_vendor_event(priv, event_dfs_cac_started, + &priv->chan); +#endif +#endif +#endif +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Issue MLAN_OID_11H_CHAN_REPORT_REQUEST ioctl to cancel dozer + * + * @param priv Pointer to the moal_private driver data struct + * @param wait_option wait option + * + * @return 0 --success, otherwise fail + */ +int +woal_11h_cancel_chan_report_ioctl(moal_private *priv, t_u8 wait_option) +{ + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_11h_cfg *ds_11hcfg = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11h_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + ds_11hcfg = (mlan_ds_11h_cfg *)req->pbuf; + + ds_11hcfg->sub_command = MLAN_OID_11H_CHAN_REPORT_REQUEST; + req->req_id = MLAN_IOCTL_11H_CFG; + req->action = MLAN_ACT_SET; + ds_11hcfg->param.chan_rpt_req.millisec_dwell_time = 0; + /* Send Channel Check command and wait until the report is ready */ + status = woal_request_ioctl(priv, req, wait_option); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief set remain channel + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * @param pchan A pointer to mlan_ds_remain_chan structure + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status +woal_set_remain_channel_ioctl(moal_private *priv, t_u8 wait_option, + mlan_ds_remain_chan *pchan) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ioctl_req *req = NULL; + mlan_ds_radio_cfg *radio_cfg = NULL; + + ENTER(); + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_radio_cfg)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + radio_cfg = (mlan_ds_radio_cfg *)req->pbuf; + radio_cfg->sub_command = MLAN_OID_REMAIN_CHAN_CFG; + req->req_id = MLAN_IOCTL_RADIO_CFG; + + req->action = MLAN_ACT_SET; + memcpy(&radio_cfg->param.remain_chan, pchan, + sizeof(mlan_ds_remain_chan)); + ret = woal_request_ioctl(priv, req, wait_option); + if (ret == MLAN_STATUS_SUCCESS) { + memcpy(pchan, &radio_cfg->param.remain_chan, + sizeof(mlan_ds_remain_chan)); + } +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +#if defined(WIFI_DIRECT_SUPPORT) +/** + * @brief set/get wifi direct mode + * + * @param priv A pointer to moal_private structure + * @param action set or get + * @param mode A pointer to wifi direct mode + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status +woal_wifi_direct_mode_cfg(moal_private *priv, t_u16 action, t_u16 *mode) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ioctl_req *req = NULL; + mlan_ds_bss *bss = NULL; + + ENTER(); + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + bss = (mlan_ds_bss *)req->pbuf; + bss->sub_command = MLAN_OID_WIFI_DIRECT_MODE; + req->req_id = MLAN_IOCTL_BSS; + + req->action = action; + if (action == MLAN_ACT_SET) + bss->param.wfd_mode = *mode; + ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (ret == MLAN_STATUS_SUCCESS) { + *mode = bss->param.wfd_mode; + PRINTM(MIOCTL, "ACT=%d, wifi_direct_mode=%d\n", action, *mode); + } +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set p2p config + * + * @param priv A pointer to moal_private structure + * @param action Action set or get + * @param p2p_config A pointer to mlan_ds_wifi_direct_config structure + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status +woal_p2p_config(moal_private *priv, t_u32 action, + mlan_ds_wifi_direct_config *p2p_config) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *misc_cfg = NULL; + + ENTER(); + if (!p2p_config) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + misc_cfg = (mlan_ds_misc_cfg *)req->pbuf; + misc_cfg->sub_command = MLAN_OID_MISC_WIFI_DIRECT_CONFIG; + req->req_id = MLAN_IOCTL_MISC_CFG; + req->action = action; + memcpy(&misc_cfg->param.p2p_config, p2p_config, + sizeof(mlan_ds_wifi_direct_config)); + ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (ret == MLAN_STATUS_SUCCESS) { + if (action == MLAN_ACT_GET) + memcpy(p2p_config, &misc_cfg->param.p2p_config, + sizeof(mlan_ds_wifi_direct_config)); + } +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} +#endif /* WIFI_DIRECT_SUPPORT */ + +#ifdef STA_SUPPORT +/** + * @brief Get STA Channel Info + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * @param channel A pointer to channel info + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail + */ +mlan_status +woal_get_sta_channel(moal_private *priv, t_u8 wait_option, + chan_band_info * channel) +{ + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_bss *bss = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + ENTER(); + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + PRINTM(MERROR, "woal_get_sta_channel req alloc fail\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* Fill request buffer */ + bss = (mlan_ds_bss *)req->pbuf; + bss->sub_command = MLAN_OID_BSS_CHAN_INFO; + req->req_id = MLAN_IOCTL_BSS; + req->action = MLAN_ACT_GET; + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, wait_option); + if (status == MLAN_STATUS_SUCCESS && channel) + memcpy(channel, &(bss->param.sta_channel), sizeof(*channel)); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return status; +} + +/** + * @brief Get RSSI info + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * @param signal A pointer tp mlan_ds_get_signal structure + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail + */ +mlan_status +woal_get_signal_info(moal_private *priv, t_u8 wait_option, + mlan_ds_get_signal *signal) +{ + int ret = 0; + mlan_ds_get_info *info = NULL; + mlan_ioctl_req *req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + ENTER(); + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_get_info)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + info = (mlan_ds_get_info *)req->pbuf; + info->sub_command = MLAN_OID_GET_SIGNAL; + info->param.signal.selector = ALL_RSSI_INFO_MASK; + req->req_id = MLAN_IOCTL_GET_INFO; + req->action = MLAN_ACT_GET; + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, wait_option); + if (status == MLAN_STATUS_SUCCESS) { + if (signal) + memcpy(signal, &info->param.signal, + sizeof(mlan_ds_get_signal)); +#ifdef STA_WEXT + if (IS_STA_WEXT(cfg80211_wext)) { + if (info->param.signal.selector & BCN_RSSI_AVG_MASK) + priv->w_stats.qual.level = + info->param.signal.bcn_rssi_avg; + if (info->param.signal.selector & BCN_NF_AVG_MASK) + priv->w_stats.qual.noise = + info->param.signal.bcn_nf_avg; + } +#endif + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return status; +} + +/** + * @brief Get scan table + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * @param scan_resp A pointer to mlan_scan_resp structure + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail + */ +mlan_status +woal_get_scan_table(moal_private *priv, t_u8 wait_option, + mlan_scan_resp *scan_resp) +{ + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_scan *scan = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + ENTER(); + + if (!scan_resp) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_scan)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + scan = (mlan_ds_scan *)req->pbuf; + scan->sub_command = MLAN_OID_SCAN_NORMAL; + req->req_id = MLAN_IOCTL_SCAN; + req->action = MLAN_ACT_GET; + memcpy((void *)&scan->param.scan_resp, (void *)scan_resp, + sizeof(mlan_scan_resp)); + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, wait_option); + if (status == MLAN_STATUS_SUCCESS) { + if (scan_resp) { + memcpy(scan_resp, &scan->param.scan_resp, + sizeof(mlan_scan_resp)); + } + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return status; +} + +/** + * @brief Request a scan + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * @param req_ssid A pointer to mlan_802_11_ssid structure + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status +woal_request_scan(moal_private *priv, + t_u8 wait_option, mlan_802_11_ssid *req_ssid) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + moal_handle *handle = priv->phandle; + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_scan *scan = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + ENTER(); + + if (MOAL_ACQ_SEMAPHORE_BLOCK(&handle->async_sem)) { + PRINTM(MERROR, "Acquire semaphore error, request_scan\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + handle->scan_pending_on_block = MTRUE; + handle->scan_priv = priv; + + /* Allocate an IOCTL request buffer */ + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_scan)); + if (ioctl_req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + scan = (mlan_ds_scan *)ioctl_req->pbuf; + + if (req_ssid && req_ssid->ssid_len != 0) { + /* Specific SSID scan */ + ioctl_req->req_id = MLAN_IOCTL_SCAN; + ioctl_req->action = MLAN_ACT_SET; + + scan->sub_command = MLAN_OID_SCAN_SPECIFIC_SSID; + + memcpy(scan->param.scan_req.scan_ssid.ssid, + req_ssid->ssid, + MIN(MLAN_MAX_SSID_LENGTH, req_ssid->ssid_len)); + scan->param.scan_req.scan_ssid.ssid_len = + MIN(MLAN_MAX_SSID_LENGTH, req_ssid->ssid_len); + } else { + /* Normal scan */ + ioctl_req->req_id = MLAN_IOCTL_SCAN; + ioctl_req->action = MLAN_ACT_SET; + + scan->sub_command = MLAN_OID_SCAN_NORMAL; + } + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, ioctl_req, wait_option); + + if (status == MLAN_STATUS_FAILURE) { + ret = MLAN_STATUS_FAILURE; + goto done; + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + + if (ret == MLAN_STATUS_FAILURE) { + handle->scan_pending_on_block = MFALSE; + handle->scan_priv = NULL; + MOAL_REL_SEMAPHORE(&handle->async_sem); + } + LEAVE(); + return ret; +} + +/** + * @brief Change Adhoc Channel + * + * @param priv A pointer to moal_private structure + * @param channel The channel to be set. + * @param wait_option wait_option + * + * @return MLAN_STATUS_SUCCESS--success, MLAN_STATUS_FAILURE--fail + */ +mlan_status +woal_change_adhoc_chan(moal_private *priv, int channel, t_u8 wait_option) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_bss_info bss_info; + mlan_ds_bss *bss = NULL; + mlan_ioctl_req *req = NULL; + + ENTER(); + + memset(&bss_info, 0, sizeof(bss_info)); + + /* Get BSS information */ + if (MLAN_STATUS_SUCCESS != + woal_get_bss_info(priv, wait_option, &bss_info)) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + if (bss_info.bss_mode == MLAN_BSS_MODE_INFRA) { + ret = MLAN_STATUS_SUCCESS; + goto done; + } + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* Get current channel */ + bss = (mlan_ds_bss *)req->pbuf; + bss->sub_command = MLAN_OID_IBSS_CHANNEL; + req->req_id = MLAN_IOCTL_BSS; + req->action = MLAN_ACT_GET; + + /* Send IOCTL request to MLAN */ + ret = woal_request_ioctl(priv, req, wait_option); + if (ret != MLAN_STATUS_SUCCESS) + goto done; + + if (bss->param.bss_chan.channel == (unsigned int)channel) { + ret = MLAN_STATUS_SUCCESS; + goto done; + } + PRINTM(MINFO, "Updating Channel from %d to %d\n", + (int)bss->param.bss_chan.channel, channel); + + if (bss_info.media_connected != MTRUE) { + ret = MLAN_STATUS_SUCCESS; + goto done; + } + + /* Do disonnect */ + bss->sub_command = MLAN_OID_BSS_STOP; + memset((t_u8 *)&bss->param.bssid, 0, ETH_ALEN); + + /* Send IOCTL request to MLAN */ + ret = woal_request_ioctl(priv, req, wait_option); + if (ret != MLAN_STATUS_SUCCESS) + goto done; + + /* Do specific SSID scanning */ + if (MLAN_STATUS_SUCCESS != + woal_request_scan(priv, wait_option, &bss_info.ssid)) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + /* Start/Join Adhoc network */ + bss->sub_command = MLAN_OID_BSS_START; + memset(&bss->param.ssid_bssid, 0, sizeof(mlan_ssid_bssid)); + memcpy(&bss->param.ssid_bssid.ssid, &bss_info.ssid, + sizeof(mlan_802_11_ssid)); + + /* Send IOCTL request to MLAN */ + ret = woal_request_ioctl(priv, req, wait_option); + +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Find the best network to associate + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * @param ssid_bssid A pointer to mlan_ssid_bssid structure + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail + */ +mlan_status +woal_find_best_network(moal_private *priv, t_u8 wait_option, + mlan_ssid_bssid *ssid_bssid) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_bss *bss = NULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u8 *mac = 0; + + ENTER(); + + if (!ssid_bssid) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* Fill request buffer */ + bss = (mlan_ds_bss *)req->pbuf; + req->req_id = MLAN_IOCTL_BSS; + req->action = MLAN_ACT_GET; + bss->sub_command = MLAN_OID_BSS_FIND_BSS; + + memcpy(&bss->param.ssid_bssid, ssid_bssid, sizeof(mlan_ssid_bssid)); + + /* Send IOCTL request to MLAN */ + ret = woal_request_ioctl(priv, req, wait_option); + if (ret == MLAN_STATUS_SUCCESS) { + memcpy(ssid_bssid, &bss->param.ssid_bssid, + sizeof(mlan_ssid_bssid)); + mac = (t_u8 *)&ssid_bssid->bssid; + PRINTM(MINFO, "Find network: ssid=%s, " MACSTR ", idx=%d\n", + ssid_bssid->ssid.ssid, MAC2STR(mac), + (int)ssid_bssid->idx); + } + +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Check if AP channel is valid for STA Region + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * @param ssid_bssid A pointer to mlan_ssid_bssid structure + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail + */ +mlan_status +woal_11d_check_ap_channel(moal_private *priv, t_u8 wait_option, + mlan_ssid_bssid *ssid_bssid) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_bss *bss = NULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u8 *mac = 0; + + ENTER(); + + if (!ssid_bssid) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* Fill request buffer */ + bss = (mlan_ds_bss *)req->pbuf; + req->req_id = MLAN_IOCTL_BSS; + req->action = MLAN_ACT_GET; + bss->sub_command = MLAN_OID_BSS_11D_CHECK_CHANNEL; + + memcpy(&bss->param.ssid_bssid, ssid_bssid, sizeof(mlan_ssid_bssid)); + + mac = (t_u8 *)&ssid_bssid->bssid; + PRINTM(MINFO, "ssid=%s, " MACSTR ", idx=%d\n", + ssid_bssid->ssid.ssid, MAC2STR(mac), (int)ssid_bssid->idx); + + /* Send IOCTL request to MLAN */ + ret = woal_request_ioctl(priv, req, wait_option); + +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Get authentication mode + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * @param auth_mode A pointer to authentication mode + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail + */ +mlan_status +woal_get_auth_mode(moal_private *priv, t_u8 wait_option, t_u32 *auth_mode) +{ + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_sec_cfg *sec = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + ENTER(); + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + sec = (mlan_ds_sec_cfg *)req->pbuf; + sec->sub_command = MLAN_OID_SEC_CFG_AUTH_MODE; + req->req_id = MLAN_IOCTL_SEC_CFG; + req->action = MLAN_ACT_GET; + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, wait_option); + if (status == MLAN_STATUS_SUCCESS && auth_mode) + *auth_mode = sec->param.auth_mode; +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return status; +} + +/** + * @brief Get encrypt mode + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * @param encrypt_mode A pointer to encrypt mode + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail + */ +mlan_status +woal_get_encrypt_mode(moal_private *priv, t_u8 wait_option, t_u32 *encrypt_mode) +{ + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_sec_cfg *sec = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + sec = (mlan_ds_sec_cfg *)req->pbuf; + sec->sub_command = MLAN_OID_SEC_CFG_ENCRYPT_MODE; + req->req_id = MLAN_IOCTL_SEC_CFG; + req->action = MLAN_ACT_GET; + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, wait_option); + if (status == MLAN_STATUS_SUCCESS && encrypt_mode) + *encrypt_mode = sec->param.encrypt_mode; +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return status; +} + +/** + * @brief Get WPA enable + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * @param enable A pointer to wpa enable status + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail + */ +mlan_status +woal_get_wpa_enable(moal_private *priv, t_u8 wait_option, t_u32 *enable) +{ + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_sec_cfg *sec = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + sec = (mlan_ds_sec_cfg *)req->pbuf; + sec->sub_command = MLAN_OID_SEC_CFG_WPA_ENABLED; + req->req_id = MLAN_IOCTL_SEC_CFG; + req->action = MLAN_ACT_GET; + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, wait_option); + if (status == MLAN_STATUS_SUCCESS && enable) + *enable = sec->param.wpa_enabled; +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return status; +} + +/** + * @brief Set authentication mode + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * @param auth_mode Authentication mode + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail + */ +mlan_status +woal_set_auth_mode(moal_private *priv, t_u8 wait_option, t_u32 auth_mode) +{ + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_sec_cfg *sec = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + ENTER(); + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + sec = (mlan_ds_sec_cfg *)req->pbuf; + sec->sub_command = MLAN_OID_SEC_CFG_AUTH_MODE; + req->req_id = MLAN_IOCTL_SEC_CFG; + req->action = MLAN_ACT_SET; + sec->param.auth_mode = auth_mode; + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, wait_option); +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return status; +} + +/** + * @brief Set encrypt mode + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * @param encrypt_mode Encryption mode + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail + */ +mlan_status +woal_set_encrypt_mode(moal_private *priv, t_u8 wait_option, t_u32 encrypt_mode) +{ + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_sec_cfg *sec = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + ENTER(); + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + sec = (mlan_ds_sec_cfg *)req->pbuf; + sec->sub_command = MLAN_OID_SEC_CFG_ENCRYPT_MODE; + req->req_id = MLAN_IOCTL_SEC_CFG; + req->action = MLAN_ACT_SET; + sec->param.encrypt_mode = encrypt_mode; + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, wait_option); +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return status; +} + +/** + * @brief Set wpa enable + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * @param enable MTRUE or MFALSE + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail + */ +mlan_status +woal_set_wpa_enable(moal_private *priv, t_u8 wait_option, t_u32 enable) +{ + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_sec_cfg *sec = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + ENTER(); + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + sec = (mlan_ds_sec_cfg *)req->pbuf; + sec->sub_command = MLAN_OID_SEC_CFG_WPA_ENABLED; + req->req_id = MLAN_IOCTL_SEC_CFG; + req->action = MLAN_ACT_SET; + sec->param.wpa_enabled = enable; + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, wait_option); +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return status; +} + +/** + * @brief enable wep key + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail + */ +mlan_status +woal_enable_wep_key(moal_private *priv, t_u8 wait_option) +{ + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_sec_cfg *sec = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + ENTER(); + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + sec = (mlan_ds_sec_cfg *)req->pbuf; + sec->sub_command = MLAN_OID_SEC_CFG_ENCRYPT_KEY; + req->req_id = MLAN_IOCTL_SEC_CFG; + req->action = MLAN_ACT_SET; + sec->param.encrypt_key.key_disable = MFALSE; + sec->param.encrypt_key.key_index = MLAN_KEY_INDEX_DEFAULT; + sec->param.encrypt_key.is_current_wep_key = MTRUE; + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, wait_option); +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return status; +} + +/** + * @brief Request user scan + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * @param scan_cfg A pointer to wlan_user_scan_config structure + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status +woal_request_userscan(moal_private *priv, + t_u8 wait_option, wlan_user_scan_cfg *scan_cfg) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_scan *scan = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + moal_handle *handle = priv->phandle; + ENTER(); + + if (MOAL_ACQ_SEMAPHORE_BLOCK(&handle->async_sem)) { + PRINTM(MERROR, "Acquire semaphore error, request_scan\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + handle->scan_pending_on_block = MTRUE; + handle->scan_priv = priv; + + /* Allocate an IOCTL request buffer */ + ioctl_req = + woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_scan) + + sizeof(wlan_user_scan_cfg)); + if (ioctl_req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + scan = (mlan_ds_scan *)ioctl_req->pbuf; + scan->sub_command = MLAN_OID_SCAN_USER_CONFIG; + ioctl_req->req_id = MLAN_IOCTL_SCAN; + ioctl_req->action = MLAN_ACT_SET; + memcpy(scan->param.user_scan.scan_cfg_buf, scan_cfg, + sizeof(wlan_user_scan_cfg)); + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, ioctl_req, wait_option); + if (status == MLAN_STATUS_FAILURE) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + + if (ret == MLAN_STATUS_FAILURE) { + handle->scan_pending_on_block = MFALSE; + handle->scan_priv = NULL; + MOAL_REL_SEMAPHORE(&handle->async_sem); + } + LEAVE(); + return ret; +} + +/** + * @brief woal_get_scan_config + * + * @param priv A pointer to moal_private structure + * @param scan_cfg A pointer to scan_cfg structure + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status +woal_get_scan_config(moal_private *priv, mlan_scan_cfg *scan_cfg) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_scan *scan = NULL; + mlan_ioctl_req *req = NULL; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_scan)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + scan = (mlan_ds_scan *)req->pbuf; + scan->sub_command = MLAN_OID_SCAN_CONFIG; + req->req_id = MLAN_IOCTL_SCAN; + req->action = MLAN_ACT_GET; + memset(&scan->param.scan_cfg, 0, sizeof(mlan_scan_cfg)); + ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (ret == MLAN_STATUS_SUCCESS && scan_cfg) + memcpy(scan_cfg, &scan->param.scan_cfg, sizeof(mlan_scan_cfg)); + +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief set scan time + * + * @param priv A pointer to moal_private structure + * @param active_scan_time Active scan time + * @param passive_scan_time Passive scan time + * @param specific_scan_time Specific scan time + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status +woal_set_scan_time(moal_private *priv, t_u16 active_scan_time, + t_u16 passive_scan_time, t_u16 specific_scan_time) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_scan *scan = NULL; + mlan_ioctl_req *req = NULL; + mlan_scan_cfg scan_cfg; + + ENTER(); + + memset(&scan_cfg, 0, sizeof(scan_cfg)); + if (MLAN_STATUS_SUCCESS != woal_get_scan_config(priv, &scan_cfg)) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_scan)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + scan = (mlan_ds_scan *)req->pbuf; + scan->sub_command = MLAN_OID_SCAN_CONFIG; + req->req_id = MLAN_IOCTL_SCAN; + req->action = MLAN_ACT_SET; + memset(&scan->param.scan_cfg, 0, sizeof(mlan_scan_cfg)); + scan_cfg.scan_time.active_scan_time = active_scan_time; + scan_cfg.scan_time.specific_scan_time = specific_scan_time; + scan_cfg.scan_time.passive_scan_time = passive_scan_time; + PRINTM(MIOCTL, "Set specific=%d, active=%d, passive=%d\n", + (int)active_scan_time, (int)passive_scan_time, + (int)specific_scan_time); + memcpy(&scan->param.scan_cfg, &scan_cfg, sizeof(mlan_scan_cfg)); + ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief request scan + * + * @param priv A pointer to moal_private structure + * @param scan_cfg A pointer to wlan_user_scan_cfg structure + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status +woal_do_scan(moal_private *priv, wlan_user_scan_cfg *scan_cfg) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + moal_handle *handle = priv->phandle; + + ENTER(); + if (handle->scan_pending_on_block == MTRUE) { + PRINTM(MINFO, "scan already in processing...\n"); + LEAVE(); + return ret; + } +#ifdef REASSOCIATION + if (MOAL_ACQ_SEMAPHORE_BLOCK(&handle->reassoc_sem)) { + PRINTM(MERROR, "Acquire semaphore error, woal_do_combo_scan\n"); + LEAVE(); + return -EBUSY; + } +#endif /* REASSOCIATION */ + priv->report_scan_result = MTRUE; + + if (!scan_cfg) + ret = woal_request_scan(priv, MOAL_NO_WAIT, NULL); + else + ret = woal_request_userscan(priv, MOAL_NO_WAIT, scan_cfg); + +#ifdef REASSOCIATION + MOAL_REL_SEMAPHORE(&handle->reassoc_sem); +#endif + LEAVE(); + return ret; +} + +/** + * @brief Cancel pending scan + * + * @param priv A pointer to moal_private structure + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status +woal_cancel_scan(moal_private *priv, t_u8 wait_option) +{ + mlan_ioctl_req *req = NULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + moal_handle *handle = priv->phandle; + moal_private *scan_priv = handle->scan_priv; +#ifdef STA_CFG80211 + unsigned long flags; +#endif + + /* If scan is in process, cancel the scan command */ + if (!handle->scan_pending_on_block || !scan_priv) + return ret; + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_scan)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + req->req_id = MLAN_IOCTL_SCAN; + req->action = MLAN_ACT_SET; + ((mlan_ds_scan *)req->pbuf)->sub_command = MLAN_OID_SCAN_CANCEL; + ret = woal_request_ioctl(scan_priv, req, wait_option); + handle->scan_pending_on_block = MFALSE; + MOAL_REL_SEMAPHORE(&handle->async_sem); +#ifdef STA_CFG80211 + spin_lock_irqsave(&handle->scan_req_lock, flags); + if (IS_STA_CFG80211(cfg80211_wext) && handle->scan_request) { + /** some supplicant can not handle SCAN abort event */ + if (scan_priv->bss_type == MLAN_BSS_TYPE_STA) + woal_cfg80211_scan_done(handle->scan_request, MTRUE); + else + woal_cfg80211_scan_done(handle->scan_request, MFALSE); + handle->scan_request = NULL; + } + spin_unlock_irqrestore(&handle->scan_req_lock, flags); +#endif + /* add 10ms delay, incase firmware delay 0x7f event after scan cancel command response */ + woal_sched_timeout(10); + handle->scan_priv = NULL; +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + return ret; +} + +/** + * @brief find ssid in scan_table + * + * @param priv A pointer to moal_private + * @param ssid_bssid A pointer to mlan_ssid_bssid structure + * + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_FAILURE + */ +int +woal_find_essid(moal_private *priv, mlan_ssid_bssid *ssid_bssid, + t_u8 wait_option) +{ + int ret = 0; + mlan_scan_resp scan_resp; + struct timeval t; + ENTER(); + + if (MLAN_STATUS_SUCCESS != + woal_get_scan_table(priv, wait_option, &scan_resp)) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } +#ifdef STA_CFG80211 + if (priv->ft_pre_connect) { + /** skip check the scan age out */ + ret = woal_find_best_network(priv, wait_option, ssid_bssid); + LEAVE(); + return ret; + } +#endif + do_gettimeofday(&t); +/** scan result timeout value */ +#define SCAN_RESULT_AGEOUT 10 + if (t.tv_sec > (scan_resp.age_in_secs + SCAN_RESULT_AGEOUT)) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + ret = woal_find_best_network(priv, wait_option, ssid_bssid); + LEAVE(); + return ret; +} + +/** + * @brief auto reconnection configure + * + * @param priv Pointer to moal_private structure + * @param cfg_mode configure mode + * @param roam_offload_cfg Pointer to woal_roam_offload_cfg structure + * + * @return Number of bytes written, negative for failure. + */ +mlan_status +woal_config_fw_roaming(moal_private *priv, t_u8 cfg_mode, + woal_roam_offload_cfg * roam_offload_cfg) +{ + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_misc_cfg *misc = NULL; + mlan_ds_misc_roam_offload *roam = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + int ret = 0; + + ENTER(); + + if (!priv || !priv->phandle) { + PRINTM(MERROR, "priv or handle is null\n"); + ret = -EFAULT; + goto done; + } + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (ioctl_req == NULL) { + ret = -ENOMEM; + goto done; + } + + misc = (mlan_ds_misc_cfg *)ioctl_req->pbuf; + misc->sub_command = MLAN_OID_MISC_ROAM_OFFLOAD; + ioctl_req->req_id = MLAN_IOCTL_MISC_CFG; + + roam = (mlan_ds_misc_roam_offload *) & misc->param.roam_offload; + roam->aplist.ap_num = 0; + ioctl_req->action = MLAN_ACT_SET; + roam->enable = priv->phandle->fw_roam_enable; + roam->config_mode = cfg_mode; + + if ((roam->config_mode == ROAM_OFFLOAD_ENABLE) && roam_offload_cfg) { + roam->userset_passphrase = roam_offload_cfg->userset_passphrase; + if (roam->userset_passphrase) + roam->enable = 0; + } + if (roam->config_mode == ROAM_OFFLOAD_PARAM_CFG) { + memcpy((t_u8 *)&roam->bssid_reconnect, + (t_u8 *)&roam_offload_cfg->bssid, MLAN_MAC_ADDR_LENGTH); + if (roam_offload_cfg->ssid_list.ssid_num) { + memcpy((t_u8 *)&roam->ssid_list, + (t_u8 *)&roam_offload_cfg->ssid_list, + sizeof(mlan_ds_misc_ssid_list)); + } + if (roam_offload_cfg->black_list.ap_num) { + memcpy((t_u8 *)&roam->black_list, + (t_u8 *)&roam_offload_cfg->black_list, + sizeof(mlan_ds_misc_roam_offload_aplist)); + } + roam->trigger_condition = roam_offload_cfg->trigger_condition; + roam->retry_count = roam_offload_cfg->retry_count; + if (roam_offload_cfg->rssi_param_set_flag) { + roam->para_rssi.set_flag = 1; + roam->para_rssi.max_rssi = roam_offload_cfg->max_rssi; + roam->para_rssi.min_rssi = roam_offload_cfg->min_rssi; + roam->para_rssi.step_rssi = roam_offload_cfg->step_rssi; + } + if (roam_offload_cfg->band_rssi_flag) { + roam->band_rssi_flag = roam_offload_cfg->band_rssi_flag; + memcpy((t_u8 *)&roam->band_rssi, + (t_u8 *)&roam_offload_cfg->band_rssi, + sizeof(mlan_ds_misc_band_rssi)); + } + if (roam_offload_cfg->bgscan_set_flag) { + roam->bgscan_set_flag = + roam_offload_cfg->bgscan_set_flag; + memcpy((t_u8 *)&roam->bgscan_cfg, + (t_u8 *)&roam_offload_cfg->bgscan_cfg, + sizeof(mlan_ds_misc_bgscan_cfg)); + } + if (roam_offload_cfg->ees_param_set_flag) { + roam->ees_param_set_flag = + roam_offload_cfg->ees_param_set_flag; + memcpy((t_u8 *)&roam->ees_cfg, + (t_u8 *)&roam_offload_cfg->ees_cfg, + sizeof(mlan_ds_misc_ees_cfg)); + } + roam->bcn_miss_threshold = roam_offload_cfg->bcn_miss_threshold; + roam->pre_bcn_miss_threshold = + roam_offload_cfg->pre_bcn_miss_threshold; + roam->repeat_count = roam_offload_cfg->repeat_count; + } + if (roam->config_mode == ROAM_OFFLOAD_SUSPEND_CFG) { + memcpy(roam->bssid_reconnect, + priv->phandle->auto_reconnect_bssid, + MLAN_MAC_ADDR_LENGTH); + roam->ssid_list.ssid_num = 1; + memcpy((t_u8 *)&roam->ssid_list.ssids[0].ssid, + (t_u8 *)&priv->phandle->auto_reconnect_ssid.ssid, + priv->phandle->auto_reconnect_ssid.ssid_len); + + roam->retry_count = priv->phandle->auto_reconnect_retry_count; + } + + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + + LEAVE(); + return ret; +} + +/** + * @brief Request user scan + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * @param scan_cfg A pointer to wlan_bgscan_cfg structure + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status +woal_request_bgscan(moal_private *priv, + t_u8 wait_option, wlan_bgscan_cfg *scan_cfg) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_scan *scan = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + ENTER(); + + /* Allocate an IOCTL request buffer */ + ioctl_req = + woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_scan) + + sizeof(wlan_bgscan_cfg)); + if (ioctl_req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + scan = (mlan_ds_scan *)ioctl_req->pbuf; + scan->sub_command = MLAN_OID_SCAN_BGSCAN_CONFIG; + ioctl_req->req_id = MLAN_IOCTL_SCAN; + ioctl_req->action = MLAN_ACT_SET; + memcpy(scan->param.user_scan.scan_cfg_buf, scan_cfg, + sizeof(wlan_bgscan_cfg)); + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, ioctl_req, wait_option); + if (status == MLAN_STATUS_FAILURE) { + ret = MLAN_STATUS_FAILURE; + goto done; + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + LEAVE(); + return ret; +} + +/** + * @brief set bgscan config + * + * @param priv A pointer to moal_private structure + * @param buf A pointer to scan command buf + * @param length buf length + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status +woal_set_bg_scan(moal_private *priv, char *buf, int length) +{ + t_u8 *ptr = buf + strlen("BGSCAN-CONFIG") + 1; + int buf_left = length - (strlen("BGSCAN-CONFIG") + 1); + int band = 0; + int num_ssid = 0; + int ssid_len = 0; + mlan_status ret = MLAN_STATUS_FAILURE; + + ENTER(); + memset(&priv->scan_cfg, 0, sizeof(priv->scan_cfg)); + priv->scan_cfg.report_condition = + BG_SCAN_SSID_MATCH | BG_SCAN_WAIT_ALL_CHAN_DONE; + while (buf_left >= 2) { + switch (*ptr) { + case WEXT_CSCAN_SSID_SECTION: + ssid_len = *(ptr + 1); + if ((buf_left < (ssid_len + 2)) || + (ssid_len > MLAN_MAX_SSID_LENGTH)) { + PRINTM(MERROR, + "Invalid ssid, buf_left=%d, ssid_len=%d\n", + buf_left, ssid_len); + buf_left = 0; + break; + } + if (ssid_len && + (num_ssid < (MRVDRV_MAX_SSID_LIST_LENGTH - 1))) { + strncpy(priv->scan_cfg.ssid_list[num_ssid].ssid, + ptr + 2, ssid_len); + priv->scan_cfg.ssid_list[num_ssid].max_len = 0; + PRINTM(MIOCTL, "BG scan: ssid=%s\n", + priv->scan_cfg.ssid_list[num_ssid].ssid); + num_ssid++; + } + buf_left -= ssid_len + 2; + ptr += ssid_len + 2; + break; + case WEXT_BGSCAN_RSSI_SECTION: + priv->scan_cfg.report_condition = + BG_SCAN_SSID_RSSI_MATCH | + BG_SCAN_WAIT_ALL_CHAN_DONE; + priv->scan_cfg.rssi_threshold = ptr[1]; + PRINTM(MIOCTL, "BG scan: rssi_threshold=%d\n", + (int)priv->scan_cfg.rssi_threshold); + ptr += 2; + buf_left -= 2; + break; + case WEXT_BGSCAN_REPEAT_SECTION: + priv->scan_cfg.repeat_count = (t_u16)ptr[1]; + PRINTM(MIOCTL, "BG scan: repeat_count=%d\n", + (int)priv->scan_cfg.repeat_count); + ptr += 2; + buf_left -= 2; + break; + case WEXT_BGSCAN_INTERVAL_SECTION: + if (buf_left < 3) { + PRINTM(MERROR, + "Invalid scan_interval, buf_left=%d\n", + buf_left); + buf_left = 0; + break; + } + priv->scan_cfg.scan_interval = + (ptr[2] << 8 | ptr[1]) * 1000; + PRINTM(MIOCTL, "BG scan: scan_interval=%d\n", + (int)priv->scan_cfg.scan_interval); + ptr += 3; + buf_left -= 3; + break; + default: + buf_left = 0; + break; + } + } + /** set bgscan when ssid_num > 0 */ + if (num_ssid) { + if (MLAN_STATUS_SUCCESS != woal_get_band(priv, &band)) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + switch (band) { + case WIFI_FREQUENCY_BAND_2GHZ: + priv->scan_cfg.chan_list[0].radio_type = + 0 | BAND_SPECIFIED; + break; + case WIFI_FREQUENCY_BAND_5GHZ: + priv->scan_cfg.chan_list[0].radio_type = + 1 | BAND_SPECIFIED; + break; + default: + break; + } + priv->scan_cfg.bss_type = MLAN_BSS_MODE_INFRA; + priv->scan_cfg.action = BG_SCAN_ACT_SET; + priv->scan_cfg.enable = MTRUE; + ret = woal_request_bgscan(priv, MOAL_IOCTL_WAIT, + &priv->scan_cfg); + } +done: + LEAVE(); + return ret; +} + +#ifdef STA_CFG80211 +/** + * @brief set bgscan and new rssi_low_threshold + * + * @param priv A pointer to moal_private structure + * @param set_rssi flag for set rssi_low_threshold + * + * @return N/A + */ +void +woal_config_bgscan_and_rssi(moal_private *priv, t_u8 set_rssi) +{ + char rssi_low[11]; + mlan_bss_info bss_info; + int band = 0; + + ENTER(); + memset(&bss_info, 0, sizeof(bss_info)); + woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info); + if (!bss_info.media_connected) { + PRINTM(MIOCTL, "We already lost connection\n"); + LEAVE(); + return; + } + memset(&priv->scan_cfg, 0, sizeof(priv->scan_cfg)); + strncpy(priv->scan_cfg.ssid_list[0].ssid, bss_info.ssid.ssid, + bss_info.ssid.ssid_len); + priv->scan_cfg.ssid_list[0].max_len = 0; + + priv->scan_cfg.report_condition = + BG_SCAN_SSID_RSSI_MATCH | BG_SCAN_WAIT_ALL_CHAN_DONE; + priv->scan_cfg.rssi_threshold = priv->rssi_low - RSSI_HYSTERESIS; + priv->scan_cfg.repeat_count = DEF_REPEAT_COUNT; + priv->scan_cfg.scan_interval = MIN_BGSCAN_INTERVAL; + woal_get_band(priv, &band); + switch (band) { + case WIFI_FREQUENCY_BAND_2GHZ: + priv->scan_cfg.chan_list[0].radio_type = 0 | BAND_SPECIFIED; + break; + case WIFI_FREQUENCY_BAND_5GHZ: + priv->scan_cfg.chan_list[0].radio_type = 1 | BAND_SPECIFIED; + break; + default: + break; + } + priv->scan_cfg.bss_type = MLAN_BSS_MODE_INFRA; + priv->scan_cfg.action = BG_SCAN_ACT_SET; + priv->scan_cfg.enable = MTRUE; + woal_request_bgscan(priv, MOAL_NO_WAIT, &priv->scan_cfg); + if (set_rssi && + ((priv->rssi_low + RSSI_HYSTERESIS) <= LOWEST_RSSI_THRESHOLD)) { + priv->rssi_low += RSSI_HYSTERESIS; + sprintf(rssi_low, "%d", priv->rssi_low); + woal_set_rssi_low_threshold(priv, rssi_low, MOAL_NO_WAIT); + } + LEAVE(); +} +#endif + +/** + * @brief stop bg scan + * + * @param priv A pointer to moal_private structure + * @param wait_option wait option + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status +woal_stop_bg_scan(moal_private *priv, t_u8 wait_option) +{ + wlan_bgscan_cfg scan_cfg; + mlan_status ret = MLAN_STATUS_SUCCESS; + ENTER(); + + memset(&scan_cfg, 0, sizeof(scan_cfg)); + scan_cfg.action = BG_SCAN_ACT_SET; + scan_cfg.enable = MFALSE; + ret = woal_request_bgscan(priv, wait_option, &scan_cfg); + + LEAVE(); + return ret; +} + +/** + * @brief set bgscan config + * + * @param handle A pointer to moal_handle structure + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +void +woal_reconfig_bgscan(moal_handle *handle) +{ + int i; + for (i = 0; i < MIN(handle->priv_num, MLAN_MAX_BSS_NUM); i++) { + if (handle->priv[i] && + (GET_BSS_ROLE(handle->priv[i]) == MLAN_BSS_ROLE_STA)) { + if (handle->priv[i]->bg_scan_start && + handle->priv[i]->bg_scan_reported) { + PRINTM(MIOCTL, "Reconfig BGSCAN\n"); + woal_request_bgscan(handle->priv[i], + MOAL_NO_WAIT, + &handle->priv[i]->scan_cfg); + handle->priv[i]->bg_scan_reported = MFALSE; + } + } + } +} + +/** + * @brief set rssi low threshold + * + * @param priv A pointer to moal_private structure + * @param rssi A pointer to low rssi + * @param wait_option Wait option + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status +woal_set_rssi_low_threshold(moal_private *priv, char *rssi, t_u8 wait_option) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *misc = NULL; + int low_rssi = 0; + + ENTER(); + if (priv->media_connected == MFALSE) + goto done; + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + misc = (mlan_ds_misc_cfg *)req->pbuf; + req->req_id = MLAN_IOCTL_MISC_CFG; + misc->sub_command = MLAN_OID_MISC_SUBSCRIBE_EVENT; + req->action = MLAN_ACT_SET; + misc->param.subscribe_event.evt_action = SUBSCRIBE_EVT_ACT_BITWISE_SET; + misc->param.subscribe_event.evt_bitmap = SUBSCRIBE_EVT_RSSI_LOW; + misc->param.subscribe_event.evt_bitmap |= SUBSCRIBE_EVT_PRE_BEACON_LOST; + misc->param.subscribe_event.pre_beacon_miss = DEFAULT_PRE_BEACON_MISS; + + if (MLAN_STATUS_SUCCESS != woal_atoi(&low_rssi, rssi)) { + ret = MLAN_STATUS_FAILURE; + goto done; + } +#ifdef STA_CFG80211 + priv->mrvl_rssi_low = low_rssi; +#endif + misc->param.subscribe_event.low_rssi = low_rssi; + misc->param.subscribe_event.low_rssi_freq = 0; + ret = woal_request_ioctl(priv, req, wait_option); + if (ret == MLAN_STATUS_FAILURE) { + PRINTM(MERROR, "request set rssi_low_threshold fail!\n"); + goto done; + } +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +#if defined(STA_CFG80211) +/** + * @brief set rssi low threshold + * + * @param priv A pointer to moal_private structure + * @param event_id event id. + * @param wait_option wait option + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status +woal_set_rssi_threshold(moal_private *priv, t_u32 event_id, t_u8 wait_option) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *misc = NULL; + + ENTER(); + if (priv->media_connected == MFALSE) + goto done; + if (priv->mrvl_rssi_low) + goto done; + if (event_id == MLAN_EVENT_ID_FW_BCN_RSSI_LOW) { + if (priv->last_rssi_low < 100) + priv->last_rssi_low += priv->cqm_rssi_hyst; + priv->last_rssi_high = abs(priv->cqm_rssi_high_thold); + } else if (event_id == MLAN_EVENT_ID_FW_BCN_RSSI_HIGH) { + priv->last_rssi_low = abs(priv->cqm_rssi_thold); + if (priv->last_rssi_high > priv->cqm_rssi_hyst) + priv->last_rssi_high -= priv->cqm_rssi_hyst; + } else { + priv->last_rssi_low = abs(priv->cqm_rssi_thold); + priv->last_rssi_high = abs(priv->cqm_rssi_high_thold); + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + misc = (mlan_ds_misc_cfg *)req->pbuf; + req->req_id = MLAN_IOCTL_MISC_CFG; + misc->sub_command = MLAN_OID_MISC_SUBSCRIBE_EVENT; + req->action = MLAN_ACT_SET; + if (!event_id && !priv->cqm_rssi_thold && !priv->cqm_rssi_hyst) + misc->param.subscribe_event.evt_action = + SUBSCRIBE_EVT_ACT_BITWISE_CLR; + else + misc->param.subscribe_event.evt_action = + SUBSCRIBE_EVT_ACT_BITWISE_SET; + misc->param.subscribe_event.evt_bitmap = + SUBSCRIBE_EVT_RSSI_LOW | SUBSCRIBE_EVT_RSSI_HIGH; + misc->param.subscribe_event.low_rssi_freq = 0; + misc->param.subscribe_event.low_rssi = priv->last_rssi_low; + misc->param.subscribe_event.high_rssi_freq = 0; + misc->param.subscribe_event.high_rssi = priv->last_rssi_high; + PRINTM(MIOCTL, "rssi_low=%d, rssi_high=%d action=%d\n", + (int)priv->last_rssi_low, (int)priv->last_rssi_high, + misc->param.subscribe_event.evt_action); + ret = woal_request_ioctl(priv, req, wait_option); +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} +#endif + +/** + * @brief Get power mode + * + * @param priv A pointer to moal_private structure + * @param powermode A pointer to powermode buf + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status +woal_get_powermode(moal_private *priv, int *powermode) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + int ps_mode; + + ENTER(); + + if (MLAN_STATUS_SUCCESS != + woal_set_get_power_mgmt(priv, MLAN_ACT_GET, &ps_mode, 0, + MOAL_IOCTL_WAIT)) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + if (ps_mode) + *powermode = MFALSE; + else + *powermode = MTRUE; + +done: + LEAVE(); + return ret; +} + +/** + * @brief set scan type + * + * @param priv A pointer to moal_private structure + * @param scan_type MLAN_SCAN_TYPE_ACTIVE/MLAN_SCAN_TYPE_PASSIVE + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status +woal_set_scan_type(moal_private *priv, t_u32 scan_type) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_scan *scan = NULL; + mlan_ioctl_req *req = NULL; + mlan_scan_cfg scan_cfg; + + ENTER(); + memset(&scan_cfg, 0, sizeof(scan_cfg)); + if (MLAN_STATUS_SUCCESS != woal_get_scan_config(priv, &scan_cfg)) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_scan)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + scan = (mlan_ds_scan *)req->pbuf; + scan->sub_command = MLAN_OID_SCAN_CONFIG; + req->req_id = MLAN_IOCTL_SCAN; + req->action = MLAN_ACT_SET; + memset(&scan->param.scan_cfg, 0, sizeof(mlan_scan_cfg)); + scan_cfg.scan_type = scan_type; + PRINTM(MIOCTL, "Set scan_type=%d\n", (int)scan_type); + memcpy(&scan->param.scan_cfg, &scan_cfg, sizeof(mlan_scan_cfg)); + ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief enable/disable ext_scan + * + * @param priv A pointer to moal_private structure + * @param enable MTRUE -- enable, MFALSE --disable + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status +woal_enable_ext_scan(moal_private *priv, t_u8 enable) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_scan *scan = NULL; + mlan_ioctl_req *req = NULL; + mlan_scan_cfg scan_cfg; + + ENTER(); + memset(&scan_cfg, 0, sizeof(scan_cfg)); + if (MLAN_STATUS_SUCCESS != woal_get_scan_config(priv, &scan_cfg)) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_scan)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + scan = (mlan_ds_scan *)req->pbuf; + scan->sub_command = MLAN_OID_SCAN_CONFIG; + req->req_id = MLAN_IOCTL_SCAN; + req->action = MLAN_ACT_SET; + memset(&scan->param.scan_cfg, 0, sizeof(mlan_scan_cfg)); + scan_cfg.ext_scan = enable; + PRINTM(MIOCTL, "Set ext_scan=%d\n", (int)enable); + memcpy(&scan->param.scan_cfg, &scan_cfg, sizeof(mlan_scan_cfg)); + ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief set power mode + * + * @param priv A pointer to moal_private structure + * @param powermode A pointer to powermode string. + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status +woal_set_powermode(moal_private *priv, char *powermode) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + int disabled; + + ENTER(); + + if (*powermode == '1') { + PRINTM(MIOCTL, "Disable power save\n"); + disabled = 1; + } else if (*powermode == '0') { + PRINTM(MIOCTL, "Enable power save\n"); + disabled = 0; + } else { + PRINTM(MERROR, "unsupported power mode\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + if (MLAN_STATUS_SUCCESS != + woal_set_get_power_mgmt(priv, MLAN_ACT_SET, &disabled, 0, + MOAL_IOCTL_WAIT)) + ret = MLAN_STATUS_FAILURE; + +done: + LEAVE(); + return ret; +} + +/** + * @brief set combo scan + * + * @param priv A pointer to moal_private structure + * @param buf A pointer to scan command buf + * @param length buf length + * + * @return 0 -- success, otherwise fail + */ +int +woal_set_combo_scan(moal_private *priv, char *buf, int length) +{ + int ret = 0; + wlan_user_scan_cfg scan_cfg; + t_u8 *ptr = buf + WEXT_CSCAN_HEADER_SIZE; + int buf_left = length - WEXT_CSCAN_HEADER_SIZE; + int num_ssid = 0; + int num_chan = 0; + int ssid_len = 0; + int i = 0; + t_u16 passive_scan_time = 0; + t_u16 specific_scan_time = 0; + + ENTER(); + memset(&scan_cfg, 0, sizeof(scan_cfg)); + while (buf_left >= 2) { + switch (*ptr) { + case WEXT_CSCAN_SSID_SECTION: + ssid_len = *(ptr + 1); + if ((buf_left < (ssid_len + 2)) || + (ssid_len > MLAN_MAX_SSID_LENGTH)) { + PRINTM(MERROR, + "Invalid ssid, buf_left=%d, ssid_len=%d\n", + buf_left, ssid_len); + buf_left = 0; + break; + } + if (ssid_len && + (num_ssid < (MRVDRV_MAX_SSID_LIST_LENGTH - 1))) { + strncpy(scan_cfg.ssid_list[num_ssid].ssid, + ptr + 2, ssid_len); + scan_cfg.ssid_list[num_ssid].max_len = 0; + PRINTM(MIOCTL, "Combo scan: ssid=%s\n", + scan_cfg.ssid_list[num_ssid].ssid); + num_ssid++; + } + buf_left -= ssid_len + 2; + ptr += ssid_len + 2; + break; + case WEXT_CSCAN_CHANNEL_SECTION: + num_chan = ptr[1]; + if ((buf_left < (num_chan + 2)) || + (num_chan > WLAN_USER_SCAN_CHAN_MAX)) { + PRINTM(MERROR, + "Invalid channel list, buf_left=%d, num_chan=%d\n", + buf_left, num_chan); + buf_left = 0; + break; + } + for (i = 0; i < num_chan; i++) { + scan_cfg.chan_list[i].chan_number = ptr[2 + i]; + PRINTM(MIOCTL, "Combo scan: chan=%d\n", + scan_cfg.chan_list[i].chan_number); + } + buf_left -= 2 + num_chan; + ptr += 2 + num_chan; + break; + case WEXT_CSCAN_PASV_DWELL_SECTION: + if (buf_left < 3) { + PRINTM(MERROR, + "Invalid PASV_DWELL_SECTION, buf_left=%d\n", + buf_left); + buf_left = 0; + break; + } + passive_scan_time = ptr[2] << 8 | ptr[1]; + ptr += 3; + buf_left -= 3; + break; + case WEXT_CSCAN_HOME_DWELL_SECTION: + if (buf_left < 3) { + PRINTM(MERROR, + "Invalid HOME_DWELL_SECTION, buf_left=%d\n", + buf_left); + buf_left = 0; + break; + } + specific_scan_time = ptr[2] << 8 | ptr[1]; + ptr += 3; + buf_left -= 3; + break; + default: + buf_left = 0; + break; + } + } + if (passive_scan_time || specific_scan_time) { + PRINTM(MIOCTL, + "Set passive_scan_time=%d specific_scan_time=%d\n", + passive_scan_time, specific_scan_time); + if (MLAN_STATUS_FAILURE == + woal_set_scan_time(priv, 0, passive_scan_time, + specific_scan_time)) { + ret = -EFAULT; + goto done; + } + } + if (num_ssid || num_chan) { + if (num_ssid) { + /* Add broadcast scan to ssid_list */ + scan_cfg.ssid_list[num_ssid].max_len = 0xff; + if (priv->scan_type == MLAN_SCAN_TYPE_PASSIVE) + woal_set_scan_type(priv, MLAN_SCAN_TYPE_ACTIVE); + } + if (MLAN_STATUS_FAILURE == woal_do_scan(priv, &scan_cfg)) + ret = -EFAULT; + if (num_ssid && (priv->scan_type == MLAN_SCAN_TYPE_PASSIVE)) + woal_set_scan_type(priv, MLAN_SCAN_TYPE_PASSIVE); + } else { + /* request broadcast scan */ + if (MLAN_STATUS_FAILURE == woal_do_scan(priv, NULL)) + ret = -EFAULT; + } +done: + LEAVE(); + return ret; +} + +/** + * @brief Get band + * + * @param priv A pointer to moal_private structure + * @param band A pointer to band buf + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status +woal_get_band(moal_private *priv, int *band) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ioctl_req *req = NULL; + mlan_ds_radio_cfg *radio_cfg = NULL; + int support_band = 0; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_radio_cfg)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + radio_cfg = (mlan_ds_radio_cfg *)req->pbuf; + radio_cfg->sub_command = MLAN_OID_BAND_CFG; + req->req_id = MLAN_IOCTL_RADIO_CFG; + /* Get config_bands, adhoc_start_band and adhoc_channel values from MLAN */ + req->action = MLAN_ACT_GET; + ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (ret != MLAN_STATUS_SUCCESS) + goto done; + + if (radio_cfg->param.band_cfg. + config_bands & (BAND_B | BAND_G | BAND_GN)) + support_band |= WIFI_FREQUENCY_BAND_2GHZ; + if (radio_cfg->param.band_cfg.config_bands & (BAND_A | BAND_AN)) + support_band |= WIFI_FREQUENCY_BAND_5GHZ; + *band = support_band; + if (support_band == WIFI_FREQUENCY_ALL_BAND) + *band = WIFI_FREQUENCY_BAND_AUTO; +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief set band + * + * @param priv A pointer to moal_private structure + * @param pband A pointer to band string. + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status +woal_set_band(moal_private *priv, char *pband) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + int band = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_radio_cfg *radio_cfg = NULL; + + ENTER(); + if (priv->media_connected == MTRUE) { + PRINTM(MERROR, "Set band is not allowed in connected state\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_radio_cfg)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + radio_cfg = (mlan_ds_radio_cfg *)req->pbuf; + radio_cfg->sub_command = MLAN_OID_BAND_CFG; + req->req_id = MLAN_IOCTL_RADIO_CFG; + + /* Get fw supported values from MLAN */ + req->action = MLAN_ACT_GET; + ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (ret != MLAN_STATUS_SUCCESS) + goto done; + + if (*pband == '0') { + PRINTM(MIOCTL, "Set band to AUTO\n"); + band = radio_cfg->param.band_cfg.fw_bands; +#if defined(STA_CFG80211) || defined(UAP_CFG80211) + if (IS_STA_OR_UAP_CFG80211(cfg80211_wext) && priv->wdev && + priv->wdev->wiphy) { + if (radio_cfg->param.band_cfg.fw_bands & BAND_A) + priv->wdev->wiphy->bands[IEEE80211_BAND_5GHZ] = + &cfg80211_band_5ghz; + priv->wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = + &cfg80211_band_2ghz; + } +#endif + } else if (*pband == '1') { + PRINTM(MIOCTL, "Set band to 5G\n"); + if (!(radio_cfg->param.band_cfg.fw_bands & BAND_A)) { + PRINTM(MERROR, "Don't support 5G band\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + band = BAND_A; + if (radio_cfg->param.band_cfg.fw_bands & BAND_AN) + band |= BAND_AN; +#if defined(STA_CFG80211) || defined(UAP_CFG80211) + if (IS_STA_OR_UAP_CFG80211(cfg80211_wext) && priv->wdev && + priv->wdev->wiphy) { + priv->wdev->wiphy->bands[IEEE80211_BAND_5GHZ] = + &cfg80211_band_5ghz; + priv->wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = NULL; + } +#endif + } else if (*pband == '2') { + PRINTM(MIOCTL, "Set band to 2G\n"); + band = BAND_B | BAND_G; + band |= BAND_GN; +#if defined(STA_CFG80211) || defined(UAP_CFG80211) + if (IS_STA_OR_UAP_CFG80211(cfg80211_wext) && priv->wdev && + priv->wdev->wiphy) { + priv->wdev->wiphy->bands[IEEE80211_BAND_5GHZ] = NULL; + priv->wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = + &cfg80211_band_2ghz; + } +#endif + } else { + PRINTM(MERROR, "unsupported band\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + /* Set config_bands to MLAN */ + req->action = MLAN_ACT_SET; + memset(&radio_cfg->param.band_cfg, 0, sizeof(mlan_ds_band_cfg)); + radio_cfg->param.band_cfg.config_bands = band; + radio_cfg->param.band_cfg.adhoc_start_band = band; + ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Add RX Filter + * + * @param priv A pointer to moal_private structure + * @param rxfilter A pointer to rxfilter string. + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status +woal_add_rxfilter(moal_private *priv, char *rxfilter) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + ENTER(); + /* Android command: + "DRIVER RXFILTER-ADD 0" + "DRIVER RXFILTER-ADD 1" + "DRIVER RXFILTER-ADD 3" */ + if (*rxfilter == '0') { + PRINTM(MIOCTL, "Add IPV4 multicast filter\n"); + priv->rx_filter |= RX_FILTER_IPV4_MULTICAST; + } else if (*rxfilter == '1') { + PRINTM(MIOCTL, "Add broadcast filter\n"); + priv->rx_filter |= RX_FILTER_BROADCAST; + } else if (*rxfilter == '2') { + PRINTM(MIOCTL, "Add unicast filter\n"); + priv->rx_filter |= RX_FILTER_UNICAST; + } else if (*rxfilter == '3') { + PRINTM(MIOCTL, "Add IPV6 multicast fitler\n"); + priv->rx_filter |= RX_FILTER_IPV6_MULTICAST; + } else { + PRINTM(MERROR, "unsupported rx fitler\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } +done: + LEAVE(); + return ret; +} + +/** + * @brief Remove RX Filter + * + * @param priv A pointer to moal_private structure + * @param rxfilter A pointer to rxfilter string. + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status +woal_remove_rxfilter(moal_private *priv, char *rxfilter) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + ENTER(); + if (*rxfilter == '0') { + PRINTM(MIOCTL, "Remove IPV4 multicast filter\n"); + priv->rx_filter &= ~RX_FILTER_IPV4_MULTICAST; + } else if (*rxfilter == '1') { + PRINTM(MIOCTL, "Remove broadcast filter\n"); + priv->rx_filter &= ~RX_FILTER_BROADCAST; + } else if (*rxfilter == '2') { + PRINTM(MIOCTL, "Remove unicast filter\n"); + priv->rx_filter &= ~RX_FILTER_UNICAST; + } else if (*rxfilter == '3') { + PRINTM(MIOCTL, "Remove IPV6 multicast fitler\n"); + priv->rx_filter &= ~RX_FILTER_IPV6_MULTICAST; + } else { + PRINTM(MERROR, "unsupported rx fitler\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } +done: + LEAVE(); + return ret; +} + +/** + * @brief Set/Get WMM IE QoS configuration + * + * @param priv A pointer to moal_private structure + * @param action Action set or get + * @param qos_cfg A pointer to QoS configuration structure + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status +woal_priv_qos_cfg(moal_private *priv, t_u32 action, char *qos_cfg) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_wmm_cfg *cfg = NULL; + mlan_ioctl_req *req = NULL; + int qosinfo = 0; + + ENTER(); + + if (qos_cfg == NULL) { + PRINTM(MERROR, "QOS info buffer is null\n"); + return MLAN_STATUS_FAILURE; + } + if ((action == MLAN_ACT_SET) && + (MLAN_STATUS_SUCCESS != woal_atoi(&qosinfo, qos_cfg))) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_wmm_cfg)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + cfg = (mlan_ds_wmm_cfg *)req->pbuf; + cfg->sub_command = MLAN_OID_WMM_CFG_QOS; + req->req_id = MLAN_IOCTL_WMM_CFG; + req->action = action; + if (action == MLAN_ACT_SET) { + cfg->param.qos_cfg = (t_u8)qosinfo; + PRINTM(MIOCTL, "set qosinfo=%d\n", qosinfo); + } + ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + + if (action == MLAN_ACT_GET) + *qos_cfg = cfg->param.qos_cfg; +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set sleep period + * + * @param priv A pointer to moal_private structure + * @param psleeppd A pointer to sleep period configuration structure + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status +woal_set_sleeppd(moal_private *priv, char *psleeppd) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_pm_cfg *pm_cfg = NULL; + mlan_ioctl_req *req = NULL; + int sleeppd = 0; + + ENTER(); + + if (MLAN_STATUS_SUCCESS != woal_atoi(&sleeppd, psleeppd)) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + PRINTM(MIOCTL, "set sleeppd=%d\n", sleeppd); + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + pm_cfg = (mlan_ds_pm_cfg *)req->pbuf; + pm_cfg->sub_command = MLAN_OID_PM_CFG_SLEEP_PD; + req->req_id = MLAN_IOCTL_PM_CFG; + if ((sleeppd <= MAX_SLEEP_PERIOD && sleeppd >= MIN_SLEEP_PERIOD) || + (sleeppd == 0) + || (sleeppd == SLEEP_PERIOD_RESERVED_FF) + ) { + req->action = MLAN_ACT_SET; + pm_cfg->param.sleep_period = sleeppd; + } else { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set scan period function + * + * @param priv A pointer to moal_private structure + * @param buf A pointer to scan command buf + * @param length buf length + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +int +woal_set_scan_cfg(moal_private *priv, char *buf, int length) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u8 *ptr = buf + NL80211_SCANCFG_HEADER_SIZE; + int buf_left = length - NL80211_SCANCFG_HEADER_SIZE; + t_u16 active_scan_time = 0; + t_u16 passive_scan_time = 0; + t_u16 specific_scan_time = 0; + + ENTER(); + while (buf_left >= 2) { + switch (*ptr) { + case NL80211_SCANCFG_ACTV_DWELL_SECTION: + if (buf_left < 3) { + PRINTM(MERROR, + "Invalid ACTV_DWELL_SECTION, buf_left=%d\n", + buf_left); + buf_left = 0; + break; + } + active_scan_time = ptr[2] << 8 | ptr[1]; + ptr += 3; + buf_left -= 3; + break; + case NL80211_SCANCFG_PASV_DWELL_SECTION: + if (buf_left < 3) { + PRINTM(MERROR, + "Invalid PASV_DWELL_SECTION, buf_left=%d\n", + buf_left); + buf_left = 0; + break; + } + passive_scan_time = ptr[2] << 8 | ptr[1]; + ptr += 3; + buf_left -= 3; + break; + case NL80211_SCANCFG_SPCF_DWELL_SECTION: + if (buf_left < 3) { + PRINTM(MERROR, + "Invalid SPCF_DWELL_SECTION, buf_left=%d\n", + buf_left); + buf_left = 0; + break; + } + specific_scan_time = ptr[2] << 8 | ptr[1]; + ptr += 3; + buf_left -= 3; + break; + default: + buf_left = 0; + break; + } + } + + if (active_scan_time || passive_scan_time || specific_scan_time) { + PRINTM(MIOCTL, + "Set active_scan_time= %d passive_scan_time=%d specific_scan_time=%d\n", + active_scan_time, passive_scan_time, specific_scan_time); + if (MLAN_STATUS_FAILURE == + woal_set_scan_time(priv, active_scan_time, + passive_scan_time, specific_scan_time)) { + ret = -EFAULT; + } + } + + LEAVE(); + return ret; +} + +/** + * @brief Set Radio On/OFF + * + * @param priv A pointer to moal_private structure + * @param option Radio Option + * + * @return 0 --success, otherwise fail + */ +int +woal_set_radio(moal_private *priv, t_u8 option) +{ + int ret = 0; + mlan_ds_radio_cfg *radio = NULL; + mlan_ioctl_req *req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + ENTER(); + if ((option != 0) && (option != 1)) { + ret = -EINVAL; + goto done; + } + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_radio_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + radio = (mlan_ds_radio_cfg *)req->pbuf; + radio->sub_command = MLAN_OID_RADIO_CTRL; + req->req_id = MLAN_IOCTL_RADIO_CFG; + req->action = MLAN_ACT_SET; + radio->param.radio_on_off = option; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) + ret = -EFAULT; + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +#endif /* STA_SUPPORT */ + +/** + * @brief Set/Get configure multi-channel policy + * + * @param priv A pointer to moal_private structure + * @param enable A pointer to enable + * @param wait_option wait_option of ioctl + * @param action action of ioctl + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status +woal_mc_policy_cfg(moal_private *priv, t_u16 *enable, + t_u8 wait_option, t_u8 action) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *cfg = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + status = MLAN_STATUS_FAILURE; + goto done; + } + + cfg = (mlan_ds_misc_cfg *)req->pbuf; + cfg->sub_command = MLAN_OID_MISC_MULTI_CHAN_POLICY; + req->req_id = MLAN_IOCTL_MISC_CFG; + req->action = action; + if (MLAN_ACT_SET == action) + cfg->param.multi_chan_policy = *enable; + status = woal_request_ioctl(priv, req, wait_option); + if (status != MLAN_STATUS_SUCCESS) + goto done; + if (MLAN_ACT_GET == action) + *enable = cfg->param.multi_chan_policy; + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return status; +} + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) +/** + * @brief Set/Get network monitor configurations + * + * @param priv Pointer to moal_private structure + * @param wait_option wait option + * @param enable Enable/Disable + * @param filter Filter flag - Management/Control/Data + * @param band_chan_cfg Network monitor band channel config + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status +woal_set_net_monitor(moal_private *priv, t_u8 wait_option, + t_u8 enable, t_u8 filter, + netmon_band_chan_cfg * band_chan_cfg) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *misc = NULL; + mlan_ds_misc_net_monitor *net_mon = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + misc = (mlan_ds_misc_cfg *)req->pbuf; + net_mon = (mlan_ds_misc_net_monitor *)&misc->param.net_mon; + misc->sub_command = MLAN_OID_MISC_NET_MONITOR; + req->req_id = MLAN_IOCTL_MISC_CFG; + req->action = MLAN_ACT_SET; + net_mon->enable_net_mon = enable; + if (net_mon->enable_net_mon) { + net_mon->filter_flag = filter; + if (net_mon->enable_net_mon == CHANNEL_SPEC_SNIFFER_MODE) { + net_mon->band = band_chan_cfg->band; + net_mon->channel = band_chan_cfg->channel; + net_mon->chan_bandwidth = band_chan_cfg->chan_bandwidth; + } + } + + status = woal_request_ioctl(priv, req, wait_option); + if (status != MLAN_STATUS_SUCCESS) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + + LEAVE(); + return ret; +} +#endif + +/** + * @brief Send delelte all BA command to MLAN + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail + */ +mlan_status +woal_delba_all(moal_private *priv, t_u8 wait_option) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_11n_cfg *cfg_11n = NULL; + mlan_ds_11n_delba *del_ba = NULL; + t_u8 zero_mac[MLAN_MAC_ADDR_LENGTH] = { 0 }; + mlan_status status = MLAN_STATUS_SUCCESS; + int ret = 0; + + req = (mlan_ioctl_req *) + woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + cfg_11n = (mlan_ds_11n_cfg *)req->pbuf; + req->req_id = MLAN_IOCTL_11N_CFG; + cfg_11n->sub_command = MLAN_OID_11N_CFG_DELBA; + + del_ba = &cfg_11n->param.del_ba; + memset(del_ba, 0, sizeof(mlan_ds_11n_delba)); + del_ba->direction = DELBA_RX | DELBA_TX; + del_ba->tid = DELBA_ALL_TIDS; + memcpy(del_ba->peer_mac_addr, zero_mac, ETH_ALEN); + + status = woal_request_ioctl(priv, req, wait_option); + + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Send 11d enable/disable command to firmware. + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * @param enable Enable/Disable 11d + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail + */ +mlan_status +woal_set_11d(moal_private *priv, t_u8 wait_option, t_u8 enable) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_snmp_mib *snmp = NULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_snmp_mib)); + if (req == NULL) { + LEAVE(); + return -ENOMEM; + } + + req->action = MLAN_ACT_SET; + req->req_id = MLAN_IOCTL_SNMP_MIB; + + snmp = (mlan_ds_snmp_mib *)req->pbuf; + snmp->sub_command = MLAN_OID_SNMP_MIB_DOT11D; + snmp->param.oid_value = enable; + + ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (ret != MLAN_STATUS_PENDING) + kfree(req); + + LEAVE(); + return ret; +} + +/** + * @brief Handle miscellaneous ioctls for asynchronous command response + * + * @param priv Pointer to moal_private structure + * @param info Pointer to mlan_ds_misc_cfg structure + * + * @return N/A + */ +void +woal_ioctl_get_misc_conf(moal_private *priv, mlan_ds_misc_cfg *info) +{ + char event[1000]; + mlan_ds_host_clock *host_clock = NULL; + ENTER(); + switch (info->sub_command) { + case MLAN_OID_MISC_GET_CORRELATED_TIME: + host_clock = &info->param.host_clock; + memset(event, 0, sizeof(event)); + sprintf(event, "%s %llu %llu", CUS_EVT_GET_CORRELATED_TIME, + host_clock->time, host_clock->fw_time); + woal_broadcast_event(priv, event, strlen(event)); + PRINTM(MERROR, "CMD = %s\n", event); + default: + break; + } +} + +module_param(disconnect_on_suspend, int, 0); +MODULE_PARM_DESC(disconnect_on_suspend, + "1: Enable disconnect wifi on suspend; 0: Disable disconnect wifi on suspend"); diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_main.c b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_main.c new file mode 100644 index 000000000000..f03e0a5e7e1e --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_main.c @@ -0,0 +1,9079 @@ +/** @file moal_main.c + * + * @brief This file contains the major functions in WLAN + * driver. + * + * Copyright (C) 2008-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +/******************************************************** +Change log: + 10/21/2008: initial version +********************************************************/ + +#include "moal_main.h" +#include "moal_sdio.h" +#ifdef UAP_SUPPORT +#include "moal_uap.h" +#endif +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#include "moal_cfg80211.h" +#include "moal_cfgvendor.h" +#endif +#ifdef STA_CFG80211 +#ifdef STA_SUPPORT +#include "moal_sta_cfg80211.h" +#endif +#endif +#ifdef UAP_CFG80211 +#ifdef UAP_SUPPORT +#include "moal_uap_cfg80211.h" +#endif +#endif +#include "moal_eth_ioctl.h" + +#include +#include +#include +#include +#include + +#ifdef CONFIG_OF +#include +#endif + +/******************************************************** + Local Variables +********************************************************/ + +#define KERN_VERSION "4X" + +/** Driver version */ +char driver_version[] = + "SD8977-%s-C" KERN_VERSION "16" MLAN_RELEASE_VERSION + "-GPL" "-(" "FP" FPNUM ")" +#ifdef DEBUG_LEVEL2 + "-dbg" +#endif + " "; + +/** Firmware name */ +char *fw_name; +int req_fw_nowait; +int fw_reload; + +/** MAC address */ +char *mac_addr; + +#ifdef MFG_CMD_SUPPORT +/** Mfg mode */ +int mfg_mode; +#endif + +/** SDIO interrupt mode (0: INT_MODE_SDIO, 1: INT_MODE_GPIO) */ +int intmode = INT_MODE_SDIO; +/** GPIO interrupt pin number */ +int gpiopin; + +#ifdef CONFIG_OF +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +/** Region alpha2 string */ +extern char *reg_alpha2; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) +extern int country_ie_ignore; +extern int beacon_hints; +#endif +#endif +extern int cfg80211_drcs; +#endif + +int drcs_chantime_mode = 0; + +/** Auto deep sleep */ +int auto_ds; + +/** IEEE PS mode */ +int ps_mode; + +/** Max Tx buffer size */ +int max_tx_buf; + +#ifdef STA_SUPPORT +/** Max STA interfaces */ +int max_sta_bss = DEF_STA_BSS; +/** STA interface name */ +char *sta_name; +#endif + +#ifdef UAP_SUPPORT +/** Max uAP interfaces */ +int max_uap_bss = DEF_UAP_BSS; +/** uAP interface name */ +char *uap_name; +#endif + +#if defined(WIFI_DIRECT_SUPPORT) +/** Max WIFIDIRECT interfaces */ +int max_wfd_bss = DEF_WIFIDIRECT_BSS; +/** WIFIDIRECT interface name */ +char *wfd_name; +#if defined(STA_CFG80211) && defined(UAP_CFG80211) +/** max VIRTUAL bss */ +int max_vir_bss = DEF_VIRTUAL_BSS; +#endif +#endif + +/** Max NAN interfaces */ +int max_nan_bss = DEF_NAN_BSS; +/** NAN interface name */ +char *nan_name; + +#ifdef SDIO_SUSPEND_RESUME +/** PM keep power */ +int pm_keep_power = 1; +/** HS when shutdown */ +int shutdown_hs; +#endif + +#if defined(STA_SUPPORT) +/** 802.11d configuration */ +int cfg_11d; +#endif + +/** fw serial download check */ +int fw_serial = 1; + +/** napi support*/ +int napi; + +/** DPD data config file */ +char *dpd_data_cfg; + +/** CAL data config file */ +char *cal_data_cfg; +/** Init config file (MAC address, register etc.) */ +char *init_cfg; + +/** Set configuration data of Tx power limitation */ +char *txpwrlimit_cfg; +/** Set configuration data of Tx power limitatio */ +char *country_txpwrlimit; +/** Allow setting tx power table of country */ +int cntry_txpwr = 0; + +/** Init hostcmd file */ +char *init_hostcmd_cfg; + +#if defined(STA_WEXT) || defined(UAP_WEXT) +/** CFG80211 and WEXT mode */ +int cfg80211_wext = STA_WEXT_MASK | UAP_WEXT_MASK; +#else +/** CFG80211 mode */ +int cfg80211_wext = STA_CFG80211_MASK | UAP_CFG80211_MASK; +#endif + +int fw_region = 1; + +/** Work queue priority */ +int wq_sched_prio; +/** Work queue scheduling policy */ +int wq_sched_policy = SCHED_NORMAL; +/** rx_work flag */ +int rx_work; + +int hw_test; + +#ifdef CONFIG_OF +int dts_enable = 1; +#endif + +#if defined(WIFI_DIRECT_SUPPORT) +#if defined(STA_CFG80211) && defined(UAP_CFG80211) +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION +int p2p_enh; +#endif +#endif +#endif + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) +int dfs_offload = 0; +#endif + +int roamoffload_in_hs = 0; + +#ifdef ANDROID_KERNEL +int wakelock_timeout = WAKE_LOCK_TIMEOUT; +#endif + +/** woal_callbacks */ +static mlan_callbacks woal_callbacks = { + .moal_get_fw_data = moal_get_fw_data, + .moal_get_hw_spec_complete = moal_get_hw_spec_complete, + .moal_init_fw_complete = moal_init_fw_complete, + .moal_shutdown_fw_complete = moal_shutdown_fw_complete, + .moal_send_packet_complete = moal_send_packet_complete, + .moal_recv_packet = moal_recv_packet, + .moal_recv_event = moal_recv_event, + .moal_ioctl_complete = moal_ioctl_complete, + .moal_alloc_mlan_buffer = moal_alloc_mlan_buffer, + .moal_free_mlan_buffer = moal_free_mlan_buffer, + + .moal_write_reg = moal_write_reg, + .moal_read_reg = moal_read_reg, + .moal_write_data_sync = moal_write_data_sync, + .moal_read_data_sync = moal_read_data_sync, + .moal_malloc = moal_malloc, + .moal_mfree = moal_mfree, + .moal_vmalloc = moal_vmalloc, + .moal_vfree = moal_vfree, + .moal_memset = moal_memset, + .moal_memcpy = moal_memcpy, + .moal_memmove = moal_memmove, + .moal_memcmp = moal_memcmp, + .moal_udelay = moal_udelay, + .moal_get_system_time = moal_get_system_time, + .moal_init_timer = moal_init_timer, + .moal_free_timer = moal_free_timer, + .moal_start_timer = moal_start_timer, + .moal_stop_timer = moal_stop_timer, + .moal_init_lock = moal_init_lock, + .moal_free_lock = moal_free_lock, + .moal_spin_lock = moal_spin_lock, + .moal_spin_unlock = moal_spin_unlock, + .moal_print = moal_print, + .moal_print_netintf = moal_print_netintf, + .moal_assert = moal_assert, + .moal_hist_data_add = moal_hist_data_add, + .moal_updata_peer_signal = moal_updata_peer_signal, + .moal_get_host_time_ns = moal_get_host_time_ns, + .moal_do_div = moal_do_div, +}; + +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) +#if defined(WIFI_DIRECT_SUPPORT) +int drv_mode = (DRV_MODE_STA | DRV_MODE_UAP | DRV_MODE_WIFIDIRECT); +#else +int drv_mode = (DRV_MODE_STA | DRV_MODE_UAP); +#endif +#else +#ifdef STA_SUPPORT +int drv_mode = DRV_MODE_STA; +#else +int drv_mode = DRV_MODE_UAP; +#endif /* STA_SUPPORT */ +#endif /* STA_SUPPORT & UAP_SUPPORT */ + +int gtk_rekey_offload = GTK_REKEY_OFFLOAD_DISABLE; + +int pmic = 0; + +t_u32 uap_oper_ctrl = 0; + +int hs_wake_interval = 400; +int indication_gpio = 0xff; + +int indrstcfg = 0xffffffff; + +/** all the feature are enabled */ +#define DEFAULT_DEV_CAP_MASK 0xffffffff +t_u32 dev_cap_mask = DEFAULT_DEV_CAP_MASK; +int sdio_rx_aggr = MTRUE; +/******************************************************** + Global Variables +********************************************************/ + +/** Semaphore for add/remove card */ +struct semaphore AddRemoveCardSem; +/** + * The global variable of a pointer to moal_handle + * structure variable + **/ +moal_handle *m_handle[MAX_MLAN_ADAPTER]; + +/** The global variable of scan beacon buffer **/ +int fixed_beacon_buffer = 0; +/** the pointer of new fwdump fname for each dump**/ +char *fwdump_fname = NULL; + +#ifdef WIFI_DIRECT_SUPPORT +int GoAgeoutTime = 0; +#endif + +int multi_dtim = 0; + +int inact_tmo = 0; + +#ifdef DEBUG_LEVEL1 +#ifdef DEBUG_LEVEL2 +#define DEFAULT_DEBUG_MASK (0xffffffff) +#else +#define DEFAULT_DEBUG_MASK (MMSG | MFATAL | MERROR) +#endif /* DEBUG_LEVEL2 */ +t_u32 drvdbg = DEFAULT_DEBUG_MASK; + +#endif /* DEBUG_LEVEL1 */ + +int woal_open(struct net_device *dev); +int woal_close(struct net_device *dev); +int woal_set_mac_address(struct net_device *dev, void *addr); +void woal_tx_timeout(struct net_device *dev); +struct net_device_stats *woal_get_stats(struct net_device *dev); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 13, 0) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) +u16 woal_select_queue(struct net_device *dev, struct sk_buff *skb, + void *accel_priv, select_queue_fallback_t fallback); +#else +u16 woal_select_queue(struct net_device *dev, struct sk_buff *skb, + void *accel_priv); +#endif +#else +u16 woal_select_queue(struct net_device *dev, struct sk_buff *skb); +#endif +#endif + +void woal_sdio_reg_dbg(moal_handle *phandle); + +mlan_debug_info info; + +static moal_handle *reset_handle; +/** Hang workqueue */ +static struct workqueue_struct *hang_workqueue; +/** Hang work */ +static struct work_struct hang_work; + +/** + * @brief This function process FW hang + * + * @param handle Pointer to structure moal_handle + * + * @return N/A + */ +static void +woal_hang_work_queue(struct work_struct *work) +{ + int i; + ENTER(); + if (!reset_handle) { + LEAVE(); + return; + } + for (i = 0; i < reset_handle->priv_num; i++) { + if (reset_handle->priv[i] && reset_handle->priv[i]->netdev) { + PRINTM(MMSG, "Close netdev %s\n", + reset_handle->priv[i]->netdev->name); + rtnl_lock(); + dev_close(reset_handle->priv[i]->netdev); + rtnl_unlock(); + break; + } + } + reset_handle = NULL; + LEAVE(); +} + +/** + * @brief This function process FW hang + * + * @param handle Pointer to structure moal_handle + * + * @return N/A + */ +void +woal_process_hang(moal_handle *handle) +{ + ENTER(); + if (reset_handle == NULL) { + PRINTM(MMSG, "Start to process hanging\n"); + reset_handle = handle; + mlan_ioctl(handle->pmlan_adapter, NULL); + queue_work(hang_workqueue, &hang_work); +#ifdef ANDROID_KERNEL +#define WAKE_LOCK_HANG 5000 +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) + __pm_wakeup_event(&reset_handle->ws, WAKE_LOCK_HANG); +#else + wake_lock_timeout(&reset_handle->wake_lock, WAKE_LOCK_HANG); +#endif +#endif + } + LEAVE(); +} + +/** + * @brief Check if any interface is active + * + * @param handle A pointer to moal_handle + * + * + * @return MTRUE/MFALSE; + */ +t_u8 +woal_is_any_interface_active(moal_handle *handle) +{ + int i; + for (i = 0; i < handle->priv_num; i++) { +#ifdef STA_SUPPORT + if (GET_BSS_ROLE(handle->priv[i]) == MLAN_BSS_ROLE_STA) { + if (handle->priv[i]->media_connected == MTRUE) + return MTRUE; + } +#endif +#ifdef UAP_SUPPORT + if (GET_BSS_ROLE(handle->priv[i]) == MLAN_BSS_ROLE_UAP) { + if (handle->priv[i]->bss_started == MTRUE) + return MTRUE; + } +#endif + } + return MFALSE; +} + +/** @brief This function set/clear pmk to FW + * + * @param priv A Pointer to the moal_private structure + * @param action set/clear action + * + * @return 0: success fail otherwise + */ +int +woal_set_clear_pmk(moal_private *priv, t_u8 action) +{ + mlan_ioctl_req *req; + mlan_ds_sec_cfg *sec; + mlan_status status; + int ret = 0; + t_u8 zero[MLAN_MAX_KEY_LENGTH] = { 0 }; + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg)); + + if (req == NULL) { + ret = -ENOMEM; + } else { + sec = (mlan_ds_sec_cfg *)req->pbuf; + sec->sub_command = MLAN_OID_SEC_CFG_PASSPHRASE; + req->req_id = MLAN_IOCTL_SEC_CFG; + req->action = action; + + if (action == MLAN_ACT_SET) { + sec->param.passphrase.psk_type = MLAN_PSK_PMK; + if (memcmp(priv->pmk.pmk, zero, MLAN_MAX_KEY_LENGTH)) + memcpy(&sec->param.passphrase.psk.pmk.pmk, + priv->pmk.pmk, MLAN_MAX_KEY_LENGTH); + if (memcmp(priv->pmk.pmk_r0, zero, MLAN_MAX_KEY_LENGTH) + && memcmp(priv->pmk.pmk_r0_name, zero, + MLAN_MAX_PMKR0_NAME_LENGTH)) { + memcpy(&sec->param.passphrase.psk.pmk.pmk_r0, + priv->pmk.pmk_r0, MLAN_MAX_KEY_LENGTH); + memcpy(&sec->param.passphrase.psk.pmk. + pmk_r0_name, priv->pmk.pmk_r0_name, + MLAN_MAX_PMKR0_NAME_LENGTH); + } + } + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (MLAN_STATUS_SUCCESS != status) + ret = -EFAULT; + if (status != MLAN_STATUS_PENDING) + kfree(req); + } + + LEAVE(); + return ret; +} + +/** + * @brief This function handle the net interface ipaddr change event + * + * @param nb pointer to the notifier_block + * @param event event type + * @param ptr pointer to event struct + * + * @return NOTIFY_DONE or NOTIFY_OK + */ +static int +woal_netdevice_event(struct notifier_block *nb, unsigned long event, void *ptr) +{ + struct in_ifaddr *ifa = (struct in_ifaddr *)ptr; + struct net_device *ndev; + moal_private *priv; + + int ret = NOTIFY_OK; +#ifdef STA_CFG80211 + char rssi_low[11]; +#endif + ENTER(); + + ndev = ifa->ifa_dev->dev; +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) + if (!ndev || ndev->netdev_ops->ndo_open != woal_open) +#else + if (!ndev || ndev->open != woal_open) +#endif + { + PRINTM(MIOCTL, "IP changes not for us, ignore. ndev[%p]\n", + ndev); + if (ndev) + PRINTM(MIOCTL, "changes on %s\n", ndev->name); + ret = NOTIFY_DONE; + goto done; + } + priv = (moal_private *)netdev_priv(ndev); + if (priv->bss_type != MLAN_BSS_TYPE_STA +#if defined(WIFI_DIRECT_SUPPORT) + && priv->bss_type != MLAN_BSS_TYPE_WIFIDIRECT +#endif + && priv->bss_type != MLAN_BSS_TYPE_NAN) { + PRINTM(MIOCTL, "Bss type [%d] is not STA/P2P, ignore\n", + (int)priv->bss_type); + ret = NOTIFY_DONE; + goto done; + } + + switch (event) { + case NETDEV_UP: + PRINTM(MIOCTL, "[%s]: New ip addr: 0x%08x\n", ndev->name, + ifa->ifa_address); + /* Save the IP addr now */ + memcpy(priv->ip_addr, &ifa->ifa_address, + sizeof(ifa->ifa_address)); + priv->ip_addr_type = IPADDR_TYPE_IPV4; +#ifdef STA_CFG80211 + if (!hw_test && priv->roaming_enabled) { + sprintf(rssi_low, "%d", priv->rssi_low); + woal_set_rssi_low_threshold(priv, rssi_low, + MOAL_IOCTL_WAIT); + } +#endif +#ifdef STA_CFG80211 + if (priv->phandle->fw_roam_enable && + (priv->phandle->fw_roam_enable != AUTO_RECONNECT) + && !roamoffload_in_hs) { + sprintf(rssi_low, "%d", priv->rssi_low); + woal_set_rssi_low_threshold(priv, rssi_low, + MOAL_IOCTL_WAIT); + if (priv->pmk_saved) { + woal_set_clear_pmk(priv, MLAN_ACT_SET); + priv->pmk_saved = false; + } + } +#endif + break; + case NETDEV_DOWN: + PRINTM(MIOCTL, "[%s]: Ip addr removed.\n", ndev->name); + priv->ip_addr_type = IPADDR_TYPE_NONE; + memset(priv->ip_addr, 0, sizeof(priv->ip_addr)); + break; + default: + PRINTM(MIOCTL, "[%s]: Ignore event: %u\n", ndev->name, + (unsigned int)event); + ret = NOTIFY_DONE; + goto done; + } + +done: + LEAVE(); + return ret; +} + +/** + * @brief This function validates a SSID as being able to be printed + * + * @param pssid SSID structure to validate + * + * @return MTRUE or MFALSE + */ +BOOLEAN +woal_ssid_valid(mlan_802_11_ssid *pssid) +{ +#ifdef ASCII_SSID_CHECK + unsigned int ssid_idx; + + ENTER(); + + for (ssid_idx = 0; ssid_idx < pssid->ssid_len; ssid_idx++) { + if ((pssid->ssid[ssid_idx] < 0x20) || + (pssid->ssid[ssid_idx] > 0x7e)) { + LEAVE(); + return MFALSE; + } + } + LEAVE(); +#endif + return MTRUE; +} + +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) +/** + * @brief Remain on Channel timeout function + * + * @param context A pointer to context + * @return N/A + */ +void +woal_remain_timer_func(void *context) +{ + moal_handle *handle = (moal_handle *)context; + moal_private *priv = handle->priv[handle->remain_bss_index]; + + ENTER(); + + PRINTM(MEVENT, "remain_timer fired.\n"); + if (handle->cookie) { + cfg80211_remain_on_channel_expired( +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 6, 0) + priv->netdev, +#else + priv->wdev, +#endif + handle->cookie, + &handle->chan, +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 8, 0) + handle->channel_type, +#endif + GFP_ATOMIC); + handle->cookie = 0; + } + handle->is_remain_timer_set = MFALSE; + + LEAVE(); + return; +} +#endif +#endif + +#if defined(WIFI_DIRECT_SUPPORT) +#if defined(STA_CFG80211) && defined(UAP_CFG80211) +/** + * @brief GO timeout function + * + * @param context A pointer to context + * @return N/A + */ +void +woal_go_timer_func(void *context) +{ + moal_handle *handle = (moal_handle *)context; + + ENTER(); + + PRINTM(MEVENT, "go_timer fired.\n"); + handle->is_go_timer_set = MFALSE; + + LEAVE(); + return; +} +#endif +#endif + +/** + * @brief check if we already connect to the AP. + * @param priv A pointer to moal_private structure + * @param ssid_bssid A pointer to mlan_ssid_bssid structure + * + * @return MTRUE/MFALSE; + */ +int +woal_is_connected(moal_private *priv, mlan_ssid_bssid *ssid_bssid) +{ + mlan_bss_info bss_info; + int ret = MFALSE; + t_u8 zero_mac[] = { 0, 0, 0, 0, 0, 0 }; + ENTER(); + memset(&bss_info, 0, sizeof(bss_info)); + if (MLAN_STATUS_SUCCESS != + woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info)) + goto done; + if (bss_info.media_connected) { + if (memcmp(ssid_bssid->bssid, zero_mac, sizeof(zero_mac))) { + if (ssid_bssid->ssid.ssid_len) { /* compare ssid and bssid */ + if ((ssid_bssid->ssid.ssid_len == + bss_info.ssid.ssid_len) && + !memcmp(ssid_bssid->ssid.ssid, + bss_info.ssid.ssid, + bss_info.ssid.ssid_len) && + !memcmp(ssid_bssid->bssid, bss_info.bssid, + MLAN_MAC_ADDR_LENGTH)) + ret = MTRUE; + } else { /* compare bssid */ + if (!memcmp + (ssid_bssid->bssid, bss_info.bssid, + MLAN_MAC_ADDR_LENGTH)) { + memcpy(&ssid_bssid->ssid, + &bss_info.ssid, + sizeof(bss_info.ssid)); + ret = MTRUE; + } + } + } else { /* compare ssid */ + if (ssid_bssid->ssid.ssid_len && + (ssid_bssid->ssid.ssid_len == + bss_info.ssid.ssid_len) && + !memcmp(ssid_bssid->ssid.ssid, bss_info.ssid.ssid, + bss_info.ssid.ssid_len)) { + memcpy(&ssid_bssid->bssid, &bss_info.bssid, + MLAN_MAC_ADDR_LENGTH); + ret = MTRUE; + } + } + } +done: + LEAVE(); + return ret; +} + +/** + * @brief Look up specific IE in a buf + * + * @param ie Pointer to IEs + * @param len Total length of ie + * @param id Element id to lookup + * + * @return Pointer of the specific IE -- success, NULL -- fail + */ +const t_u8 * +woal_parse_ie_tlv(const t_u8 *ie, int len, t_u8 id) +{ + int left_len = len; + const t_u8 *pos = ie; + int length; + + /* IE format: + * | u8 | id | + * | u8 | len | + * | var | data | + */ + while (left_len >= 2) { + length = *(pos + 1); + if ((*pos == id) && (length + 2) <= left_len) + return pos; + pos += (length + 2); + left_len -= (length + 2); + } + + return NULL; +} + +/** + * @brief Get mode + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option (MOAL_IOCTL_WAIT or MOAL_NO_WAIT) + * + * @return Wireless mode + */ +t_u32 +woal_get_mode(moal_private *priv, t_u8 wait_option) +{ + int ret = 0; + mlan_ds_bss *bss = NULL; + mlan_ioctl_req *req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + t_u32 mode = 0; + + ENTER(); + +#if defined(STA_WEXT) || defined(UAP_WEXT) + mode = priv->w_stats.status; +#endif + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + bss = (mlan_ds_bss *)req->pbuf; + bss->sub_command = MLAN_OID_BSS_MODE; + req->req_id = MLAN_IOCTL_BSS; + req->action = MLAN_ACT_GET; + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, wait_option); + if (status == MLAN_STATUS_SUCCESS) { + switch (bss->param.bss_mode) { + case MLAN_BSS_MODE_INFRA: + mode = MW_MODE_INFRA; + break; + case MLAN_BSS_MODE_IBSS: + mode = MW_MODE_ADHOC; + break; + default: + mode = MW_MODE_AUTO; + break; + } + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return mode; +} + +/******************************************************** + Local Functions +********************************************************/ +/** + * @brief This function update the default firmware name + * + * @param handle A pointer to moal_handle structure + * + * @return N/A + */ +static void +woal_update_firmware_name(moal_handle *handle) +{ + if (fw_name) { + handle->drv_mode.fw_name = fw_name; + } else { + if (!fw_serial || handle->fw_reload || fw_reload) + handle->drv_mode.fw_name = DEFAULT_WLAN_FW_NAME; + else +#if defined(UAP_SUPPORT) && defined(STA_SUPPORT) + handle->drv_mode.fw_name = DEFAULT_AP_STA_FW_NAME; +#else +#ifdef UAP_SUPPORT + handle->drv_mode.fw_name = DEFAULT_AP_FW_NAME; +#else + handle->drv_mode.fw_name = DEFAULT_FW_NAME; +#endif /* UAP_SUPPORT */ +#endif /* UAP_SUPPORT && STA_SUPPORT */ + } +} + +/** + * @brief This function dynamically populates the driver mode table + * + * @param handle A pointer to moal_handle structure + * @param drv_mode_local Driver mode + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +woal_update_drv_tbl(moal_handle *handle, int drv_mode_local) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + unsigned int intf_num = 0; + int i = 0, j = 0; + mlan_bss_attr *bss_tbl = NULL; +#if defined(WIFI_DIRECT_SUPPORT) +#if defined(STA_CFG80211) && defined(UAP_CFG80211) + int last_wfd_index = 0; +#endif +#endif + + ENTER(); + + /* Calculate number of interfaces */ +#ifdef STA_SUPPORT + if (drv_mode_local & DRV_MODE_STA) { + if ((max_sta_bss < 1) || (max_sta_bss > MAX_STA_BSS)) { + PRINTM(MWARN, + "Unsupported max_sta_bss (%d), setting to default\n", + max_sta_bss); + max_sta_bss = DEF_STA_BSS; + } + intf_num += max_sta_bss; + } +#endif /* STA_SUPPORT */ + +#ifdef UAP_SUPPORT + if (drv_mode_local & DRV_MODE_UAP) { + if ((max_uap_bss < 1) || (max_uap_bss > MAX_UAP_BSS)) { + PRINTM(MWARN, + "Unsupported max_uap_bss (%d), setting to default\n", + max_uap_bss); + max_uap_bss = DEF_UAP_BSS; + } + intf_num += max_uap_bss; + } +#endif /* UAP_SUPPORT */ + +#if defined(WIFI_DIRECT_SUPPORT) + if (drv_mode_local & DRV_MODE_WIFIDIRECT) { + if ((max_wfd_bss < 1) || (max_wfd_bss > MAX_WIFIDIRECT_BSS)) { + PRINTM(MWARN, + "Unsupported max_wfd_bss (%d), setting to default\n", + max_wfd_bss); + max_wfd_bss = DEF_WIFIDIRECT_BSS; + } + intf_num += max_wfd_bss; +#if defined(STA_CFG80211) && defined(UAP_CFG80211) + intf_num += max_vir_bss; +#endif + } +#endif /* WIFI_DIRECT_SUPPORT && V14_FEATURE */ + + if (drv_mode_local & DRV_MODE_NAN) { + if ((max_nan_bss < 1) || (max_nan_bss > MAX_NAN_BSS)) { + PRINTM(MWARN, + "Unsupported max_nan_bss (%d), setting to default\n", + max_nan_bss); + max_nan_bss = DEF_NAN_BSS; + } + intf_num += max_nan_bss; + } + + /* Create BSS attribute table */ + if ((intf_num == 0) || (intf_num > MLAN_MAX_BSS_NUM)) { + PRINTM(MERROR, "Unsupported number of BSS %d\n", intf_num); + ret = MLAN_STATUS_FAILURE; + goto done; + } else { + /* Create new table */ + bss_tbl = kmalloc(sizeof(mlan_bss_attr) * intf_num, GFP_KERNEL); + if (!bss_tbl) { + PRINTM(MERROR, + "Could not create BSS attribute table\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + } + + /* Populate BSS attribute table */ +#ifdef STA_SUPPORT + if (drv_mode_local & DRV_MODE_STA) { + for (j = 0; j < max_sta_bss; j++) { + if (i >= intf_num) + break; + bss_tbl[i].bss_type = MLAN_BSS_TYPE_STA; + bss_tbl[i].frame_type = MLAN_DATA_FRAME_TYPE_ETH_II; + bss_tbl[i].active = MTRUE; + bss_tbl[i].bss_priority = 0; + bss_tbl[i].bss_num = j; + bss_tbl[i].bss_virtual = MFALSE; + i++; + } + } +#endif /* STA_SUPPORT */ + +#ifdef UAP_SUPPORT + if (drv_mode_local & DRV_MODE_UAP) { + for (j = 0; j < max_uap_bss; j++) { + if (i >= intf_num) + break; + bss_tbl[i].bss_type = MLAN_BSS_TYPE_UAP; + bss_tbl[i].frame_type = MLAN_DATA_FRAME_TYPE_ETH_II; + bss_tbl[i].active = MTRUE; + bss_tbl[i].bss_priority = 0; + bss_tbl[i].bss_num = j; + bss_tbl[i].bss_virtual = MFALSE; + i++; + } + } +#endif /* UAP_SUPPORT */ + +#if defined(WIFI_DIRECT_SUPPORT) + if (drv_mode_local & DRV_MODE_WIFIDIRECT) { + for (j = 0; j < max_wfd_bss; j++) { + if (i >= intf_num) + break; + bss_tbl[i].bss_type = MLAN_BSS_TYPE_WIFIDIRECT; + bss_tbl[i].frame_type = MLAN_DATA_FRAME_TYPE_ETH_II; + bss_tbl[i].active = MTRUE; + bss_tbl[i].bss_priority = 0; + bss_tbl[i].bss_num = j; + bss_tbl[i].bss_virtual = MFALSE; + i++; + } +#if defined(STA_CFG80211) && defined(UAP_CFG80211) + last_wfd_index = j; +#endif + } +#endif /* WIFI_DIRECT_SUPPORT && V14_FEATURE */ + + if (drv_mode_local & DRV_MODE_NAN) { + for (j = 0; j < max_nan_bss; j++) { + if (i >= intf_num) + break; + bss_tbl[i].bss_type = MLAN_BSS_TYPE_NAN; + bss_tbl[i].frame_type = MLAN_DATA_FRAME_TYPE_ETH_II; + bss_tbl[i].active = MTRUE; + bss_tbl[i].bss_priority = 0; + bss_tbl[i].bss_num = j; + bss_tbl[i].bss_virtual = MFALSE; + i++; + } + } + +#if defined(WIFI_DIRECT_SUPPORT) +#if defined(STA_CFG80211) && defined(UAP_CFG80211) + /** append virtual interface at the end of table */ + for (j = 0; j < max_vir_bss; j++) { + if (i >= intf_num) + break; + bss_tbl[i].bss_type = MLAN_BSS_TYPE_WIFIDIRECT; + bss_tbl[i].frame_type = MLAN_DATA_FRAME_TYPE_ETH_II; + bss_tbl[i].active = MTRUE; + bss_tbl[i].bss_priority = 0; + bss_tbl[i].bss_num = j + last_wfd_index; + bss_tbl[i].bss_virtual = MTRUE; + i++; + } +#endif +#endif + /* Clear existing table, if any */ + kfree(handle->drv_mode.bss_attr); + handle->drv_mode.bss_attr = NULL; + + /* Create moal_drv_mode entry */ + handle->drv_mode.drv_mode = drv_mode; + handle->drv_mode.intf_num = intf_num; + handle->drv_mode.bss_attr = bss_tbl; + + /* update default firmware name */ + woal_update_firmware_name(handle); +done: + LEAVE(); + return ret; +} + +#ifdef CONFIG_OF +/** + * @brief This function read the initial parameter from device tress + * + * @return N/A + */ +static void +woal_init_from_dev_tree(void) +{ + struct device_node *dt_node = NULL; + struct property *prop; + t_u32 data; + const char *string_data; + + ENTER(); + + if (!dts_enable) { + PRINTM(MIOCTL, "DTS is disabled!"); + return; + } + + dt_node = of_find_node_by_name(NULL, "sd8xxx-wlan"); + if (!dt_node) { + LEAVE(); + return; + } + for_each_property_of_node(dt_node, prop) { + if (!strncmp(prop->name, "drv_mode", strlen("drv_mode"))) { + if (!of_property_read_u32(dt_node, prop->name, &data)) { + PRINTM(MIOCTL, "drv_mode=0x%x\n", data); + drv_mode = data; + } + } +#ifdef DEBUG_LEVEL1 + else if (!strncmp(prop->name, "drvdbg", strlen("drvdbg"))) { + if (!of_property_read_u32(dt_node, prop->name, &data)) { + PRINTM(MIOCTL, "drvdbg=0x%x\n", data); + drvdbg = data; + } + } +#endif + else if (!strncmp + (prop->name, "dev_cap_mask", strlen("dev_cap_mask"))) { + if (!of_property_read_u32(dt_node, prop->name, &data)) { + PRINTM(MIOCTL, "dev_cap_mask=0x%x\n", data); + dev_cap_mask = data; + } + } else if (!strncmp(prop->name, "hw_test", strlen("hw_test"))) { + if (!of_property_read_u32(dt_node, prop->name, &data)) { + PRINTM(MIOCTL, "hw_test=0x%x\n", data); + hw_test = data; + } + } +#ifdef MFG_CMD_SUPPORT + else if (!strncmp(prop->name, "mfg_mode", strlen("mfg_mode"))) { + if (!of_property_read_u32(dt_node, prop->name, &data)) { + PRINTM(MIOCTL, "mfg_mode=0x%x\n", data); + mfg_mode = data; + } + } +#endif + else if (!strncmp(prop->name, "mac_addr", strlen("mac_addr"))) { + if (!of_property_read_string + (dt_node, prop->name, &string_data)) { + mac_addr = (char *)string_data; + PRINTM(MIOCTL, "mac_addr=%s\n", mac_addr); + } + } else if (!strncmp(prop->name, "fw_name", strlen("fw_name"))) { + if (!of_property_read_string + (dt_node, prop->name, &string_data)) { + fw_name = (char *)string_data; + PRINTM(MIOCTL, "fw_name=%s\n", fw_name); + } + } +#if defined(STA_WEXT) || defined(UAP_WEXT) + else if (!strncmp + (prop->name, "cfg80211_wext", + strlen("cfg80211_wext"))) { + if (!of_property_read_u32(dt_node, prop->name, &data)) { + PRINTM(MIOCTL, "cfg80211_wext=0x%x\n", data); + cfg80211_wext = data; + } + } +#endif +#ifdef STA_SUPPORT + else if (!strncmp(prop->name, "sta_name", strlen("sta_name"))) { + if (!of_property_read_string + (dt_node, prop->name, &string_data)) { + sta_name = (char *)string_data; + PRINTM(MIOCTL, "sta_name=%s\n", sta_name); + } + } +#endif +#if defined(WIFI_DIRECT_SUPPORT) + else if (!strncmp(prop->name, "wfd_name", strlen("wfd_name"))) { + if (!of_property_read_string + (dt_node, prop->name, &string_data)) { + wfd_name = (char *)string_data; + PRINTM(MIOCTL, "wfd_name=%s\n", wfd_name); + } + } +#endif +#if defined(STA_CFG80211) || defined(UAP_CFG80211) + else if (!strncmp + (prop->name, "reg_alpha2", strlen("reg_alpha2"))) { + if (!of_property_read_string + (dt_node, prop->name, &string_data)) { + reg_alpha2 = (char *)string_data; + PRINTM(MIOCTL, "reg_alpha2=%s\n", reg_alpha2); + } + } +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + else if (!strncmp + (prop->name, "country_ie_ignore", + strlen("country_ie_ignore"))) { + if (!of_property_read_u32(dt_node, prop->name, &data)) { + PRINTM(MIOCTL, "country_ie_ignore=0x%x\n", + data); + country_ie_ignore = data; + } + } else if (!strncmp + (prop->name, "beacon_hints", + strlen("beacon_hints"))) { + if (!of_property_read_u32(dt_node, prop->name, &data)) { + PRINTM(MIOCTL, "beacon_hints=0x%x\n", data); + beacon_hints = data; + } + } +#endif +#endif +#if defined(WIFI_DIRECT_SUPPORT) +#if defined(STA_CFG80211) && defined(UAP_CFG80211) + else if (!strncmp + (prop->name, "max_vir_bss", strlen("max_vir_bss"))) { + if (!of_property_read_u32(dt_node, prop->name, &data)) { + PRINTM(MIOCTL, "max_vir_bss=0x%x\n", data); + max_vir_bss = data; + } + } else if (!strncmp(prop->name, "p2p_enh", strlen("p2p_enh"))) { + if (!of_property_read_u32(dt_node, prop->name, &data)) { + PRINTM(MIOCTL, "p2p_enh=0x%x\n", data); + p2p_enh = data; + } + } else if (!strncmp + (prop->name, "cfg80211_drcs", + strlen("cfg80211_drcs"))) { + if (!of_property_read_u32(dt_node, prop->name, &data)) { + PRINTM(MIOCTL, "cfg80211_drcs=0x%x\n", data); + cfg80211_drcs = data; + } + } +#endif +#endif + else if (!strncmp + (prop->name, "dpd_data_cfg", strlen("dpd_data_cfg"))) { + if (!of_property_read_string + (dt_node, prop->name, &string_data)) { + dpd_data_cfg = (char *)string_data; + PRINTM(MIOCTL, "dpd_data_cfg=%s\n", + dpd_data_cfg); + } + } else if (!strncmp(prop->name, "init_cfg", strlen("init_cfg"))) { + if (!of_property_read_string + (dt_node, prop->name, &string_data)) { + init_cfg = (char *)string_data; + PRINTM(MIOCTL, "init_cfg=%s\n", init_cfg); + } + } else if (!strncmp + (prop->name, "cal_data_cfg", + strlen("cal_data_cfg"))) { + if (!of_property_read_string + (dt_node, prop->name, &string_data)) { + cal_data_cfg = (char *)string_data; + PRINTM(MIOCTL, "cal_data_cfg=%s\n", + cal_data_cfg); + } + } else if (!strncmp + (prop->name, "txpwrlimit_cfg", + strlen("txpwrlimit_cfg"))) { + if (!of_property_read_string + (dt_node, prop->name, &string_data)) { + txpwrlimit_cfg = (char *)string_data; + PRINTM(MIOCTL, "txpwrlimit_cfg=%s\n", + txpwrlimit_cfg); + } + } else if (!strncmp + (prop->name, "cntry_txpwr", strlen("cntry_txpwr"))) { + if (!of_property_read_u32(dt_node, prop->name, &data)) { + cntry_txpwr = data; + PRINTM(MIOCTL, "cntry_txpwr=%d\n", cntry_txpwr); + } + } else if (!strncmp(prop->name, "pmic", strlen("pmic"))) { + if (!of_property_read_u32(dt_node, prop->name, &data)) { + pmic = data; + PRINTM(MIOCTL, "pmic=%d\n", pmic); + } + } else if (!strncmp + (prop->name, "hs_wake_interval", + strlen("hs_wake_interval"))) { + if (!of_property_read_u32(dt_node, prop->name, &data)) { + hs_wake_interval = data; + PRINTM(MIOCTL, "hs_wake_interval=%d\n", + hs_wake_interval); + } + } else if (!strncmp + (prop->name, "indication_gpio", + strlen("indication_gpio"))) { + if (!of_property_read_u32(dt_node, prop->name, &data)) { + indication_gpio = (t_u8)data; + PRINTM(MIOCTL, "indication_gpio=%d\n", + indication_gpio); + } + } +#ifdef WIFI_DIRECT_SUPPORT + else if (!strncmp + (prop->name, "GoAgeoutTime", strlen("GoAgeoutTime"))) { + if (!of_property_read_u32(dt_node, prop->name, &data)) { + GoAgeoutTime = data; + PRINTM(MIOCTL, "GoAgeoutTime=%d\n", + GoAgeoutTime); + } + } +#endif + else if (!strncmp(prop->name, "indrstcfg", strlen("indrstcfg"))) { + if (!of_property_read_u32(dt_node, prop->name, &data)) { + indrstcfg = data; + PRINTM(MIOCTL, "indrstcfg=%d\n", indrstcfg); + } + } else if (!strncmp + (prop->name, "drcs_chantime_mode", + strlen("drcs_chantime_mode"))) { + if (!of_property_read_u32(dt_node, prop->name, &data)) { + drcs_chantime_mode = data; + PRINTM(MIOCTL, "drcs_chantime_mode=%d\n", + drcs_chantime_mode); + } + } else if (!strncmp + (prop->name, "fixed_beacon_buffer", + strlen("fixed_beacon_buffer"))) { + if (!of_property_read_u32(dt_node, prop->name, &data)) { + fixed_beacon_buffer = data; + PRINTM(MIOCTL, "fixed_beacon_buffer=%d\n", + fixed_beacon_buffer); + } + } else if (!strncmp + (prop->name, "multi_dtim", strlen("multi_dtim"))) { + if (!of_property_read_u32(dt_node, prop->name, &data)) { + multi_dtim = data; + PRINTM(MIOCTL, "multi_dtim=%d\n", multi_dtim); + } + } else if (!strncmp + (prop->name, "inact_tmo", strlen("inact_tmo"))) { + if (!of_property_read_u32(dt_node, prop->name, &data)) { + inact_tmo = data; + PRINTM(MIOCTL, "inact_tmo=%d\n", inact_tmo); + } + } +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + else if (!strncmp + (prop->name, "dfs_offload", strlen("dfs_offload"))) { + if (!of_property_read_u32(dt_node, prop->name, &data)) { + dfs_offload = data; + PRINTM(MIOCTL, "dfs_offload=%d\n", dfs_offload); + } + } +#endif + else if (!strncmp + (prop->name, "roamoffload_in_hs", + strlen("roamoffload_in_hs"))) { + if (!of_property_read_u32(dt_node, prop->name, &data)) { + roamoffload_in_hs = data; + PRINTM(MIOCTL, "roamoffload_in_hs=%d\n", + roamoffload_in_hs); + } + } else if (!strncmp + (prop->name, "gtk_rekey_offload", + strlen("gtk_rekey_offload"))) { + if (!of_property_read_u32(dt_node, prop->name, &data)) { + gtk_rekey_offload = data; + PRINTM(MIOCTL, "gtk_rekey_offload=%d\n", + gtk_rekey_offload); + } + } + } + LEAVE(); + return; +} +#endif + +/** + * @brief This function initializes software + * + * @param handle A pointer to moal_handle structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status +woal_init_sw(moal_handle *handle) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + unsigned int i; + mlan_device device; + t_void *pmlan; + + ENTER(); + + /* Initialize moal_handle structure */ + handle->hardware_status = HardwareStatusInitializing; + handle->main_state = MOAL_STATE_IDLE; + +#ifdef STA_SUPPORT + if ((drv_mode & DRV_MODE_STA) +#ifdef STA_WEXT + && !IS_STA_WEXT(cfg80211_wext) +#endif +#ifdef STA_CFG80211 + && !IS_STA_CFG80211(cfg80211_wext) +#endif + ) { + PRINTM(MERROR, + "STA without WEXT or CFG80211 bit definition!\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } +#endif /* STA_SUPPORT */ + +#if defined(STA_CFG80211) && defined(STA_SUPPORT) + if (IS_STA_CFG80211(cfg80211_wext)) + cfg80211_wext |= STA_CFG80211_MASK | UAP_CFG80211_MASK; +#endif + +#if defined(UAP_CFG80211) && defined(UAP_SUPPORT) + if (IS_UAP_CFG80211(cfg80211_wext)) + cfg80211_wext |= STA_CFG80211_MASK | UAP_CFG80211_MASK; +#endif + + memcpy(handle->driver_version, driver_version, strlen(driver_version)); + + if (woal_update_drv_tbl(handle, drv_mode) != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "Could not update driver mode table\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + /** user config file */ + init_waitqueue_head(&handle->init_user_conf_wait_q); + + /* PnP and power profile */ + handle->surprise_removed = MFALSE; + init_waitqueue_head(&handle->init_wait_q); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) + spin_lock_init(&handle->queue_lock); +#endif + spin_lock_init(&handle->driver_lock); + spin_lock_init(&handle->ioctl_lock); + spin_lock_init(&handle->scan_req_lock); + +#if defined(SDIO_SUSPEND_RESUME) + handle->is_suspended = MFALSE; + handle->hs_activated = MFALSE; + handle->hs_auto_arp = MTRUE; + handle->suspend_fail = MFALSE; +#ifdef SDIO_SUSPEND_RESUME + handle->suspend_notify_req = MFALSE; +#endif + handle->hs_skip_count = 0; + handle->hs_force_count = 0; + handle->cmd52_func = 0; + handle->cmd52_reg = 0; + handle->cmd52_val = 0; +#if defined(STA_CFG80211) || defined(UAP_CFG80211) + handle->scan_chan_gap = DEF_SCAN_CHAN_GAP; +#ifdef WIFI_DIRECT_SUPPORT + handle->miracast_scan_time = DEF_MIRACAST_SCAN_TIME; +#define DEF_NOA_DURATION 0 +#define DEF_NOA_INTERVAL 100 + handle->noa_duration = DEF_NOA_DURATION; + handle->noa_interval = DEF_NOA_INTERVAL; +#endif +#endif + init_waitqueue_head(&handle->hs_activate_wait_q); +#endif + + /* Initialize measurement wait queue */ + handle->meas_wait_q_woken = MFALSE; + handle->meas_start_jiffies = 0; + handle->cac_period = MFALSE; + handle->delay_bss_start = MFALSE; + init_waitqueue_head(&handle->meas_wait_q); +#if defined(UAP_SUPPORT) + handle->chsw_wait_q_woken = MFALSE; + init_waitqueue_head(&handle->chsw_wait_q); +#endif + +#ifdef DFS_TESTING_SUPPORT + handle->cac_period_jiffies = 0; +#endif +#ifdef UAP_CFG80211 +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) + memset(&handle->dfs_channel, 0, sizeof(struct cfg80211_chan_def)); + woal_initialize_timer(&handle->cac_timer, woal_cac_timer_func, handle); + handle->is_cac_timer_set = MFALSE; + handle->cac_bss_index = 0xff; +#endif +#endif + handle->mon_if = NULL; + +#ifdef REASSOCIATION + MOAL_INIT_SEMAPHORE(&handle->reassoc_sem); + handle->reassoc_on = 0; + + /* Initialize the timer for the reassociation */ + woal_initialize_timer(&handle->reassoc_timer, + woal_reassoc_timer_func, handle); + + handle->is_reassoc_timer_set = MFALSE; +#endif /* REASSOCIATION */ + +#if defined(WIFI_DIRECT_SUPPORT) +#if defined(STA_CFG80211) && defined(UAP_CFG80211) + /* Initialize the timer for GO timeout */ + woal_initialize_timer(&handle->go_timer, woal_go_timer_func, handle); + + handle->is_go_timer_set = MFALSE; +#endif +#endif + +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + handle->remain_on_channel = MFALSE; + + /* Initialize the timer for remain on channel */ + woal_initialize_timer(&handle->remain_timer, + woal_remain_timer_func, handle); + + handle->is_remain_timer_set = MFALSE; +#endif +#endif + + /* Register to MLAN */ + memset(&device, 0, sizeof(mlan_device)); + device.pmoal_handle = handle; + + device.sdio_rx_aggr_enable = sdio_rx_aggr; + +#ifdef MFG_CMD_SUPPORT + device.mfg_mode = (t_u32)mfg_mode; +#endif + device.int_mode = (t_u32)intmode; + device.gpio_pin = (t_u32)gpiopin; +#ifdef DEBUG_LEVEL1 + device.drvdbg = drvdbg; +#endif + device.fixed_beacon_buffer = (t_u32)fixed_beacon_buffer; + device.auto_ds = (t_u32)auto_ds; + device.ps_mode = (t_u32)ps_mode; + device.max_tx_buf = (t_u32)max_tx_buf; +#if defined(STA_SUPPORT) + device.cfg_11d = (t_u32)cfg_11d; +#endif + device.indrstcfg = (t_u32)indrstcfg; + device.drcs_chantime_mode = (t_u32)drcs_chantime_mode; +#if defined(SDIO_MULTI_PORT_TX_AGGR) || defined(SDIO_MULTI_PORT_RX_AGGR) +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36) + device.max_segs = + ((struct sdio_mmc_card *)handle->card)->func->card->host-> + max_segs; + device.max_seg_size = + ((struct sdio_mmc_card *)handle->card)->func->card->host-> + max_seg_size; +#endif + PRINTM(MMSG, "SDIO: max_segs=%d max_seg_size=%d\n", device.max_segs, + device.max_seg_size); +#endif +#ifdef SDIO_MULTI_PORT_TX_AGGR +#ifdef MMC_QUIRK_BLKSZ_FOR_BYTE_MODE + device.mpa_tx_cfg = MLAN_INIT_PARA_ENABLED; +#else + device.mpa_tx_cfg = MLAN_INIT_PARA_DISABLED; +#endif +#endif +#ifdef SDIO_MULTI_PORT_RX_AGGR +#ifdef MMC_QUIRK_BLKSZ_FOR_BYTE_MODE + device.mpa_rx_cfg = MLAN_INIT_PARA_ENABLED; +#else + device.mpa_rx_cfg = MLAN_INIT_PARA_DISABLED; +#endif +#endif + + if (rx_work == MLAN_INIT_PARA_ENABLED) + device.rx_work = MTRUE; + else if (rx_work == MLAN_INIT_PARA_DISABLED) + device.rx_work = MFALSE; + else { + if (num_possible_cpus() > 1) + device.rx_work = MTRUE; + else + device.rx_work = MFALSE; + } + PRINTM(MMSG, "rx_work=%d cpu_num=%d\n", device.rx_work, + num_possible_cpus()); + if (napi) + device.rx_work = MTRUE; + + device.dev_cap_mask = dev_cap_mask; + + device.multi_dtim = multi_dtim; + + device.inact_tmo = inact_tmo; + device.hs_wake_interval = hs_wake_interval; + device.indication_gpio = indication_gpio; + + for (i = 0; i < handle->drv_mode.intf_num; i++) { + device.bss_attr[i].bss_type = + handle->drv_mode.bss_attr[i].bss_type; + device.bss_attr[i].frame_type = + handle->drv_mode.bss_attr[i].frame_type; + device.bss_attr[i].active = handle->drv_mode.bss_attr[i].active; + device.bss_attr[i].bss_priority = + handle->drv_mode.bss_attr[i].bss_priority; + device.bss_attr[i].bss_num = + handle->drv_mode.bss_attr[i].bss_num; + device.bss_attr[i].bss_virtual = + handle->drv_mode.bss_attr[i].bss_virtual; + } + memcpy(&device.callbacks, &woal_callbacks, sizeof(mlan_callbacks)); + if (fw_region) + device.fw_region = MTRUE; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32) + sdio_claim_host(((struct sdio_mmc_card *)handle->card)->func); +#endif + if (MLAN_STATUS_SUCCESS == mlan_register(&device, &pmlan)) + handle->pmlan_adapter = pmlan; + else + ret = MLAN_STATUS_FAILURE; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32) + sdio_release_host(((struct sdio_mmc_card *)handle->card)->func); +#endif + + LEAVE(); + return ret; +} + +/** + * @brief This function frees the structure of moal_handle + * + * @param handle A pointer to moal_handle structure + * + * @return N/A + */ +static void +woal_free_moal_handle(moal_handle *handle) +{ + ENTER(); + if (!handle) { + PRINTM(MERROR, "The handle is NULL\n"); + LEAVE(); + return; + } +#if defined(STA_CFG80211) || defined(UAP_CFG80211) + /* Unregister wiphy device and free */ + if (handle->wiphy) { + wiphy_unregister(handle->wiphy); + wiphy_free(handle->wiphy); +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + kfree(handle->regd); +#endif + handle->wiphy = NULL; + } +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25) + if ((handle->nl_sk) && ((handle->nl_sk)->sk_socket)) { + sock_release((handle->nl_sk)->sk_socket); + handle->nl_sk = NULL; + } +#else + netlink_kernel_release(handle->nl_sk); +#endif + + if (handle->pmlan_adapter) + mlan_unregister(handle->pmlan_adapter); + + /* Free BSS attribute table */ + kfree(handle->drv_mode.bss_attr); + handle->drv_mode.bss_attr = NULL; + PRINTM(MINFO, "Free Adapter\n"); + if (atomic_read(&handle->lock_count) || + atomic_read(&handle->malloc_count) || + atomic_read(&handle->mbufalloc_count)) { + PRINTM(MERROR, + "mlan has memory leak: lock_count=%d, malloc_count=%d, mbufalloc_count=%d\n", + atomic_read(&handle->lock_count), + atomic_read(&handle->malloc_count), + atomic_read(&handle->mbufalloc_count)); + } + /* Free allocated memory for fwdump filename */ + kfree(handle->fwdump_fname); + if (fwdump_fname) { + kfree(fwdump_fname); + fwdump_fname = NULL; + } + /* Free the moal handle itself */ + kfree(handle); + LEAVE(); +} + +/** + * @brief WOAL get one line data from ASCII format data + * + * @param data Source data + * @param size Source data length + * @param line_pos Destination data + * @return routnine status + */ +static t_size +parse_cfg_get_line(t_u8 *data, t_size size, t_u8 *line_pos) +{ + t_u8 *src, *dest; + static t_s32 pos; + + ENTER(); + + if (pos >= size) { /* reach the end */ + pos = 0; /* Reset position for rfkill */ + LEAVE(); + return -1; + } + memset(line_pos, 0, MAX_LINE_LEN); + src = data + pos; + dest = line_pos; + + while (pos < size && *src != '\x0A' && *src != '\0') { + if (*src != ' ' && *src != '\t') /* parse space */ + *dest++ = *src++; + else + src++; + pos++; + } + /* parse new line */ + pos++; + *dest = '\0'; + LEAVE(); + return strlen(line_pos); +} + +/** + * @brief Process register access request + * @param type_string String format Register type + * @param offset_string String format Register offset + * @param value_string String format Pointer to value + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +static t_u32 +woal_process_regrdwr(moal_handle *handle, t_u8 *type_string, + t_u8 *offset_string, t_u8 *value_string) +{ + mlan_status ret = MLAN_STATUS_FAILURE; + int type, offset, value; + pmlan_ioctl_req ioctl_req = NULL; + mlan_ds_reg_mem *reg = NULL; + + ENTER(); + + /* Alloc ioctl_req */ + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_reg_mem)); + + if (ioctl_req == NULL) { + PRINTM(MERROR, "Can't alloc memory\n"); + goto done; + } + + if (MLAN_STATUS_SUCCESS != woal_atoi(&type, type_string)) + goto done; + if (MLAN_STATUS_SUCCESS != woal_atoi(&offset, offset_string)) + goto done; + if (MLAN_STATUS_SUCCESS != woal_atoi(&value, value_string)) + goto done; + + ioctl_req->req_id = MLAN_IOCTL_REG_MEM; + ioctl_req->action = MLAN_ACT_SET; + + reg = (mlan_ds_reg_mem *)ioctl_req->pbuf; + reg->sub_command = MLAN_OID_REG_RW; + if (type < 5) { + reg->param.reg_rw.type = type; + } else { + PRINTM(MERROR, "Unsupported Type\n"); + goto done; + } + reg->param.reg_rw.offset = offset; + reg->param.reg_rw.value = value; + + /* request ioctl for STA */ + ret = woal_request_ioctl(handle->priv[0], ioctl_req, MOAL_IOCTL_WAIT); + if (ret != MLAN_STATUS_SUCCESS) + goto done; + PRINTM(MINFO, "Register type: %d, offset: 0x%x, value: 0x%x\n", type, + offset, value); + ret = MLAN_STATUS_SUCCESS; + +done: + if (ret != MLAN_STATUS_PENDING) + kfree(ioctl_req); + LEAVE(); + return ret; +} + +#ifdef UAP_SUPPORT +/** + * @brief set uap operation contrl value + * + * @param handle MOAL handle + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status +woal_set_uap_operation_ctrl(moal_handle *handle) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + moal_private *priv = NULL; + mlan_ds_bss *bss = NULL; + mlan_ioctl_req *req = NULL; + + ENTER(); + + priv = woal_get_priv(handle, MLAN_BSS_ROLE_UAP); + if (!priv) { + PRINTM(MERROR, + "woal_set_uap_operation_ctrl failed, no uap interface\n"); + LEAVE(); + return ret; + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + bss = (mlan_ds_bss *)req->pbuf; + bss->sub_command = MLAN_OID_UAP_OPER_CTRL; + req->req_id = MLAN_IOCTL_BSS; + req->action = MLAN_ACT_SET; + + bss->param.ap_oper_ctrl.ctrl_value = + (t_u16)((uap_oper_ctrl & 0xffff0000) >> 16); + bss->param.ap_oper_ctrl.chan_opt = (t_u16)(uap_oper_ctrl & 0xffff); + PRINTM(MMSG, "Uap oper_ctrl=0x%x chan_opt=0x%x\n", + bss->param.ap_oper_ctrl.ctrl_value, + bss->param.ap_oper_ctrl.chan_opt); + ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + + LEAVE(); + return ret; + +} +#endif + +/** + * @brief WOAL parse ASCII format data to MAC address + * + * @param handle MOAL handle + * @param data Source data + * @param size data length + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +static t_u32 +woal_process_init_cfg(moal_handle *handle, t_u8 *data, t_size size) +{ + mlan_status ret = MLAN_STATUS_FAILURE; + t_u8 *pos; + t_u8 *intf_s, *intf_e; + t_u8 s[MAX_LINE_LEN]; /* 1 line data */ + t_size line_len; + t_u8 index = 0; + t_u32 i; + t_u8 bss_mac_addr[MAX_MAC_ADDR_LEN]; + t_u8 bss_mac_name[MAX_PARAM_LEN]; + t_u8 type[MAX_PARAM_LEN]; + t_u8 offset[MAX_PARAM_LEN]; + t_u8 value[MAX_PARAM_LEN]; + + ENTER(); + + while ((line_len = parse_cfg_get_line(data, size, s)) != -1) { + + pos = s; + while (*pos == ' ' || *pos == '\t') + pos++; + + if (*pos == '#' || (*pos == '\r' && *(pos + 1) == '\n') || + *pos == '\n' || *pos == '\0') + continue; /* Needn't process this line */ + + /* Process MAC addr */ + if (strncmp(pos, "mac_addr", 8) == 0) { + intf_s = strchr(pos, '='); + if (intf_s != NULL) + intf_e = strchr(intf_s, ':'); + else + intf_e = NULL; + if (intf_s != NULL && intf_e != NULL) { + strncpy(bss_mac_addr, intf_e + 1, + MAX_MAC_ADDR_LEN - 1); + bss_mac_addr[MAX_MAC_ADDR_LEN - 1] = '\0'; + if ((intf_e - intf_s) > MAX_PARAM_LEN) { + PRINTM(MERROR, + "Too long interface name %d\n", + __LINE__); + goto done; + } + strncpy(bss_mac_name, intf_s + 1, + intf_e - intf_s - 1); + bss_mac_name[intf_e - intf_s - 1] = '\0'; + for (i = 0; i < handle->priv_num; i++) { + if (strcmp + (bss_mac_name, + handle->priv[i]->netdev->name) == + 0) { + memset(handle->priv[i]-> + current_addr, 0, + ETH_ALEN); + PRINTM(MINFO, + "Interface name: %s mac: %s\n", + bss_mac_name, + bss_mac_addr); + woal_mac2u8(handle->priv[i]-> + current_addr, + bss_mac_addr); +#if defined(WIFI_DIRECT_SUPPORT) +#if defined(STA_CFG80211) && defined(UAP_CFG80211) +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION + if (handle->priv[i]->bss_type == + MLAN_BSS_TYPE_WIFIDIRECT) { + handle->priv[i]-> + current_addr[0] + |= 0x02; + PRINTM(MCMND, + "Set WFD device addr: " + MACSTR "\n", + MAC2STR(handle-> + priv[i]-> + current_addr)); + } +#endif +#endif +#endif + /* Set WLAN MAC addresses */ + if (MLAN_STATUS_SUCCESS != + woal_request_set_mac_address + (handle->priv[i])) { + PRINTM(MERROR, + "Set MAC address failed\n"); + goto done; + } + memcpy(handle->priv[i]->netdev-> + dev_addr, + handle->priv[i]-> + current_addr, ETH_ALEN); + index++; /* Mark found one interface matching */ + } + } + } else { + PRINTM(MERROR, "Wrong config file format %d\n", + __LINE__); + goto done; + } + } + /* Process REG value */ + else if (strncmp(pos, "wlan_reg", 8) == 0) { + intf_s = strchr(pos, '='); + if (intf_s != NULL) + intf_e = strchr(intf_s, ','); + else + intf_e = NULL; + if (intf_s != NULL && intf_e != NULL) { + /* Copy type */ + strncpy(type, intf_s + 1, 1); + type[1] = '\0'; + } else { + PRINTM(MERROR, "Wrong config file format %d\n", + __LINE__); + goto done; + } + intf_s = intf_e + 1; + intf_e = strchr(intf_s, ','); + if (intf_e != NULL) { + if ((intf_e - intf_s) >= MAX_PARAM_LEN) { + PRINTM(MERROR, + "Regsier offset is too long %d\n", + __LINE__); + goto done; + } + /* Copy offset */ + strncpy(offset, intf_s, intf_e - intf_s); + offset[intf_e - intf_s] = '\0'; + } else { + PRINTM(MERROR, "Wrong config file format %d\n", + __LINE__); + goto done; + } + intf_s = intf_e + 1; + if ((strlen(intf_s) >= MAX_PARAM_LEN)) { + PRINTM(MERROR, "Regsier value is too long %d\n", + __LINE__); + goto done; + } + /* Copy value */ + strncpy(value, intf_s, + MIN((MAX_PARAM_LEN - 1), strlen(intf_s))); + + if (MLAN_STATUS_SUCCESS != + woal_process_regrdwr(handle, type, offset, value)) { + PRINTM(MERROR, "Access Reg failed\n"); + goto done; + } + PRINTM(MINFO, "Reg type: %s, offset: %s, value: %s\n", + type, offset, value); + } + } + + if (index == 0) + PRINTM(MINFO, "Can't find any matching MAC Address"); + ret = MLAN_STATUS_SUCCESS; + +done: + LEAVE(); + return ret; +} + +/** + * @brief WOAL parse ASCII format raw data to hex format + * + * @param handle MOAL handle + * @param data Source data + * @param size data length + * @param wait_option wait option + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +static t_u32 +woal_process_hostcmd_cfg(moal_handle *handle, t_u8 *data, t_size size, + t_u8 wait_option) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u8 *pos = data; + t_u8 *intf_s, *intf_e; + t_u8 *buf = NULL; + t_u8 *ptr = NULL; + t_u32 cmd_len = 0; + t_u8 start_raw = MFALSE; + gfp_t flag; + +#define CMD_STR "MRVL_CMDhostcmd" +#define CMD_BUF_LEN 2048 + + ENTER(); + flag = (in_atomic() || irqs_disabled())? GFP_ATOMIC : GFP_KERNEL; + buf = kzalloc(CMD_BUF_LEN, flag); + if (!buf) { + PRINTM(MERROR, "Could not allocate buffer space!\n"); + goto done; + } + ptr = buf; + strcpy(ptr, CMD_STR); + ptr = buf + strlen(CMD_STR) + sizeof(t_u32); + while ((pos - data) < size) { + while (*pos == ' ' || *pos == '\t') + pos++; + if (*pos == '#') { /* Line comment */ + while (*pos != '\n') + pos++; + pos++; + } + if ((*pos == '\r' && *(pos + 1) == '\n') || + *pos == '\n' || *pos == '\0') { + pos++; + continue; /* Needn't process this line */ + } + + if (*pos == '}') { + cmd_len = + *((t_u16 *)(buf + strlen(CMD_STR) + + sizeof(t_u32) + sizeof(t_u16))); + memcpy(buf + strlen(CMD_STR), &cmd_len, sizeof(t_u32)); + + /* fire the hostcommand from here */ + woal_priv_hostcmd(handle->priv[0], buf, CMD_BUF_LEN, + wait_option); + memset(buf + strlen(CMD_STR), 0, + CMD_BUF_LEN - strlen(CMD_STR)); + ptr = buf + strlen(CMD_STR) + sizeof(t_u32); + start_raw = MFALSE; + pos++; + continue; + } + + if (start_raw == MFALSE) { + intf_s = strchr(pos, '='); + if (intf_s) + intf_e = strchr(intf_s, '{'); + else + intf_e = NULL; + + if (intf_s && intf_e) { + start_raw = MTRUE; + pos = intf_e + 1; + continue; + } + } + + if (start_raw) { + /* Raw data block exists */ + while (*pos != '\n') { + if ((*pos <= 'f' && *pos >= 'a') || + (*pos <= 'F' && *pos >= 'A') || + (*pos <= '9' && *pos >= '0')) { + *ptr++ = woal_atox(pos); + pos += 2; + } else + pos++; + } + } + } + +done: + kfree(buf); + LEAVE(); + return ret; +} + +#define INIT_CFG_DATA 0x00 +#define INIT_HOSTCMD_CFG_DATA 0x02 +#define COUNTRY_POWER_TABLE 0x04 + +/** + * @brief Request init conf firmware callback + * This function is invoked by request_firmware_nowait system call + * + * @param firmware A pointer to firmware image + * @param context A pointer to moal_handle structure + * + * @return N/A + */ +static void +woal_request_init_user_conf_callback(const struct firmware *firmware, + void *context) +{ + moal_handle *handle; + + ENTER(); + + handle = (moal_handle *)context; + if (!handle) { + LEAVE(); + return; + } + if (firmware) + handle->user_data = firmware; + else + PRINTM(MERROR, "User init config request firmware failed\n"); + + handle->init_user_conf_wait_flag = MTRUE; + wake_up_interruptible(&handle->init_user_conf_wait_q); + + LEAVE(); + return; +} + +/** + * @brief Request init conf firmware callback + * This function is invoked by request_firmware_nowait system call + * + * @param firmware A pointer to firmware image + * @param context A pointer to moal_handle structure + * + * @return N/A + */ +static void +woal_request_init_dpd_conf_callback(const struct firmware *firmware, + void *context) +{ + moal_handle *handle; + + ENTER(); + + handle = (moal_handle *)context; + if (!handle) { + LEAVE(); + return; + } + if (firmware && handle) + handle->dpd_data = firmware; + else + PRINTM(MERROR, "User init cfg data request firmware failed\n"); + + handle->init_user_conf_wait_flag = MTRUE; + wake_up_interruptible(&handle->init_user_conf_wait_q); + + LEAVE(); + return; +} + +/** + * @brief Request init conf firmware callback + * This function is invoked by request_firmware_nowait system call + * + * @param firmware A pointer to firmware image + * @param context A pointer to moal_handle structure + * + * @return N/A + */ +static void +woal_request_init_txpwr_conf_callback(const struct firmware *firmware, + void *context) +{ + moal_handle *handle; + + ENTER(); + + handle = (moal_handle *)context; + if (!handle) { + LEAVE(); + return; + } + if (firmware && handle) + handle->txpwr_data = firmware; + else + PRINTM(MERROR, "User init cfg data request firmware failed\n"); + + handle->init_user_conf_wait_flag = MTRUE; + wake_up_interruptible(&handle->init_user_conf_wait_q); + + LEAVE(); + return; +} + +/** + * @brief Request init conf firmware callback + * This function is invoked by request_firmware_nowait system call + * + * @param firmware A pointer to firmware image + * @param context A pointer to moal_handle structure + * + * @return N/A + */ +static void +woal_request_init_cfg_data_callback(const struct firmware *firmware, + void *context) +{ + moal_handle *handle; + + ENTER(); + + handle = (moal_handle *)context; + if (!handle) { + LEAVE(); + return; + } + if (firmware && handle) + handle->init_cfg_data = firmware; + else + PRINTM(MERROR, "User init cfg data request firmware failed\n"); + + handle->init_user_conf_wait_flag = MTRUE; + wake_up_interruptible(&handle->init_user_conf_wait_q); + + LEAVE(); + return; +} + +/** + * @brief WOAL set user defined init data and param + * + * @param handle MOAL handle structure + * @param type type argument + * @param wait_option wait option + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +static t_u32 +woal_set_user_init_data(moal_handle *handle, int type, t_u8 wait_option) +{ + mlan_status ret = MLAN_STATUS_FAILURE; + t_u8 *cfg_data = NULL; + t_size len; + + ENTER(); + + if (type == INIT_CFG_DATA) { + if (req_fw_nowait) { +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32) + if ((request_firmware_nowait + (THIS_MODULE, FW_ACTION_HOTPLUG, init_cfg, + handle->hotplug_device, GFP_KERNEL, handle, + woal_request_init_cfg_data_callback)) < 0) { +#else +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13) + if ((request_firmware_nowait + (THIS_MODULE, FW_ACTION_HOTPLUG, init_cfg, + handle->hotplug_device, handle, + woal_request_init_cfg_data_callback)) < 0) { +#else + if ((request_firmware_nowait + (THIS_MODULE, init_cfg, handle->hotplug_device, + handle, + woal_request_init_cfg_data_callback)) < 0) { +#endif +#endif + PRINTM(MERROR, + "Init config file request_firmware_nowait() failed\n"); + goto done; + } + handle->init_user_conf_wait_flag = MFALSE; + wait_event_interruptible(handle->init_user_conf_wait_q, + handle-> + init_user_conf_wait_flag); + } else { + if ((request_firmware + (&handle->init_cfg_data, init_cfg, + handle->hotplug_device)) < 0) { + PRINTM(MERROR, + "Init config file request_firmware() failed\n"); + goto done; + } + } + } else if (type == COUNTRY_POWER_TABLE) { + if (req_fw_nowait) { +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32) + if ((request_firmware_nowait + (THIS_MODULE, FW_ACTION_HOTPLUG, + country_txpwrlimit, handle->hotplug_device, + GFP_KERNEL, handle, + woal_request_init_user_conf_callback)) < 0) { +#else +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13) + if ((request_firmware_nowait + (THIS_MODULE, FW_ACTION_HOTPLUG, + country_txpwrlimit, handle->hotplug_device, + handle, + woal_request_init_user_conf_callback)) < 0) { +#else + if ((request_firmware_nowait + (THIS_MODULE, country_txpwrlimit, + handle->hotplug_device, handle, + woal_request_init_user_conf_callback)) < 0) { +#endif +#endif + PRINTM(MERROR, + "country txpwrlimit config file request_firmware_nowait() failed\n"); + goto done; + } + handle->init_user_conf_wait_flag = MFALSE; + wait_event_interruptible(handle->init_user_conf_wait_q, + handle-> + init_user_conf_wait_flag); + } else { + int status = + request_firmware(&handle->user_data, + country_txpwrlimit, + handle->hotplug_device); + /* File does not exist, skip download */ + if (status == -ENOENT) { + PRINTM(MIOCTL, + "Country power table file does not exist\n"); + LEAVE(); + return MLAN_STATUS_SUCCESS; + } else if (status) { + PRINTM(MERROR, + "country txpwrlimit config file request_firmware() failed\n"); + goto done; + } + } + } else if (type == INIT_HOSTCMD_CFG_DATA) { + if (req_fw_nowait) { +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32) + if ((request_firmware_nowait + (THIS_MODULE, FW_ACTION_HOTPLUG, init_hostcmd_cfg, + handle->hotplug_device, GFP_KERNEL, handle, + woal_request_init_user_conf_callback)) < 0) { +#else +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13) + if ((request_firmware_nowait + (THIS_MODULE, FW_ACTION_HOTPLUG, init_hostcmd_cfg, + handle->hotplug_device, handle, + woal_request_init_user_conf_callback)) < 0) { +#else + if ((request_firmware_nowait + (THIS_MODULE, init_hostcmd_cfg, + handle->hotplug_device, handle, + woal_request_init_user_conf_callback)) < 0) { +#endif +#endif + PRINTM(MERROR, + "Init hostcmd config file request_firmware_nowait() failed\n"); + goto done; + } + handle->init_user_conf_wait_flag = MFALSE; + wait_event_interruptible(handle->init_user_conf_wait_q, + handle-> + init_user_conf_wait_flag); + } else { + if ((request_firmware + (&handle->user_data, init_hostcmd_cfg, + handle->hotplug_device)) < 0) { + PRINTM(MERROR, + "Init hostcmd config file request_firmware() failed\n"); + goto done; + } + } + } + + if (handle->user_data) { + cfg_data = (t_u8 *)(handle->user_data)->data; + len = (handle->user_data)->size; + if (type == INIT_HOSTCMD_CFG_DATA + || type == COUNTRY_POWER_TABLE) { + if (MLAN_STATUS_SUCCESS != + woal_process_hostcmd_cfg(handle, cfg_data, len, + wait_option)) { + PRINTM(MERROR, + "Can't process hostcmd config file\n"); + goto done; + } + } + ret = MLAN_STATUS_SUCCESS; + } else if (type == INIT_CFG_DATA && handle->init_cfg_data) { + PRINTM(MIOCTL, "Load init_cfg success\n"); + ret = MLAN_STATUS_SUCCESS; + } +done: + if (handle->user_data) { + release_firmware(handle->user_data); + handle->user_data = NULL; + } + + LEAVE(); + return ret; +} + +static int woal_netdevice_event(struct notifier_block *nb, + unsigned long event, void *ptr); + +/** + * @brief Add interfaces DPC + * + * @param handle A pointer to moal_handle structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status +woal_add_card_dpc(moal_handle *handle) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + int i; + char str_buf[MLAN_MAX_VER_STR_LEN]; + ENTER(); + +#ifdef CONFIG_PROC_FS + /* Initialize proc fs */ + woal_proc_init(handle); +#endif /* CONFIG_PROC_FS */ + + /* Add interfaces */ + for (i = 0; i < handle->drv_mode.intf_num; i++) { + if (handle->drv_mode.bss_attr[i].bss_virtual) + continue; + if (!woal_add_interface + (handle, handle->priv_num, + handle->drv_mode.bss_attr[i].bss_type)) { + ret = MLAN_STATUS_FAILURE; + goto err; + } + } + woal_get_version(handle, str_buf, sizeof(str_buf) - 1); + PRINTM(MMSG, "wlan: version = %s\n", str_buf); + + handle->woal_notifier.notifier_call = woal_netdevice_event; + if (register_inetaddr_notifier(&handle->woal_notifier)) { + PRINTM(MFATAL, + "Error registering register_inetaddr_notifier\n"); + goto err; + } +#ifdef MFG_CMD_SUPPORT + if (mfg_mode == MLAN_INIT_PARA_ENABLED) + goto done; +#endif + + if (init_cfg && handle->init_cfg_data) { + if (MLAN_STATUS_SUCCESS != + woal_process_init_cfg(handle, + (t_u8 *)(handle->init_cfg_data)->data, + (handle->init_cfg_data)->size)) { + PRINTM(MERROR, "Can't process init config file\n"); + ret = MLAN_STATUS_FAILURE; + goto err; + } + } + + if (pmic) { + if (MLAN_STATUS_SUCCESS != + woal_pmic_configure(handle, MOAL_IOCTL_WAIT)) { + PRINTM(MFATAL, "Failed to configure PMIC\n"); + ret = MLAN_STATUS_FAILURE; + goto err; + } + } + +#ifdef UAP_SUPPORT + if (uap_oper_ctrl) + woal_set_uap_operation_ctrl(handle); +#endif + +#ifdef MFG_CMD_SUPPORT +done: +#endif +err: + if (init_cfg && handle->init_cfg_data) { + release_firmware(handle->init_cfg_data); + handle->init_cfg_data = NULL; + } + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "Failed to add interface\n"); + unregister_inetaddr_notifier(&handle->woal_notifier); + + for (i = 0; i < MIN(MLAN_MAX_BSS_NUM, handle->priv_num); i++) + woal_remove_interface(handle, i); + handle->priv_num = 0; +#ifdef CONFIG_PROC_FS + woal_proc_exit(handle); +#endif + } + LEAVE(); + return ret; +} + +/** + * @brief Download and Initialize firmware DPC + * + * @param handle A pointer to moal_handle structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status +woal_init_fw_dpc(moal_handle *handle) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_fw_image fw; + t_u8 retry = 0; + + mlan_init_param param; + + ENTER(); + + if (handle->firmware) { + memset(&fw, 0, sizeof(mlan_fw_image)); + fw.pfw_buf = (t_u8 *)handle->firmware->data; + fw.fw_len = handle->firmware->size; + if (fw_reload == FW_RELOAD_SDIO_INBAND_RESET) + fw.fw_reload = fw_reload; + else + fw.fw_reload = 0; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32) + sdio_claim_host(((struct sdio_mmc_card *)handle->card)->func); +#endif + ret = mlan_dnld_fw(handle->pmlan_adapter, &fw); +#ifdef MFG_CMD_SUPPORT + if (mfg_mode == MLAN_INIT_PARA_ENABLED) + fw_name = NULL; +#endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32) + sdio_release_host(((struct sdio_mmc_card *)handle->card)->func); +#endif + if (ret == MLAN_STATUS_FAILURE) { + PRINTM(MERROR, + "WLAN: Fail download FW with nowwait: %d\n", + req_fw_nowait); + woal_sdio_reg_dbg(handle); + goto done; + } + PRINTM(MMSG, "WLAN FW is active\n"); + } + + /** data request */ + memset(¶m, 0, sizeof(mlan_init_param)); + + if (dpd_data_cfg && strncmp(dpd_data_cfg, "none", strlen("none"))) { + if (req_fw_nowait) { +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32) + if ((request_firmware_nowait + (THIS_MODULE, FW_ACTION_HOTPLUG, dpd_data_cfg, + handle->hotplug_device, GFP_KERNEL, handle, + woal_request_init_dpd_conf_callback)) < 0) { +#else +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13) + if ((request_firmware_nowait + (THIS_MODULE, FW_ACTION_HOTPLUG, dpd_data_cfg, + handle->hotplug_device, handle, + woal_request_init_dpd_conf_callback)) < 0) { +#else + if ((request_firmware_nowait + (THIS_MODULE, dpd_data_cfg, handle->hotplug_device, + handle, + woal_request_init_dpd_conf_callback)) < 0) { +#endif +#endif + PRINTM(MERROR, + "DPD data request_firmware_nowait() failed\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + handle->init_user_conf_wait_flag = MFALSE; + wait_event_interruptible(handle->init_user_conf_wait_q, + handle-> + init_user_conf_wait_flag); + } else { + if ((request_firmware + (&handle->dpd_data, dpd_data_cfg, + handle->hotplug_device)) < 0) { + PRINTM(MERROR, + "DPD data request_firmware() failed\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + } + if (handle->dpd_data) { + param.pdpd_data_buf = (t_u8 *)handle->dpd_data->data; + param.dpd_data_len = handle->dpd_data->size; + } + } + if (txpwrlimit_cfg && strncmp(txpwrlimit_cfg, "none", strlen("none"))) { + if (req_fw_nowait) { +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32) + if ((request_firmware_nowait + (THIS_MODULE, FW_ACTION_HOTPLUG, txpwrlimit_cfg, + handle->hotplug_device, GFP_KERNEL, handle, + woal_request_init_txpwr_conf_callback)) < 0) { +#else +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13) + if ((request_firmware_nowait + (THIS_MODULE, FW_ACTION_HOTPLUG, txpwrlimit_cfg, + handle->hotplug_device, handle, + woal_request_init_txpwr_conf_callback)) < 0) { +#else + if ((request_firmware_nowait + (THIS_MODULE, txpwrlimit_cfg, + handle->hotplug_device, handle, + woal_request_init_txpwr_conf_callback)) < 0) { +#endif +#endif + PRINTM(MERROR, "Region txpwrlimit cfg data " + "request_firmware_nowait() failed\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + handle->init_user_conf_wait_flag = MFALSE; + wait_event_interruptible(handle->init_user_conf_wait_q, + handle-> + init_user_conf_wait_flag); + } else { + if ((request_firmware + (&handle->txpwr_data, txpwrlimit_cfg, + handle->hotplug_device)) < 0) { + PRINTM(MERROR, + "Region txpwrlimit cfg data " + "request_firmware() failed\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + } + if (handle->txpwr_data) { + param.ptxpwr_data_buf = + (t_u8 *)handle->txpwr_data->data; + param.txpwr_data_len = handle->txpwr_data->size; + } + } + + if (cal_data_cfg && strncmp(cal_data_cfg, "none", strlen("none"))) { + if (req_fw_nowait) { +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32) + if ((request_firmware_nowait + (THIS_MODULE, FW_ACTION_HOTPLUG, cal_data_cfg, + handle->hotplug_device, GFP_KERNEL, handle, + woal_request_init_user_conf_callback)) < 0) { +#else +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13) + if ((request_firmware_nowait + (THIS_MODULE, FW_ACTION_HOTPLUG, cal_data_cfg, + handle->hotplug_device, handle, + woal_request_init_user_conf_callback)) < 0) { +#else + if ((request_firmware_nowait + (THIS_MODULE, cal_data_cfg, handle->hotplug_device, + handle, + woal_request_init_user_conf_callback)) < 0) { +#endif +#endif + PRINTM(MERROR, + "Cal data request_firmware_nowait() failed\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + handle->init_user_conf_wait_flag = MFALSE; + wait_event_interruptible(handle->init_user_conf_wait_q, + handle-> + init_user_conf_wait_flag); + } else { + if ((request_firmware + (&handle->user_data, cal_data_cfg, + handle->hotplug_device)) < 0) { + PRINTM(MERROR, + "Cal data request_firmware() failed\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + } + } else if (!cal_data_cfg) { + PRINTM(MERROR, + "Please add cal_data_cfg for 8887/8977/8997/8987/9098\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + if (handle->user_data) { + param.pcal_data_buf = (t_u8 *)handle->user_data->data; + param.cal_data_len = handle->user_data->size; + } + + handle->hardware_status = HardwareStatusFwReady; + if (ret != MLAN_STATUS_SUCCESS) + goto done; + if (handle->fw_reload) + goto done; + handle->init_wait_q_woken = MFALSE; + + ret = mlan_set_init_param(handle->pmlan_adapter, ¶m); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32) + sdio_claim_host(((struct sdio_mmc_card *)handle->card)->func); +#endif + ret = mlan_init_fw(handle->pmlan_adapter); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32) + sdio_release_host(((struct sdio_mmc_card *)handle->card)->func); +#endif + if (ret == MLAN_STATUS_FAILURE) + goto done; + else if (ret == MLAN_STATUS_SUCCESS) { + handle->hardware_status = HardwareStatusReady; + goto done; + } + /* Wait for mlan_init to complete */ + while (wait_event_interruptible(handle->init_wait_q, + handle->init_wait_q_woken) == + -ERESTARTSYS && retry < MAX_RETRY_CNT) { + retry++; + } + if (handle->hardware_status != HardwareStatusReady) { + woal_moal_debug_info(woal_get_priv(handle, MLAN_BSS_ROLE_ANY), + handle, MTRUE); +#if defined(DEBUG_LEVEL1) + if (drvdbg & MFW_D) { + drvdbg &= ~MFW_D; + woal_dump_firmware_info_v3(handle); + } +#endif + ret = MLAN_STATUS_FAILURE; + goto done; + } + ret = MLAN_STATUS_SUCCESS; +done: + if (handle->dpd_data) { + release_firmware(handle->dpd_data); + handle->dpd_data = NULL; + } + if (handle->txpwr_data) { + release_firmware(handle->txpwr_data); + handle->txpwr_data = NULL; + } + if (handle->user_data) { + release_firmware(handle->user_data); + handle->user_data = NULL; + } + LEAVE(); + return ret; +} + +/** + * @brief Request firmware DPC + * + * @param handle A pointer to moal_handle structure + * @param firmware A pointer to firmware image + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status +woal_request_fw_dpc(moal_handle *handle, const struct firmware *firmware) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + struct timeval tstamp; + + ENTER(); + + if (!firmware) { + do_gettimeofday(&tstamp); + if (tstamp.tv_sec > + (handle->req_fw_time.tv_sec + REQUEST_FW_TIMEOUT)) { + PRINTM(MERROR, + "No firmware image found. Skipping download\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + PRINTM(MERROR, + "request_firmware_nowait failed for %s. Retrying..\n", + handle->drv_mode.fw_name); + woal_sched_timeout(MOAL_TIMER_1S); + woal_request_fw(handle); + LEAVE(); + return ret; + } + handle->firmware = firmware; + + ret = woal_init_fw_dpc(handle); + if (ret) + goto done; + ret = woal_add_card_dpc(handle); + if (ret) + goto done; + +done: + /* We should hold the semaphore until callback finishes execution */ + MOAL_REL_SEMAPHORE(&AddRemoveCardSem); + LEAVE(); + return ret; +} + +/** + * @brief Request firmware callback + * This function is invoked by request_firmware_nowait system call + * + * @param firmware A pointer to firmware image + * @param context A pointer to moal_handle structure + * + * @return N/A + */ +static void +woal_request_fw_callback(const struct firmware *firmware, void *context) +{ + ENTER(); + woal_request_fw_dpc((moal_handle *)context, firmware); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32) + if (firmware) + release_firmware(firmware); +#endif + LEAVE(); + return; +} + +/** + * @brief Download firmware using helper + * + * @param handle A pointer to moal_handle structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +woal_request_fw(moal_handle *handle) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + int err; + t_u32 revision_id = 0; + + ENTER(); + + if (!fw_name) { +#define REV_ID_REG 0xc8 +/** Revision ID register */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32) + sdio_claim_host(((struct sdio_mmc_card *)handle->card)->func); +#endif + woal_read_reg(handle, REV_ID_REG, &revision_id); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32) + sdio_release_host(((struct sdio_mmc_card *)handle->card)->func); +#endif + switch (revision_id) { + case SD8977_V0: + if (fw_serial && !handle->fw_reload && !fw_reload) + handle->drv_mode.fw_name = SD8977_V0_FW_NAME; + else + handle->drv_mode.fw_name = + SD8977_WLAN_V0_FW_NAME; + break; + case SD8977_V1: + if (fw_serial && !handle->fw_reload && !fw_reload) + handle->drv_mode.fw_name = SD8977_V1_FW_NAME; + else + handle->drv_mode.fw_name = + SD8977_WLAN_V1_FW_NAME; + break; + case SD8977_V2: + if (fw_serial && !handle->fw_reload && !fw_reload) + handle->drv_mode.fw_name = SD8977_V2_FW_NAME; + else + handle->drv_mode.fw_name = + SD8977_WLAN_V2_FW_NAME; + break; + default: + break; + } + } + + PRINTM(MMSG, "Request firmware: %s\n", handle->drv_mode.fw_name); + if (req_fw_nowait) { +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32) + err = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + handle->drv_mode.fw_name, + handle->hotplug_device, + GFP_KERNEL, handle, + woal_request_fw_callback); +#else +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13) + err = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + handle->drv_mode.fw_name, + handle->hotplug_device, handle, + woal_request_fw_callback); +#else + err = request_firmware_nowait(THIS_MODULE, + handle->drv_mode.fw_name, + handle->hotplug_device, handle, + woal_request_fw_callback); +#endif +#endif + if (err < 0) { + PRINTM(MFATAL, + "WLAN: request_firmware_nowait() failed, error code = %d\n", + err); + ret = MLAN_STATUS_FAILURE; + } + } else { + err = request_firmware(&handle->firmware, + handle->drv_mode.fw_name, + handle->hotplug_device); + if (err < 0) { + PRINTM(MFATAL, + "WLAN: request_firmware() failed, error code = %d\n", + err); + ret = MLAN_STATUS_FAILURE; + } else { + if (handle->fw_reload) + ret = woal_init_fw_dpc(handle); + else + ret = woal_request_fw_dpc(handle, + handle->firmware); + release_firmware(handle->firmware); + } + } + + LEAVE(); + return ret; +} + +/** + * @brief This function initializes firmware + * + * @param handle A pointer to moal_handle structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status +woal_init_fw(moal_handle *handle) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + do_gettimeofday(&handle->req_fw_time); + + ret = woal_request_fw(handle); + if (ret < 0) { + PRINTM(MFATAL, "woal_request_fw failed\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + +done: + LEAVE(); + return ret; +} + +/** + * @brief This function will fill in the mlan_buffer + * + * @param pmbuf A pointer to mlan_buffer + * @param skb A pointer to struct sk_buff + * + * @return N/A + */ +static void +woal_fill_mlan_buffer(moal_private *priv, + mlan_buffer *pmbuf, struct sk_buff *skb) +{ + struct timeval tstamp; + struct ethhdr *eth; + t_u8 tid; + dot11_txcontrol *txcontrol; + t_u8 tx_ctrl_flag = MFALSE; + int i = 0; + ENTER(); + /* + * skb->priority values from 256->263 are magic values to + * directly indicate a specific 802.1d priority. This is used + * to allow 802.1d priority to be passed directly in from VLAN + * tags, etc. + */ + if (IS_SKB_MAGIC_VLAN(skb)) { + tid = GET_VLAN_PRIO(skb); + } else { + eth = (struct ethhdr *)skb->data; + + switch (eth->h_proto) { + case __constant_htons(ETH_P_IP): + tid = priv->dscp_map[SKB_TOS(skb) >> DSCP_OFFSET]; + if (tid == 0xFF) + tid = (IPTOS_PREC(SKB_TOS(skb)) >> + IPTOS_OFFSET); + PRINTM(MDAT_D, + "packet type ETH_P_IP: dscp[%x], map[%x], tid=%d\n", + SKB_TOS(skb) >> DSCP_OFFSET, + priv->dscp_map[SKB_TOS(skb) >> DSCP_OFFSET], + tid); + break; + case __constant_htons(ETH_P_IPV6): + tid = SKB_TIDV6(skb); + PRINTM(MDAT_D, + "packet type ETH_P_IPV6: %04x, tid=%#x prio=%#x\n", + eth->h_proto, tid, skb->priority); + break; + case __constant_htons(ETH_P_ARP): + tid = 0; + PRINTM(MDATA, "ARP packet %04x\n", eth->h_proto); + break; + default: + tid = 0; + if (priv->tx_protocols.protocol_num) { + for (i = 0; i < priv->tx_protocols.protocol_num; + i++) { + if (eth->h_proto == + __constant_htons(priv->tx_protocols. + protocols[i])) + tx_ctrl_flag = MTRUE; + } + } + if (tx_ctrl_flag) { + txcontrol = + (dot11_txcontrol *) (skb->data + + sizeof(struct + ethhdr)); + pmbuf->u.tx_info.data_rate = + txcontrol->datarate; + pmbuf->u.tx_info.channel = txcontrol->channel; + pmbuf->u.tx_info.bw = txcontrol->bw; + pmbuf->u.tx_info.tx_power.val = + txcontrol->power; + pmbuf->u.tx_info.retry_limit = + txcontrol->retry_limit; + tid = txcontrol->priority; + memmove(skb->data + sizeof(dot11_txcontrol), + skb->data, sizeof(struct ethhdr)); + skb_pull(skb, sizeof(dot11_txcontrol)); + pmbuf->flags |= MLAN_BUF_FLAG_TX_CTRL; + } + break; + } + } + + skb->priority = tid; + + /* Record the current time the packet was queued; used to determine + * the amount of time the packet was queued in the driver before it + * was sent to the firmware. The delay is then sent along with the + * packet to the firmware for aggregate delay calculation for stats + * and MSDU lifetime expiry. + */ + do_gettimeofday(&tstamp); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) + skb->tstamp = timeval_to_ktime(tstamp); +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 14) + skb_set_timestamp(skb, &tstamp); +#else + memcpy(&skb->stamp, &tstamp, sizeof(skb->stamp)); +#endif + + pmbuf->pdesc = skb; + pmbuf->pbuf = skb->head + sizeof(mlan_buffer); + pmbuf->data_offset = skb->data - (skb->head + sizeof(mlan_buffer)); + pmbuf->data_len = skb->len; + pmbuf->priority = skb->priority; + pmbuf->buf_type = 0; + pmbuf->in_ts_sec = (t_u32)tstamp.tv_sec; + pmbuf->in_ts_usec = (t_u32)tstamp.tv_usec; + + LEAVE(); + return; +} + +/** + * @brief This function opens the network device for monitor interface + * + * @param dev A pointer to net_device structure + * + * @return 0 -- success, otherwise fail + */ +int +woal_mon_open(struct net_device *ndev) +{ + ENTER(); + LEAVE(); + return 0; +} + +/** + * @brief This function closes the network device for monitor interface + * + * @param dev A pointer to net_device structure + * + * @return 0 -- success, otherwise fail + */ +int +woal_mon_close(struct net_device *ndev) +{ + ENTER(); + LEAVE(); + return 0; +} + +/** + * @brief This function sets the MAC address to firmware for monitor interface + * + * @param dev A pointer to net_device structure + * @param addr MAC address to set + * + * @return 0 -- success, otherwise fail + */ +int +woal_mon_set_mac_address(struct net_device *ndev, void *addr) +{ + ENTER(); + LEAVE(); + return 0; +} + +/** + * @brief This function sets multicast address to firmware for monitor interface + * + * @param dev A pointer to net_device structure + * + * @return 0 -- success, otherwise fail + */ +void +woal_mon_set_multicast_list(struct net_device *ndev) +{ + ENTER(); + LEAVE(); +} + +/** + * @brief This function handles packet transmission for monitor interface + * + * @param skb A pointer to sk_buff structure + * @param dev A pointer to net_device structure + * + * @return 0 -- success, otherwise fail + */ +int +woal_mon_hard_start_xmit(struct sk_buff *skb, struct net_device *ndev) +{ + int len_rthdr; + int qos_len = 0; + int dot11_hdr_len = 24; + int snap_len = 6; + unsigned char *pdata; + unsigned short fc; + unsigned char src_mac_addr[6]; + unsigned char dst_mac_addr[6]; + struct ieee80211_hdr *dot11_hdr; + struct ieee80211_radiotap_header *prthdr = + (struct ieee80211_radiotap_header *)skb->data; + monitor_iface *mon_if = netdev_priv(ndev); + + ENTER(); + + if (mon_if == NULL || mon_if->base_ndev == NULL) { + goto fail; + } + + /* check for not even having the fixed radiotap header part */ + if (unlikely(skb->len < sizeof(struct ieee80211_radiotap_header))) { + PRINTM(MERROR, "Invalid radiotap hdr length," + "skb->len: %d\n", skb->len); + goto fail; /* too short to be possibly valid */ + } + + /* is it a header version we can trust to find length from? */ + if (unlikely(prthdr->it_version)) + goto fail; /* only version 0 is supported */ + + /* then there must be a radiotap header with a length we can use */ + len_rthdr = ieee80211_get_radiotap_len(skb->data); + + /* does the skb contain enough to deliver on the alleged length? */ + if (unlikely(skb->len < len_rthdr)) { + PRINTM(MERROR, "Invalid data length," + "skb->len: %d\n", skb->len); + goto fail; /* skb too short for claimed rt header extent */ + } + + /* Skip the ratiotap header */ + skb_pull(skb, len_rthdr); + + dot11_hdr = (struct ieee80211_hdr *)skb->data; + fc = le16_to_cpu(dot11_hdr->frame_control); + if ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA) { + /* Check if this ia a Wireless Distribution System (WDS) frame + * which has 4 MAC addresses + */ + if (dot11_hdr->frame_control & 0x0080) + qos_len = 2; + if ((dot11_hdr->frame_control & 0x0300) == 0x0300) + dot11_hdr_len += 6; + + memcpy(dst_mac_addr, dot11_hdr->addr1, sizeof(dst_mac_addr)); + memcpy(src_mac_addr, dot11_hdr->addr2, sizeof(src_mac_addr)); + + /* Skip the 802.11 header, QoS (if any) and SNAP, but leave spaces for + * for two MAC addresses + */ + skb_pull(skb, + dot11_hdr_len + qos_len + snap_len - + sizeof(src_mac_addr) * 2); + pdata = (unsigned char *)skb->data; + memcpy(pdata, dst_mac_addr, sizeof(dst_mac_addr)); + memcpy(pdata + sizeof(dst_mac_addr), src_mac_addr, + sizeof(src_mac_addr)); + + LEAVE(); + return woal_hard_start_xmit(skb, mon_if->base_ndev); + } + +fail: + dev_kfree_skb(skb); + LEAVE(); + return NETDEV_TX_OK; +} + +/** + * @brief This function returns the network statistics + * + * @param dev A pointer to net_device structure + * + * @return A pointer to net_device_stats structure + */ +struct net_device_stats * +woal_mon_get_stats(struct net_device *dev) +{ + monitor_iface *mon_if = (monitor_iface *)netdev_priv(dev); + return &mon_if->stats; +} + +static const struct net_device_ops woal_cfg80211_mon_if_ops = { + .ndo_open = woal_mon_open, + .ndo_start_xmit = woal_mon_hard_start_xmit, + .ndo_stop = woal_mon_close, + .ndo_get_stats = woal_mon_get_stats, + .ndo_set_mac_address = woal_mon_set_mac_address, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) + .ndo_set_rx_mode = woal_mon_set_multicast_list, +#else + .ndo_set_multicast_list = woal_mon_set_multicast_list, +#endif +}; + +/** + * @brief This function setup monitor interface + * + * @param dev A pointer to net_device structure + * @param addr MAC address to set + * + * @return 0 -- success, otherwise fail + */ + +void +woal_mon_if_setup(struct net_device *dev) +{ + ENTER(); + ether_setup(dev); + dev->netdev_ops = &woal_cfg80211_mon_if_ops; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 11, 9) + dev->needs_free_netdev = true; +#else + dev->destructor = free_netdev; +#endif + LEAVE(); +} + +/** + * @brief Request the driver to add a monitor interface + * + * @param priv A pointer to moal_private + * @param name Virtual interface name + * @param name_assign_type Interface name assignment type + * @param sniffer_mode Sniffer mode + * + * @return A pointer to monitor_iface + */ +monitor_iface * +woal_prepare_mon_if(moal_private *priv, + const char *name, + unsigned char name_assign_type, int sniffer_mode) +{ + int ret = 0; + moal_handle *handle = priv->phandle; + struct net_device *ndev = NULL; + monitor_iface *mon_if = NULL; + + ENTER(); + + if (sniffer_mode != CHANNEL_SPEC_SNIFFER_MODE) { + PRINTM(MERROR, "Sniffer mode is not valid\n"); + ret = -EFAULT; + goto fail; + } + if ((sniffer_mode == CHANNEL_SPEC_SNIFFER_MODE) && + woal_is_any_interface_active(handle)) { + PRINTM(MERROR, + "Cannot start channel specified net monitor when Interface Active\n"); + ret = -EFAULT; + goto fail; + } +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) + ndev = alloc_netdev_mq(sizeof(*mon_if), name, name_assign_type, + woal_mon_if_setup, 1); +#else + ndev = alloc_netdev_mq(sizeof(*mon_if), name, NET_NAME_UNKNOWN, + woal_mon_if_setup, 1); +#endif +#else + ndev = alloc_netdev_mq(sizeof(*mon_if), name, woal_mon_if_setup, 1); +#endif +#else + ndev = alloc_netdev_mq(sizeof(*mon_if), name, woal_mon_if_setup); +#endif + if (!ndev) { + PRINTM(MFATAL, "Init virtual ethernet device failed\n"); + ret = -EFAULT; + goto fail; + } + + ret = dev_alloc_name(ndev, ndev->name); + if (ret < 0) { + PRINTM(MFATAL, "Net device alloc name fail.\n"); + ret = -EFAULT; + goto fail; + } + //?memcpy(ndev->dev_addr, ndev->perm_addr, ETH_ALEN); + + mon_if = netdev_priv(ndev); + memcpy(mon_if->ifname, ndev->name, IFNAMSIZ); + + ndev->type = ARPHRD_IEEE80211_RADIOTAP; + ndev->netdev_ops = &woal_cfg80211_mon_if_ops; + + mon_if->priv = priv; + mon_if->mon_ndev = ndev; + mon_if->base_ndev = priv->netdev; + mon_if->sniffer_mode = sniffer_mode; + mon_if->radiotap_enabled = 1; + mon_if->flag = 1; + +fail: + if (ret) { + if (ndev) + free_netdev(ndev); + LEAVE(); + return NULL; + } + + LEAVE(); + return mon_if; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33) +static struct device_type wlan_type = {.name = "wlan", }; +#endif + +#ifdef STA_SUPPORT +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) +/** Network device handlers */ +const struct net_device_ops woal_netdev_ops = { + .ndo_open = woal_open, + .ndo_start_xmit = woal_hard_start_xmit, + .ndo_stop = woal_close, + .ndo_do_ioctl = woal_do_ioctl, + .ndo_set_mac_address = woal_set_mac_address, + .ndo_tx_timeout = woal_tx_timeout, + .ndo_get_stats = woal_get_stats, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) + .ndo_set_rx_mode = woal_set_multicast_list, +#else + .ndo_set_multicast_list = woal_set_multicast_list, +#endif + .ndo_select_queue = woal_select_queue, + .ndo_validate_addr = eth_validate_addr, +}; +#endif + +/** + * @brief This function initializes the private structure + * and dev structure for station mode + * + * @param dev A pointer to net_device structure + * @param priv A pointer to moal_private structure + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +woal_init_sta_dev(struct net_device *dev, moal_private *priv) +{ + ENTER(); + + /* Setup the OS Interface to our functions */ +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 29) + dev->open = woal_open; + dev->hard_start_xmit = woal_hard_start_xmit; + dev->stop = woal_close; + dev->do_ioctl = woal_do_ioctl; + dev->set_mac_address = woal_set_mac_address; + dev->tx_timeout = woal_tx_timeout; + dev->get_stats = woal_get_stats; + dev->set_multicast_list = woal_set_multicast_list; +#else + dev->netdev_ops = &woal_netdev_ops; +#endif + dev->watchdog_timeo = MRVDRV_DEFAULT_WATCHDOG_TIMEOUT; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0) + dev->needed_headroom += MLAN_MIN_DATA_HEADER_LEN + sizeof(mlan_buffer) + + priv->extra_tx_head_len; +#else + dev->hard_header_len += MLAN_MIN_DATA_HEADER_LEN + sizeof(mlan_buffer) + + priv->extra_tx_head_len; +#endif +#ifdef STA_WEXT + if (IS_STA_WEXT(cfg80211_wext)) { +#if WIRELESS_EXT < 21 + dev->get_wireless_stats = woal_get_wireless_stats; +#endif + dev->wireless_handlers = + (struct iw_handler_def *)&woal_handler_def; + } +#endif + dev->flags |= IFF_BROADCAST | IFF_MULTICAST; + +#ifdef STA_CFG80211 + if (IS_STA_CFG80211(cfg80211_wext)) + init_waitqueue_head(&priv->ft_wait_q); +#endif + LEAVE(); + return MLAN_STATUS_SUCCESS; +} +#endif /* STA_SUPPORT */ + +#ifdef UAP_SUPPORT +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) +/** Network device handlers */ +const struct net_device_ops woal_uap_netdev_ops = { + .ndo_open = woal_open, + .ndo_start_xmit = woal_hard_start_xmit, + .ndo_stop = woal_close, + .ndo_do_ioctl = woal_uap_do_ioctl, + .ndo_set_mac_address = woal_set_mac_address, + .ndo_tx_timeout = woal_tx_timeout, + .ndo_get_stats = woal_get_stats, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) + .ndo_set_rx_mode = woal_uap_set_multicast_list, +#else + .ndo_set_multicast_list = woal_uap_set_multicast_list, +#endif + .ndo_select_queue = woal_select_queue, + .ndo_validate_addr = eth_validate_addr, +}; +#endif + +/** + * @brief This function initializes the private structure + * and dev structure for uap mode + * + * @param dev A pointer to net_device structure + * @param priv A pointer to moal_private structure + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +woal_init_uap_dev(struct net_device *dev, moal_private *priv) +{ + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + /* Setup the OS Interface to our functions */ +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 29) + dev->open = woal_open; + dev->hard_start_xmit = woal_hard_start_xmit; + dev->stop = woal_close; + dev->set_mac_address = woal_set_mac_address; + dev->tx_timeout = woal_tx_timeout; + dev->get_stats = woal_get_stats; + dev->do_ioctl = woal_uap_do_ioctl; + dev->set_multicast_list = woal_uap_set_multicast_list; +#else + dev->netdev_ops = &woal_uap_netdev_ops; +#endif + dev->watchdog_timeo = MRVDRV_DEFAULT_UAP_WATCHDOG_TIMEOUT; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0) + dev->needed_headroom += MLAN_MIN_DATA_HEADER_LEN + sizeof(mlan_buffer) + + priv->extra_tx_head_len; +#else + dev->hard_header_len += MLAN_MIN_DATA_HEADER_LEN + sizeof(mlan_buffer) + + priv->extra_tx_head_len; +#endif +#ifdef UAP_WEXT + if (IS_UAP_WEXT(cfg80211_wext)) { +#if WIRELESS_EXT < 21 + dev->get_wireless_stats = woal_get_uap_wireless_stats; +#endif + dev->wireless_handlers = + (struct iw_handler_def *)&woal_uap_handler_def; + } +#endif /* UAP_WEXT */ + dev->flags |= IFF_BROADCAST | IFF_MULTICAST; + + LEAVE(); + return status; +} +#endif /* UAP_SUPPORT */ + +/** + * @brief This function adds a new interface. It will + * allocate, initialize and register the device. + * + * @param handle A pointer to moal_handle structure + * @param bss_index BSS index number + * @param bss_type BSS type + * + * @return A pointer to the new priv structure + */ +moal_private * +woal_add_interface(moal_handle *handle, t_u8 bss_index, t_u8 bss_type) +{ + struct net_device *dev = NULL; + moal_private *priv = NULL; + char name[256]; + int i = 0; +#ifdef UAP_CFG80211 +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) + char csa_str[256]; +#endif +#endif + ENTER(); + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) +#define MAX_WMM_QUEUE 4 + /* Allocate an Ethernet device */ + dev = alloc_etherdev_mq(sizeof(moal_private), MAX_WMM_QUEUE); +#else + dev = alloc_etherdev(sizeof(moal_private)); +#endif + if (!dev) { + PRINTM(MFATAL, "Init virtual ethernet device failed\n"); + goto error; + } + /* Allocate device name */ +#ifdef STA_SUPPORT + memset(name, 0, sizeof(name)); + if (sta_name) + snprintf(name, sizeof(name), "%s%%d", sta_name); + else + sprintf(name, default_mlan_name); + if ((bss_type == MLAN_BSS_TYPE_STA) && (dev_alloc_name(dev, name) < 0)) { + PRINTM(MERROR, "Could not allocate mlan device name\n"); + goto error; + } +#endif +#ifdef UAP_SUPPORT + memset(name, 0, sizeof(name)); + if (uap_name) + snprintf(name, sizeof(name), "%s%%d", uap_name); + else + sprintf(name, default_uap_name); + if ((bss_type == MLAN_BSS_TYPE_UAP) && (dev_alloc_name(dev, name) < 0)) { + PRINTM(MERROR, "Could not allocate uap device name\n"); + goto error; + } +#endif +#if defined(WIFI_DIRECT_SUPPORT) + memset(name, 0, sizeof(name)); + if (wfd_name) + snprintf(name, sizeof(name), "%s%%d", wfd_name); + else + sprintf(name, default_wfd_name); + if ((bss_type == MLAN_BSS_TYPE_WIFIDIRECT) && + (dev_alloc_name(dev, name) < 0)) { + PRINTM(MERROR, "Could not allocate wifidirect device name\n"); + goto error; + } +#endif + memset(name, 0, sizeof(name)); + if (nan_name) + snprintf(name, sizeof(name), "%s%%d", nan_name); + else + sprintf(name, default_nan_name); + if ((bss_type == MLAN_BSS_TYPE_NAN) && (dev_alloc_name(dev, name) < 0)) { + PRINTM(MERROR, "Could not allocate nan device name\n"); + goto error; + } + priv = (moal_private *)netdev_priv(dev); + /* Save the priv to handle */ + handle->priv[bss_index] = priv; + + /* Use the same handle structure */ + priv->phandle = handle; + priv->netdev = dev; + priv->bss_index = bss_index; + priv->bss_type = bss_type; + priv->extra_tx_head_len = 0; + if (bss_type == MLAN_BSS_TYPE_STA) + priv->bss_role = MLAN_BSS_ROLE_STA; + else if (bss_type == MLAN_BSS_TYPE_UAP) + priv->bss_role = MLAN_BSS_ROLE_UAP; +#if defined(WIFI_DIRECT_SUPPORT) + else if (bss_type == MLAN_BSS_TYPE_WIFIDIRECT) + priv->bss_role = MLAN_BSS_ROLE_STA; +#endif + else if (bss_type == MLAN_BSS_TYPE_NAN) + priv->bss_role = MLAN_BSS_ROLE_STA; + + INIT_LIST_HEAD(&priv->tcp_sess_queue); + spin_lock_init(&priv->tcp_sess_lock); +#ifdef STA_SUPPORT + INIT_LIST_HEAD(&priv->tdls_list); + spin_lock_init(&priv->tdls_lock); +#endif + + INIT_LIST_HEAD(&priv->tx_stat_queue); + spin_lock_init(&priv->tx_stat_lock); +#ifdef STA_CFG80211 +#ifdef STA_SUPPORT + spin_lock_init(&priv->connect_lock); +#endif +#endif + +#ifdef STA_SUPPORT + INIT_LIST_HEAD(&priv->pmksa_cache_list); + if (bss_type == MLAN_BSS_TYPE_STA) { + init_waitqueue_head(&priv->okc_wait_q); + spin_lock_init(&priv->pmksa_list_lock); + priv->okc_roaming_ie = NULL; + priv->okc_ie_len = 0; + } +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24) + SET_MODULE_OWNER(dev); +#endif +#ifdef STA_SUPPORT + if (bss_type == MLAN_BSS_TYPE_STA +#if defined(WIFI_DIRECT_SUPPORT) + || bss_type == MLAN_BSS_TYPE_WIFIDIRECT +#endif + || bss_type == MLAN_BSS_TYPE_NAN) + woal_init_sta_dev(dev, priv); +#endif +#ifdef UAP_SUPPORT + if (bss_type == MLAN_BSS_TYPE_UAP) { + if (MLAN_STATUS_SUCCESS != woal_init_uap_dev(dev, priv)) + goto error; + } +#endif + if (!handle->priv_num +#ifdef MFG_CMD_SUPPORT + && (mfg_mode != MLAN_INIT_PARA_ENABLED) +#endif + ) { + if (init_cfg) { + if (MLAN_STATUS_SUCCESS != + woal_set_user_init_data(handle, INIT_CFG_DATA, + MOAL_IOCTL_WAIT)) { + PRINTM(MFATAL, + "Set user init data and param failed\n"); + goto error; + } + } + if (init_hostcmd_cfg) { + if (MLAN_STATUS_SUCCESS != + woal_set_user_init_data(handle, + INIT_HOSTCMD_CFG_DATA, + MOAL_IOCTL_WAIT)) { + PRINTM(MFATAL, + "Set user init hostcmd data and param failed\n"); + goto error; + } + } + } + + handle->priv_num++; +#if defined(STA_CFG80211) || defined(UAP_CFG80211) + if (!priv->phandle->wiphy && IS_STA_OR_UAP_CFG80211(cfg80211_wext)) { + if (woal_register_cfg80211(priv)) { + PRINTM(MERROR, "Cannot register with cfg80211\n"); + goto error; + } + } +#endif + +#ifdef STA_CFG80211 +#ifdef STA_SUPPORT + if ((priv->bss_role == MLAN_BSS_ROLE_STA) && + IS_STA_CFG80211(cfg80211_wext)) { + if (bss_type == MLAN_BSS_TYPE_STA +#if defined(WIFI_DIRECT_SUPPORT) +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION + || bss_type == MLAN_BSS_TYPE_WIFIDIRECT +#endif +#endif + || bss_type == MLAN_BSS_TYPE_NAN) + /* Register cfg80211 for STA or Wifi direct */ + if (woal_register_sta_cfg80211(dev, bss_type)) { + PRINTM(MERROR, + "Cannot register STA with cfg80211\n"); + goto error; + } + } +#endif /* STA_SUPPORT */ +#endif /* STA_CFG80211 */ +#ifdef UAP_CFG80211 +#ifdef UAP_SUPPORT + if ((priv->bss_role == MLAN_BSS_ROLE_UAP) && + IS_UAP_CFG80211(cfg80211_wext)) { + /* Register cfg80211 for UAP */ + if (woal_register_uap_cfg80211(dev, bss_type)) { + PRINTM(MERROR, "Cannot register UAP with cfg80211\n"); + goto error; + } + } +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) + strcpy(csa_str, "CSA"); + strcat(csa_str, name); + priv->csa_workqueue = + alloc_workqueue(csa_str, + WQ_HIGHPRI | WQ_MEM_RECLAIM | WQ_UNBOUND, 1); + if (!priv->csa_workqueue) { + PRINTM(MERROR, "cannot alloc csa workqueue \n"); + goto error; + } + INIT_DELAYED_WORK(&priv->csa_work, woal_csa_work_queue); +#endif +#endif +#endif /*UAP_CFG80211 */ + + /* Initialize priv structure */ + woal_init_priv(priv, MOAL_IOCTL_WAIT); + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) + SET_NETDEV_DEV(dev, handle->hotplug_device); +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33) + SET_NETDEV_DEVTYPE(dev, &wlan_type); +#endif + + /* Register network device */ + if (register_netdev(dev)) { + PRINTM(MERROR, "Cannot register virtual network device\n"); + goto error; + } + netif_carrier_off(dev); + woal_stop_queue(dev); + + PRINTM(MINFO, "%s: Marvell 802.11 Adapter\n", dev->name); + /* Set MAC address from the insmod command line */ + if (handle->set_mac_addr) { + memset(priv->current_addr, 0, ETH_ALEN); + memcpy(priv->current_addr, handle->mac_addr, ETH_ALEN); +#if defined(WIFI_DIRECT_SUPPORT) +#if defined(STA_CFG80211) && defined(UAP_CFG80211) +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION + if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT) { + priv->current_addr[0] |= 0x02; + PRINTM(MCMND, "Set WFD device addr: " MACSTR "\n", + MAC2STR(priv->current_addr)); + } +#endif +#endif +#endif + + if (MLAN_STATUS_SUCCESS != woal_request_set_mac_address(priv)) { + PRINTM(MERROR, "Set MAC address failed\n"); + goto error; + } + memcpy(dev->dev_addr, priv->current_addr, ETH_ALEN); + } + /* Set MAC address for UAPx/MLANx/WFDx and let them different with each other */ + if (priv->bss_index > 0 +#ifdef WIFI_DIRECT_SUPPORT + && priv->bss_type != MLAN_BSS_TYPE_WIFIDIRECT +#endif + ) { + priv->current_addr[4] += priv->bss_index; + woal_request_set_mac_address(priv); + memcpy(dev->dev_addr, priv->current_addr, ETH_ALEN); + PRINTM(MCMND, "Set %s interface addr: " MACSTR "\n", dev->name, + MAC2STR(priv->current_addr)); + } + if (bss_type == MLAN_BSS_TYPE_STA || + priv->bss_type == MLAN_BSS_TYPE_UAP) { + mlan_fw_info fw_info; + woal_request_get_fw_info(priv, MOAL_IOCTL_WAIT, &fw_info); + + for (i = 0; i < handle->histogram_table_num; i++) { + priv->hist_data[i] = kmalloc(sizeof(hgm_data) + + RX_RATE_MAX * + sizeof(atomic_t) + , GFP_KERNEL); + if (!(priv->hist_data[i])) { + PRINTM(MERROR, + "kmalloc priv->hist_data[%d] failed\n", + i); + goto error; + } + } + if (priv->hist_data) + woal_hist_data_reset(priv); + } +#ifdef CONFIG_PROC_FS + woal_create_proc_entry(priv); +#ifdef PROC_DEBUG + woal_debug_entry(priv); +#endif /* PROC_DEBUG */ +#endif /* CONFIG_PROC_FS */ + + LEAVE(); + return priv; +error: + handle->priv_num = bss_index; +#if defined(STA_CFG80211) || defined(UAP_CFG80211) + /* Unregister wiphy device and free */ + if (priv) { + if (priv->wdev && IS_STA_OR_UAP_CFG80211(cfg80211_wext)) + priv->wdev = NULL; +#ifdef UAP_CFG80211 +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) + if (priv->csa_workqueue) { + destroy_workqueue(priv->csa_workqueue); + priv->csa_workqueue = NULL; + } +#endif +#endif + } +#endif + if (dev && dev->reg_state == NETREG_REGISTERED) + unregister_netdev(dev); + if (dev) + free_netdev(dev); + LEAVE(); + return NULL; +} + +/** + * @brief This function removes an interface. + * + * @param handle A pointer to the moal_handle structure + * @param bss_index BSS index number + * + * @return N/A + */ +void +woal_remove_interface(moal_handle *handle, t_u8 bss_index) +{ + struct net_device *dev = NULL; + moal_private *priv = handle->priv[bss_index]; +#if defined(STA_WEXT) || defined(UAP_WEXT) + union iwreq_data wrqu; +#endif + int i = 0; + + ENTER(); + if (!priv || !priv->netdev) + goto error; + dev = priv->netdev; + + if (priv->media_connected == MTRUE) { + priv->media_connected = MFALSE; +#if defined(STA_WEXT) || defined(UAP_WEXT) + if (IS_STA_OR_UAP_WEXT(cfg80211_wext) && + GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) { + memset(wrqu.ap_addr.sa_data, 0x00, ETH_ALEN); + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + wireless_send_event(priv->netdev, SIOCGIWAP, &wrqu, + NULL); + } +#endif + } + woal_flush_tcp_sess_queue(priv); + + woal_flush_tx_stat_queue(priv); + + if (priv->bss_type == MLAN_BSS_TYPE_STA) + woal_flush_tdls_list(priv); +#ifdef STA_CFG80211 + if (priv->bss_type == MLAN_BSS_TYPE_STA && + IS_STA_CFG80211(cfg80211_wext)) { + woal_flush_pmksa_list(priv); + if (priv->okc_roaming_ie) { + kfree(priv->okc_roaming_ie); + priv->okc_roaming_ie = NULL; + priv->okc_ie_len = 0; + } + } +#endif + + if (priv->bss_type == MLAN_BSS_TYPE_STA || + priv->bss_type == MLAN_BSS_TYPE_UAP) { + for (i = 0; i < handle->histogram_table_num; i++) { + kfree(priv->hist_data[i]); + priv->hist_data[i] = NULL; + } + } +#ifdef CONFIG_PROC_FS +#ifdef PROC_DEBUG + /* Remove proc debug */ + woal_debug_remove(priv); +#endif /* PROC_DEBUG */ + woal_proc_remove(priv); +#endif /* CONFIG_PROC_FS */ + /* Last reference is our one */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37) + PRINTM(MINFO, "refcnt = %d\n", atomic_read(&dev->refcnt)); +#else + PRINTM(MINFO, "refcnt = %d\n", netdev_refcnt_read(dev)); +#endif + + PRINTM(MINFO, "netdev_finish_unregister: %s\n", dev->name); + + if (dev->reg_state == NETREG_REGISTERED) + unregister_netdev(dev); + +#if defined(STA_CFG80211) || defined(UAP_CFG80211) + /* Unregister wiphy device and free */ + if (priv->wdev && IS_STA_OR_UAP_CFG80211(cfg80211_wext)) + priv->wdev = NULL; +#ifdef UAP_CFG80211 +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) + if (priv->csa_workqueue) { + flush_workqueue(priv->csa_workqueue); + destroy_workqueue(priv->csa_workqueue); + priv->csa_workqueue = NULL; + } +#endif +#endif +#endif + + /* Clear the priv in handle */ + priv->phandle->priv[priv->bss_index] = NULL; + priv->phandle = NULL; + priv->netdev = NULL; + free_netdev(dev); +error: + LEAVE(); + return; +} + +/** + * @brief Configure pmic in firmware + * + * @param handle A pointer to moal_handle + * @param wait_option Wait option + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, + * otherwise fail + */ +mlan_status +woal_pmic_configure(moal_handle *handle, t_u8 wait_option) +{ + moal_private *priv = NULL; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *misc = NULL; + mlan_status status; + + ENTER(); + + priv = woal_get_priv(handle, MLAN_BSS_ROLE_ANY); + if (!priv) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + /* Allocate an IOCTL request buffer */ + req = (mlan_ioctl_req *) + woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + status = MLAN_STATUS_FAILURE; + goto done; + } + + /* Fill request buffer */ + misc = (mlan_ds_misc_cfg *)req->pbuf; + misc->sub_command = MLAN_OID_MISC_PMIC_CFG; + req->req_id = MLAN_IOCTL_MISC_CFG; + req->action = MLAN_ACT_SET; + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, wait_option); +done: + kfree(req); + LEAVE(); + return status; +} + +/** + * @brief Send FW shutdown command to MLAN + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, + * otherwise fail + */ +static mlan_status +woal_shutdown_fw(moal_private *priv, t_u8 wait_option) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *misc = NULL; + mlan_status status; + + ENTER(); + + /* Allocate an IOCTL request buffer */ + req = (mlan_ioctl_req *) + woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + status = MLAN_STATUS_FAILURE; + goto done; + } + + /* Fill request buffer */ + misc = (mlan_ds_misc_cfg *)req->pbuf; + misc->sub_command = MLAN_OID_MISC_INIT_SHUTDOWN; + misc->param.func_init_shutdown = MLAN_FUNC_SHUTDOWN; + req->req_id = MLAN_IOCTL_MISC_CFG; + req->action = MLAN_ACT_SET; + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, wait_option); + /* add 100 ms delay to avoid back to back init/shutdown */ + mdelay(100); +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return status; +} + +/** + * @brief Return hex value of a give character + * + * @param chr Character to be converted + * + * @return The converted character if chr is a valid hex, else 0 + */ +int +woal_hexval(char chr) +{ + if (chr >= '0' && chr <= '9') + return chr - '0'; + if (chr >= 'A' && chr <= 'F') + return chr - 'A' + 10; + if (chr >= 'a' && chr <= 'f') + return chr - 'a' + 10; + + return 0; +} + +#ifdef STA_SUPPORT +#endif + +/** + * @brief This function cancel all works in the queue + * and destroy the main workqueue. + * + * @param handle A pointer to moal_handle + * + * @return N/A + */ +static void +woal_terminate_workqueue(moal_handle *handle) +{ + ENTER(); + + /* Terminate main workqueue */ + if (handle->workqueue) { + flush_workqueue(handle->workqueue); + destroy_workqueue(handle->workqueue); + handle->workqueue = NULL; + } + if (handle->rx_workqueue) { + flush_workqueue(handle->rx_workqueue); + destroy_workqueue(handle->rx_workqueue); + handle->rx_workqueue = NULL; + } + LEAVE(); +} + +/******************************************************** + Global Functions +********************************************************/ + +/** + * @brief This function opens the network device + * + * @param dev A pointer to net_device structure + * + * @return 0 --success, otherwise fail + */ +int +woal_open(struct net_device *dev) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + t_u8 carrier_on = MFALSE; + + ENTER(); + + if (priv->phandle->surprise_removed == MTRUE) { + PRINTM(MERROR, + "open is not allowed in surprise remove state.\n"); + LEAVE(); + return -EFAULT; + } +#if defined(SYSKT) + /* On some systems the device open handler will be called before HW ready. + Use the following flag check and wait function to work around the issue. */ + { + int i = 0; + + while ((priv->phandle->hardware_status != HardwareStatusReady) + && (i < MAX_WAIT_DEVICE_READY_COUNT)) { + i++; + woal_sched_timeout(100); + } + if (i >= MAX_WAIT_DEVICE_READY_COUNT) { + PRINTM(MFATAL, + "HW not ready, wlan_open() return failure\n"); + LEAVE(); + return -EFAULT; + } + } +#endif /* USB || SYSKT || SYSKT_MULTI */ + if (!MODULE_GET) { + LEAVE(); + return -EFAULT; + } +#ifdef UAP_SUPPORT + if ((GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) && + (priv->media_connected)) + carrier_on = MTRUE; +#endif +#ifdef STA_SUPPORT + if ((GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) && + (priv->media_connected || priv->is_adhoc_link_sensed)) + carrier_on = MTRUE; +#endif +#if defined(WIFI_DIRECT_SUPPORT) +#if defined(STA_CFG80211) && defined(UAP_CFG80211) +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION + if (!p2p_enh) { + if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT && + IS_STA_CFG80211(cfg80211_wext)) { + priv->phandle->wiphy->interface_modes |= + MBIT(NL80211_IFTYPE_P2P_GO) | + MBIT(NL80211_IFTYPE_P2P_CLIENT); + } + } +#endif +#endif +#endif + if (carrier_on == MTRUE) { + if (!netif_carrier_ok(priv->netdev)) + netif_carrier_on(priv->netdev); + woal_wake_queue(priv->netdev); + } else { + if (netif_carrier_ok(priv->netdev)) + netif_carrier_off(priv->netdev); + } + + LEAVE(); + return 0; +} + +/** + * @brief This function closes the network device + * + * @param dev A pointer to net_device structure + * + * @return 0 + */ +int +woal_close(struct net_device *dev) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + + ENTER(); + woal_flush_tx_stat_queue(priv); + +#ifdef STA_SUPPORT +#ifdef STA_CFG80211 + if (IS_STA_CFG80211(cfg80211_wext) && + (priv->bss_type == MLAN_BSS_TYPE_STA)) + woal_clear_conn_params(priv); + woal_cancel_scan(priv, MOAL_IOCTL_WAIT); + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 11, 0) + if (IS_STA_CFG80211(cfg80211_wext) && priv->wdev->current_bss) + cfg80211_disconnected(priv->netdev, 0, NULL, 0, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 2, 0) + true, +#endif + GFP_KERNEL); +#endif + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) + if (IS_STA_CFG80211(cfg80211_wext) && priv->sched_scanning) { + woal_stop_bg_scan(priv, MOAL_IOCTL_WAIT); + priv->bg_scan_start = MFALSE; + priv->bg_scan_reported = MFALSE; + cfg80211_sched_scan_stopped(priv->wdev->wiphy +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) + , 0 +#endif + ); + priv->sched_scanning = MFALSE; + } +#endif +#endif +#endif + if (!priv->bss_virtual) + woal_stop_queue(priv->netdev); +#if defined(WIFI_DIRECT_SUPPORT) +#if defined(STA_CFG80211) && defined(UAP_CFG80211) +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION + if (!p2p_enh) { + if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT && + !priv->bss_virtual && + IS_STA_CFG80211(cfg80211_wext) && + IS_UAP_CFG80211(cfg80211_wext)) { + priv->phandle->wiphy->interface_modes &= + ~(MBIT(NL80211_IFTYPE_P2P_GO) | + MBIT(NL80211_IFTYPE_P2P_CLIENT)); + } + } +#endif +#endif +#endif + MODULE_PUT; + + LEAVE(); + return 0; +} + +/** + * @brief This function sets the MAC address to firmware. + * + * @param dev A pointer to mlan_private structure + * @param addr MAC address to set + * + * @return 0 --success, otherwise fail + */ +int +woal_set_mac_address(struct net_device *dev, void *addr) +{ + int ret = 0; + moal_private *priv = (moal_private *)netdev_priv(dev); + struct sockaddr *phw_addr = (struct sockaddr *)addr; + t_u8 prev_addr[ETH_ALEN]; + + ENTER(); + + if (priv->phandle->surprise_removed == MTRUE) { + PRINTM(MERROR, + "Set mac address is not allowed in surprise remove state.\n"); + LEAVE(); + return -EFAULT; + } + + memcpy(prev_addr, priv->current_addr, ETH_ALEN); + memset(priv->current_addr, 0, ETH_ALEN); + /* dev->dev_addr is 6 bytes */ + HEXDUMP("dev->dev_addr:", dev->dev_addr, ETH_ALEN); + + HEXDUMP("addr:", (t_u8 *)phw_addr->sa_data, ETH_ALEN); + memcpy(priv->current_addr, phw_addr->sa_data, ETH_ALEN); +#if defined(WIFI_DIRECT_SUPPORT) +#if defined(STA_CFG80211) && defined(UAP_CFG80211) +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION + if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT) { + priv->current_addr[0] |= 0x02; + PRINTM(MCMND, "Set WFD device addr: " MACSTR "\n", + MAC2STR(priv->current_addr)); + } +#endif +#endif +#endif + if (MLAN_STATUS_SUCCESS != woal_request_set_mac_address(priv)) { + PRINTM(MERROR, "Set MAC address failed\n"); + /* For failure restore the MAC address */ + memcpy(priv->current_addr, prev_addr, ETH_ALEN); + ret = -EFAULT; + goto done; + } + HEXDUMP("priv->MacAddr:", priv->current_addr, ETH_ALEN); + memcpy(dev->dev_addr, priv->current_addr, ETH_ALEN); +done: + LEAVE(); + return ret; +} + +/** + * @brief Check driver status + * + * @param handle A pointer to moal_handle + * + * @return MTRUE/MFALSE + */ +t_u8 +woal_check_driver_status(moal_handle *handle) +{ + moal_private *priv = NULL; + struct timeval t; + int i = 0; + ENTER(); + + priv = woal_get_priv(handle, MLAN_BSS_ROLE_ANY); + if (!priv || woal_get_debug_info(priv, MOAL_IOCTL_WAIT, &info)) { + PRINTM(MERROR, + "Could not retrieve debug information from MLAN\n"); + LEAVE(); + return MTRUE; + } +#define MOAL_CMD_TIMEOUT_MAX 9 +#define MOAL_CMD_TIMEOUT 20 + do_gettimeofday(&t); + if (info.pending_cmd && + (t.tv_sec > (info.dnld_cmd_in_secs + MOAL_CMD_TIMEOUT_MAX))) { + if (t.tv_sec > (info.dnld_cmd_in_secs + MOAL_CMD_TIMEOUT) && + !info.num_cmd_timeout) { + PRINTM(MERROR, "Ignore invalid time, wait=%d\n", + (int)(t.tv_sec - info.dnld_cmd_in_secs)); + } else { + PRINTM(MERROR, "Timeout cmd id = 0x%x wait=%d\n", + info.pending_cmd, + (int)(t.tv_sec - info.dnld_cmd_in_secs)); + LEAVE(); + return MTRUE; + } + } + if (info.num_cmd_timeout) { + PRINTM(MERROR, "num_cmd_timeout = %d\n", info.num_cmd_timeout); + PRINTM(MERROR, "Timeout cmd id = 0x%x, act = 0x%x\n", + info.timeout_cmd_id, info.timeout_cmd_act); + LEAVE(); + return MTRUE; + } + if (info.num_cmd_host_to_card_failure) { + PRINTM(MERROR, "num_cmd_host_to_card_failure = %d\n", + info.num_cmd_host_to_card_failure); + LEAVE(); + return MTRUE; + } + if (info.num_no_cmd_node) { + PRINTM(MERROR, "num_no_cmd_node = %d\n", info.num_no_cmd_node); + LEAVE(); + return MTRUE; + } + for (i = 0; i < handle->priv_num; i++) { + priv = handle->priv[i]; + if (priv) { + if (priv->num_tx_timeout >= NUM_TX_TIMEOUT_THRESHOLD) { + PRINTM(MERROR, "num_tx_timeout = %d\n", + priv->num_tx_timeout); + LEAVE(); + return MTRUE; + } + } + } + if (info.pm_wakeup_card_req && info.pm_wakeup_fw_try) { +#define MAX_WAIT_TIME 3 + if (t.tv_sec > (info.pm_wakeup_in_secs + MAX_WAIT_TIME)) { + PRINTM(MERROR, + "wakeup_dev_req=%d wakeup_tries=%d wait=%d\n", + info.pm_wakeup_card_req, info.pm_wakeup_fw_try, + (int)(t.tv_sec - info.pm_wakeup_in_secs)); + LEAVE(); + return MTRUE; + } + } + LEAVE(); + return MFALSE; +} + +/** + * @brief Display MLAN debug information + * + * @param priv A pointer to moal_private + * + * @return N/A + */ +void +woal_mlan_debug_info(moal_private *priv) +{ + int i; +#ifdef SDIO_MULTI_PORT_TX_AGGR + int j; + t_u8 mp_aggr_pkt_limit = SDIO_MP_AGGR_DEF_PKT_LIMIT; +#endif + char str[512] = { 0 }; + char *s; + + ENTER(); + + if (!priv || woal_get_debug_info(priv, MOAL_IOCTL_WAIT, &info)) { + PRINTM(MERROR, + "Could not retrieve debug information from MLAN\n"); + LEAVE(); + return; + } + PRINTM(MERROR, "------------mlan_debug_info-------------\n"); + PRINTM(MERROR, "mlan_processing =%d\n", info.mlan_processing); + PRINTM(MERROR, "main_lock_flag =%d\n", info.main_lock_flag); + PRINTM(MERROR, "main_process_cnt =%d\n", info.main_process_cnt); + PRINTM(MERROR, "delay_task_flag =%d\n", info.delay_task_flag); + PRINTM(MERROR, "mlan_rx_processing =%d\n", info.mlan_rx_processing); + PRINTM(MERROR, "rx_pkts_queued=%d\n", info.rx_pkts_queued); + PRINTM(MERROR, "tx_pkts_queued=%d\n", info.tx_pkts_queued); + PRINTM(MERROR, "num_cmd_timeout = %d\n", info.num_cmd_timeout); + PRINTM(MERROR, "dbg.num_cmd_timeout = %d\n", info.dbg_num_cmd_timeout); + PRINTM(MERROR, "Timeout cmd id = 0x%x, act = 0x%x\n", + info.timeout_cmd_id, info.timeout_cmd_act); + + PRINTM(MERROR, "last_cmd_index = %d\n", info.last_cmd_index); + for (s = str, i = 0; i < DBG_CMD_NUM; i++) + s += sprintf(s, "0x%x ", info.last_cmd_id[i]); + PRINTM(MERROR, "last_cmd_id = %s\n", str); + for (s = str, i = 0; i < DBG_CMD_NUM; i++) + s += sprintf(s, "0x%x ", info.last_cmd_act[i]); + PRINTM(MERROR, "last_cmd_act = %s\n", str); + PRINTM(MERROR, "last_cmd_resp_index = %d\n", info.last_cmd_resp_index); + for (s = str, i = 0; i < DBG_CMD_NUM; i++) + s += sprintf(s, "0x%x ", info.last_cmd_resp_id[i]); + PRINTM(MERROR, "last_cmd_resp_id = %s\n", str); + PRINTM(MERROR, "last_event_index = %d\n", info.last_event_index); + for (s = str, i = 0; i < DBG_CMD_NUM; i++) + s += sprintf(s, "0x%x ", info.last_event[i]); + PRINTM(MERROR, "last_event = %s", str); + + PRINTM(MERROR, "num_data_h2c_failure = %d\n", + info.num_tx_host_to_card_failure); + PRINTM(MERROR, "num_cmd_h2c_failure = %d\n", + info.num_cmd_host_to_card_failure); + PRINTM(MERROR, "num_alloc_buffer_failure = %d\n", + info.num_alloc_buffer_failure); + PRINTM(MERROR, "num_pkt_dropped = %d\n", info.num_pkt_dropped); + + PRINTM(MERROR, "num_data_c2h_failure = %d\n", + info.num_rx_card_to_host_failure); + PRINTM(MERROR, "num_cmdevt_c2h_failure = %d\n", + info.num_cmdevt_card_to_host_failure); + PRINTM(MERROR, "num_int_read_failure = %d\n", + info.num_int_read_failure); + PRINTM(MERROR, "last_int_status = %d\n", info.last_int_status); + + PRINTM(MERROR, "num_event_deauth = %d\n", info.num_event_deauth); + PRINTM(MERROR, "num_event_disassoc = %d\n", info.num_event_disassoc); + PRINTM(MERROR, "num_event_link_lost = %d\n", info.num_event_link_lost); + PRINTM(MERROR, "num_cmd_deauth = %d\n", info.num_cmd_deauth); + PRINTM(MERROR, "num_cmd_assoc_success = %d\n", + info.num_cmd_assoc_success); + PRINTM(MERROR, "num_cmd_assoc_failure = %d\n", + info.num_cmd_assoc_failure); + PRINTM(MERROR, "cmd_resp_received = %d\n", info.cmd_resp_received); + PRINTM(MERROR, "event_received = %d\n", info.event_received); + + PRINTM(MERROR, "max_tx_buf_size = %d\n", info.max_tx_buf_size); + PRINTM(MERROR, "tx_buf_size = %d\n", info.tx_buf_size); + PRINTM(MERROR, "curr_tx_buf_size = %d\n", info.curr_tx_buf_size); + + PRINTM(MERROR, "data_sent=%d cmd_sent=%d\n", info.data_sent, + info.cmd_sent); + + PRINTM(MERROR, "ps_mode=%d ps_state=%d\n", info.ps_mode, info.ps_state); + PRINTM(MERROR, "wakeup_dev_req=%d wakeup_tries=%d\n", + info.pm_wakeup_card_req, info.pm_wakeup_fw_try); + PRINTM(MERROR, "hs_configured=%d hs_activated=%d\n", + info.is_hs_configured, info.hs_activated); + PRINTM(MERROR, "pps_uapsd_mode=%d sleep_pd=%d\n", + info.pps_uapsd_mode, info.sleep_pd); + PRINTM(MERROR, "tx_lock_flag = %d\n", info.tx_lock_flag); + PRINTM(MERROR, "port_open = %d\n", info.port_open); + PRINTM(MERROR, "scan_processing = %d\n", info.scan_processing); + + PRINTM(MERROR, "mp_rd_bitmap=0x%x curr_rd_port=0x%x\n", + (unsigned int)info.mp_rd_bitmap, info.curr_rd_port); + PRINTM(MERROR, "mp_wr_bitmap=0x%x curr_wr_port=0x%x\n", + (unsigned int)info.mp_wr_bitmap, info.curr_wr_port); + PRINTM(MERROR, "mp_invalid_update=%d\n", info.mp_invalid_update); +#ifdef SDIO_MULTI_PORT_TX_AGGR + PRINTM(MERROR, "last_recv_wr_bitmap=0x%x last_mp_index = %d\n", + info.last_recv_wr_bitmap, info.last_mp_index); + for (i = 0; i < SDIO_MP_DBG_NUM; i++) { + for (s = str, j = 0; j < mp_aggr_pkt_limit; j++) + s += sprintf(s, "0x%02x ", + info.last_mp_wr_info[i * + mp_aggr_pkt_limit + + j]); + + PRINTM(MERROR, + "mp_wr_bitmap: 0x%x mp_wr_ports=0x%x len=%d curr_wr_port=0x%x\n%s\n", + info.last_mp_wr_bitmap[i], info.last_mp_wr_ports[i], + info.last_mp_wr_len[i], info.last_curr_wr_port[i], str); + } +#endif + for (i = 0; i < info.ralist_num; i++) { + PRINTM(MERROR, + "ralist ra: %02x:%02x:%02x:%02x:%02x:%02x tid=%d pkts=%d pause=%d\n", + info.ralist[i].ra[0], info.ralist[i].ra[1], + info.ralist[i].ra[2], info.ralist[i].ra[3], + info.ralist[i].ra[4], info.ralist[i].ra[5], + info.ralist[i].tid, info.ralist[i].total_pkts, + info.ralist[i].tx_pause); + } + + PRINTM(MERROR, "------------mlan_debug_info End-------------\n"); + LEAVE(); +} + +/** + * @brief This function handle the shutdown timeout issue + * + * @param handle Pointer to structure moal_handle + * + * @return N/A + */ +void +woal_ioctl_timeout(moal_handle *handle) +{ + moal_private *priv = NULL; + + ENTER(); + + PRINTM(MMSG, "woal_ioctl_timout.\n"); + priv = woal_get_priv(handle, MLAN_BSS_ROLE_ANY); + if (priv) { + woal_mlan_debug_info(priv); + woal_moal_debug_info(priv, NULL, MFALSE); + } + LEAVE(); + return; +} + +/** + * @brief This function handles the timeout of packet + * transmission + * + * @param dev A pointer to net_device structure + * + * @return N/A + */ +void +woal_tx_timeout(struct net_device *dev) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + + ENTER(); + + priv->num_tx_timeout++; + PRINTM(MERROR, "%lu : %s (bss=%d): Tx timeout (%d)\n", + jiffies, dev->name, priv->bss_index, priv->num_tx_timeout); + woal_set_trans_start(dev); + + if (priv->num_tx_timeout == NUM_TX_TIMEOUT_THRESHOLD) { + woal_mlan_debug_info(priv); + woal_moal_debug_info(priv, NULL, MFALSE); + woal_broadcast_event(priv, CUS_EVT_DRIVER_HANG, + strlen(CUS_EVT_DRIVER_HANG)); +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + if (IS_STA_OR_UAP_CFG80211(cfg80211_wext)) + woal_cfg80211_vendor_event(priv, event_hang, + CUS_EVT_DRIVER_HANG, + strlen(CUS_EVT_DRIVER_HANG)); +#endif +#endif + priv->phandle->driver_state = MTRUE; + woal_process_hang(priv->phandle); + } + + LEAVE(); +} + +/** + * @brief This function returns the network statistics + * + * @param dev A pointer to net_device structure + * + * @return A pointer to net_device_stats structure + */ +struct net_device_stats * +woal_get_stats(struct net_device *dev) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + return &priv->stats; +} + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) +/** + * @brief This function handles wmm queue select + * + * @param dev A pointer to net_device structure + * @param skb A pointer to sk_buff structure + * + * @return tx_queue index (0-3) + */ +u16 +woal_select_queue(struct net_device *dev, struct sk_buff *skb +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 13, 0) + , void *accel_priv +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + , select_queue_fallback_t fallback +#endif +#endif + ) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + struct ethhdr *eth = NULL; + t_u8 tid = 0; + t_u8 index = 0; + + ENTER(); + + /* + * skb->priority values from 256->263 are magic values to + * directly indicate a specific 802.1d priority. This is used + * to allow 802.1d priority to be passed directly in from VLAN + * tags, etc. + */ + if (IS_SKB_MAGIC_VLAN(skb)) { + tid = GET_VLAN_PRIO(skb); + } else { + eth = (struct ethhdr *)skb->data; + switch (eth->h_proto) { + case __constant_htons(ETH_P_IP): + tid = priv->dscp_map[SKB_TOS(skb) >> DSCP_OFFSET]; + if (tid == 0xFF) + tid = (IPTOS_PREC(SKB_TOS(skb)) >> + IPTOS_OFFSET); + break; + case __constant_htons(ETH_P_IPV6): + tid = SKB_TIDV6(skb); + break; + case __constant_htons(ETH_P_ARP): + default: + break; + } + } + + index = mlan_select_wmm_queue(priv->phandle->pmlan_adapter, + priv->bss_index, tid); + PRINTM(MDATA, "select queue: tid=%d, index=%d\n", tid, index); + LEAVE(); + return index; +} +#endif + +/** + * @brief This function flush tx status queue + * + * @param priv A pointer to moal_private structure + * + * @return N/A + */ +void +woal_flush_tx_stat_queue(moal_private *priv) +{ + struct tx_status_info *tx_info = NULL, *tmp_node; + unsigned long flags; + struct sk_buff *skb = NULL; + spin_lock_irqsave(&priv->tx_stat_lock, flags); + list_for_each_entry_safe(tx_info, tmp_node, &priv->tx_stat_queue, link) { + list_del(&tx_info->link); + spin_unlock_irqrestore(&priv->tx_stat_lock, flags); + skb = (struct sk_buff *)tx_info->tx_skb; + if (tx_info->tx_cookie) { +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 37) +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 6, 0) + cfg80211_mgmt_tx_status(priv->netdev, + tx_info->tx_cookie, skb->data, + skb->len, true, GFP_ATOMIC); +#else + cfg80211_mgmt_tx_status(priv->wdev, tx_info->tx_cookie, + skb->data, skb->len, true, + GFP_ATOMIC); +#endif +#endif +#endif + } + dev_kfree_skb_any(skb); + kfree(tx_info); + spin_lock_irqsave(&priv->tx_stat_lock, flags); + } + INIT_LIST_HEAD(&priv->tx_stat_queue); + spin_unlock_irqrestore(&priv->tx_stat_lock, flags); +} + +/** + * @brief This function gets tx info from tx_stat_queue + * + * @param priv A pointer to moal_private structure + * @param tx_seq_num tx seq number + * + * @return A pointer to the tcp tx_status_info structure, if found. + * Otherwise, null + */ +struct tx_status_info * +woal_get_tx_info(moal_private *priv, t_u8 tx_seq_num) +{ + struct tx_status_info *tx_info = NULL; + ENTER(); + + list_for_each_entry(tx_info, &priv->tx_stat_queue, link) { + if (tx_info->tx_seq_num == tx_seq_num) { + LEAVE(); + return tx_info; + } + } + LEAVE(); + return NULL; +} + +/** + * @brief This function remove tx info from queue + * + * @param priv A pointer to moal_private structure + * @param tx_seq_num tx seq number + * + * @return N/A + */ +void +woal_remove_tx_info(moal_private *priv, t_u8 tx_seq_num) +{ + struct tx_status_info *tx_info, *tmp = NULL; + unsigned long flags; + ENTER(); + + spin_lock_irqsave(&priv->tx_stat_lock, flags); + list_for_each_entry_safe(tx_info, tmp, &priv->tx_stat_queue, link) { + if (tx_info->tx_seq_num == tx_seq_num) { + list_del(&tx_info->link); + dev_kfree_skb_any((struct sk_buff *)tx_info->tx_skb); + kfree(tx_info); + break; + } + } + spin_unlock_irqrestore(&priv->tx_stat_lock, flags); + + LEAVE(); +} + +/** + * @brief This function flush tcp session queue + * + * @param priv A pointer to moal_private structure + * + * @return N/A + */ +void +woal_flush_tdls_list(moal_private *priv) +{ + struct tdls_peer *peer = NULL, *tmp_node; + unsigned long flags; + spin_lock_irqsave(&priv->tdls_lock, flags); + list_for_each_entry_safe(peer, tmp_node, &priv->tdls_list, link) { + list_del(&peer->link); + kfree(peer); + } + INIT_LIST_HEAD(&priv->tdls_list); + spin_unlock_irqrestore(&priv->tdls_lock, flags); + priv->tdls_check_tx = MFALSE; +} + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) +/** + * @brief check the tx packet for tdls auto set up + * + * @param priv A pointer to moal_private structure + * @param skb A pointer to skb buffer. + * + * @return N/A + */ +void +woal_tdls_check_tx(moal_private *priv, struct sk_buff *skb) +{ + struct tdls_peer *peer = NULL; + unsigned long flags; + t_u8 ra[MLAN_MAC_ADDR_LENGTH]; + ENTER(); + memcpy(ra, skb->data, MLAN_MAC_ADDR_LENGTH); + spin_lock_irqsave(&priv->tdls_lock, flags); + list_for_each_entry(peer, &priv->tdls_list, link) { + if (!memcmp(peer->peer_addr, ra, ETH_ALEN)) { + if (peer->rssi && + (peer->rssi <= TDLS_RSSI_HIGH_THRESHOLD)) { + if ((peer->link_status == TDLS_NOT_SETUP) && + (peer->num_failure < + TDLS_MAX_FAILURE_COUNT)) { + peer->link_status = + TDLS_SETUP_INPROGRESS; + PRINTM(MMSG, + "Wlan: Set up TDLS link,peer=" + MACSTR " rssi=%d\n", + MAC2STR(peer->peer_addr), + -peer->rssi); + cfg80211_tdls_oper_request(priv->netdev, + peer-> + peer_addr, + NL80211_TDLS_SETUP, + 0, + GFP_ATOMIC); + priv->tdls_check_tx = MFALSE; + } + + } + break; + } + } + spin_unlock_irqrestore(&priv->tdls_lock, flags); + LEAVE(); +} +#endif + +/** + * @brief This function flush tcp session queue + * + * @param priv A pointer to moal_private structure + * + * @return N/A + */ +void +woal_flush_tcp_sess_queue(moal_private *priv) +{ + struct tcp_sess *tcp_sess = NULL, *tmp_node; + unsigned long flags; + struct sk_buff *skb; + spin_lock_irqsave(&priv->tcp_sess_lock, flags); + list_for_each_entry_safe(tcp_sess, tmp_node, &priv->tcp_sess_queue, + link) { + list_del(&tcp_sess->link); + if (tcp_sess->is_timer_set) + woal_cancel_timer(&tcp_sess->ack_timer); + skb = (struct sk_buff *)tcp_sess->ack_skb; + if (skb) + dev_kfree_skb_any(skb); + kfree(tcp_sess); + } + INIT_LIST_HEAD(&priv->tcp_sess_queue); + priv->tcp_ack_drop_cnt = 0; + priv->tcp_ack_cnt = 0; + spin_unlock_irqrestore(&priv->tcp_sess_lock, flags); +} + +/** + * @brief This function gets tcp session from the tcp session queue + * + * @param priv A pointer to moal_private structure + * @param src_ip IP address of the device + * @param src_port TCP port of the device + * @param dst_ip IP address of the client + * @param dst_port TCP port of the client + * + * @return A pointer to the tcp session data structure, if found. + * Otherwise, null + */ +static inline struct tcp_sess * +woal_get_tcp_sess(moal_private *priv, + t_u32 src_ip, t_u16 src_port, t_u32 dst_ip, t_u16 dst_port) +{ + struct tcp_sess *tcp_sess = NULL; + ENTER(); + + list_for_each_entry(tcp_sess, &priv->tcp_sess_queue, link) { + if ((tcp_sess->src_ip_addr == src_ip) && + (tcp_sess->src_tcp_port == src_port) && + (tcp_sess->dst_ip_addr == dst_ip) && + (tcp_sess->dst_tcp_port == dst_port)) { + LEAVE(); + return tcp_sess; + } + } + LEAVE(); + return NULL; +} + +/** + * @brief This function send the holding tcp ack packet + * re-assoc thread. + * + * @param context A pointer to context + * @return N/A + */ +void +woal_tcp_ack_timer_func(void *context) +{ + struct tcp_sess *tcp_session = (struct tcp_sess *)context; + moal_private *priv = (moal_private *)tcp_session->priv; + unsigned long flags; + mlan_buffer *pmbuf; + struct sk_buff *skb; + mlan_status status; +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) + t_u32 index = 0; +#endif + ENTER(); + spin_lock_irqsave(&priv->tcp_sess_lock, flags); + tcp_session->is_timer_set = MFALSE; + skb = (struct sk_buff *)tcp_session->ack_skb; + pmbuf = (mlan_buffer *)tcp_session->pmbuf; + tcp_session->ack_skb = NULL; + tcp_session->pmbuf = NULL; + spin_unlock_irqrestore(&priv->tcp_sess_lock, flags); + if (skb && pmbuf) { + status = mlan_send_packet(priv->phandle->pmlan_adapter, pmbuf); + switch (status) { + case MLAN_STATUS_PENDING: + atomic_inc(&priv->phandle->tx_pending); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) + index = skb_get_queue_mapping(skb); + atomic_inc(&priv->wmm_tx_pending[index]); + if (atomic_read(&priv->wmm_tx_pending[index]) >= + MAX_TX_PENDING) { + struct netdev_queue *txq = + netdev_get_tx_queue(priv->netdev, + index); + netif_tx_stop_queue(txq); + PRINTM(MINFO, "Stop Kernel Queue : %d\n", + index); + } +#else + if (atomic_read(&priv->phandle->tx_pending) >= + MAX_TX_PENDING) + woal_stop_queue(priv->netdev); +#endif /*#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,29) */ + queue_work(priv->phandle->workqueue, + &priv->phandle->main_work); + break; + case MLAN_STATUS_SUCCESS: + priv->stats.tx_packets++; + priv->stats.tx_bytes += skb->len; + dev_kfree_skb_any(skb); + break; + case MLAN_STATUS_FAILURE: + default: + priv->stats.tx_dropped++; + dev_kfree_skb_any(skb); + break; + } + } + LEAVE(); + return; +} + +/** + * @brief This function send the tcp ack + * + * + * @param priv A pointer to moal_private structure + * @param tcp_session A pointer to tcp_session + * @return N/A + */ +void +woal_send_tcp_ack(moal_private *priv, struct tcp_sess *tcp_session) +{ + mlan_status status; + struct sk_buff *skb = (struct sk_buff *)tcp_session->ack_skb; + mlan_buffer *pmbuf = (mlan_buffer *)tcp_session->pmbuf; +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) + t_u32 index = 0; +#endif + ENTER(); + if (tcp_session->is_timer_set) { + woal_cancel_timer(&tcp_session->ack_timer); + tcp_session->is_timer_set = MFALSE; + } + tcp_session->ack_skb = NULL; + tcp_session->pmbuf = NULL; + status = mlan_send_packet(priv->phandle->pmlan_adapter, pmbuf); + switch (status) { + case MLAN_STATUS_PENDING: + atomic_inc(&priv->phandle->tx_pending); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) + index = skb_get_queue_mapping(skb); + atomic_inc(&priv->wmm_tx_pending[index]); + if (atomic_read(&priv->wmm_tx_pending[index]) >= MAX_TX_PENDING) { + struct netdev_queue *txq = + netdev_get_tx_queue(priv->netdev, index); + netif_tx_stop_queue(txq); + PRINTM(MINFO, "Stop Kernel Queue : %d\n", index); + } +#else + if (atomic_read(&priv->phandle->tx_pending) >= MAX_TX_PENDING) + woal_stop_queue(priv->netdev); +#endif /*#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,29) */ + queue_work(priv->phandle->workqueue, &priv->phandle->main_work); + break; + case MLAN_STATUS_SUCCESS: + priv->stats.tx_packets++; + priv->stats.tx_bytes += skb->len; + dev_kfree_skb_any(skb); + break; + case MLAN_STATUS_FAILURE: + default: + priv->stats.tx_dropped++; + dev_kfree_skb_any(skb); + break; + } + LEAVE(); +} + +/** + * @brief This function get the tcp ack session node + * + * @param priv A pointer to moal_private structure + * @param pmbuf A pointer to mlan_buffer associated with a skb + * + * @return 1, if it's dropped; 0, if not dropped + */ +int +woal_process_tcp_ack(moal_private *priv, mlan_buffer *pmbuf) +{ + int ret = 0; + unsigned long flags; + struct tcp_sess *tcp_session; + struct ethhdr *ethh = NULL; + struct iphdr *iph = NULL; + struct tcphdr *tcph = NULL; + t_u32 ack_seq; + struct sk_buff *skb; + + ENTER(); + + /** check the tcp packet */ + ethh = (struct ethhdr *)(pmbuf->pbuf + pmbuf->data_offset); + if (ntohs(ethh->h_proto) != ETH_P_IP) { + LEAVE(); + return 0; + } + iph = (struct iphdr *)((t_u8 *)ethh + sizeof(struct ethhdr)); + if (iph->protocol != IPPROTO_TCP) { + LEAVE(); + return 0; + } + tcph = (struct tcphdr *)((t_u8 *)iph + iph->ihl * 4); + + if (*((t_u8 *)tcph + 13) == 0x10) { + /* Only replace ACK */ + if (ntohs(iph->tot_len) > (iph->ihl + tcph->doff) * 4) { + /* Don't drop ACK with payload */ + /* TODO: should we delete previous TCP session */ + LEAVE(); + return ret; + } + priv->tcp_ack_cnt++; + spin_lock_irqsave(&priv->tcp_sess_lock, flags); + tcp_session = woal_get_tcp_sess(priv, iph->saddr, + tcph->source, iph->daddr, + tcph->dest); + if (!tcp_session) { + tcp_session = + kmalloc(sizeof(struct tcp_sess), GFP_ATOMIC); + if (!tcp_session) { + PRINTM(MERROR, "Fail to allocate tcp_sess.\n"); + spin_unlock_irqrestore(&priv->tcp_sess_lock, + flags); + goto done; + } + tcp_session->ack_skb = pmbuf->pdesc; + tcp_session->pmbuf = pmbuf; + pmbuf->flags |= MLAN_BUF_FLAG_TCP_ACK; + tcp_session->src_ip_addr = iph->saddr; + tcp_session->dst_ip_addr = iph->daddr; + tcp_session->src_tcp_port = tcph->source; + tcp_session->dst_tcp_port = tcph->dest; + tcp_session->ack_seq = ntohl(tcph->ack_seq); + tcp_session->priv = (void *)priv; + skb = (struct sk_buff *)pmbuf->pdesc; + skb->cb[0] = 0; + /* Initialize the timer for tcp ack */ + woal_initialize_timer(&tcp_session->ack_timer, + woal_tcp_ack_timer_func, + tcp_session); + tcp_session->is_timer_set = MTRUE; + woal_mod_timer(&tcp_session->ack_timer, MOAL_TIMER_1MS); + list_add_tail(&tcp_session->link, + &priv->tcp_sess_queue); + spin_unlock_irqrestore(&priv->tcp_sess_lock, flags); + ret = HOLD_TCP_ACK; + LEAVE(); + return ret; + } else if (!tcp_session->ack_skb) { + tcp_session->ack_skb = pmbuf->pdesc; + tcp_session->pmbuf = pmbuf; + pmbuf->flags |= MLAN_BUF_FLAG_TCP_ACK; + tcp_session->ack_seq = ntohl(tcph->ack_seq); + tcp_session->priv = (void *)priv; + skb = (struct sk_buff *)pmbuf->pdesc; + skb->cb[0] = 0; + tcp_session->is_timer_set = MTRUE; + woal_mod_timer(&tcp_session->ack_timer, MOAL_TIMER_1MS); + spin_unlock_irqrestore(&priv->tcp_sess_lock, flags); + ret = HOLD_TCP_ACK; + LEAVE(); + return ret; + } + ack_seq = ntohl(tcph->ack_seq); + skb = (struct sk_buff *)tcp_session->ack_skb; + if (likely(ack_seq > tcp_session->ack_seq) && + (skb->len == pmbuf->data_len)) { + memcpy(skb->data, pmbuf->pbuf + pmbuf->data_offset, + pmbuf->data_len); + tcp_session->ack_seq = ack_seq; + ret = DROP_TCP_ACK; + skb->cb[0]++; +//We will drop 90% tcp ack +#define TCP_ACK_MAX_HOLD 9 + if (skb->cb[0] >= TCP_ACK_MAX_HOLD) + woal_send_tcp_ack(priv, tcp_session); + spin_unlock_irqrestore(&priv->tcp_sess_lock, flags); + skb = (struct sk_buff *)pmbuf->pdesc; + dev_kfree_skb_any(skb); + priv->tcp_ack_drop_cnt++; + } else { + pmbuf->flags |= MLAN_BUF_FLAG_TCP_ACK; + spin_unlock_irqrestore(&priv->tcp_sess_lock, flags); + LEAVE(); + return ret; + } + } +done: + LEAVE(); + return ret; +} + +/** + * @brief This function handles packet transmission + * + * @param skb A pointer to sk_buff structure + * @param dev A pointer to net_device structure + * + * @return 0 --success + */ +int +woal_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + mlan_buffer *pmbuf = NULL; + mlan_status status; + struct sk_buff *new_skb = NULL; +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) + t_u32 index = 0; +#endif + int ret = 0; + ENTER(); + PRINTM(MDATA, "%lu : %s (bss=%d): Data <= kernel\n", + jiffies, dev->name, priv->bss_index); + + if (priv->phandle->surprise_removed == MTRUE) { + dev_kfree_skb_any(skb); + priv->stats.tx_dropped++; + goto done; + } + priv->num_tx_timeout = 0; + if (!skb->len || (skb->len > ETH_FRAME_LEN)) { + PRINTM(MERROR, "Tx Error: Bad skb length %d : %d\n", + skb->len, ETH_FRAME_LEN); + dev_kfree_skb_any(skb); + priv->stats.tx_dropped++; + goto done; + } + if (skb_headroom(skb) < (MLAN_MIN_DATA_HEADER_LEN + + sizeof(mlan_buffer) + + priv->extra_tx_head_len)) { + PRINTM(MWARN, "Tx: Insufficient skb headroom %d\n", + skb_headroom(skb)); + /* Insufficient skb headroom - allocate a new skb */ + new_skb = skb_realloc_headroom(skb, MLAN_MIN_DATA_HEADER_LEN + + sizeof(mlan_buffer) + + priv->extra_tx_head_len); + if (unlikely(!new_skb)) { + PRINTM(MERROR, "Tx: Cannot allocate skb\n"); + dev_kfree_skb_any(skb); + priv->stats.tx_dropped++; + goto done; + } + if (new_skb != skb) + dev_kfree_skb_any(skb); + skb = new_skb; + PRINTM(MINFO, "new skb headroom %d\n", skb_headroom(skb)); + } + pmbuf = (mlan_buffer *)skb->head; + memset((t_u8 *)pmbuf, 0, sizeof(mlan_buffer)); + pmbuf->bss_index = priv->bss_index; + woal_fill_mlan_buffer(priv, pmbuf, skb); + if (priv->enable_tcp_ack_enh == MTRUE) { + ret = woal_process_tcp_ack(priv, pmbuf); + if (ret) + goto done; + } +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + if (priv->enable_auto_tdls && priv->tdls_check_tx) + woal_tdls_check_tx(priv, skb); +#endif + + status = mlan_send_packet(priv->phandle->pmlan_adapter, pmbuf); + switch (status) { + case MLAN_STATUS_PENDING: + atomic_inc(&priv->phandle->tx_pending); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) + index = skb_get_queue_mapping(skb); + atomic_inc(&priv->wmm_tx_pending[index]); + if (atomic_read(&priv->wmm_tx_pending[index]) >= MAX_TX_PENDING) { + struct netdev_queue *txq = + netdev_get_tx_queue(priv->netdev, index); + netif_tx_stop_queue(txq); + PRINTM(MINFO, "Stop Kernel Queue : %d\n", index); + } +#else + if (atomic_read(&priv->phandle->tx_pending) >= MAX_TX_PENDING) + woal_stop_queue(priv->netdev); +#endif /*#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,29) */ + if (!mlan_is_main_process_running(priv->phandle->pmlan_adapter)) + queue_work(priv->phandle->workqueue, + &priv->phandle->main_work); + break; + case MLAN_STATUS_SUCCESS: + priv->stats.tx_packets++; + priv->stats.tx_bytes += skb->len; + dev_kfree_skb_any(skb); + break; + case MLAN_STATUS_FAILURE: + default: + priv->stats.tx_dropped++; + dev_kfree_skb_any(skb); + break; + } +done: + LEAVE(); + return 0; +} + +/** + * @brief Convert ascii string to Hex integer + * + * @param d A pointer to integer buf + * @param s A pointer to ascii string + * @param dlen The byte number of ascii string in hex + * + * @return Number of integer + */ +int +woal_ascii2hex(t_u8 *d, char *s, t_u32 dlen) +{ + unsigned int i; + t_u8 n; + + ENTER(); + + memset(d, 0x00, dlen); + + for (i = 0; i < dlen * 2; i++) { + if ((s[i] >= 48) && (s[i] <= 57)) + n = s[i] - 48; + else if ((s[i] >= 65) && (s[i] <= 70)) + n = s[i] - 55; + else if ((s[i] >= 97) && (s[i] <= 102)) + n = s[i] - 87; + else + break; + if (!(i % 2)) + n = n * 16; + d[i / 2] += n; + } + + LEAVE(); + return i; +} + +/** + * @brief Return integer value of a given ascii string + * + * @param data Converted data to be returned + * @param a String to be converted + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +woal_atoi(int *data, char *a) +{ + int i, val = 0, len; + int mul = 1; + + ENTER(); + + len = strlen(a); + if (len > 2) { + if (!strncmp(a, "0x", 2)) { + a = a + 2; + len -= 2; + *data = woal_atox(a); + LEAVE(); + return MLAN_STATUS_SUCCESS; + } + } + for (i = 0; i < len; i++) { + if (isdigit(a[i])) { + val = val * 10 + (a[i] - '0'); + } else { + if ((i == 0) && (a[i] == '-')) { + mul = -1; + } else { + PRINTM(MERROR, "Invalid char %c in string %s\n", + a[i], a); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + } + } + *data = (mul * val); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Return hex value of a given ascii string + * + * @param a String to be converted to ascii + * + * @return The converted character if a is a valid hex, else 0 + */ +int +woal_atox(char *a) +{ + int i = 0; + + ENTER(); + + while (isxdigit(*a)) + i = i * 16 + woal_hexval(*a++); + + LEAVE(); + return i; +} + +/** + * @brief Extension of strsep lib command. This function will also take care + * escape character + * + * @param s A pointer to array of chars to process + * @param delim The delimiter character to end the string + * @param esc The escape character to ignore for delimiter + * + * @return Pointer to the separated string if delim found, else NULL + */ +char * +woal_strsep(char **s, char delim, char esc) +{ + char *se = *s, *sb; + + ENTER(); + + if (!(*s) || (*se == '\0')) { + LEAVE(); + return NULL; + } + + for (sb = *s; *sb != '\0'; ++sb) { + if (*sb == esc && *(sb + 1) == esc) { + /* + * We get a esc + esc seq then keep the one esc + * and chop off the other esc character + */ + memmove(sb, sb + 1, strlen(sb)); + continue; + } + if (*sb == esc && *(sb + 1) == delim) { + /* + * We get a delim + esc seq then keep the delim + * and chop off the esc character + */ + memmove(sb, sb + 1, strlen(sb)); + continue; + } + if (*sb == delim) + break; + } + + if (*sb == '\0') + sb = NULL; + else + *sb++ = '\0'; + + *s = sb; + + LEAVE(); + return se; +} + +/** + * @brief Convert mac address from string to t_u8 buffer. + * + * @param mac_addr The buffer to store the mac address in. + * @param buf The source of mac address which is a string. + * + * @return N/A + */ +void +woal_mac2u8(t_u8 *mac_addr, char *buf) +{ + char *begin, *end, *mac_buff; + int i; + + ENTER(); + + if (!buf) { + LEAVE(); + return; + } + + mac_buff = kzalloc(strlen(buf) + 1, GFP_KERNEL); + if (!mac_buff) { + LEAVE(); + return; + } + memcpy(mac_buff, buf, strlen(buf)); + + begin = mac_buff; + for (i = 0; i < ETH_ALEN; ++i) { + end = woal_strsep(&begin, ':', '/'); + if (end) + mac_addr[i] = woal_atox(end); + } + + kfree(mac_buff); + LEAVE(); +} + +#ifdef STA_SUPPORT +/** + * @brief This function sets multicast addresses to firmware + * + * @param dev A pointer to net_device structure + * + * @return N/A + */ +void +woal_set_multicast_list(struct net_device *dev) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + ENTER(); + woal_request_set_multicast_list(priv, dev); + LEAVE(); +} +#endif + +/** + * @brief This function initializes the private structure + * and set default value to the member of moal_private. + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * + * @return N/A + */ +void +woal_init_priv(moal_private *priv, t_u8 wait_option) +{ + ENTER(); +#ifdef STA_SUPPORT + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) { + priv->current_key_index = 0; + priv->rate_index = AUTO_RATE; + priv->is_adhoc_link_sensed = MFALSE; + priv->scan_type = MLAN_SCAN_TYPE_ACTIVE; + priv->bg_scan_start = MFALSE; + priv->bg_scan_reported = MFALSE; + memset(&priv->nick_name, 0, sizeof(priv->nick_name)); + priv->num_tx_timeout = 0; + priv->rx_filter = 0; + +#ifdef REASSOCIATION + priv->reassoc_on = MFALSE; + priv->set_asynced_essid_flag = MFALSE; +#endif +#ifdef STA_CFG80211 + memset(&priv->sme_current, 0, + sizeof(struct cfg80211_connect_params)); +#endif + } +#endif /* STA_SUPPORT */ +#ifdef UAP_SUPPORT + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) { + priv->bss_started = MFALSE; +#ifdef UAP_CFG80211 +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) + memset(&priv->chan, 0, sizeof(struct cfg80211_chan_def)); + memset(&priv->csa_chan, 0, sizeof(struct cfg80211_chan_def)); + priv->uap_tx_blocked = MFALSE; + memset(&priv->beacon_after, 0, + sizeof(struct cfg80211_beacon_data)); +#endif +#endif + } +#endif + + memset(&priv->tx_protocols, 0, sizeof(dot11_protocol)); + memset(&priv->rx_protocols, 0, sizeof(dot11_protocol)); + priv->media_connected = MFALSE; + + memset(priv->dscp_map, 0xFF, sizeof(priv->dscp_map)); + +#if defined(STA_CFG80211) || defined(UAP_CFG80211) + priv->probereq_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + priv->beacon_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + priv->proberesp_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + priv->assocresp_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + priv->beacon_wps_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + priv->proberesp_p2p_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + priv->assocresp_qos_map_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + priv->proberesp_vendor_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; +#endif + +#ifdef STA_SUPPORT + priv->pmk_saved = MFALSE; + memset(&priv->pmk, 0, sizeof(mlan_pmk_t)); +#endif + + priv->enable_tcp_ack_enh = MTRUE; + + priv->enable_auto_tdls = MFALSE; + priv->tdls_check_tx = MFALSE; + + priv->gtk_data_ready = MFALSE; + memset(&priv->gtk_rekey_data, 0, sizeof(mlan_ds_misc_gtk_rekey_data)); + + woal_request_get_fw_info(priv, wait_option, NULL); + +#if defined(WIFI_DIRECT_SUPPORT) +#if defined(STA_CFG80211) && defined(UAP_CFG80211) +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION +#ifdef MFG_CMD_SUPPORT + if (mfg_mode != MLAN_INIT_PARA_ENABLED) +#endif + if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT) { + if (priv->bss_virtual) { + if (priv->pa_netdev) { + memcpy(priv->current_addr, + priv->pa_netdev->dev_addr, + ETH_ALEN); + priv->current_addr[4] ^= 0x80; + woal_request_set_mac_address(priv); + memcpy(priv->netdev->dev_addr, + priv->current_addr, ETH_ALEN); + PRINTM(MCMND, + "Set WFD interface addr: " MACSTR + "\n", + MAC2STR(priv->current_addr)); + } + } else { + priv->current_addr[0] |= 0x02; + woal_request_set_mac_address(priv); + memcpy(priv->netdev->dev_addr, + priv->current_addr, ETH_ALEN); + PRINTM(MCMND, + "Set WFD device addr: " MACSTR "\n", + MAC2STR(priv->current_addr)); + } + } +#endif +#endif +#endif +#ifdef UAP_SUPPORT +#if defined(DFS_TESTING_SUPPORT) + priv->user_cac_period_msec = 0; +#endif +#endif + LEAVE(); +} + +/** + * @brief Reset all interfaces if all_intf flag is TRUE, + * otherwise specified interface only + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * @param all_intf TRUE : all interfaces + * FALSE : current interface only + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +int +woal_reset_intf(moal_private *priv, t_u8 wait_option, int all_intf) +{ + int ret = MLAN_STATUS_SUCCESS; + int intf_num; + moal_handle *handle = NULL; + mlan_bss_info bss_info; + + ENTER(); + + if (!priv) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + handle = priv->phandle; + + /* Stop queue and detach device */ + if (!all_intf) { + woal_stop_queue(priv->netdev); + netif_device_detach(priv->netdev); + } else { + for (intf_num = 0; intf_num < handle->priv_num; intf_num++) { + woal_stop_queue(handle->priv[intf_num]->netdev); + netif_device_detach(handle->priv[intf_num]->netdev); + } + } + + /* Get BSS info */ + memset(&bss_info, 0, sizeof(bss_info)); + woal_get_bss_info(priv, wait_option, &bss_info); + +#ifdef STA_SUPPORT + woal_cancel_scan(priv, wait_option); +#endif + + /* Cancel host sleep */ + if (bss_info.is_hs_configured) { + if (MLAN_STATUS_SUCCESS != woal_cancel_hs(priv, wait_option)) { + ret = -EFAULT; + goto done; + } + } + + /* Disconnect from network */ + if (!all_intf) { + /* Disconnect specified interface only */ + if ((priv->media_connected == MTRUE) +#ifdef UAP_SUPPORT + || (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) +#endif + ) { + woal_disconnect(priv, wait_option, NULL, + DEF_DEAUTH_REASON_CODE); + priv->media_connected = MFALSE; + } + } else { + /* Disconnect all interfaces */ + for (intf_num = 0; intf_num < handle->priv_num; intf_num++) { + if (handle->priv[intf_num]->media_connected == MTRUE +#ifdef UAP_SUPPORT + || (GET_BSS_ROLE(handle->priv[intf_num]) == + MLAN_BSS_ROLE_UAP) +#endif + ) { + woal_disconnect(handle->priv[intf_num], + wait_option, NULL, + DEF_DEAUTH_REASON_CODE); + handle->priv[intf_num]->media_connected = + MFALSE; + } + } + } + +#ifdef REASSOCIATION + /* Reset the reassoc timer and status */ + if (!all_intf) { + handle->reassoc_on &= ~MBIT(priv->bss_index); + priv->reassoc_on = MFALSE; + priv->set_asynced_essid_flag = MFALSE; + } else { + handle->reassoc_on = 0; + for (intf_num = 0; intf_num < handle->priv_num; intf_num++) { + handle->priv[intf_num]->reassoc_on = MFALSE; + handle->priv[intf_num]->set_asynced_essid_flag = MFALSE; + } + } + if (!handle->reassoc_on && handle->is_reassoc_timer_set) { + woal_cancel_timer(&handle->reassoc_timer); + handle->is_reassoc_timer_set = MFALSE; + } +#endif /* REASSOCIATION */ + +#if defined(WIFI_DIRECT_SUPPORT) +#if defined(STA_CFG80211) && defined(UAP_CFG80211) + if (handle->is_go_timer_set) { + woal_cancel_timer(&handle->go_timer); + handle->is_go_timer_set = MFALSE; + } +#endif +#endif + +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + if (handle->is_remain_timer_set) { + woal_cancel_timer(&handle->remain_timer); + woal_remain_timer_func(handle); + } +#endif +#endif + +done: + LEAVE(); + return ret; +} + +/** + * @brief This function return the point to structure moal_private + * + * @param handle Pointer to structure moal_handle + * @param bss_index BSS index number + * + * @return moal_private pointer or NULL + */ +moal_private * +woal_bss_index_to_priv(moal_handle *handle, t_u8 bss_index) +{ + int i; + + ENTER(); + if (!handle) { + LEAVE(); + return NULL; + } + for (i = 0; i < MLAN_MAX_BSS_NUM; i++) { + if (handle->priv[i] && + (handle->priv[i]->bss_index == bss_index)) { + LEAVE(); + return handle->priv[i]; + } + } + + LEAVE(); + return NULL; +} + +/** + * @brief This function alloc mlan_buffer. + * @param handle A pointer to moal_handle structure + * @param size buffer size to allocate + * + * @return mlan_buffer pointer or NULL + */ +pmlan_buffer +woal_alloc_mlan_buffer(moal_handle *handle, int size) +{ + mlan_buffer *pmbuf = NULL; + struct sk_buff *skb; + gfp_t flag; + + ENTER(); + + flag = (in_atomic() || irqs_disabled())? GFP_ATOMIC : GFP_KERNEL; + if (size <= 0) { + PRINTM(MERROR, "Buffer size must be positive\n"); + LEAVE(); + return NULL; + } + + skb = __dev_alloc_skb(size + sizeof(mlan_buffer), flag); + if (!skb) { + PRINTM(MERROR, "%s: No free skb\n", __func__); + LEAVE(); + return NULL; + } + skb_reserve(skb, sizeof(mlan_buffer)); + pmbuf = (mlan_buffer *)skb->head; + memset((u8 *)pmbuf, 0, sizeof(mlan_buffer)); + pmbuf->pdesc = (t_void *)skb; + pmbuf->pbuf = (t_u8 *)skb->data; + atomic_inc(&handle->mbufalloc_count); + LEAVE(); + return pmbuf; +} + +/** + * @brief This function alloc mlan_ioctl_req. + * + * @param size buffer size to allocate + * + * @return mlan_ioctl_req pointer or NULL + */ +pmlan_ioctl_req +woal_alloc_mlan_ioctl_req(int size) +{ + mlan_ioctl_req *req = NULL; + gfp_t flag; + + ENTER(); + + flag = (in_atomic() || irqs_disabled())? GFP_ATOMIC : GFP_KERNEL; + req = kzalloc((sizeof(mlan_ioctl_req) + size + sizeof(int) + + sizeof(wait_queue)), flag); + if (!req) { + PRINTM(MERROR, "%s: Fail to alloc ioctl buffer\n", __func__); + LEAVE(); + return NULL; + } + req->pbuf = (t_u8 *)req + sizeof(mlan_ioctl_req) + sizeof(wait_queue); + req->buf_len = (t_u32)size; + req->reserved_1 = (t_ptr)((t_u8 *)req + sizeof(mlan_ioctl_req)); + + LEAVE(); + return req; +} + +/** + * @brief This function frees mlan_buffer. + * @param handle A pointer to moal_handle structure + * @param pmbuf Pointer to mlan_buffer + * + * @return N/A + */ +void +woal_free_mlan_buffer(moal_handle *handle, pmlan_buffer pmbuf) +{ + ENTER(); + if (!pmbuf) { + LEAVE(); + return; + } + if (pmbuf->pdesc) + dev_kfree_skb_any((struct sk_buff *)pmbuf->pdesc); + else + PRINTM(MERROR, "free mlan buffer without pdesc\n"); + atomic_dec(&handle->mbufalloc_count); + LEAVE(); + return; +} + +#ifdef STA_SUPPORT +#endif /* STA_SUPPORT */ + +/** + * @brief This function handles events generated by firmware + * + * @param priv A pointer to moal_private structure + * @param payload A pointer to payload buffer + * @param len Length of the payload + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +woal_broadcast_event(moal_private *priv, t_u8 *payload, t_u32 len) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + struct sk_buff *skb = NULL; + struct nlmsghdr *nlh = NULL; + moal_handle *handle = priv->phandle; + struct net_device *netdev = priv->netdev; + struct sock *sk = handle->nl_sk; + + ENTER(); + + /* interface name to be prepended to event */ + if ((len + IFNAMSIZ) > NL_MAX_PAYLOAD +#ifdef WIFI_DIRECT_SUPPORT + * 2 +#endif + ) { + PRINTM(MERROR, "event size is too big, len=%d\n", (int)len); + ret = MLAN_STATUS_FAILURE; + goto done; + } + if (sk) { + /* Allocate skb */ +#ifdef WIFI_DIRECT_SUPPORT + if ((len + IFNAMSIZ) > NL_MAX_PAYLOAD) { + skb = alloc_skb(NLMSG_SPACE(NL_MAX_PAYLOAD * 2), + GFP_ATOMIC); + if (!skb) { + PRINTM(MERROR, + "Could not allocate skb for netlink\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + } else { +#endif + skb = alloc_skb(NLMSG_SPACE(NL_MAX_PAYLOAD), + GFP_ATOMIC); + if (!skb) { + PRINTM(MERROR, + "Could not allocate skb for netlink\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } +#ifdef WIFI_DIRECT_SUPPORT + } +#endif + nlh = (struct nlmsghdr *)skb->data; + nlh->nlmsg_len = NLMSG_SPACE(len + IFNAMSIZ); + + /* From kernel */ + nlh->nlmsg_pid = 0; + nlh->nlmsg_flags = 0; + + /* Data */ + skb_put(skb, nlh->nlmsg_len); + memcpy(NLMSG_DATA(nlh), netdev->name, IFNAMSIZ); + memcpy(((t_u8 *)(NLMSG_DATA(nlh))) + IFNAMSIZ, payload, len); + + /* From Kernel */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 7, 0) + NETLINK_CB(skb).pid = 0; +#else + NETLINK_CB(skb).portid = 0; +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) + /* Multicast message */ + NETLINK_CB(skb).dst_pid = 0; +#endif + + /* Multicast group number */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 14) + NETLINK_CB(skb).dst_groups = NL_MULTICAST_GROUP; +#else + NETLINK_CB(skb).dst_group = NL_MULTICAST_GROUP; +#endif + + /* Send message */ + ret = netlink_broadcast(sk, skb, 0, NL_MULTICAST_GROUP, + GFP_ATOMIC); + if (ret) { + PRINTM(MWARN, "netlink_broadcast failed: ret=%d\n", + ret); + goto done; + } + + ret = MLAN_STATUS_SUCCESS; + } else { + PRINTM(MERROR, + "Could not send event through NETLINK. Link down.\n"); + ret = MLAN_STATUS_FAILURE; + } +done: + LEAVE(); + return ret; +} + +#ifdef REASSOCIATION +/** + * @brief This function handles re-association. it is triggered + * by re-assoc timer. + * + * @param data A pointer to wlan_thread structure + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +int +woal_reassociation_thread(void *data) +{ + moal_thread *pmoal_thread = data; + moal_private *priv = NULL; + moal_handle *handle = (moal_handle *)pmoal_thread->handle; +#if CFG80211_VERSION_CODE < KERNEL_VERSION(4, 13, 0) + wait_queue_t wait; +#else + wait_queue_entry_t wait; +#endif + int i; + BOOLEAN reassoc_timer_req; + mlan_802_11_ssid req_ssid; + mlan_ssid_bssid ssid_bssid; + mlan_status status; + mlan_bss_info bss_info; + t_u32 timer_val = MOAL_TIMER_10S; + t_u8 zero_mac[] = { 0, 0, 0, 0, 0, 0 }; + ENTER(); + + woal_activate_thread(pmoal_thread); + init_waitqueue_entry(&wait, current); + + current->flags |= PF_NOFREEZE; + + for (;;) { + add_wait_queue(&pmoal_thread->wait_q, &wait); + set_current_state(TASK_INTERRUPTIBLE); + + schedule(); + + set_current_state(TASK_RUNNING); + remove_wait_queue(&pmoal_thread->wait_q, &wait); + + /* Cancel re-association timer */ + if (handle->is_reassoc_timer_set == MTRUE) { + woal_cancel_timer(&handle->reassoc_timer); + handle->is_reassoc_timer_set = MFALSE; + } + + if (handle->surprise_removed) + break; + if (kthread_should_stop()) + break; + + if (handle->hardware_status != HardwareStatusReady) { + PRINTM(MINFO, + "Reassoc: Hardware status is not correct\n"); + continue; + } + + PRINTM(MEVENT, "Reassoc: Thread waking up...\n"); + reassoc_timer_req = MFALSE; +#ifdef STA_CFG80211 + for (i = 0; + i < MIN(handle->priv_num, MLAN_MAX_BSS_NUM) && + (priv = handle->priv[i]); i++) { + if (priv->roaming_required) { + priv->roaming_required = MFALSE; + PRINTM(MEVENT, "Try to roaming......\n"); + woal_start_roaming(priv); + break; + } + } +#endif + + for (i = 0; + i < MIN(handle->priv_num, MLAN_MAX_BSS_NUM) && + (priv = handle->priv[i]); i++) { + + if (priv->reassoc_required == MFALSE) { + priv->set_asynced_essid_flag = MFALSE; + continue; + } + + memset(&bss_info, 0x00, sizeof(bss_info)); + + if (MLAN_STATUS_SUCCESS != woal_get_bss_info(priv, + MOAL_IOCTL_WAIT, + &bss_info)) + { + PRINTM(MINFO, "Ressoc: Fail to get bss info\n"); + priv->reassoc_required = MFALSE; + priv->set_asynced_essid_flag = MFALSE; + continue; + } + + if (bss_info.bss_mode != MLAN_BSS_MODE_INFRA || + priv->media_connected != MFALSE) { + PRINTM(MINFO, + "Reassoc: ad-hoc mode or media connected\n"); + priv->reassoc_required = MFALSE; + priv->set_asynced_essid_flag = MFALSE; + continue; + } + /** avoid on going scan from other thread */ + if (handle->scan_pending_on_block) { + reassoc_timer_req = MTRUE; + break; + } + + /* The semaphore is used to avoid reassociation thread and + wlan_set_scan/wlan_set_essid interrupting each other. + Reassociation should be disabled completely by application if + wlan_set_user_scan_ioctl/wlan_set_wap is used. + */ + if (MOAL_ACQ_SEMAPHORE_BLOCK(&handle->reassoc_sem)) { + PRINTM(MERROR, + "Acquire semaphore error, reassociation thread\n"); + reassoc_timer_req = MTRUE; + break; + } + + PRINTM(MINFO, "Reassoc: Required ESSID: %s\n", + priv->prev_ssid_bssid.ssid.ssid); + PRINTM(MINFO, "Reassoc: Performing Active Scan\n"); + + memset(&req_ssid, 0x00, sizeof(mlan_802_11_ssid)); + memcpy(&req_ssid, + &priv->prev_ssid_bssid.ssid, + sizeof(mlan_802_11_ssid)); + + /* Do specific SSID scanning */ + if (MLAN_STATUS_SUCCESS != + woal_request_scan(priv, MOAL_IOCTL_WAIT, + &req_ssid)) { + PRINTM(MERROR, + "Reassoc: Fail to do specific scan\n"); + reassoc_timer_req = MTRUE; + MOAL_REL_SEMAPHORE(&handle->reassoc_sem); + break; + } + + if (handle->surprise_removed) { + MOAL_REL_SEMAPHORE(&handle->reassoc_sem); + break; + } + + memset(&ssid_bssid, 0, sizeof(mlan_ssid_bssid)); + + if (priv->set_asynced_essid_flag == MTRUE) { + if (priv->assoc_with_mac && + memcmp(priv->prev_ssid_bssid.bssid, + zero_mac, MLAN_MAC_ADDR_LENGTH)) { + /* Search AP by BSSID & SSID */ + PRINTM(MINFO, + "Reassoc: Search AP by BSSID & SSID\n"); + memcpy(&ssid_bssid.bssid, + &priv->prev_ssid_bssid.bssid, + MLAN_MAC_ADDR_LENGTH); + } else { + /* Search AP by ESSID for asynced essid setting */ + PRINTM(MINFO, + "Set asynced essid: Search AP by ESSID\n"); + } + + memcpy(&ssid_bssid.ssid, + &priv->prev_ssid_bssid.ssid, + sizeof(mlan_802_11_ssid)); + } else { + /* Search AP by BSSID first */ + PRINTM(MINFO, + "Reassoc: Search AP by BSSID first\n"); + memcpy(&ssid_bssid.bssid, + &priv->prev_ssid_bssid.bssid, + MLAN_MAC_ADDR_LENGTH); + } + + status = woal_find_best_network(priv, MOAL_IOCTL_WAIT, + &ssid_bssid); +#ifdef STA_WEXT + if (status == MLAN_STATUS_SUCCESS) { + if (MLAN_STATUS_SUCCESS != + woal_11d_check_ap_channel(priv, + MOAL_IOCTL_WAIT, + &ssid_bssid)) { + PRINTM(MERROR, + "Reassoc: The AP's channel is invalid for current region\n"); + status = MLAN_STATUS_FAILURE; + } + } +#endif + /** The find AP without ssid, we need re-search */ + if (status == MLAN_STATUS_SUCCESS && + !ssid_bssid.ssid.ssid_len) { + PRINTM(MINFO, + "Reassoc: Skip AP without ssid\n"); + status = MLAN_STATUS_FAILURE; + } + + if (priv->set_asynced_essid_flag != MTRUE && + MLAN_STATUS_SUCCESS != status) { + PRINTM(MINFO, + "Reassoc: AP not found in scan list\n"); + PRINTM(MINFO, "Reassoc: Search AP by SSID\n"); + /* Search AP by SSID */ + memset(&ssid_bssid, 0, sizeof(mlan_ssid_bssid)); + memcpy(&ssid_bssid.ssid, + &priv->prev_ssid_bssid.ssid, + sizeof(mlan_802_11_ssid)); + status = woal_find_best_network(priv, + MOAL_IOCTL_WAIT, + &ssid_bssid); +#ifdef STA_WEXT + if (status == MLAN_STATUS_SUCCESS) { + if (MLAN_STATUS_SUCCESS != + woal_11d_check_ap_channel(priv, + MOAL_IOCTL_WAIT, + &ssid_bssid)) + { + PRINTM(MERROR, + "Reassoc: The AP's channel is invalid for current region\n"); + status = MLAN_STATUS_FAILURE; + } + } +#endif + } + + if (status == MLAN_STATUS_SUCCESS) { + /* set the wep key */ + if (bss_info.wep_status) + woal_enable_wep_key(priv, + MOAL_IOCTL_WAIT); + /* Zero SSID implies use BSSID to connect */ + memset(&ssid_bssid.ssid, 0, + sizeof(mlan_802_11_ssid)); + status = woal_bss_start(priv, MOAL_IOCTL_WAIT, + &ssid_bssid); + } + + if (priv->media_connected == MFALSE) + reassoc_timer_req = MTRUE; + else { + mlan_ds_rate *rate = NULL; + mlan_ioctl_req *req = NULL; + + reassoc_timer_req = MFALSE; + if (priv->set_asynced_essid_flag == MTRUE) { + memset(&bss_info, 0, sizeof(bss_info)); + if (MLAN_STATUS_SUCCESS != + woal_get_bss_info(priv, + MOAL_IOCTL_WAIT, + &bss_info)) { + PRINTM(MINFO, + "Set asynced essid: Fail to get bss info after assoc\n"); + } else { + memcpy(&priv->prev_ssid_bssid. + ssid, &bss_info.ssid, + sizeof + (mlan_802_11_ssid)); + memcpy(&priv->prev_ssid_bssid. + bssid, &bss_info.bssid, + MLAN_MAC_ADDR_LENGTH); + } + priv->set_asynced_essid_flag = MFALSE; + } + if (priv->rate_index != AUTO_RATE) { + req = woal_alloc_mlan_ioctl_req(sizeof + (mlan_ds_rate)); + + if (req == NULL) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + rate = (mlan_ds_rate *)req->pbuf; + rate->param.rate_cfg.rate_type = + MLAN_RATE_INDEX; + rate->sub_command = MLAN_OID_RATE_CFG; + req->req_id = MLAN_IOCTL_RATE; + + req->action = MLAN_ACT_SET; + + rate->param.rate_cfg.rate = + priv->rate_index; + + status = woal_request_ioctl(priv, req, + MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + if (status != + MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + kfree(req); + } + } + + MOAL_REL_SEMAPHORE(&handle->reassoc_sem); + } + + if (handle->surprise_removed) + break; + + if (reassoc_timer_req == MTRUE) { + handle->is_reassoc_timer_set = MTRUE; + if (priv && (priv->set_asynced_essid_flag == MTRUE)) { + PRINTM(MERROR, + "Set Async ESSID: No AP found or assoc failed.\n"); + priv->set_asynced_essid_flag = MFALSE; + } else { + PRINTM(MEVENT, + "Reassoc: No AP found or assoc failed. " + "Restarting re-assoc Timer: %d\n", + (int)timer_val); + woal_mod_timer(&handle->reassoc_timer, + timer_val); + } + } else { + if (priv) { + priv->set_asynced_essid_flag = MFALSE; + } + } + } + woal_deactivate_thread(pmoal_thread); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function triggers re-association by waking up + * re-assoc thread. + * + * @param context A pointer to context + * @return N/A + */ +void +woal_reassoc_timer_func(void *context) +{ + moal_handle *handle = (moal_handle *)context; + + ENTER(); + + PRINTM(MINFO, "reassoc_timer fired.\n"); + handle->is_reassoc_timer_set = MFALSE; + + PRINTM(MINFO, "Waking Up the Reassoc Thread\n"); + wake_up_interruptible(&handle->reassoc_thread.wait_q); + + LEAVE(); + return; +} +#endif /* REASSOCIATION */ + +#ifdef STA_SUPPORT +/** + * @brief update dscp mapping from assoc_resp/reassoc_resp + * + * @param priv Pointer to the moal_private driver data struct + * + * @return N/A + */ +void +woal_update_dscp_mapping(moal_private *priv) +{ + mlan_ds_misc_assoc_rsp assoc_rsp; + IEEEtypes_AssocRsp_t *passoc_rsp = NULL; + IEEEtypes_Header_t *qos_mapping_ie = NULL; + DSCP_Range_t *pdscp_range = NULL; + t_u8 dscp_except_num = 0; + DSCP_Exception_t dscp_except[MAX_DSCP_EXCEPTION_NUM]; + int i, j; + ENTER(); + + memset(&assoc_rsp, 0, sizeof(mlan_ds_misc_assoc_rsp)); + woal_get_assoc_rsp(priv, &assoc_rsp, MOAL_NO_WAIT); + passoc_rsp = (IEEEtypes_AssocRsp_t *)assoc_rsp.assoc_resp_buf; + memset(priv->dscp_map, 0xFF, sizeof(priv->dscp_map)); + qos_mapping_ie = + (IEEEtypes_Header_t *)woal_parse_ie_tlv(passoc_rsp->ie_buffer, + assoc_rsp. + assoc_resp_len - + ASSOC_RESP_FIXED_SIZE, + QOS_MAPPING); + if (qos_mapping_ie && + (qos_mapping_ie->len >= (sizeof(DSCP_Range_t) * MAX_NUM_TID))) { + dscp_except_num = + (qos_mapping_ie->len - + sizeof(DSCP_Range_t) * MAX_NUM_TID) / + sizeof(DSCP_Exception_t); + if (dscp_except_num > MAX_DSCP_EXCEPTION_NUM) { + PRINTM(MERROR, "dscp_except_num exceeds MAX limit\n"); + LEAVE(); + return; + } + memcpy(dscp_except, + (t_u8 *)qos_mapping_ie + sizeof(IEEEtypes_Header_t), + dscp_except_num * sizeof(DSCP_Exception_t)); + pdscp_range = + (DSCP_Range_t *)((t_u8 *)qos_mapping_ie + + sizeof(IEEEtypes_Header_t) + + dscp_except_num * + sizeof(DSCP_Exception_t)); + for (i = 0; i < MAX_NUM_TID; i++) { + PRINTM(MEVENT, "TID %d: dscp_low=%d, dscp_high=%d\n", i, + pdscp_range->dscp_low_value, + pdscp_range->dscp_high_value); + if (pdscp_range->dscp_low_value != 0xff && + pdscp_range->dscp_high_value != 0xff && + pdscp_range->dscp_high_value <= 63) { + for (j = pdscp_range->dscp_low_value; + j <= pdscp_range->dscp_high_value; j++) + priv->dscp_map[j] = i; + } + pdscp_range++; + } + for (i = 0; i < dscp_except_num; i++) { + if ((dscp_except[i].dscp_value <= 63) && + (dscp_except[i].user_priority <= 7)) { + PRINTM(MEVENT, + "dscp excpt: value=%d priority=%d\n", + dscp_except[i].dscp_value, + dscp_except[i].user_priority); + priv->dscp_map[dscp_except[i].dscp_value] = + dscp_except[i].user_priority; + } + } + } + LEAVE(); +} + +/** + * @brief Sends disconnect event + * + * @param priv A pointer to moal_private struct + * @return N/A + */ +t_void +woal_send_disconnect_to_system(moal_private *priv) +{ + int custom_len = 0; + t_u8 event_buf[32]; +#ifdef STA_WEXT + union iwreq_data wrqu; +#endif +#ifdef STA_CFG80211 + unsigned long flags; +#endif + mlan_ds_misc_gtk_rekey_data zero_gtk; + + ENTER(); + priv->media_connected = MFALSE; + woal_stop_queue(priv->netdev); + if (netif_carrier_ok(priv->netdev)) + netif_carrier_off(priv->netdev); + woal_flush_tcp_sess_queue(priv); + + priv->gtk_data_ready = MFALSE; + memset(&zero_gtk, 0x00, sizeof(zero_gtk)); + if (gtk_rekey_offload == GTK_REKEY_OFFLOAD_ENABLE && + memcmp(&priv->gtk_rekey_data, &zero_gtk, + sizeof(priv->gtk_rekey_data)) != 0) { + PRINTM(MCMND, "clear GTK in woal_send_disconnect_to_system\n"); + woal_set_rekey_data(priv, NULL, MLAN_ACT_CLEAR); + } + memset(&priv->gtk_rekey_data, 0, sizeof(mlan_ds_misc_gtk_rekey_data)); + + if (priv->bss_type == MLAN_BSS_TYPE_STA) + woal_flush_tdls_list(priv); +#ifdef STA_CFG80211 + if (priv->bss_type == MLAN_BSS_TYPE_STA && + IS_STA_CFG80211(cfg80211_wext)) { + woal_flush_pmksa_list(priv); + if (priv->okc_roaming_ie) { + kfree(priv->okc_roaming_ie); + priv->okc_roaming_ie = NULL; + priv->okc_ie_len = 0; + } + } +#endif + if (priv->bss_type == MLAN_BSS_TYPE_STA && priv->hist_data) + woal_hist_data_reset(priv); + +#ifdef STA_WEXT + if (IS_STA_WEXT(cfg80211_wext)) { + memset(wrqu.ap_addr.sa_data, 0x00, ETH_ALEN); + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + wireless_send_event(priv->netdev, SIOCGIWAP, &wrqu, NULL); + } +#endif +#ifdef STA_CFG80211 + if (IS_STA_CFG80211(cfg80211_wext)) { + spin_lock_irqsave(&priv->connect_lock, flags); + if (!priv->cfg_disconnect && !priv->cfg_connect && + priv->wdev && + priv->wdev->iftype != NL80211_IFTYPE_ADHOC + && ((priv->bss_type != MLAN_BSS_TYPE_STA) || + (priv->bss_type == MLAN_BSS_TYPE_STA && + priv->sme_current.ssid)) + ) { + PRINTM(MMSG, + "wlan: Disconnected from " MACSTR + ": Reason code %d\n", MAC2STR(priv->cfg_bssid), + WLAN_REASON_DEAUTH_LEAVING); + spin_unlock_irqrestore(&priv->connect_lock, flags); + /* This function must be called only when disconnect issued by + the FW, i.e. disconnected by AP. For IBSS mode this call is + not valid */ + cfg80211_disconnected(priv->netdev, + WLAN_REASON_DEAUTH_LEAVING, NULL, + 0, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 2, 0) + false, +#endif + GFP_KERNEL); + } else { + spin_unlock_irqrestore(&priv->connect_lock, flags); + } + if (!woal_is_any_interface_active(priv->phandle)) + woal_set_scan_time(priv, ACTIVE_SCAN_CHAN_TIME, + PASSIVE_SCAN_CHAN_TIME, + SPECIFIC_SCAN_CHAN_TIME); + priv->ft_ie_len = 0; + priv->ft_pre_connect = MFALSE; + memset(priv->dscp_map, 0xFF, sizeof(priv->dscp_map)); + } +#endif /* STA_CFG80211 */ + + memset(event_buf, 0, sizeof(event_buf)); + custom_len = strlen(CUS_EVT_AP_CONNECTED); + strncpy(event_buf, CUS_EVT_AP_CONNECTED, + MIN((sizeof(event_buf) - 1), custom_len)); + woal_broadcast_event(priv, event_buf, custom_len + ETH_ALEN); + LEAVE(); +} +#endif /* STA_SUPPORT */ + +#define OFFSET_SEQNUM 4 +/** + * @brief This function stores the FW dumps received from events in a file + * + * @param phandle A pointer to moal_handle + * @param pmevent A pointer to mlan_event structure + * + * @return N/A + */ + +t_void +woal_store_firmware_dump(moal_handle *phandle, mlan_event *pmevent) +{ + struct file *pfile_fwdump = NULL; + loff_t pos = 0; + t_u16 seqnum; + t_u8 path_name[64]; + + ENTER(); + if (phandle->fwdump_fname) + pfile_fwdump = + filp_open(phandle->fwdump_fname, + O_CREAT | O_WRONLY | O_APPEND, 0644); + else { + seqnum = woal_le16_to_cpu(*(t_u16 *) + (pmevent->event_buf + OFFSET_SEQNUM)); + if (seqnum == 1) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 1, 0) + /** Create dump directort*/ + woal_create_dump_dir(phandle, path_name, + sizeof(path_name)); +#else + memset(path_name, 0, sizeof(path_name)); + strcpy(path_name, "/data"); +#endif + PRINTM(MMSG, "Firmware Dump directory name is %s\n", + path_name); + woal_dump_drv_info(phandle, path_name); + if (fwdump_fname) { + memset(fwdump_fname, 0, 64); + } else { + gfp_t flag; + flag = (in_atomic() || + irqs_disabled())? GFP_ATOMIC : + GFP_KERNEL; + fwdump_fname = kzalloc(64, flag); + } + sprintf(fwdump_fname, "%s/file_fwdump", path_name); + pfile_fwdump = + filp_open(fwdump_fname, + O_CREAT | O_WRONLY | O_APPEND, 0644); + if (IS_ERR(pfile_fwdump)) { + memset(fwdump_fname, 0, 64); + sprintf(fwdump_fname, "%s/%s", "/var", + "file_fwdump"); + pfile_fwdump = + filp_open(fwdump_fname, + O_CREAT | O_WRONLY | O_APPEND, + 0644); + } + } else + pfile_fwdump = + filp_open(fwdump_fname, + O_CREAT | O_WRONLY | O_APPEND, 0644); + } + if (IS_ERR(pfile_fwdump)) { + PRINTM(MERROR, "Cannot create firmware dump file\n"); + LEAVE(); + return; + } + vfs_write(pfile_fwdump, pmevent->event_buf, pmevent->event_len, &pos); + filp_close(pfile_fwdump, NULL); + LEAVE(); + return; +} + +#define DRV_INFO_SIZE 0x40000 +#define ROW_SIZE_16 16 +#define ROW_SIZE_32 32 +/** + * @brief This function save moal_priv's debug log + * + * @param phandle A pointer to moal_handle + * @param buf A pointer buffer saving log + * + * @return The length of this log + */ +static int +woal_dump_priv_drv_info(moal_handle *handle, t_u8 *buf) +{ + char *ptr = (char *)buf; + int index; +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) + int i = 0; +#endif + moal_private *priv; + + ENTER(); + if (!handle || !buf) { + PRINTM(MMSG, "%s: can't retreive info\n", __func__); + LEAVE(); + return 0; + } + for (index = 0; index < MIN(handle->priv_num, MLAN_MAX_BSS_NUM); + index++) { + priv = handle->priv[index]; + if (priv) { + ptr += sprintf(ptr, "[Interface : %s]\n", + priv->proc_entry_name); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) + ptr += sprintf(ptr, "wmm_tx_pending[0] = %d\n", + atomic_read(&priv->wmm_tx_pending[0])); + ptr += sprintf(ptr, "wmm_tx_pending[1] = %d\n", + atomic_read(&priv->wmm_tx_pending[1])); + ptr += sprintf(ptr, "wmm_tx_pending[2] = %d\n", + atomic_read(&priv->wmm_tx_pending[2])); + ptr += sprintf(ptr, "wmm_tx_pending[3] = %d\n", + atomic_read(&priv->wmm_tx_pending[3])); +#endif + ptr += sprintf(ptr, "Media state = \"%s\"\n", + ((priv->media_connected == + MFALSE) ? "Disconnected" : + "Connected")); + ptr += sprintf(ptr, "carrier %s\n", + ((netif_carrier_ok(priv->netdev)) ? "on" + : "off")); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) + for (i = 0; i < (priv->netdev->num_tx_queues); i++) { + ptr += sprintf(ptr, "tx queue %d: %s\n", i, + ((netif_tx_queue_stopped + (netdev_get_tx_queue + (priv->netdev, + i))) ? "stopped" : + "started")); + } +#else + ptr += sprintf(ptr, "tx queue %s\n", + ((netif_queue_stopped(priv->netdev)) ? + "stopped" : "started")); +#endif + ptr += sprintf(ptr, "%s: num_tx_timeout = %d\n", + priv->netdev->name, + priv->num_tx_timeout); + } + } + + LEAVE(); + return ptr - (char *)buf; +} + +#define SDIO_SCRATCH_REG 0xE8 +/** + * @brief This function save sdio reg info + * + * @param phandle A pointer to moal_handle + * @param buf A pointer buffer saving log + * + * @return The length of this log + */ +static int +woal_dump_sdio_reg_info(moal_handle *phandle, t_u8 *drv_buf) +{ + char *drv_ptr = (char *)drv_buf; + int ret = 0; + t_u8 loop, index = 0, func, data; + unsigned int reg, reg_start, reg_end; + unsigned int scratch_reg = SDIO_SCRATCH_REG; + unsigned int reg_table[] = { 0x08, 0x58, 0x5C, 0x5D, 0x60, 0x61, 0x62, + 0x64, 0x65, 0x66, 0x68, 0x69, 0x6a + }; + char buf[256], *ptr; + + ENTER(); + + if (!phandle || !drv_buf) { + PRINTM(MMSG, "%s: can't retreive info\n", __func__); + LEAVE(); + return 0; + } + mlan_pm_wakeup_card(phandle->pmlan_adapter); + + drv_ptr += sprintf(drv_ptr, "--------sdio_reg_debug_info---------\n"); + sdio_claim_host(((struct sdio_mmc_card *)phandle->card)->func); + for (loop = 0; loop < 5; loop++) { + memset(buf, 0, sizeof(buf)); + ptr = buf; + if (loop == 0) { + /* Read the registers of SDIO function0 */ + func = loop; + reg_start = 0; + reg_end = 9; + + } else if (loop == 1) { + /* Read the registers of SDIO function1 */ + func = loop; + reg_start = 0x10; + reg_end = 0x17; + } else if (loop == 2) { + /* Read specific registers of SDIO function1 */ + index = 0; + func = 1; + reg_start = reg_table[index++]; + reg_end = + reg_table[sizeof(reg_table) / sizeof(int) - 1]; + } else { + /* Read the scratch registers of SDIO function1 */ + if (loop == 4) + mdelay(100); + func = 1; + reg_start = scratch_reg; + reg_end = scratch_reg + 10; + } + if (loop != 2) + ptr += sprintf(ptr, "SDIO Func%d (%#x-%#x): ", func, + reg_start, reg_end); + else + ptr += sprintf(ptr, "SDIO Func%d: ", func); + for (reg = reg_start; reg <= reg_end;) { + if (func == 0) + data = sdio_f0_readb(((struct sdio_mmc_card *) + phandle->card)->func, reg, + &ret); + else + data = sdio_readb(((struct sdio_mmc_card *) + phandle->card)->func, reg, + &ret); + if (loop == 2) + ptr += sprintf(ptr, "(%#x) ", reg); + if (!ret) + ptr += sprintf(ptr, "%02x ", data); + else { + ptr += sprintf(ptr, "ERR"); + break; + } + if (loop == 2 && reg < reg_end) + reg = reg_table[index++]; + else + reg++; + } + drv_ptr += sprintf(drv_ptr, "%s\n", buf); + } + sdio_release_host(((struct sdio_mmc_card *)phandle->card)->func); + + drv_ptr += + sprintf(drv_ptr, "--------sdio_reg_debug_info End---------\n"); + + LEAVE(); + return drv_ptr - (char *)drv_buf; +} + +/** + * @brief This function save moal_handle's info + * + * @param phandle A pointer to moal_handle + * @param buf A pointer buffer saving log + * + * @return The length of this log + */ +static int +woal_dump_moal_drv_info(moal_handle *phandle, t_u8 *buf) +{ + char *ptr; + char str_buf[MLAN_MAX_VER_STR_LEN]; + + ENTER(); + if (!phandle || !buf) { + PRINTM(MMSG, "%s: can't retreive info\n", __func__); + LEAVE(); + return 0; + } + ptr = (char *)buf; + ptr += sprintf(ptr, "------------moal_debug_info-------------\n"); + woal_get_version(phandle, str_buf, sizeof(str_buf) - 1); + ptr += sprintf(ptr, "Driver version = %s\n", str_buf); + ptr += sprintf(ptr, "main_state = %d\n", phandle->main_state); + ptr += sprintf(ptr, "ioctl_pending = %d\n", + atomic_read(&phandle->ioctl_pending)); + ptr += sprintf(ptr, "tx_pending = %d\n", + atomic_read(&phandle->tx_pending)); + ptr += sprintf(ptr, "rx_pending = %d\n", + atomic_read(&phandle->rx_pending)); + ptr += sprintf(ptr, "lock_count = %d\n", + atomic_read(&phandle->lock_count)); + ptr += sprintf(ptr, "malloc_count = %d\n", + atomic_read(&phandle->malloc_count)); + ptr += sprintf(ptr, "mbufalloc_count = %d\n", + atomic_read(&phandle->mbufalloc_count)); +#if defined(SDIO_SUSPEND_RESUME) + ptr += sprintf(ptr, "hs_skip_count = %u\n", phandle->hs_skip_count); + ptr += sprintf(ptr, "hs_force_count = %u\n", phandle->hs_force_count); +#endif + + ptr += woal_dump_priv_drv_info(phandle, ptr); + ptr += sprintf(ptr, "------------moal_debug_info End-------------\n"); + + ptr += woal_dump_sdio_reg_info(phandle, ptr); + + LEAVE(); + return ptr - (char *)buf; +} + +/** + * @brief This function save mlan's info + * + * @param phandle A pointer to moal_handle + * @param buf A pointer buffer saving log + * + * @return The length of this log + */ +static int +woal_dump_mlan_drv_info(moal_private *priv, t_u8 *buf) +{ + char *ptr = (char *)buf; + int i; +#ifdef SDIO_MULTI_PORT_TX_AGGR + int j; + t_u8 mp_aggr_pkt_limit = SDIO_MP_AGGR_DEF_PKT_LIMIT; +#endif + char str[11 * DBG_CMD_NUM + 1] = { 0 }; + char *s; + + ENTER(); + if (!priv || woal_get_debug_info(priv, MOAL_IOCTL_WAIT, &info)) { + PRINTM(MERROR, + "Could not retrieve debug information from MLAN\n"); + LEAVE(); + return 0; + } + ptr += sprintf(ptr, "------------mlan_debug_info-------------\n"); + ptr += sprintf(ptr, "mlan_processing =%d\n", info.mlan_processing); + ptr += sprintf(ptr, "main_lock_flag =%d\n", info.main_lock_flag); + ptr += sprintf(ptr, "main_process_cnt =%d\n", info.main_process_cnt); + ptr += sprintf(ptr, "delay_task_flag =%d\n", info.delay_task_flag); + ptr += sprintf(ptr, "mlan_rx_processing =%d\n", + info.mlan_rx_processing); + ptr += sprintf(ptr, "rx_pkts_queued =%d\n", info.rx_pkts_queued); + ptr += sprintf(ptr, "tx_pkts_queued =%d\n", info.tx_pkts_queued); + + ptr += sprintf(ptr, "num_cmd_timeout = %d\n", info.num_cmd_timeout); + ptr += sprintf(ptr, "dbg.num_cmd_timeout = %d\n", + info.dbg_num_cmd_timeout); + ptr += sprintf(ptr, "Timeout cmd id = 0x%x, act = 0x%x\n", + info.timeout_cmd_id, info.timeout_cmd_act); + ptr += sprintf(ptr, "last_cmd_index = %d\n", info.last_cmd_index); + for (s = str, i = 0; i < DBG_CMD_NUM; i++) + s += sprintf(s, "0x%x ", info.last_cmd_id[i]); + ptr += sprintf(ptr, "last_cmd_id = %s\n", str); + + for (s = str, i = 0; i < DBG_CMD_NUM; i++) + s += sprintf(s, "0x%x ", info.last_cmd_act[i]); + + ptr += sprintf(ptr, "last_cmd_act = %s\n", str); + ptr += sprintf(ptr, "last_cmd_resp_index = %d\n", + info.last_cmd_resp_index); + for (s = str, i = 0; i < DBG_CMD_NUM; i++) + s += sprintf(s, "0x%x ", info.last_cmd_resp_id[i]); + + ptr += sprintf(ptr, "last_cmd_resp_id = %s\n", str); + ptr += sprintf(ptr, "last_event_index = %d\n", info.last_event_index); + for (s = str, i = 0; i < DBG_CMD_NUM; i++) + s += sprintf(s, "0x%x ", info.last_event[i]); + + ptr += sprintf(ptr, "last_event = %s\n", str); + ptr += sprintf(ptr, "num_data_h2c_failure = %d\n", + info.num_tx_host_to_card_failure); + ptr += sprintf(ptr, "num_cmd_h2c_failure = %d\n", + info.num_cmd_host_to_card_failure); + ptr += sprintf(ptr, "num_alloc_buffer_failure = %d\n", + info.num_alloc_buffer_failure); + ptr += sprintf(ptr, "num_pkt_dropped = %d\n", info.num_pkt_dropped); + ptr += sprintf(ptr, "num_data_c2h_failure = %d\n", + info.num_rx_card_to_host_failure); + ptr += sprintf(ptr, "num_cmdevt_c2h_failure = %d\n", + info.num_cmdevt_card_to_host_failure); + ptr += sprintf(ptr, "num_int_read_failure = %d\n", + info.num_int_read_failure); + ptr += sprintf(ptr, "last_int_status = %d\n", info.last_int_status); + ptr += sprintf(ptr, "num_event_deauth = %d\n", info.num_event_deauth); + ptr += sprintf(ptr, "num_event_disassoc = %d\n", + info.num_event_disassoc); + ptr += sprintf(ptr, "num_event_link_lost = %d\n", + info.num_event_link_lost); + ptr += sprintf(ptr, "num_cmd_deauth = %d\n", info.num_cmd_deauth); + ptr += sprintf(ptr, "num_cmd_assoc_success = %d\n", + info.num_cmd_assoc_success); + ptr += sprintf(ptr, "num_cmd_assoc_failure = %d\n", + info.num_cmd_assoc_failure); + ptr += sprintf(ptr, "cmd_resp_received = %d\n", info.cmd_resp_received); + ptr += sprintf(ptr, "event_received = %d\n", info.event_received); + ptr += sprintf(ptr, "max_tx_buf_size = %d\n", info.max_tx_buf_size); + ptr += sprintf(ptr, "tx_buf_size = %d\n", info.tx_buf_size); + ptr += sprintf(ptr, "curr_tx_buf_size = %d\n", info.curr_tx_buf_size); + + ptr += sprintf(ptr, "data_sent=%d cmd_sent=%d\n", info.data_sent, + info.cmd_sent); + ptr += sprintf(ptr, "ps_mode=%d ps_state=%d\n", info.ps_mode, + info.ps_state); + ptr += sprintf(ptr, "wakeup_dev_req=%d wakeup_tries=%d\n", + info.pm_wakeup_card_req, info.pm_wakeup_fw_try); + ptr += sprintf(ptr, "hs_configured=%d hs_activated=%d\n", + info.is_hs_configured, info.hs_activated); + ptr += sprintf(ptr, "pps_uapsd_mode=%d sleep_pd=%d\n", + info.pps_uapsd_mode, info.sleep_pd); + ptr += sprintf(ptr, "tx_lock_flag = %d\n", info.tx_lock_flag); + ptr += sprintf(ptr, "port_open = %d\n", info.port_open); + ptr += sprintf(ptr, "scan_processing = %d\n", info.scan_processing); + + ptr += sprintf(ptr, "mp_rd_bitmap=0x%x curr_rd_port=0x%x\n", + (unsigned int)info.mp_rd_bitmap, info.curr_rd_port); + ptr += sprintf(ptr, "mp_wr_bitmap=0x%x curr_wr_port=0x%x\n", + (unsigned int)info.mp_wr_bitmap, info.curr_wr_port); + ptr += sprintf(ptr, "mp_invalid_update=%d\n", info.mp_invalid_update); +#ifdef SDIO_MULTI_PORT_TX_AGGR + ptr += sprintf(ptr, "last_recv_wr_bitmap=0x%x last_mp_index = %d\n", + info.last_recv_wr_bitmap, info.last_mp_index); + for (i = 0; i < SDIO_MP_DBG_NUM; i++) { + for (s = str, j = 0; j < mp_aggr_pkt_limit; j++) + s += sprintf(s, "0x%02x ", + info.last_mp_wr_info[i * + mp_aggr_pkt_limit + + j]); + + ptr += sprintf(ptr, + "mp_wr_bitmap: 0x%x mp_wr_ports=0x%x len=%d curr_wr_port=0x%x\n%s\n", + info.last_mp_wr_bitmap[i], + info.last_mp_wr_ports[i], info.last_mp_wr_len[i], + info.last_curr_wr_port[i], str); + } +#endif + ptr += sprintf(ptr, "------------mlan_debug_info End-------------\n"); + + LEAVE(); + return ptr - (char *)buf; +} + +/** + * @brief This function dump hex to file + * + * @param phandle A pointer to moal_handle + * @param buf A pointer to buffer to dump + * @param len lengh of buf + * @param ascii Whether add ascii at the end + * @param save_buf Buffer which is saved to + * + * @return The length of this log + */ +static int +woal_save_hex_dump(int rowsize, const void *buf, size_t len, + bool ascii, t_u8 *save_buf) +{ + const u8 *ptr = buf; + int i, linelen, remaining = len; + unsigned char linebuf[ROW_SIZE_32 * 3 + 2 + ROW_SIZE_32 + 1]; + char *pos = (char *)save_buf; + + if (rowsize != ROW_SIZE_16 && rowsize != ROW_SIZE_32) + rowsize = ROW_SIZE_16; + + for (i = 0; i < len; i += rowsize) { + linelen = min(remaining, rowsize); + remaining -= rowsize; + + hex_dump_to_buffer(ptr + i, linelen, rowsize, 1, linebuf, + sizeof(linebuf), ascii); + + pos += sprintf(pos, "%p: %s\n", ptr + i, linebuf); + } + + return pos - (char *)save_buf; +} + +/** + * @brief This function dump moal hex to file + * + * @param phandle A pointer to moal_handle + * @param buf A pointer to buffer + * + * @return The length of this log + */ +static int +woal_dump_moal_hex(moal_handle *phandle, t_u8 *buf) +{ + char *ptr = (char *)buf; + int i; + ENTER(); + + if (!phandle || !buf) { + PRINTM(MMSG, "%s: can't retreive info\n", __func__); + LEAVE(); + return 0; + } + + ptr += sprintf(ptr, "<--moal_handle-->\n"); + ptr += sprintf(ptr, "moal_handle=%p, size=%ld(0x%lx)\n", phandle, + (long int)sizeof(*phandle), + (long unsigned int)sizeof(*phandle)); + ptr += woal_save_hex_dump(ROW_SIZE_16, phandle, sizeof(*phandle), MTRUE, + ptr); + ptr += sprintf(ptr, "<--moal_handle End-->\n"); + + for (i = 0; i < phandle->priv_num; i++) { + ptr += sprintf(ptr, "<--moal_private(%d)-->\n", i); + ptr += sprintf(ptr, "moal_private=%p, size=%ld(0x%lx)\n", + phandle->priv[i], + (long int)sizeof(*(phandle->priv[i])), + (long unsigned int)sizeof(*(phandle->priv[i]))); + ptr += woal_save_hex_dump(ROW_SIZE_16, phandle->priv[i], + sizeof(*(phandle->priv[i])), MTRUE, + ptr); + ptr += sprintf(ptr, "<--moal_private(%d) End-->\n", i); + } + LEAVE(); + return ptr - (char *)buf; +} + +/** + * @brief This function dump mlan hex to file + * + * @param priv A pointer to moal_private structure + * @param buf A pointer to buffer + * @param pfile A pointer to file structure + * + * @return The length of this log + */ +static int +woal_dump_mlan_hex(moal_private *priv, t_u8 *buf, struct file *pfile) +{ + char *ptr = (char *)buf; + int i; + int len = 0; + + ENTER(); + + if (!buf || !priv || !pfile || + woal_get_debug_info(priv, MOAL_IOCTL_WAIT, &info)) { + PRINTM(MMSG, "%s: can't retreive info\n", __func__); + LEAVE(); + return 0; + } + + ptr += sprintf(ptr, "<--mlan_adapter-->\n"); + ptr += sprintf(ptr, "mlan_adapter=%p, size=%d(0x%x)\n", + info.mlan_adapter, info.mlan_adapter_size, + info.mlan_adapter_size); + ptr += woal_save_hex_dump(ROW_SIZE_16, info.mlan_adapter, + info.mlan_adapter_size, MTRUE, ptr); + ptr += sprintf(ptr, "<--mlan_adapter End-->\n"); + vfs_write(pfile, buf, ptr - (char *)buf, &pfile->f_pos); + len += ptr - (char *)buf; +#ifdef SDIO_MULTI_PORT_TX_AGGR + if (info.mpa_buf && info.mpa_buf_size) { + ptr = (char *)buf; + ptr += sprintf(ptr, "<--mlan_mpa_buf-->\n"); + ptr += sprintf(ptr, "mlan_mpa_buf=%p, size=%d(0x%x)\n", + info.mpa_buf, info.mpa_buf_size, + info.mpa_buf_size); + ptr += woal_save_hex_dump(ROW_SIZE_16, info.mpa_buf, + info.mpa_buf_size, MTRUE, ptr); + ptr += sprintf(ptr, "<--mlan_mpa_buf End-->\n"); + vfs_write(pfile, buf, ptr - (char *)buf, &pfile->f_pos); + len += ptr - (char *)buf; + } +#endif + for (i = 0; i < info.mlan_priv_num; i++) { + ptr = (char *)buf; + ptr += sprintf(ptr, "<--mlan_private(%d)-->\n", i); + ptr += sprintf(ptr, "mlan_private=%p, size=%d(0x%x)\n", + info.mlan_priv[i], info.mlan_priv_size[i], + info.mlan_priv_size[i]); + ptr += woal_save_hex_dump(ROW_SIZE_16, info.mlan_priv[i], + info.mlan_priv_size[i], MTRUE, ptr); + ptr += sprintf(ptr, "<--mlan_private(%d) End-->\n", i); + vfs_write(pfile, buf, ptr - (char *)buf, &pfile->f_pos); + len += ptr - (char *)buf; + } + + LEAVE(); + return len; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 1, 0) +/** + * @brief This function create dump directory + * + * @param phandle A pointer to moal_handle + * @param dir_buf A pointer to dir_buf buffer + * @param buf_size Size of dir_buf buffer + * + * @return N/A + */ +void +woal_create_dump_dir(moal_handle *phandle, char *dir_buf, int buf_size) +{ + struct dentry *dentry; + struct path path; + t_u32 sec, usec; + int ret; + + ENTER(); + + if (!phandle || !dir_buf) { + PRINTM(MERROR, "Can't create directory\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + moal_get_system_time(phandle, &sec, &usec); + memset(dir_buf, 0, buf_size); + sprintf(dir_buf, "%s%u", "/data/dump_", sec); + + dentry = kern_path_create(AT_FDCWD, dir_buf, &path, 1); + if (IS_ERR(dentry)) { + PRINTM(MMSG, + "Create directory %s error, try create dir in /var", + dir_buf); + memset(dir_buf, 0, buf_size); + sprintf(dir_buf, "%s%u", "/var/dump_", sec); + dentry = kern_path_create(AT_FDCWD, dir_buf, &path, 1); + } + if (IS_ERR(dentry)) { + PRINTM(MMSG, "Create directory %s error, use default folder", + dir_buf); + goto default_dir; + } + ret = vfs_mkdir(path.dentry->d_inode, dentry, 0777); +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0) + mutex_unlock(&path.dentry->d_inode->i_mutex); +#else + inode_unlock(path.dentry->d_inode); +#endif + + if (ret < 0) { + PRINTM(MMSG, "Create directory failure, use default folder\n"); + goto default_dir; + } else { + PRINTM(MMSG, "Create directory %s successfully\n", dir_buf); + goto done; + } + +default_dir: + memset(dir_buf, 0, buf_size); + sprintf(dir_buf, "%s", "/data"); +done: + LEAVE(); +} +#endif + +/** + * @brief This function save dump buf to file + * + * @param dir_name A pointer to directory name + * @param file_name A pointer to file name + * @param buf A pointer to dump data + * @param buf_len The length of dump buf + * + * @return SUCCESS OR FAILURE + */ +mlan_status +woal_save_dump_info_to_file(char *dir_name, char *file_name, t_u8 *buf, + t_u32 buf_len) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + struct file *pfile = NULL; + t_u8 name[64]; + mm_segment_t fs; + loff_t pos; + + ENTER(); + + if (!dir_name || !file_name || !buf) { + PRINTM(MERROR, "Can't save dump info to file\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + memset(name, 0, sizeof(name)); + sprintf(name, "%s/%s", dir_name, file_name); + pfile = filp_open(name, O_CREAT | O_RDWR, 0644); + if (IS_ERR(pfile)) { + PRINTM(MMSG, + "Create file %s error, try to save dump file in /var\n", + name); + memset(name, 0, sizeof(name)); + sprintf(name, "%s/%s", "/var", file_name); + pfile = filp_open(name, O_CREAT | O_RDWR, 0644); + } + if (IS_ERR(pfile)) { + PRINTM(MERROR, "Create Dump file for %s error\n", name); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + PRINTM(MMSG, "Dump data %s saved in %s\n", file_name, name); + + fs = get_fs(); + set_fs(KERNEL_DS); + pos = 0; + vfs_write(pfile, buf, buf_len, &pos); + filp_close(pfile, NULL); + set_fs(fs); + + PRINTM(MMSG, "Dump data %s saved in %s successfully\n", file_name, + name); + +done: + LEAVE(); + return ret; +} + +/** + * @brief This function dump drv info to file + * + * @param phandle A pointer to moal_handle + * @param dir_name A pointer to directory name + * + * @return N/A + */ +void +woal_dump_drv_info(moal_handle *phandle, t_u8 *dir_name) +{ + int ret = 0; + struct file *pfile = NULL; + mm_segment_t fs; + t_u8 *drv_buf; + t_u8 file_name[64]; + t_u32 len = 0; + t_u32 total_len = 0; + + ENTER(); + + PRINTM(MMSG, "=== START DRIVER INFO DUMP==="); + ret = moal_vmalloc(phandle, DRV_INFO_SIZE + 1, &drv_buf); + if ((ret != MLAN_STATUS_SUCCESS) || !drv_buf) { + PRINTM(MERROR, "Error: vmalloc drv buffer failed!\n"); + goto done; + } + memset(file_name, 0, sizeof(file_name)); + sprintf(file_name, "%s/%s", dir_name, "file_drv_info"); + pfile = filp_open(file_name, O_CREAT | O_RDWR, 0644); + if (IS_ERR(pfile)) { + PRINTM(MMSG, + "Create file %s error, try create /var/file_drv_info", + file_name); + pfile = filp_open("/var/file_drv_info", O_CREAT | O_RDWR, 0644); + } else { + PRINTM(MMSG, "DRV dump data in %s\n", file_name); + } + if (IS_ERR(pfile)) { + PRINTM(MMSG, "Create file_drv_info file failed\n"); + goto done; + } + fs = get_fs(); + set_fs(KERNEL_DS); + + len = woal_dump_moal_drv_info(phandle, drv_buf); + total_len += len; + vfs_write(pfile, drv_buf, len, &pfile->f_pos); + + len = woal_dump_mlan_drv_info(woal_get_priv(phandle, MLAN_BSS_ROLE_ANY), + drv_buf); + total_len += len; + vfs_write(pfile, drv_buf, len, &pfile->f_pos); + + len = woal_dump_moal_hex(phandle, drv_buf); + total_len += len; + vfs_write(pfile, drv_buf, len, &pfile->f_pos); + + len = woal_dump_mlan_hex(woal_get_priv(phandle, MLAN_BSS_ROLE_ANY), + drv_buf, pfile); + total_len += len; + + PRINTM(MMSG, "Drv info total bytes = %ld (0x%lx)\n", + (long int)total_len, (long unsigned int)total_len); + + filp_close(pfile, NULL); + set_fs(fs); + + PRINTM(MMSG, "=== DRIVER INFO DUMP END==="); +done: + if (drv_buf) + moal_vfree(phandle, drv_buf); + LEAVE(); +} + +#define DEBUG_HOST_READY 0xCC +#define DEBUG_FW_DONE 0xFF +#define DEBUG_MEMDUMP_FINISH 0xFE +#define MAX_POLL_TRIES 100 + +#define DEBUG_DUMP_CTRL_REG 0xF9 +#define DEBUG_DUMP_START_REG 0xF1 +#define DEBUG_DUMP_END_REG 0xF8 + +typedef enum { + DUMP_TYPE_ITCM = 0, + DUMP_TYPE_DTCM = 1, + DUMP_TYPE_SQRAM = 2, + DUMP_TYPE_APU_REGS = 3, + DUMP_TYPE_CIU_REGS = 4, + DUMP_TYPE_ICU_REGS = 5, + DUMP_TYPE_MAC_REGS = 6, + DUMP_TYPE_EXTEND_7 = 7, + DUMP_TYPE_EXTEND_8 = 8, + DUMP_TYPE_EXTEND_9 = 9, + DUMP_TYPE_EXTEND_10 = 10, + DUMP_TYPE_EXTEND_11 = 11, + DUMP_TYPE_EXTEND_12 = 12, + DUMP_TYPE_EXTEND_13 = 13, + DUMP_TYPE_EXTEND_LAST = 14 +} dumped_mem_type; + +#define MAX_NAME_LEN 8 +#define MAX_FULL_NAME_LEN 32 +t_u8 *name_prefix = "/data/file_"; + +typedef struct { + t_u8 mem_name[MAX_NAME_LEN]; + t_u8 *mem_Ptr; + struct file *pfile_mem; + t_u8 done_flag; +} memory_type_mapping; + +memory_type_mapping mem_type_mapping_tbl = { "DUMP", NULL, NULL, 0xDD }; + +typedef enum { + RDWR_STATUS_SUCCESS = 0, + RDWR_STATUS_FAILURE = 1, + RDWR_STATUS_DONE = 2 +} rdwr_status; + +/** + * @brief This function read/write firmware via cmd52 + * + * @param phandle A pointer to moal_handle + * @param doneflag A flag + * + * @return MLAN_STATUS_SUCCESS + */ +rdwr_status +woal_cmd52_rdwr_firmware(moal_handle *phandle, t_u8 doneflag) +{ + int ret = 0; + int tries = 0; + t_u8 ctrl_data = 0; + t_u8 dbg_dump_ctrl_reg = 0; + t_u8 debug_host_ready = 0; + + dbg_dump_ctrl_reg = DEBUG_DUMP_CTRL_REG; + debug_host_ready = DEBUG_HOST_READY; + sdio_writeb(((struct sdio_mmc_card *)phandle->card)->func, + debug_host_ready, dbg_dump_ctrl_reg, &ret); + if (ret) { + PRINTM(MERROR, "SDIO Write ERR\n"); + return RDWR_STATUS_FAILURE; + } + for (tries = 0; tries < MAX_POLL_TRIES; tries++) { + ctrl_data = + sdio_readb(((struct sdio_mmc_card *)phandle->card)-> + func, dbg_dump_ctrl_reg, &ret); + if (ret) { + PRINTM(MERROR, "SDIO READ ERR\n"); + return RDWR_STATUS_FAILURE; + } + if (ctrl_data == DEBUG_FW_DONE) + break; + if (doneflag && ctrl_data == doneflag) + return RDWR_STATUS_DONE; + if (ctrl_data != debug_host_ready) { + PRINTM(MMSG, + "The ctrl reg was changed, re-try again!\n"); + sdio_writeb(((struct sdio_mmc_card *)phandle->card)-> + func, debug_host_ready, dbg_dump_ctrl_reg, + &ret); + if (ret) { + PRINTM(MERROR, "SDIO Write ERR\n"); + return RDWR_STATUS_FAILURE; + } + } + udelay(100); + } + if (ctrl_data == debug_host_ready) { + PRINTM(MERROR, "Fail to pull ctrl_data\n"); + return RDWR_STATUS_FAILURE; + } + + return RDWR_STATUS_SUCCESS; +} + +/** + * @brief This function dump firmware memory to file + * + * @param phandle A pointer to moal_handle + * + * @return N/A + */ +void +woal_dump_firmware_info_v3(moal_handle *phandle) +{ + + int ret = 0; + int tries = 0; + unsigned int reg, reg_start, reg_end; + t_u8 *dbg_ptr = NULL; + t_u8 *temp_Ptr = NULL; + t_u32 sec, usec; + t_u8 start_flag = 0; + t_u8 doneflag = 0; + rdwr_status stat; + t_u32 memory_size = 0; + t_u8 path_name[64], file_name[32]; + t_u8 *end_ptr = NULL; + t_u8 dbg_dump_start_reg = 0; + t_u8 dbg_dump_end_reg = 0; + t_u8 dbg_dump_ctrl_reg = 0; + memory_type_mapping *pmem_type_mapping_tbl = &mem_type_mapping_tbl; + + if (!phandle) { + PRINTM(MERROR, "Could not dump firmwware info\n"); + return; + } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 1, 0) + /** Create dump directort*/ + woal_create_dump_dir(phandle, path_name, sizeof(path_name)); +#else + memset(path_name, 0, sizeof(path_name)); + strcpy(path_name, "/data"); +#endif + PRINTM(MMSG, "Directory name is %s\n", path_name); + + woal_dump_drv_info(phandle, path_name); + + dbg_dump_start_reg = DEBUG_DUMP_START_REG; + dbg_dump_end_reg = DEBUG_DUMP_END_REG; + dbg_dump_ctrl_reg = DEBUG_DUMP_CTRL_REG; + + mlan_pm_wakeup_card(phandle->pmlan_adapter); + phandle->fw_dump = MTRUE; + sdio_claim_host(((struct sdio_mmc_card *)phandle->card)->func); + /* start dump fw memory */ + moal_get_system_time(phandle, &sec, &usec); + PRINTM(MMSG, "==== DEBUG MODE OUTPUT START: %u.%06u ====\n", sec, usec); + /* read the number of the memories which will dump */ + if (RDWR_STATUS_FAILURE == woal_cmd52_rdwr_firmware(phandle, doneflag)) + goto done; + + /** check the reg which indicate dump starting */ + for (reg = dbg_dump_start_reg; reg <= dbg_dump_end_reg; reg++) { + for (tries = 0; tries < MAX_POLL_TRIES; tries++) { + start_flag = + sdio_readb(((struct sdio_mmc_card *)phandle-> + card)->func, reg, &ret); + if (ret) { + PRINTM(MMSG, "SDIO READ ERR\n"); + goto done; + } + /** 0 means dump starting*/ + if (start_flag == 0) + break; + if (tries == MAX_POLL_TRIES) { + PRINTM(MMSG, "FW not ready to dump\n"); + goto done; + } + udelay(100); + } + } + + memory_size = 0xF0000; + PRINTM(MMSG, "%s_SIZE=0x%x\n", pmem_type_mapping_tbl->mem_name, + memory_size); + ret = moal_vmalloc(phandle, memory_size + 1, + (t_u8 **)&pmem_type_mapping_tbl->mem_Ptr); + if ((ret != MLAN_STATUS_SUCCESS) || !pmem_type_mapping_tbl->mem_Ptr) { + PRINTM(MERROR, "Error: vmalloc %s buffer failed!!!\n", + pmem_type_mapping_tbl->mem_name); + goto done; + } + dbg_ptr = pmem_type_mapping_tbl->mem_Ptr; + end_ptr = dbg_ptr + memory_size; + + doneflag = pmem_type_mapping_tbl->done_flag; + moal_get_system_time(phandle, &sec, &usec); + PRINTM(MMSG, "Start %s output %u.%06u, please wait...\n", + pmem_type_mapping_tbl->mem_name, sec, usec); + do { + stat = woal_cmd52_rdwr_firmware(phandle, doneflag); + if (RDWR_STATUS_FAILURE == stat) + goto done; + + reg_start = dbg_dump_start_reg; + reg_end = dbg_dump_end_reg; + for (reg = reg_start; reg <= reg_end; reg++) { + *dbg_ptr = + sdio_readb(((struct sdio_mmc_card *)phandle-> + card)->func, reg, &ret); + if (ret) { + PRINTM(MMSG, "SDIO READ ERR\n"); + goto done; + } + dbg_ptr++; + if (dbg_ptr >= end_ptr) { + PRINTM(MMSG, + "pre-allocced buf is not enough\n"); + ret = moal_vmalloc(phandle, + memory_size + 0x4000 + 1, + (t_u8 **)&temp_Ptr); + if ((ret != MLAN_STATUS_SUCCESS) || !temp_Ptr) { + PRINTM(MERROR, + "Error: vmalloc buffer failed!!!\n"); + goto done; + } + moal_memcpy(phandle, temp_Ptr, + pmem_type_mapping_tbl->mem_Ptr, + memory_size); + moal_vfree(phandle, + pmem_type_mapping_tbl->mem_Ptr); + pmem_type_mapping_tbl->mem_Ptr = temp_Ptr; + temp_Ptr = NULL; + dbg_ptr = + pmem_type_mapping_tbl->mem_Ptr + + memory_size; + memory_size += 0x4000; + end_ptr = + pmem_type_mapping_tbl->mem_Ptr + + memory_size; + } + + } + if (RDWR_STATUS_DONE == stat) { + PRINTM(MMSG, "%s done:" +#ifdef MLAN_64BIT + "size = 0x%lx\n", +#else + "size = 0x%x\n", +#endif + pmem_type_mapping_tbl->mem_name, + dbg_ptr - pmem_type_mapping_tbl->mem_Ptr); + + memset(file_name, 0, sizeof(file_name)); + sprintf(file_name, "%s%s", "file_sdio_", + pmem_type_mapping_tbl->mem_name); + if (MLAN_STATUS_SUCCESS != + woal_save_dump_info_to_file(path_name, file_name, + pmem_type_mapping_tbl-> + mem_Ptr, + dbg_ptr - + pmem_type_mapping_tbl-> + mem_Ptr)) + PRINTM(MMSG, "Can't save dump file %s in %s\n", + file_name, path_name); + moal_vfree(phandle, pmem_type_mapping_tbl->mem_Ptr); + pmem_type_mapping_tbl->mem_Ptr = NULL; + break; + } + } while (1); + + moal_get_system_time(phandle, &sec, &usec); + PRINTM(MMSG, "==== DEBUG MODE OUTPUT END: %u.%06u ====\n", sec, usec); + /* end dump fw memory */ +done: + phandle->fw_dump = MFALSE; + sdio_release_host(((struct sdio_mmc_card *)phandle->card)->func); + if (pmem_type_mapping_tbl->mem_Ptr) { + moal_vfree(phandle, pmem_type_mapping_tbl->mem_Ptr); + pmem_type_mapping_tbl->mem_Ptr = NULL; + } + PRINTM(MMSG, "==== DEBUG MODE END ====\n"); + return; +} + +/** + * @brief This function reads and displays SDIO registers for debugging + * + * @param phandle A pointer to moal_handle + * + * @return N/A + */ +void +woal_sdio_reg_dbg(moal_handle *phandle) +{ + int ret = 0; + t_u8 loop, index = 0, func, data; + unsigned int reg, reg_start, reg_end; + unsigned int scratch_reg = SDIO_SCRATCH_REG; + unsigned int reg_table[] = { 0x08, 0x58, 0x5C, 0x5D, 0x60, 0x61, 0x62, + 0x64, 0x65, 0x66, 0x68, 0x69, 0x6a + }; + char buf[256], *ptr; + + mlan_pm_wakeup_card(phandle->pmlan_adapter); + + sdio_claim_host(((struct sdio_mmc_card *)phandle->card)->func); + for (loop = 0; loop < 5; loop++) { + memset(buf, 0, sizeof(buf)); + ptr = buf; + if (loop == 0) { + /* Read the registers of SDIO function0 */ + func = loop; + reg_start = 0; + reg_end = 9; + + } else if (loop == 1) { + /* Read the registers of SDIO function1 */ + func = loop; + reg_start = 0x10; + reg_end = 0x17; + } else if (loop == 2) { + /* Read specific registers of SDIO function1 */ + index = 0; + func = 1; + reg_start = reg_table[index++]; + reg_end = reg_table[ARRAY_SIZE(reg_table) - 1]; + } else { + /* Read the scratch registers of SDIO function1 */ + if (loop == 4) + mdelay(100); + func = 1; + reg_start = scratch_reg; + reg_end = scratch_reg + 10; + } + if (loop != 2) + ptr += sprintf(ptr, "SDIO Func%d (%#x-%#x): ", func, + reg_start, reg_end); + else + ptr += sprintf(ptr, "SDIO Func%d: ", func); + for (reg = reg_start; reg <= reg_end;) { + if (func == 0) + data = sdio_f0_readb(((struct sdio_mmc_card *) + phandle->card)->func, reg, + &ret); + else + data = sdio_readb(((struct sdio_mmc_card *) + phandle->card)->func, reg, + &ret); + if (loop == 2) + ptr += sprintf(ptr, "(%#x) ", reg); + if (!ret) + ptr += sprintf(ptr, "%02x ", data); + else { + ptr += sprintf(ptr, "ERR"); + break; + } + if (loop == 2 && reg < reg_end) + reg = reg_table[index++]; + else + reg++; + } + PRINTM(MMSG, "%s\n", buf); + } + sdio_release_host(((struct sdio_mmc_card *)phandle->card)->func); +} + +/** + * @brief This function displays extra MOAL debug information + * + * @param priv A pointer to moal_private + * @param handle A pointer to moal_handle + * @param flag Indicates whether register read can be done directly + * + * @return N/A + */ +void +woal_moal_debug_info(moal_private *priv, moal_handle *handle, u8 flag) +{ + moal_handle *phandle = NULL; + char buf[MLAN_MAX_VER_STR_LEN]; + int i = 0; + + ENTER(); + + if (!priv) { + if (handle) { + phandle = handle; + } else { + PRINTM(MERROR, + "Could not retrieve debug information from MOAL\n"); + LEAVE(); + return; + } + } else { + phandle = priv->phandle; + } + + woal_get_version(phandle, buf, sizeof(buf) - 1); + PRINTM(MERROR, "Driver version = %s\n", buf); + PRINTM(MERROR, "main_state = %d\n", phandle->main_state); + PRINTM(MERROR, "ioctl_pending = %d\n", + atomic_read(&phandle->ioctl_pending)); + PRINTM(MERROR, "tx_pending = %d\n", atomic_read(&phandle->tx_pending)); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) + if (priv) { + PRINTM(MERROR, "wmm_tx_pending[0] = %d\n", + atomic_read(&priv->wmm_tx_pending[0])); + PRINTM(MERROR, "wmm_tx_pending[1] = %d\n", + atomic_read(&priv->wmm_tx_pending[1])); + PRINTM(MERROR, "wmm_tx_pending[2] = %d\n", + atomic_read(&priv->wmm_tx_pending[2])); + PRINTM(MERROR, "wmm_tx_pending[3] = %d\n", + atomic_read(&priv->wmm_tx_pending[3])); + } +#endif + PRINTM(MERROR, "rx_pending = %d\n", atomic_read(&phandle->rx_pending)); + PRINTM(MERROR, "lock_count = %d\n", atomic_read(&phandle->lock_count)); + PRINTM(MERROR, "malloc_count = %d\n", + atomic_read(&phandle->malloc_count)); + PRINTM(MERROR, "mbufalloc_count = %d\n", + atomic_read(&phandle->mbufalloc_count)); +#if defined(SDIO_SUSPEND_RESUME) + PRINTM(MERROR, "hs_skip_count = %u\n", phandle->hs_skip_count); + PRINTM(MERROR, "hs_force_count = %u\n", phandle->hs_force_count); +#endif + + if (priv) { + PRINTM(MERROR, "Media state = \"%s\"\n", + ((priv->media_connected == + MFALSE) ? "Disconnected" : "Connected")); + PRINTM(MERROR, "carrier %s\n", + ((netif_carrier_ok(priv->netdev)) ? "on" : "off")); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) + for (i = 0; i < (priv->netdev->num_tx_queues); i++) { + PRINTM(MERROR, "tx queue %d: %s\n", i, + ((netif_tx_queue_stopped + (netdev_get_tx_queue(priv->netdev, i))) ? + "stopped" : "started")); + } +#else + PRINTM(MERROR, "tx queue %s\n", + ((netif_queue_stopped(priv->netdev)) ? "stopped" : + "started")); +#endif + } + + for (i = 0; i < phandle->priv_num; i++) { + priv = phandle->priv[i]; + if (priv) + PRINTM(MERROR, "%s: num_tx_timeout = %d\n", + priv->netdev->name, priv->num_tx_timeout); + } + +#if defined(SDIO_SUSPEND_RESUME) + if (phandle->is_suspended == MTRUE) { + LEAVE(); + return; + } +#endif + + /* Display SDIO registers */ + if (flag && + ((phandle->main_state == MOAL_END_MAIN_PROCESS) || + (phandle->main_state == MOAL_STATE_IDLE))) { + woal_sdio_reg_dbg(phandle); + } else { + phandle->sdio_reg_dbg = MTRUE; + queue_work(phandle->workqueue, &phandle->main_work); + } + + LEAVE(); + return; +} + +/** + * @brief Download power table to firmware for a specific country + * + * @param priv A pointer to moal_private + * @param country ISO 3166-1 alpha-2 country code + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +woal_request_country_power_table(moal_private *priv, char *country) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + moal_handle *handle = NULL; + char country_name[] = "txpower_XX.bin"; + char file_path[256]; + char *last_slash = NULL; + + ENTER(); + + if (!priv || !priv->phandle) { + PRINTM(MERROR, "Priv or handle is null\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + if (!country) { + PRINTM(MERROR, "Country is null\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + handle = priv->phandle; + + /* Replace XX with ISO 3166-1 alpha-2 country code */ + strncpy(strstr(country_name, "XX"), country, strlen(country)); + + memset(file_path, 0, sizeof(file_path)); + /* file_path should be Null terminated */ + if (fw_name && (strlen(fw_name) < sizeof(file_path))) { + strncpy(file_path, fw_name, + MIN((sizeof(file_path) - 1), strlen(fw_name))); + last_slash = strrchr(file_path, '/'); + if (last_slash) + memset(last_slash + 1, 0, + sizeof(file_path) - 1 - (last_slash - + file_path)); + else + memset(file_path, 0, sizeof(file_path)); + } else { + strncpy(file_path, "mrvl/", sizeof(file_path)); + } + strncpy(file_path + strlen(file_path), country_name, + strlen(country_name)); + country_txpwrlimit = file_path; + + if (MLAN_STATUS_SUCCESS != + woal_set_user_init_data(handle, COUNTRY_POWER_TABLE, + MOAL_IOCTL_WAIT)) { + PRINTM(MFATAL, "Download power table to firmware failed\n"); + ret = MLAN_STATUS_FAILURE; + } + LEAVE(); + return ret; +} + +/** + * @brief napi polling call back function. + * + * @param napi A pointer to napi_struct + * @param budget the limit of packets driver should poll + * + * @return packets received + */ +int +woal_netdev_poll_rx(struct napi_struct *napi, int budget) +{ + moal_handle *handle = container_of(napi, moal_handle, napi_rx); + t_u8 recv = budget; + + ENTER(); + if (handle->surprise_removed == MTRUE) { + napi_complete(napi); + LEAVE(); + return 0; + } + mlan_rx_process(handle->pmlan_adapter, &recv); + if (recv < budget) + napi_complete(napi); + LEAVE(); + return recv; +} + +/** + * @brief This workqueue function handles rx_process + * + * @param work A pointer to work_struct + * + * @return N/A + */ +t_void +woal_rx_work_queue(struct work_struct *work) +{ + moal_handle *handle = container_of(work, moal_handle, rx_work); + ENTER(); + if (handle->surprise_removed == MTRUE) { + LEAVE(); + return; + } + mlan_rx_process(handle->pmlan_adapter, NULL); + LEAVE(); +} + +/** + * @brief This workqueue function handles main_process + * + * @param work A pointer to work_struct + * + * @return N/A + */ +t_void +woal_main_work_queue(struct work_struct *work) +{ + moal_handle *handle = container_of(work, moal_handle, main_work); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) + struct sched_param sp = {.sched_priority = wq_sched_prio }; +#endif + + ENTER(); + + if (handle->surprise_removed == MTRUE) { + LEAVE(); + return; + } + + if (handle->sdio_reg_dbg == MTRUE) { + handle->sdio_reg_dbg = MFALSE; + woal_sdio_reg_dbg(handle); +#if defined(DEBUG_LEVEL1) + if (drvdbg & MFW_D) { + drvdbg &= ~MFW_D; + woal_dump_firmware_info_v3(handle); + } +#endif + LEAVE(); + return; + } +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) + /* Change the priority and scheduling policy of main work queue */ + if ((wq_sched_prio != current->rt_priority) || + (wq_sched_policy != current->policy)) { + PRINTM(MMSG, + "Set work queue priority %d and scheduling policy %d\n", + wq_sched_prio, wq_sched_policy); + sched_setscheduler(current, wq_sched_policy, &sp); + } +#endif + + handle->main_state = MOAL_ENTER_WORK_QUEUE; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32) + sdio_claim_host(((struct sdio_mmc_card *)handle->card)->func); +#endif + handle->main_state = MOAL_START_MAIN_PROCESS; + /* Call MLAN main process */ + mlan_main_process(handle->pmlan_adapter); + handle->main_state = MOAL_END_MAIN_PROCESS; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32) + sdio_release_host(((struct sdio_mmc_card *)handle->card)->func); +#endif + + LEAVE(); +} + +/** + * @brief Handles interrupt + * + */ +/** + * @param handle A pointer to moal_handle struct + * + * @return MLAN_STATUS_FAILURE-- when the interrupt is not for us. + */ +mlan_status +woal_interrupt(moal_handle *handle) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + ENTER(); + handle->main_state = MOAL_RECV_INT; + PRINTM(MINTR, "*\n"); + if (handle->surprise_removed == MTRUE) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + /* call mlan_interrupt to read int status */ + mlan_interrupt(handle->pmlan_adapter); +#ifdef SDIO_SUSPEND_RESUME + if (handle->is_suspended) { + PRINTM(MINTR, "Receive interrupt in hs_suspended\n"); + LEAVE(); + return ret; + } +#endif + handle->main_state = MOAL_START_MAIN_PROCESS; + /* Call MLAN main process */ + mlan_main_process(handle->pmlan_adapter); + handle->main_state = MOAL_END_MAIN_PROCESS; + LEAVE(); + return ret; +} + +/** + * @brief This function adds the card. it will probe the + * card, allocate the mlan_private and initialize the device. + * + * @param card A pointer to card + * + * @return A pointer to moal_handle structure + */ +moal_handle * +woal_add_card(void *card) +{ + moal_handle *handle = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + int netlink_num = NETLINK_MARVELL; + int index = 0; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) + struct netlink_kernel_cfg cfg = { + .groups = NL_MULTICAST_GROUP, + }; +#endif + + ENTER(); + + if (MOAL_ACQ_SEMAPHORE_BLOCK(&AddRemoveCardSem)) + goto exit_sem_err; + + /* Allocate buffer for moal_handle */ + handle = kzalloc(sizeof(moal_handle), GFP_KERNEL); + if (!handle) { + PRINTM(MERROR, "Allocate buffer for moal_handle failed!\n"); + goto err_handle; + } + + /* Init moal_handle */ + handle->card = card; + /* Save the handle */ + for (index = 0; index < MAX_MLAN_ADAPTER; index++) { + if (m_handle[index] == NULL) + break; + } + if (index < MAX_MLAN_ADAPTER) { + m_handle[index] = handle; + handle->handle_idx = index; + } else { + PRINTM(MERROR, "Exceeded maximum cards supported!\n"); + goto err_kmalloc; + } + + if (mac_addr +#ifdef MFG_CMD_SUPPORT + && mfg_mode != MLAN_INIT_PARA_ENABLED +#endif + ) { + t_u8 temp[20]; + t_u8 len = strlen(mac_addr) + 1; + if (len < sizeof(temp)) { + memcpy(temp, mac_addr, len); + handle->set_mac_addr = 1; + /* note: the following function overwrites the temp buffer */ + woal_mac2u8(handle->mac_addr, temp); + } + } + + handle->histogram_table_num = 1; + + ((struct sdio_mmc_card *)card)->handle = handle; +#ifdef SPI_SUPPORT + ((struct woal_spi_card *)card)->handle = handle; +#endif + +#ifdef STA_SUPPORT + handle->scan_pending_on_block = MFALSE; + MOAL_INIT_SEMAPHORE(&handle->async_sem); +#endif + + /* Init SW */ + if (MLAN_STATUS_SUCCESS != woal_init_sw(handle)) { + PRINTM(MFATAL, "Software Init Failed\n"); + goto err_kmalloc; + } + + do { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 14) + handle->nl_sk = netlink_kernel_create(netlink_num, NULL); +#else +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) + handle->nl_sk = + netlink_kernel_create(netlink_num, NL_MULTICAST_GROUP, + NULL, THIS_MODULE); +#else +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24) + handle->nl_sk = + netlink_kernel_create(netlink_num, NL_MULTICAST_GROUP, + NULL, NULL, THIS_MODULE); +#else +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0) + handle->nl_sk = + netlink_kernel_create(&init_net, netlink_num, + NL_MULTICAST_GROUP, NULL, NULL, + THIS_MODULE); +#else +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 7, 0) + handle->nl_sk = + netlink_kernel_create(&init_net, netlink_num, + THIS_MODULE, &cfg); +#else + handle->nl_sk = + netlink_kernel_create(&init_net, netlink_num, &cfg); +#endif +#endif +#endif +#endif +#endif + if (handle->nl_sk) { + PRINTM(MINFO, "Netlink number = %d\n", netlink_num); + handle->netlink_num = netlink_num; + break; + } + netlink_num--; + } while (netlink_num > 0); + + if (handle->nl_sk == NULL) { + PRINTM(MERROR, + "Could not initialize netlink event passing mechanism!\n"); + goto err_kmalloc; + } + + /* Create workqueue for main process */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 14) + /* For kernel less than 2.6.14 name can not be + * greater than 10 characters */ + handle->workqueue = create_workqueue("MOAL_WORKQ"); +#else +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) + handle->workqueue = + alloc_workqueue("MOAL_WORK_QUEUE", + WQ_HIGHPRI | WQ_MEM_RECLAIM | WQ_UNBOUND, 1); +#else + handle->workqueue = create_workqueue("MOAL_WORK_QUEUE"); +#endif +#endif + if (!handle->workqueue) + goto err_kmalloc; + + MLAN_INIT_WORK(&handle->main_work, woal_main_work_queue); + + if (!napi) { + /* Create workqueue for rx process */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 14) + /* For kernel less than 2.6.14 name can not be + * greater than 10 characters */ + handle->rx_workqueue = create_workqueue("MOAL_RX_WORKQ"); +#else +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) + handle->rx_workqueue = + alloc_workqueue("MOAL_RX_WORK_QUEUE", + WQ_HIGHPRI | WQ_MEM_RECLAIM | + WQ_UNBOUND, 1); +#else + handle->rx_workqueue = create_workqueue("MOAL_RX_WORK_QUEUE"); +#endif +#endif + if (!handle->rx_workqueue) { + woal_terminate_workqueue(handle); + goto err_kmalloc; + } + MLAN_INIT_WORK(&handle->rx_work, woal_rx_work_queue); + } +#define NAPI_BUDGET 64 + if (napi) { + init_dummy_netdev(&handle->napi_dev); + netif_napi_add(&handle->napi_dev, &handle->napi_rx, + woal_netdev_poll_rx, NAPI_BUDGET); + napi_enable(&handle->napi_rx); + } + +#ifdef REASSOCIATION + PRINTM(MINFO, "Starting re-association thread...\n"); + handle->reassoc_thread.handle = handle; + woal_create_thread(woal_reassociation_thread, + &handle->reassoc_thread, "woal_reassoc_service"); + + while (!handle->reassoc_thread.pid) + woal_sched_timeout(2); +#endif /* REASSOCIATION */ + + /* Register the device. Fill up the private data structure with + * relevant information from the card and request for the required + * IRQ. + */ + if (woal_register_dev(handle) != MLAN_STATUS_SUCCESS) { + PRINTM(MFATAL, "Failed to register wlan device!\n"); + goto err_registerdev; + } +#ifdef ANDROID_KERNEL +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) + wakeup_source_init(&handle->ws, "mwlan"); +#else + wake_lock_init(&handle->wake_lock, WAKE_LOCK_SUSPEND, "mwlan"); +#endif +#endif + + /* Init FW and HW */ + if (MLAN_STATUS_SUCCESS != woal_init_fw(handle)) { + PRINTM(MFATAL, "Firmware Init Failed\n"); + goto err_init_fw; + } + + LEAVE(); + return handle; + +err_init_fw: + if ((handle->hardware_status == HardwareStatusFwReady) || + (handle->hardware_status == HardwareStatusReady)) { + PRINTM(MINFO, "shutdown mlan\n"); + handle->init_wait_q_woken = MFALSE; + status = mlan_shutdown_fw(handle->pmlan_adapter); + if (status == MLAN_STATUS_PENDING) + wait_event_interruptible(handle->init_wait_q, + handle->init_wait_q_woken); + } +#ifdef ANDROID_KERNEL +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) + wakeup_source_trash(&handle->ws); +#else + wake_lock_destroy(&handle->wake_lock); +#endif +#endif + /* Unregister device */ + PRINTM(MINFO, "unregister device\n"); + woal_unregister_dev(handle); +err_registerdev: + handle->surprise_removed = MTRUE; +#ifdef REASSOCIATION + if (handle->reassoc_thread.pid) + wake_up_interruptible(&handle->reassoc_thread.wait_q); + /* waiting for main thread quit */ + while (handle->reassoc_thread.pid) + woal_sched_timeout(2); +#endif /* REASSOCIATION */ + if (napi) + netif_napi_del(&handle->napi_rx); + woal_terminate_workqueue(handle); +err_kmalloc: + woal_free_moal_handle(handle); + if (index < MAX_MLAN_ADAPTER) + m_handle[index] = NULL; + ((struct sdio_mmc_card *)card)->handle = NULL; +err_handle: + MOAL_REL_SEMAPHORE(&AddRemoveCardSem); +exit_sem_err: + LEAVE(); + return NULL; +} + +/** + * @brief This function removes the card. + * + * @param card A pointer to card + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +woal_remove_card(void *card) +{ + moal_handle *handle = NULL; + moal_private *priv = NULL; + mlan_status status; + int i; + int index = 0; + + ENTER(); + + if (MOAL_ACQ_SEMAPHORE_BLOCK(&AddRemoveCardSem)) + goto exit_sem_err; + /* Find the correct handle */ + for (index = 0; index < MAX_MLAN_ADAPTER; index++) { + if (m_handle[index] && (m_handle[index]->card == card)) { + handle = m_handle[index]; + break; + } + } + if (!handle) + goto exit_remove; + handle->surprise_removed = MTRUE; + + flush_workqueue(handle->workqueue); + if (handle->rx_workqueue) + flush_workqueue(handle->rx_workqueue); + + if (napi) { + napi_disable(&handle->napi_rx); + netif_napi_del(&handle->napi_rx); + } + + /* Stop data */ + for (i = 0; i < MIN(handle->priv_num, MLAN_MAX_BSS_NUM); i++) { + priv = handle->priv[i]; + if (priv) { + woal_stop_queue(priv->netdev); + if (netif_carrier_ok(priv->netdev)) + netif_carrier_off(priv->netdev); + } + } + if ((handle->hardware_status == HardwareStatusFwReady) || + (handle->hardware_status == HardwareStatusReady)) { + /* Shutdown firmware */ + PRINTM(MIOCTL, "mlan_shutdown_fw.....\n"); + handle->init_wait_q_woken = MFALSE; + + status = mlan_shutdown_fw(handle->pmlan_adapter); + if (status == MLAN_STATUS_PENDING) + wait_event_interruptible(handle->init_wait_q, + handle->init_wait_q_woken); + PRINTM(MIOCTL, "mlan_shutdown_fw done!\n"); + } + if (atomic_read(&handle->rx_pending) || atomic_read(&handle->tx_pending) + || atomic_read(&handle->ioctl_pending)) { + PRINTM(MERROR, + "ERR: rx_pending=%d,tx_pending=%d,ioctl_pending=%d\n", + atomic_read(&handle->rx_pending), + atomic_read(&handle->tx_pending), + atomic_read(&handle->ioctl_pending)); + } + unregister_inetaddr_notifier(&handle->woal_notifier); + +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + if (handle->is_remain_timer_set) { + woal_cancel_timer(&handle->remain_timer); + woal_remain_timer_func(handle); + } +#endif +#endif +#if defined(WIFI_DIRECT_SUPPORT) +#if defined(STA_CFG80211) && defined(UAP_CFG80211) + if (handle->is_go_timer_set) { + woal_cancel_timer(&handle->go_timer); + handle->is_go_timer_set = MFALSE; + } +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION + /* Remove virtual interface */ + woal_remove_virtual_interface(handle); +#endif +#endif +#endif + /* Remove interface */ + for (i = 0; i < MIN(MLAN_MAX_BSS_NUM, handle->priv_num); i++) + woal_remove_interface(handle, i); + + woal_terminate_workqueue(handle); + +#ifdef UAP_CFG80211 +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) + if (handle->is_cac_timer_set) { + woal_cancel_timer(&handle->cac_timer); + handle->is_cac_timer_set = MFALSE; + } +#endif +#endif +#ifdef REASSOCIATION + PRINTM(MINFO, "Free reassoc_timer\n"); + if (handle->is_reassoc_timer_set) { + woal_cancel_timer(&handle->reassoc_timer); + handle->is_reassoc_timer_set = MFALSE; + } + if (handle->reassoc_thread.pid) + wake_up_interruptible(&handle->reassoc_thread.wait_q); + + /* waiting for main thread quit */ + while (handle->reassoc_thread.pid) + woal_sched_timeout(2); +#endif /* REASSOCIATION */ +#ifdef CONFIG_PROC_FS + woal_proc_exit(handle); +#endif + /* Unregister device */ + PRINTM(MINFO, "unregister device\n"); + woal_unregister_dev(handle); +#ifdef ANDROID_KERNEL +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) + wakeup_source_trash(&handle->ws); +#else + wake_lock_destroy(&handle->wake_lock); +#endif +#endif + /* Free adapter structure */ + PRINTM(MINFO, "Free Adapter\n"); + woal_free_moal_handle(handle); + + for (index = 0; index < MAX_MLAN_ADAPTER; index++) { + if (m_handle[index] == handle) { + m_handle[index] = NULL; + break; + } + } +exit_remove: + MOAL_REL_SEMAPHORE(&AddRemoveCardSem); +exit_sem_err: + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +#ifdef CONFIG_PROC_FS +/** + * @brief This function switch the drv_mode + * + * @param handle A pointer to moal_handle structure + * @param mode new drv_mode to switch. + * + * @return MLAN_STATUS_SUCCESS /MLAN_STATUS_FAILURE /MLAN_STATUS_PENDING + */ +mlan_status +woal_switch_drv_mode(moal_handle *handle, t_u32 mode) +{ + unsigned int i; + mlan_status status = MLAN_STATUS_SUCCESS; + moal_private *priv = NULL; + + ENTER(); + + if (MOAL_ACQ_SEMAPHORE_BLOCK(&AddRemoveCardSem)) + goto exit_sem_err; + + if (woal_update_drv_tbl(handle, mode) != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "Could not update driver mode table!\n"); + status = MLAN_STATUS_FAILURE; + goto exit; + } + + /* Reset all interfaces */ + priv = woal_get_priv(handle, MLAN_BSS_ROLE_ANY); + woal_reset_intf(priv, MOAL_IOCTL_WAIT, MTRUE); + + status = woal_shutdown_fw(priv, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "func shutdown failed!\n"); + goto exit; + } + + /* Shutdown firmware */ + PRINTM(MIOCTL, "mlan_shutdown_fw.....\n"); + handle->init_wait_q_woken = MFALSE; + status = mlan_shutdown_fw(handle->pmlan_adapter); + if (status == MLAN_STATUS_PENDING) + wait_event_interruptible(handle->init_wait_q, + handle->init_wait_q_woken); + PRINTM(MIOCTL, "mlan_shutdown_fw done!\n"); + if (atomic_read(&handle->rx_pending) || atomic_read(&handle->tx_pending) + || atomic_read(&handle->ioctl_pending)) { + PRINTM(MERROR, + "ERR: rx_pending=%d,tx_pending=%d,ioctl_pending=%d\n", + atomic_read(&handle->rx_pending), + atomic_read(&handle->tx_pending), + atomic_read(&handle->ioctl_pending)); + } + + unregister_inetaddr_notifier(&handle->woal_notifier); + + /* Remove interface */ + for (i = 0; i < MIN(MLAN_MAX_BSS_NUM, handle->priv_num); i++) + woal_remove_interface(handle, i); + + /* Unregister mlan */ + if (handle->pmlan_adapter) { + mlan_unregister(handle->pmlan_adapter); + if (atomic_read(&handle->lock_count) || + atomic_read(&handle->malloc_count) || + atomic_read(&handle->mbufalloc_count)) { + PRINTM(MERROR, + "mlan has memory leak: lock_count=%d, malloc_count=%d, mbufalloc_count=%d\n", + atomic_read(&handle->lock_count), + atomic_read(&handle->malloc_count), + atomic_read(&handle->mbufalloc_count)); + } + handle->pmlan_adapter = NULL; + } + + handle->priv_num = 0; + drv_mode = mode; + /* Init SW */ + if (woal_init_sw(handle)) { + PRINTM(MFATAL, "Software Init Failed\n"); + goto exit; + } + /* Init FW and HW */ + if (woal_init_fw(handle)) { + PRINTM(MFATAL, "Firmware Init Failed\n"); + goto exit; + } + LEAVE(); + return status; +exit: + MOAL_REL_SEMAPHORE(&AddRemoveCardSem); +exit_sem_err: + LEAVE(); + return status; +} +#endif + +#define FW_POLL_TRIES 100 +#define FW_RESET_REG 0x0EE +#define FW_RESET_VAL 0x99 + +/** + * @brief This function reload fw + * + * @param handle A pointer to moal_handle structure + * + * @return 0--success, otherwise failure + */ +static int +woal_reset_and_reload_fw(moal_handle *handle) +{ + int ret = 0, tries = 0; + t_u32 value = 1; + t_u32 reset_reg = FW_RESET_REG; + t_u8 reset_val = FW_RESET_VAL; + + ENTER(); + mlan_pm_wakeup_card(handle->pmlan_adapter); + + /** wait SOC fully wake up */ + for (tries = 0; tries < FW_POLL_TRIES; ++tries) { + ret = woal_write_reg(handle, reset_reg, 0xba); + if (ret == MLAN_STATUS_SUCCESS) { + woal_read_reg(handle, reset_reg, &value); + if (value == 0xba) { + PRINTM(MMSG, "FW wake up\n"); + break; + } + } + udelay(1000); + } + /* Write register to notify FW */ + if (woal_write_reg(handle, reset_reg, reset_val) != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "Failed to write register.\n"); + ret = -EFAULT; + goto done; + } + /* Poll register around 100 ms */ + for (tries = 0; tries < FW_POLL_TRIES; ++tries) { + woal_read_reg(handle, reset_reg, &value); + if (value == 0) + /* FW is ready */ + break; + udelay(1000); + } + + if (value) { + PRINTM(MERROR, "Failed to poll FW reset register %X=0x%x\n", + reset_reg, value); + ret = -EFAULT; + goto done; + } + /* Download FW */ + ret = woal_request_fw(handle); + if (ret) { + ret = -EFAULT; + goto done; + } + PRINTM(MMSG, "FW Reload successfully."); +done: + LEAVE(); + return ret; +} + +/** + * @brief This function reload fw + * + * @param handle A pointer to moal_handle structure + * + * @return 0--success, otherwise failure + */ +static int +woal_reload_fw(moal_handle *handle) +{ + int ret = 0; + ENTER(); + /* Download FW */ + ret = woal_request_fw(handle); + if (ret) { + ret = -EFAULT; + goto done; + } + PRINTM(MMSG, "FW Reload successfully."); +done: + LEAVE(); + return ret; +} + +/** + * @brief This function reload fw + * + * @param handle A pointer to moal_handle structure + * @param mode FW reload mode + * + * @return 0--success, otherwise failure + */ +void +woal_request_fw_reload(moal_handle *handle, t_u8 mode) +{ + int intf_num; + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *misc = NULL; +#if defined(WIFI_DIRECT_SUPPORT) +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) +#if defined(STA_WEXT) || defined(UAP_WEXT) + t_u8 bss_role = MLAN_BSS_ROLE_STA; +#endif +#endif +#endif /* WIFI_DIRECT_SUPPORT && V14_FEATURE */ + + ENTER(); + + /** start block IOCTL */ + handle->driver_state = MTRUE; + if (mode == FW_RELOAD_WITH_EMULATION) { + fw_reload = FW_RELOAD_WITH_EMULATION; + PRINTM(MMSG, "FW reload with re-emulation...\n"); + LEAVE(); + return; + } + + /** detach network interface */ + for (intf_num = 0; intf_num < handle->priv_num; intf_num++) { + woal_stop_queue(handle->priv[intf_num]->netdev); + netif_device_detach(handle->priv[intf_num]->netdev); + } + handle->fw_reload = MTRUE; + woal_update_firmware_name(handle); + if (mode == FW_RELOAD_NO_EMULATION) { + ret = woal_reload_fw(handle); + } else if (mode == FW_RELOAD_SDIO_INBAND_RESET) + ret = woal_reset_and_reload_fw(handle); + else + ret = -EFAULT; + if (ret) { + PRINTM(MERROR, "FW reload fail\n"); + goto done; + } + /** un-block IOCTL */ + handle->fw_reload = MFALSE; + handle->driver_state = MFALSE; + /* Restart the firmware */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req) { + misc = (mlan_ds_misc_cfg *)req->pbuf; + misc->sub_command = MLAN_OID_MISC_WARM_RESET; + misc->param.fw_reload = MTRUE; + req->req_id = MLAN_IOCTL_MISC_CFG; + req->action = MLAN_ACT_SET; + if (MLAN_STATUS_SUCCESS != + woal_request_ioctl(woal_get_priv(handle, MLAN_BSS_ROLE_ANY), + req, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + kfree(req); + goto done; + } + kfree(req); + } + handle->hardware_status = HardwareStatusReady; + /* Reset all interfaces */ + ret = woal_reset_intf(woal_get_priv(handle, MLAN_BSS_ROLE_ANY), + MOAL_IOCTL_WAIT, MTRUE); + /* Initialize private structures */ + for (intf_num = 0; intf_num < handle->priv_num; intf_num++) { + woal_init_priv(handle->priv[intf_num], MOAL_IOCTL_WAIT); +#if defined(WIFI_DIRECT_SUPPORT) +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) +#if defined(STA_WEXT) || defined(UAP_WEXT) + if ((handle->priv[intf_num]->bss_type == + MLAN_BSS_TYPE_WIFIDIRECT) && + (GET_BSS_ROLE(handle->priv[intf_num]) == + MLAN_BSS_ROLE_UAP)) { + if (MLAN_STATUS_SUCCESS != + woal_bss_role_cfg(handle->priv[intf_num], + MLAN_ACT_SET, MOAL_IOCTL_WAIT, + &bss_role)) { + ret = -EFAULT; + goto done; + } + } +#endif /* STA_WEXT || UAP_WEXT */ +#endif /* STA_SUPPORT && UAP_SUPPORT */ +#endif /* WIFI_DIRECT_SUPPORT && V14_FEATURE */ + } + + /* Enable interfaces */ + for (intf_num = 0; intf_num < handle->priv_num; intf_num++) { + netif_device_attach(handle->priv[intf_num]->netdev); + woal_start_queue(handle->priv[intf_num]->netdev); + } +done: + LEAVE(); + return; +} + +/** + * @brief This function initializes module. + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static int +woal_init_module(void) +{ + int ret = (int)MLAN_STATUS_SUCCESS; + int index = 0; + + ENTER(); + + PRINTM(MMSG, "wlan: Loading MWLAN driver\n"); + /* Init the wlan_private pointer array first */ + for (index = 0; index < MAX_MLAN_ADAPTER; index++) + m_handle[index] = NULL; + /* Init mutex */ + MOAL_INIT_SEMAPHORE(&AddRemoveCardSem); + +#ifdef CONFIG_OF + woal_init_from_dev_tree(); +#endif + + /* Create workqueue for hang process */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 14) + /* For kernel less than 2.6.14 name can not be greater than 10 + characters */ + hang_workqueue = create_workqueue("MOAL_HANG_WORKQ"); +#else +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) + hang_workqueue = alloc_workqueue("MOAL_HANG_WORK_QUEUE", + WQ_HIGHPRI | WQ_MEM_RECLAIM | + WQ_UNBOUND, 1); +#else + hang_workqueue = create_workqueue("MOAL_HANG_WORK_QUEUE"); +#endif +#endif + MLAN_INIT_WORK(&hang_work, woal_hang_work_queue); + + /* Register with bus */ + ret = woal_bus_register(); + if (ret == MLAN_STATUS_SUCCESS) + PRINTM(MMSG, "wlan: Driver loaded successfully\n"); + else + PRINTM(MMSG, "wlan: Driver loading failed\n"); + + LEAVE(); + return ret; +} + +/** + * @brief This function cleans module + * + * @return N/A + */ +static void +woal_cleanup_module(void) +{ + moal_handle *handle = NULL; + int index = 0; + int i; +#if defined(STA_SUPPORT) && defined(STA_CFG80211) + unsigned long flags; +#endif + + ENTER(); + + PRINTM(MMSG, "wlan: Unloading MWLAN driver\n"); + if (MOAL_ACQ_SEMAPHORE_BLOCK(&AddRemoveCardSem)) + goto exit_sem_err; + for (index = 0; index < MAX_MLAN_ADAPTER; index++) { + handle = m_handle[index]; + if (!handle) + continue; + if (!handle->priv_num) + goto exit; + if (MTRUE == woal_check_driver_status(handle)) + goto exit; + +#ifdef SDIO_SUSPEND_RESUME +#ifdef MMC_PM_KEEP_POWER + if (handle->is_suspended == MTRUE) { + woal_sdio_resume(& + (((struct sdio_mmc_card *)handle-> + card)->func)->dev); + } +#endif /* MMC_PM_KEEP_POWER */ +#endif /* SDIO_SUSPEND_RESUME */ + + /* Unregister all connected radiotap net devices */ + if (handle->mon_if) { +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + woal_set_net_monitor(handle->mon_if->priv, + MOAL_IOCTL_WAIT, MFALSE, 0, NULL); + if (handle->ioctl_timeout) { + woal_ioctl_timeout(handle); + goto exit; + } +#endif + netif_device_detach(handle->mon_if->mon_ndev); + if (handle->mon_if->mon_ndev->reg_state == + NETREG_REGISTERED) + unregister_netdev(handle->mon_if->mon_ndev); + handle->mon_if = NULL; + } + + for (i = 0; i < handle->priv_num; i++) { +#ifdef STA_SUPPORT + if (GET_BSS_ROLE(handle->priv[i]) == MLAN_BSS_ROLE_STA) { + if (handle->priv[i]->media_connected == MTRUE) { + woal_disconnect(handle->priv[i], + MOAL_IOCTL_WAIT_TIMEOUT, + NULL, + DEF_DEAUTH_REASON_CODE); + if (handle->ioctl_timeout) { + woal_ioctl_timeout(handle); + goto exit; + } + } +#ifdef STA_CFG80211 + if (IS_STA_CFG80211(cfg80211_wext) && + (handle->priv[i]->bss_type == + MLAN_BSS_TYPE_STA)) + woal_clear_conn_params(handle->priv[i]); + spin_lock_irqsave(&handle->scan_req_lock, + flags); + if (IS_STA_CFG80211(cfg80211_wext) && + handle->scan_request) { + woal_cfg80211_scan_done(handle-> + scan_request, + MTRUE); + handle->scan_request = NULL; + handle->scan_priv = NULL; + } + spin_unlock_irqrestore(&handle->scan_req_lock, + flags); +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) + if (IS_STA_CFG80211(cfg80211_wext) && + handle->priv[i]->sched_scanning) { + woal_stop_bg_scan(handle->priv[i], + MOAL_IOCTL_WAIT_TIMEOUT); + if (handle->ioctl_timeout) { + woal_ioctl_timeout(handle); + goto exit; + } + handle->priv[i]->bg_scan_start = MFALSE; + handle->priv[i]->bg_scan_reported = + MFALSE; + cfg80211_sched_scan_stopped(handle-> + priv[i]-> + wdev->wiphy +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) + , 0 +#endif + ); + handle->priv[i]->sched_scanning = + MFALSE; + } +#endif +#endif + } +#endif +#ifdef UAP_SUPPORT + if (GET_BSS_ROLE(handle->priv[i]) == MLAN_BSS_ROLE_UAP) { +#ifdef MFG_CMD_SUPPORT + if (mfg_mode != MLAN_INIT_PARA_ENABLED) +#endif + woal_disconnect(handle->priv[i], + MOAL_IOCTL_WAIT_TIMEOUT, + NULL, + DEF_DEAUTH_REASON_CODE); + if (handle->ioctl_timeout) { + woal_ioctl_timeout(handle); + goto exit; + } + } +#endif +#if defined(STA_CFG80211) || defined(UAP_CFG80211) + woal_clear_all_mgmt_ies(handle->priv[i], + MOAL_IOCTL_WAIT_TIMEOUT); + if (handle->ioctl_timeout) { + woal_ioctl_timeout(handle); + goto exit; + } + woal_flush_tx_stat_queue(handle->priv[i]); +#endif + + } + +#ifdef MFG_CMD_SUPPORT + if (mfg_mode != MLAN_INIT_PARA_ENABLED) +#endif + woal_set_deep_sleep(woal_get_priv + (handle, MLAN_BSS_ROLE_ANY), + MOAL_IOCTL_WAIT_TIMEOUT, MFALSE, 0); + +#ifdef MFG_CMD_SUPPORT + if (mfg_mode != MLAN_INIT_PARA_ENABLED) +#endif + woal_shutdown_fw(woal_get_priv + (handle, MLAN_BSS_ROLE_ANY), + MOAL_IOCTL_WAIT_TIMEOUT); + if (handle->ioctl_timeout) { + woal_ioctl_timeout(handle); + goto exit; + } + } + +exit: + MOAL_REL_SEMAPHORE(&AddRemoveCardSem); +exit_sem_err: + /* Unregister from bus */ + woal_bus_unregister(); + PRINTM(MMSG, "wlan: Driver unloaded\n"); + if (hang_workqueue) { + flush_workqueue(hang_workqueue); + destroy_workqueue(hang_workqueue); + hang_workqueue = NULL; + } + + LEAVE(); +} + +#ifndef MODULE +#ifdef MFG_CMD_SUPPORT +/** + * @brief This function handle the mfg_mode from kernel boot command + * + * @param str buffer for mfg_mode + * @return N/A + */ +static int __init +mfg_mode_setup(char *str) +{ + int val = -1; + get_option(&str, &val); + if (val > 0) + mfg_mode = 1; + PRINTM(MMSG, "mfg_mode=%d\n", mfg_mode); + return 1; +} + +__setup("mfg_mode=", mfg_mode_setup); +#endif +#endif + +module_init(woal_init_module); +module_exit(woal_cleanup_module); + +module_param(hw_test, int, 0660); +MODULE_PARM_DESC(hw_test, "0: Disable hardware test; 1: Enable hardware test"); +#ifdef CONFIG_OF +module_param(dts_enable, int, 0); +MODULE_PARM_DESC(dts_enable, "0: Disable DTS; 1: Enable DTS"); +#endif +module_param(fw_name, charp, 0660); +MODULE_PARM_DESC(fw_name, "Firmware name"); +module_param(req_fw_nowait, int, 0); +MODULE_PARM_DESC(req_fw_nowait, + "0: Use request_firmware API; 1: Use request_firmware_nowait API"); +module_param(fw_reload, int, 0); +MODULE_PARM_DESC(fw_reload, + "0: disable fw_reload; 1: enable fw reload feature"); +module_param(fw_serial, int, 0); +MODULE_PARM_DESC(fw_serial, + "0: support parallel download FW; 1: support serial download FW"); +module_param(fw_region, int, 0); +MODULE_PARM_DESC(fw_region, "1: create channel regulatory domain from FW"); +module_param(mac_addr, charp, 0660); +MODULE_PARM_DESC(mac_addr, "MAC address"); +#ifdef MFG_CMD_SUPPORT +module_param(mfg_mode, int, 0660); +MODULE_PARM_DESC(mfg_mode, + "0: Download normal firmware; 1: Download MFG firmware"); +#endif /* MFG_CMD_SUPPORT */ +module_param(drv_mode, int, 0660); +#if defined(WIFI_DIRECT_SUPPORT) +MODULE_PARM_DESC(drv_mode, + "Bit 0: STA; Bit 1: uAP; Bit 2: WIFIDIRECT; Bit 4: NAN"); +#else +MODULE_PARM_DESC(drv_mode, "Bit 0: STA; Bit 1: uAP; Bit 4: NAN"); +#endif /* WIFI_DIRECT_SUPPORT && V14_FEATURE */ +#ifdef STA_SUPPORT +module_param(max_sta_bss, int, 0); +MODULE_PARM_DESC(max_sta_bss, "Number of STA interfaces (1)"); +module_param(sta_name, charp, 0); +MODULE_PARM_DESC(sta_name, "STA interface name"); +#endif /* STA_SUPPORT */ +#ifdef UAP_SUPPORT +module_param(max_uap_bss, int, 0); +MODULE_PARM_DESC(max_uap_bss, "Number of uAP interfaces (1)"); +module_param(uap_name, charp, 0); +MODULE_PARM_DESC(uap_name, "uAP interface name"); +#endif /* UAP_SUPPORT */ +#if defined(WIFI_DIRECT_SUPPORT) +module_param(max_wfd_bss, int, 0); +MODULE_PARM_DESC(max_wfd_bss, "Number of WIFIDIRECT interfaces (1)"); +module_param(wfd_name, charp, 0); +MODULE_PARM_DESC(wfd_name, "WIFIDIRECT interface name"); +#if defined(STA_CFG80211) && defined(UAP_CFG80211) +module_param(max_vir_bss, int, 0); +MODULE_PARM_DESC(max_vir_bss, "Number of Virtual interfaces (0)"); +#endif +#endif /* WIFI_DIRECT_SUPPORT && V14_FEATURE */ +module_param(nan_name, charp, 0); +MODULE_PARM_DESC(nan_name, "NAN interface name"); +module_param(max_nan_bss, int, 0); +MODULE_PARM_DESC(max_nan_bss, "Number of NAN interfaces (1)"); +#ifdef DEBUG_LEVEL1 +module_param(drvdbg, uint, 0660); +MODULE_PARM_DESC(drvdbg, "Driver debug"); +#endif /* DEBUG_LEVEL1 */ +module_param(auto_ds, int, 0660); +MODULE_PARM_DESC(auto_ds, + "0: MLAN default; 1: Enable auto deep sleep; 2: Disable auto deep sleep"); +module_param(ps_mode, int, 0660); +MODULE_PARM_DESC(ps_mode, + "0: MLAN default; 1: Enable IEEE PS mode; 2: Disable IEEE PS mode"); +module_param(max_tx_buf, int, 0); +MODULE_PARM_DESC(max_tx_buf, "Maximum Tx buffer size (2048/4096/8192)"); + +module_param(intmode, int, 0); +MODULE_PARM_DESC(intmode, "0: INT_MODE_SDIO, 1: INT_MODE_GPIO"); +module_param(gpiopin, int, 0); +MODULE_PARM_DESC(gpiopin, "255:new GPIO int mode, other vlue: gpio pin number"); + +#ifdef SDIO_SUSPEND_RESUME +module_param(pm_keep_power, int, 0); +MODULE_PARM_DESC(pm_keep_power, "1: PM keep power; 0: PM no power"); +module_param(shutdown_hs, int, 0); +MODULE_PARM_DESC(shutdown_hs, + "1: Enable HS when shutdown; 0: No HS when shutdown"); +#endif +#if defined(STA_SUPPORT) +module_param(cfg_11d, int, 0); +MODULE_PARM_DESC(cfg_11d, + "0: MLAN default; 1: Enable 802.11d; 2: Disable 802.11d"); +#endif +module_param(dpd_data_cfg, charp, 0); +MODULE_PARM_DESC(dpd_data_cfg, "DPD data file name"); +module_param(init_cfg, charp, 0); +MODULE_PARM_DESC(init_cfg, "Init config file name"); +module_param(cal_data_cfg, charp, 0); +MODULE_PARM_DESC(cal_data_cfg, "Calibration data file name"); +module_param(txpwrlimit_cfg, charp, 0); +MODULE_PARM_DESC(txpwrlimit_cfg, + "Set configuration data of Tx power limitation"); +module_param(cntry_txpwr, int, 0); +MODULE_PARM_DESC(cntry_txpwr, + "Allow setting tx power table of country; 0: disable (default), 1: enable."); +module_param(init_hostcmd_cfg, charp, 0); +MODULE_PARM_DESC(init_hostcmd_cfg, "Init hostcmd file name"); +module_param(cfg80211_wext, int, 0660); +MODULE_PARM_DESC(cfg80211_wext, +#ifdef STA_WEXT + "Bit 0: STA WEXT; " +#endif +#ifdef UAP_WEXT + "Bit 1: UAP WEXT; " +#endif +#ifdef STA_CFG80211 + "Bit 2: STA CFG80211; " +#endif +#ifdef UAP_CFG80211 + "Bit 3: UAP CFG80211;" +#endif + ); +module_param(wq_sched_prio, int, 0); +module_param(wq_sched_policy, int, 0); +MODULE_PARM_DESC(wq_sched_prio, "Priority of work queue"); +MODULE_PARM_DESC(wq_sched_policy, + "0: SCHED_NORMAL; 1: SCHED_FIFO; 2: SCHED_RR; 3: SCHED_BATCH; 5: SCHED_IDLE"); +module_param(rx_work, int, 0); +MODULE_PARM_DESC(rx_work, + "0: default; 1: Enable rx_work_queue; 2: Disable rx_work_queue"); +#if defined(WIFI_DIRECT_SUPPORT) +#if defined(STA_CFG80211) && defined(UAP_CFG80211) +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION +module_param(p2p_enh, int, 0); +MODULE_PARM_DESC(p2p_enh, "1: Enable enhanced P2P; 0: Disable enhanced P2P"); +#endif +#endif +#endif + +#ifdef ANDROID_KERNEL +module_param(wakelock_timeout, int, 0); +MODULE_PARM_DESC(wakelock_timeout, "set wakelock_timeout value (ms)"); +#endif + +module_param(dev_cap_mask, uint, 0); +MODULE_PARM_DESC(dev_cap_mask, "Device capability mask"); + +module_param(sdio_rx_aggr, int, 0); +MODULE_PARM_DESC(sdio_rx_aggr, + "1: Enable SDIO rx aggr; 0: Disable SDIO rx aggr"); + +module_param(pmic, int, 0); +MODULE_PARM_DESC(pmic, + "1: Send pmic configure cmd to firmware; 0: No pmic configure cmd sent to firmware"); + +module_param(uap_oper_ctrl, uint, 0); +MODULE_PARM_DESC(uap_oper_ctrl, "0:default; 0x20001:uap restarts on channel 6"); + +module_param(hs_wake_interval, int, 0660); +MODULE_PARM_DESC(hs_wake_interval, + "Host sleep wakeup interval,it will round to nearest multiple dtim*beacon_period in fw"); +module_param(indication_gpio, int, 0); +MODULE_PARM_DESC(indication_gpio, + "GPIO to indicate wakeup source; high four bits: level for normal wakeup; low four bits: GPIO pin number."); + +module_param(indrstcfg, int, 0); +MODULE_PARM_DESC(indrstcfg, + "Independent reset configuration; high byte: GPIO pin number; low byte: IR mode"); + +module_param(fixed_beacon_buffer, int, 0); +MODULE_PARM_DESC(fixed_beacon_buffer, + "0: allocate default buffer size; 1: allocate max buffer size."); + +#ifdef WIFI_DIRECT_SUPPORT +module_param(GoAgeoutTime, int, 0); +MODULE_PARM_DESC(GoAgeoutTime, + "0: use default ageout time; set Go age out time (TU 100ms)"); +#endif + +module_param(gtk_rekey_offload, int, 0); +MODULE_PARM_DESC(gtk_rekey_offload, + "0: disable gtk_rekey_offload; 1: enable gtk_rekey_offload (default); 2: enable gtk_rekey_offload in suspend mode only;"); + +module_param(multi_dtim, int, 0); +MODULE_PARM_DESC(multi_dtim, "DTIM interval"); + +module_param(inact_tmo, int, 0); +MODULE_PARM_DESC(inact_tmo, "IEEE ps inactivity timout value"); + +module_param(napi, int, 0); +MODULE_PARM_DESC(napi, "1: enable napi api; 0: disable napi"); + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) +module_param(dfs_offload, int, 0); +MODULE_PARM_DESC(dfs_offload, "1: enable dfs offload; 0: disable dfs offload."); +#endif + +module_param(drcs_chantime_mode, int, 0); +MODULE_PARM_DESC(drcs_chantime_mode, + "0: use default value;Bit31~Bit24:Channel time for channel index0;Bit23~Bit16:mode for channel index0;Bit15~Bit8:Channel time for channel index1;Bit7~Bit0:mode for channel index1; mode:0--PM1,1--Null2Self."); + +module_param(roamoffload_in_hs, int, 0); +MODULE_PARM_DESC(roamoffload_in_hs, + "1: enable fw roaming only when host suspend; 0: always enable fw roaming."); + +MODULE_DESCRIPTION("M-WLAN Driver"); +MODULE_AUTHOR("Marvell International Ltd."); +MODULE_VERSION(MLAN_RELEASE_VERSION); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_main.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_main.h new file mode 100644 index 000000000000..9d10a993b7b0 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_main.h @@ -0,0 +1,2748 @@ +/** @file moal_main.h + * + * @brief This file contains wlan driver specific defines etc. + * + * Copyright (C) 2008-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +/******************************************************** +Change log: + 10/21/2008: initial version +********************************************************/ + +#ifndef _MOAL_MAIN_H +#define _MOAL_MAIN_H + +/* warnfix for FS redefination if any? */ +#ifdef FS +#undef FS +#endif + +/* Linux header files */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 1, 0) +#include +#include +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18) +#include +#endif + +/* ASM files */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) +#include +#else +#include +#endif +#include +#include +#include +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) +#include +#else +#include +#endif + +#include + +/* Net header files */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifdef ANDROID_KERNEL +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) +#include +#include +#else +#include +#endif +#endif + +#include + +#include "mlan.h" +#include "moal_shim.h" +/* Wireless header */ +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#include +#include +#include +#endif +#if defined(STA_WEXT) || defined(UAP_WEXT) +#include +#include +#include "moal_wext.h" +#endif +#ifdef STA_WEXT +#include "moal_priv.h" +#endif + +#ifndef MIN +/** Find minimum */ +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#endif + +/** Find maximum */ +#ifndef MAX +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#endif + +#define COMPAT_VERSION_CODE KERNEL_VERSION( 0, 0, 0) +#define CFG80211_VERSION_CODE MAX(LINUX_VERSION_CODE, COMPAT_VERSION_CODE) + +/** + * Reason Code 3: STA is leaving (or has left) IBSS or ESS + */ +#define DEF_DEAUTH_REASON_CODE (0x3) + +/** + * 802.1 Local Experimental 1. + */ + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 24) +#define REFDATA __refdata +#else +#define REFDATA +#endif + +/** + * Linux Kernels later 3.9 use CONFIG_PM_RUNTIME instead of + * CONFIG_USB_SUSPEND + * Linux Kernels later 3.19 use CONFIG_PM instead of + * CONFIG_PM_RUNTIME + */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) +#ifdef CONFIG_PM +#ifndef CONFIG_USB_SUSPEND +#define CONFIG_USB_SUSPEND +#endif +#ifndef CONFIG_PM_RUNTIME +#define CONFIG_PM_RUNTIME +#endif +#endif +#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) */ +#ifdef CONFIG_PM_RUNTIME +#ifndef CONFIG_USB_SUSPEND +#define CONFIG_USB_SUSPEND +#endif +#endif +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) */ +#endif + +/** + * Linux kernel later 3.10 use strncasecmp instead of strnicmp + */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) +#define strnicmp strncasecmp +#endif + +/** + * Linux kernel later 4.7 use nl80211_band instead of ieee80211_band + * Linux kernel later 4.7 use new macro + */ +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 7, 0) +#define ieee80211_band nl80211_band +#define IEEE80211_BAND_2GHZ NL80211_BAND_2GHZ +#define IEEE80211_BAND_5GHZ NL80211_BAND_5GHZ +#define IEEE80211_NUM_BANDS NUM_NL80211_BANDS +#endif + +/** +* interface name +*/ +#define default_mlan_name "wlan%%d" +#define default_uap_name "uap%%d" +#define default_wfd_name "p2p%%d" +#define default_nan_name "nan%%d" +#define default_mpl_name "mpl%d" +#define default_11p_name "ocb%d" +#define mwiphy_name "mwiphy%d" + +#ifdef OPENWRT +#ifdef mwiphy_name +#undef mwiphy_name +#define mwiphy_name "phy%d" +#endif +#ifdef default_mlan_name +#undef default_mlan_name +#define default_mlan_name "wlan%%d" +#endif +#endif + +/** + * define write_can_lock() to fix compile issue on ACTIA platform + */ +#if !defined(write_can_lock) && defined(CONFIG_PREEMPT_RT_FULL) +#define write_can_lock(X) 1 +#endif + +#define MLAN_ACTION_FRAME_CATEGORY_OFFSET (40) +#define MLAN_ACTION_FRAME_ACTION_OFFSET (41) + +/** Define BOOLEAN */ +typedef t_u8 BOOLEAN; + +/** Driver version */ +extern char driver_version[]; + +/** Private structure for MOAL */ +typedef struct _moal_private moal_private; +/** Handle data structure for MOAL */ +typedef struct _moal_handle moal_handle; + +/** Hardware status codes */ +typedef enum _MOAL_HARDWARE_STATUS { + HardwareStatusReady, + HardwareStatusInitializing, + HardwareStatusFwReady, + HardwareStatusReset, + HardwareStatusClosing, + HardwareStatusNotReady +} MOAL_HARDWARE_STATUS; + +/** fw cap info 11p */ +#define FW_CAPINFO_80211P MBIT(24) +/** fw cap info BGA */ +#define FW_CAPINFO_80211BGA (MBIT(8)|MBIT(9)|MBIT(10)) + +/** moal_wait_option */ +enum { + MOAL_NO_WAIT, + MOAL_IOCTL_WAIT, + MOAL_IOCTL_WAIT_TIMEOUT +}; + +/** moal_main_state */ +enum { + MOAL_STATE_IDLE, + MOAL_RECV_INT, + MOAL_ENTER_WORK_QUEUE, + MOAL_START_MAIN_PROCESS, + MOAL_END_MAIN_PROCESS +}; + +/** HostCmd_Header */ +typedef struct _HostCmd_Header { + /** Command */ + t_u16 command; + /** Size */ + t_u16 size; +} HostCmd_Header; + +/* + * OS timer specific + */ + +/** Timer structure */ +typedef struct _moal_drv_timer { + /** Timer list */ + struct timer_list tl; + /** Timer function */ + void (*timer_function) (void *context); + /** Timer function context */ + void *function_context; + /** Time period */ + t_u32 time_period; + /** Is timer periodic ? */ + t_u32 timer_is_periodic; + /** Is timer cancelled ? */ + t_u32 timer_is_canceled; +} moal_drv_timer, *pmoal_drv_timer; + +typedef struct { + t_u8 dialog_token; + t_u8 follow_up_dialog_token; + t_u32 t1; + t_u32 t4; + t_u8 t1_err; + t_u8 t4_err; +} __attribute__ ((packed)) moal_wnm_tm_msmt; + +/** wlan_802_11_header */ +typedef struct { + /** Frame Control */ + t_u16 frm_ctl; + /** Duration ID */ + t_u16 duration_id; + /** Address1 */ + mlan_802_11_mac_addr addr1; + /** Address2 */ + mlan_802_11_mac_addr addr2; + /** Address3 */ + mlan_802_11_mac_addr addr3; + /** Sequence Control */ + t_u16 seq_ctl; + /** Address4 */ + mlan_802_11_mac_addr addr4; +} __attribute__ ((packed)) moal_wlan_802_11_header; + +typedef struct { + /** t2 time */ + t_u32 t2; + /** t2 error */ + t_u8 t2_err; + /** t3 time */ + t_u32 t3; + /** t3 error */ + t_u8 t3_err; + /** ingress time */ + t_u64 ingress_time; +} __attribute__ ((packed)) moal_timestamps; + +typedef struct { + t_u8 vendor_specific; + t_u8 length; + t_u8 data[0]; +} __attribute__ ((packed)) moal_ptp_context; + +/** + * @brief Timer handler + * + * @param fcontext Timer context + * + * @return N/A + */ +static inline void +woal_timer_handler(unsigned long fcontext) +{ + pmoal_drv_timer timer = (pmoal_drv_timer)fcontext; + + timer->timer_function(timer->function_context); + + if (timer->timer_is_periodic == MTRUE) { + mod_timer(&timer->tl, + jiffies + ((timer->time_period * HZ) / 1000)); + } else { + timer->timer_is_canceled = MTRUE; + timer->time_period = 0; + } +} + +/** + * @brief Initialize timer + * + * @param timer Timer structure + * @param TimerFunction Timer function + * @param FunctionContext Timer function context + * + * @return N/A + */ +static inline void +woal_initialize_timer(pmoal_drv_timer timer, + void (*TimerFunction) (void *context), + void *FunctionContext) +{ + /* First, setup the timer to trigger the wlan_timer_handler proxy */ + init_timer(&timer->tl); + timer->tl.function = woal_timer_handler; + timer->tl.data = (t_ptr)timer; + + /* Then tell the proxy which function to call and what to pass it */ + timer->timer_function = TimerFunction; + timer->function_context = FunctionContext; + timer->timer_is_canceled = MTRUE; + timer->time_period = 0; + timer->timer_is_periodic = MFALSE; +} + +/** + * @brief Modify timer + * + * @param timer Timer structure + * @param millisecondperiod Time period in millisecond + * + * @return N/A + */ +static inline void +woal_mod_timer(pmoal_drv_timer timer, t_u32 millisecondperiod) +{ + timer->time_period = millisecondperiod; + mod_timer(&timer->tl, jiffies + (millisecondperiod * HZ) / 1000); + timer->timer_is_canceled = MFALSE; +} + +/** + * @brief Cancel timer + * + * @param timer Timer structure + * + * @return N/A + */ +static inline void +woal_cancel_timer(moal_drv_timer *timer) +{ + if (timer->timer_is_periodic || in_atomic() || irqs_disabled()) + del_timer(&timer->tl); + else + del_timer_sync(&timer->tl); + timer->timer_is_canceled = MTRUE; + timer->time_period = 0; +} + +#ifdef REASSOCIATION +/* + * OS Thread Specific + */ + +#include + +/** Kernel thread structure */ +typedef struct _moal_thread { + /** Task control structrue */ + struct task_struct *task; + /** Pointer to wait_queue_head */ + wait_queue_head_t wait_q; + /** PID */ + pid_t pid; + /** Pointer to moal_handle */ + void *handle; +} moal_thread; + +/** + * @brief Activate thread + * + * @param thr Thread structure + * @return N/A + */ +static inline void +woal_activate_thread(moal_thread *thr) +{ + /** Initialize the wait queue */ + init_waitqueue_head(&thr->wait_q); + + /** Record the thread pid */ + thr->pid = current->pid; +} + +/** + * @brief De-activate thread + * + * @param thr Thread structure + * @return N/A + */ +static inline void +woal_deactivate_thread(moal_thread *thr) +{ + /* Reset the pid */ + thr->pid = 0; +} + +/** + * @brief Create and run the thread + * + * @param threadfunc Thread function + * @param thr Thread structure + * @param name Thread name + * @return N/A + */ +static inline void +woal_create_thread(int (*threadfunc) (void *), moal_thread *thr, char *name) +{ + /* Create and run the thread */ + thr->task = kthread_run(threadfunc, thr, "%s", name); +} +#endif /* REASSOCIATION */ + +/* The following macros are neccessary to retain compatibility + * around the workqueue chenges happened in kernels >= 2.6.20: + * - INIT_WORK changed to take 2 arguments and let the work function + * get its own data through the container_of macro + * - delayed works have been split from normal works to save some + * memory usage in struct work_struct + */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) +/** Work_queue work initialization */ +#define MLAN_INIT_WORK(_work, _fun) INIT_WORK(_work, ((void (*)(void *))_fun), _work) +/** Work_queue delayed work initialization */ +#define MLAN_INIT_DELAYED_WORK(_work, _fun) INIT_WORK(_work, ((void (*)(void *))_fun), _work) +/** Work_queue container parameter */ +#define MLAN_DELAYED_CONTAINER_OF(_ptr, _type, _m) container_of(_ptr, _type, _m) +#else /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) */ +/** Work_queue work initialization */ +#define MLAN_INIT_WORK INIT_WORK +/** Work_queue delayed work initialization */ +#define MLAN_INIT_DELAYED_WORK INIT_DELAYED_WORK +/** Work_queue container parameter */ +#define MLAN_DELAYED_CONTAINER_OF(_ptr, _type, _m) container_of(_ptr, _type, _m.work) +#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) */ + +/** + * @brief Schedule timeout + * + * @param millisec Timeout duration in milli second + * + * @return N/A + */ +static inline void +woal_sched_timeout(t_u32 millisec) +{ + set_current_state(TASK_INTERRUPTIBLE); + + schedule_timeout((millisec * HZ) / 1000); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) +#define IN6PTON_XDIGIT 0x00010000 +#define IN6PTON_DIGIT 0x00020000 +#define IN6PTON_COLON_MASK 0x00700000 +#define IN6PTON_COLON_1 0x00100000 /* single : requested */ +#define IN6PTON_COLON_2 0x00200000 /* second : requested */ +#define IN6PTON_COLON_1_2 0x00400000 /* :: requested */ +#define IN6PTON_DOT 0x00800000 /* . */ +#define IN6PTON_DELIM 0x10000000 +#define IN6PTON_NULL 0x20000000 /* first/tail */ +#define IN6PTON_UNKNOWN 0x40000000 + +static inline int +xdigit2bin(char c, int delim) +{ + if (c == delim || c == '\0') + return IN6PTON_DELIM; + if (c == ':') + return IN6PTON_COLON_MASK; + if (c == '.') + return IN6PTON_DOT; + if (c >= '0' && c <= '9') + return IN6PTON_XDIGIT | IN6PTON_DIGIT | (c - '0'); + if (c >= 'a' && c <= 'f') + return IN6PTON_XDIGIT | (c - 'a' + 10); + if (c >= 'A' && c <= 'F') + return IN6PTON_XDIGIT | (c - 'A' + 10); + if (delim == -1) + return IN6PTON_DELIM; + return IN6PTON_UNKNOWN; +} + +static inline int +in4_pton(const char *src, int srclen, u8 *dst, int delim, const char **end) +{ + const char *s; + u8 *d; + u8 dbuf[4]; + int ret = 0; + int i; + int w = 0; + + if (srclen < 0) + srclen = strlen(src); + s = src; + d = dbuf; + i = 0; + while (1) { + int c; + c = xdigit2bin(srclen > 0 ? *s : '\0', delim); + if (! + (c & + (IN6PTON_DIGIT | IN6PTON_DOT | IN6PTON_DELIM | + IN6PTON_COLON_MASK))) { + goto out; + } + if (c & (IN6PTON_DOT | IN6PTON_DELIM | IN6PTON_COLON_MASK)) { + if (w == 0) + goto out; + *d++ = w & 0xff; + w = 0; + i++; + if (c & (IN6PTON_DELIM | IN6PTON_COLON_MASK)) { + if (i != 4) + goto out; + break; + } + goto cont; + } + w = (w * 10) + c; + if ((w & 0xffff) > 255) + goto out; +cont: + if (i >= 4) + goto out; + s++; + srclen--; + } + ret = 1; + memcpy(dst, dbuf, sizeof(dbuf)); +out: + if (end) + *end = s; + return ret; +} +#endif /* < 2.6.19 */ + +#ifndef __ATTRIB_ALIGN__ +#define __ATTRIB_ALIGN__ __attribute__((aligned(4))) +#endif + +#ifndef __ATTRIB_PACK__ +#define __ATTRIB_PACK__ __attribute__ ((packed)) +#endif + +/** Get module */ +#define MODULE_GET try_module_get(THIS_MODULE) +/** Put module */ +#define MODULE_PUT module_put(THIS_MODULE) + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37) +/** Initialize semaphore */ +#define MOAL_INIT_SEMAPHORE(x) init_MUTEX(x) +/** Initialize semaphore */ +#define MOAL_INIT_SEMAPHORE_LOCKED(x) init_MUTEX_LOCKED(x) +#else +/** Initialize semaphore */ +#define MOAL_INIT_SEMAPHORE(x) sema_init(x, 1) +/** Initialize semaphore */ +#define MOAL_INIT_SEMAPHORE_LOCKED(x) sema_init(x, 0) +#endif + +/** Acquire semaphore and with blocking */ +#define MOAL_ACQ_SEMAPHORE_BLOCK(x) down_interruptible(x) +/** Acquire semaphore without blocking */ +#define MOAL_ACQ_SEMAPHORE_NOBLOCK(x) down_trylock(x) +/** Release semaphore */ +#define MOAL_REL_SEMAPHORE(x) up(x) + +/** Request FW timeout in second */ +#define REQUEST_FW_TIMEOUT 30 + +#if defined(SYSKT) +/** Max loop count (* 100ms) for waiting device ready at init time */ +#define MAX_WAIT_DEVICE_READY_COUNT 50 +#endif + +/** Default watchdog timeout */ +#define MRVDRV_DEFAULT_WATCHDOG_TIMEOUT (10 * HZ) + +#ifdef UAP_SUPPORT +/** Default watchdog timeout + Increase the value to avoid kernel Tx timeout message in case + station in PS mode or left. + The default value of PS station ageout timer is 40 seconds. + Hence, the watchdog timer is set to a value higher than it. +*/ +#define MRVDRV_DEFAULT_UAP_WATCHDOG_TIMEOUT (41 * HZ) +#endif + +/* IOCTL Timeout */ +#define MOAL_IOCTL_TIMEOUT (20 * HZ) + +#ifdef ANDROID_KERNEL +/** Wake lock timeout in msec */ +#define WAKE_LOCK_TIMEOUT 3000 +/** Roaming Wake lock timeout in msec */ +#define ROAMING_WAKE_LOCK_TIMEOUT 10000 +#endif + +/** Threshold value of number of times the Tx timeout happened */ +#define NUM_TX_TIMEOUT_THRESHOLD 5 + +/** Custom event : DRIVER HANG */ +#define CUS_EVT_DRIVER_HANG "EVENT=DRIVER_HANG" + +/** TDLS connected event */ +#define CUS_EVT_TDLS_CONNECTED "EVENT=TDLS_CONNECTED" +/** TDLS tear down event */ +#define CUS_EVT_TDLS_TEARDOWN "EVENT=TDLS_TEARDOWN" +/** wmm info */ +#define WMM_TYPE_INFO 0 +/** wmm parameter */ +#define WMM_TYPE_PARAMETER 1 + +/** AP connected event */ +#define CUS_EVT_AP_CONNECTED "EVENT=AP_CONNECTED" + +/** Custom event : BW changed */ +#define CUS_EVT_BW_CHANGED "EVENT=BW_CHANGED" +/** Custom event : OBSS scan parameter */ +#define CUS_EVT_OBSS_SCAN_PARAM "EVENT=OBSS_SCAN_PARAM" + +/** Custom event : AdHoc link sensed */ +#define CUS_EVT_ADHOC_LINK_SENSED "EVENT=ADHOC_LINK_SENSED" +/** Custom event : AdHoc link lost */ +#define CUS_EVT_ADHOC_LINK_LOST "EVENT=ADHOC_LINK_LOST" +/** Custom event : MIC failure, unicast */ +#define CUS_EVT_MLME_MIC_ERR_UNI "MLME-MICHAELMICFAILURE.indication unicast" +/** Custom event : MIC failure, multicast */ +#define CUS_EVT_MLME_MIC_ERR_MUL "MLME-MICHAELMICFAILURE.indication multicast" +/** Custom event : Beacon RSSI low */ +#define CUS_EVT_BEACON_RSSI_LOW "EVENT=BEACON_RSSI_LOW" +/** Custom event : Beacon SNR low */ +#define CUS_EVT_BEACON_SNR_LOW "EVENT=BEACON_SNR_LOW" +/** Custom event : Beacon RSSI high */ +#define CUS_EVT_BEACON_RSSI_HIGH "EVENT=BEACON_RSSI_HIGH" +/** Custom event : Beacon SNR high */ +#define CUS_EVT_BEACON_SNR_HIGH "EVENT=BEACON_SNR_HIGH" +/** Custom event : Max fail */ +#define CUS_EVT_MAX_FAIL "EVENT=MAX_FAIL" +/** Custom event : Data RSSI low */ +#define CUS_EVT_DATA_RSSI_LOW "EVENT=DATA_RSSI_LOW" +/** Custom event : Data SNR low */ +#define CUS_EVT_DATA_SNR_LOW "EVENT=DATA_SNR_LOW" +/** Custom event : Data RSSI high */ +#define CUS_EVT_DATA_RSSI_HIGH "EVENT=DATA_RSSI_HIGH" +/** Custom event : Data SNR high */ +#define CUS_EVT_DATA_SNR_HIGH "EVENT=DATA_SNR_HIGH" +/** Custom event : Link Quality */ +#define CUS_EVT_LINK_QUALITY "EVENT=LINK_QUALITY" +/** Custom event : Port Release */ +#define CUS_EVT_PORT_RELEASE "EVENT=PORT_RELEASE" +/** Custom event : Pre-Beacon Lost */ +#define CUS_EVT_PRE_BEACON_LOST "EVENT=PRE_BEACON_LOST" + +/** Custom event : Deep Sleep awake */ +#define CUS_EVT_DEEP_SLEEP_AWAKE "EVENT=DS_AWAKE" + +#define CUS_EVT_GET_CORRELATED_TIME "EVENT=CORRELATED-TIME" +#define CUS_EVT_TIMING_MSMT_CONFIRM "EVENT=TIMING-MSMT-CONFIRM" +#define CUS_EVT_TM_FRAME_INDICATION "EVENT=TIMING-MSMT-FRAME" + +/** Custom event : Host Sleep activated */ +#define CUS_EVT_HS_ACTIVATED "HS_ACTIVATED" +/** Custom event : Host Sleep deactivated */ +#define CUS_EVT_HS_DEACTIVATED "HS_DEACTIVATED" +/** Custom event : Host Sleep wakeup */ +#define CUS_EVT_HS_WAKEUP "HS_WAKEUP" + +/** Wakeup Reason */ +typedef enum { + NO_HSWAKEUP_REASON = 0, //0.unknown + BCAST_DATA_MATCHED, // 1. Broadcast data matched + MCAST_DATA_MATCHED, // 2. Multicast data matched + UCAST_DATA_MATCHED, // 3. Unicast data matched + MASKTABLE_EVENT_MATCHED, // 4. Maskable event matched + NON_MASKABLE_EVENT_MATCHED, // 5. Non-maskable event matched + NON_MASKABLE_CONDITION_MATCHED, // 6. Non-maskable condition matched (EAPoL rekey) + MAGIC_PATTERN_MATCHED, // 7. Magic pattern matched + CONTROL_FRAME_MATCHED, // 8. Control frame matched + MANAGEMENT_FRAME_MATCHED, // 9. Management frame matched + GTK_REKEY_FAILURE, //10. GTK rekey failure + RESERVED // Others: reserved +} HSWakeupReason_t; + +/** Custom event : WEP ICV error */ +#define CUS_EVT_WEP_ICV_ERR "EVENT=WEP_ICV_ERR" + +/** Custom event : Channel Switch Announcment */ +#define CUS_EVT_CHANNEL_SWITCH_ANN "EVENT=CHANNEL_SWITCH_ANN" + +/** Custom indiciation message sent to the application layer for WMM changes */ +#define WMM_CONFIG_CHANGE_INDICATION "WMM_CONFIG_CHANGE.indication" + +#ifdef UAP_SUPPORT +/** Custom event : STA connected */ +#define CUS_EVT_STA_CONNECTED "EVENT=STA_CONNECTED" +/** Custom event : STA disconnected */ +#define CUS_EVT_STA_DISCONNECTED "EVENT=STA_DISCONNECTED" +#endif + +/** 10 seconds */ +#define MOAL_TIMER_10S 10000 +/** 5 seconds */ +#define MOAL_TIMER_5S 5000 +/** 1 second */ +#define MOAL_TIMER_1S 1000 +/** 1 milisecond */ +#define MOAL_TIMER_1MS 1 + +/** passive scan time */ +#define PASSIVE_SCAN_CHAN_TIME 110 +/** active scan time */ +#define ACTIVE_SCAN_CHAN_TIME 110 +/** specific scan time */ +#define SPECIFIC_SCAN_CHAN_TIME 110 +/** passive scan time */ +#define INIT_PASSIVE_SCAN_CHAN_TIME 80 +/** active scan time */ +#define INIT_ACTIVE_SCAN_CHAN_TIME 80 +/** specific scan time */ +#define INIT_SPECIFIC_SCAN_CHAN_TIME 80 +/** specific scan time after connected */ +#define MIN_SPECIFIC_SCAN_CHAN_TIME 40 + +/** Default value of re-assoc timer */ +#define REASSOC_TIMER_DEFAULT 500 + +/** Netlink protocol number */ +#define NETLINK_MARVELL (MAX_LINKS - 1) +/** Netlink maximum payload size */ +#define NL_MAX_PAYLOAD 1024 +/** Netlink multicast group number */ +#define NL_MULTICAST_GROUP 1 + +#define MAX_RX_PENDING_THRHLD 50 + +/** high rx pending packets */ +#define HIGH_RX_PENDING 100 +/** low rx pending packets */ +#define LOW_RX_PENDING 80 + +/** MAX Tx Pending count */ +#define MAX_TX_PENDING 100 + +/** LOW Tx Pending count */ +#define LOW_TX_PENDING 80 + +/** Offset for subcommand */ +#define SUBCMD_OFFSET 4 + +/** default scan channel gap */ +#define DEF_SCAN_CHAN_GAP 50 +/** default scan time per channel in miracast mode */ +#define DEF_MIRACAST_SCAN_TIME 20 + +/** Macro to extract the TOS field from a skb */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) +#define SKB_TOS(skb) (ip_hdr(skb)->tos) +#else +#define SKB_TOS(skb) (skb->nh.iph->tos) +#endif +#define SKB_TIDV6(skb) (ipv6_get_dsfield(ipv6_hdr(skb))) +#define IS_SKB_MAGIC_VLAN(skb) (skb->priority >= 256 && skb->priority <= 263) +#define GET_VLAN_PRIO(skb) (skb->priority - 256) + +/** Offset for TOS field in the IP header */ +#define IPTOS_OFFSET 5 + +/** Offset for DSCP in the tos field */ +#define DSCP_OFFSET 2 + +/** max retry count for wait_event_interupptible_xx while loop */ +#define MAX_RETRY_CNT 100 +/** wait_queue structure */ +typedef struct _wait_queue { + /** wait_queue_head */ + wait_queue_head_t wait; + /** Wait condition */ + BOOLEAN condition; + /** Start time */ + long start_time; + /** Status from MLAN */ + mlan_status status; + /** flag for wait_timeout */ + t_u8 wait_timeout; + /** retry count */ + t_u8 retry; +} wait_queue, *pwait_queue; + +/** Auto Rate */ +#define AUTO_RATE 0xFF + +#define STA_WEXT_MASK MBIT(0) +#define UAP_WEXT_MASK MBIT(1) +#define STA_CFG80211_MASK MBIT(2) +#define UAP_CFG80211_MASK MBIT(3) +#ifdef STA_CFG80211 +#ifdef STA_SUPPORT +/** Is STA CFG80211 enabled in module param */ +#define IS_STA_CFG80211(x) (x & STA_CFG80211_MASK) +#endif +#endif +#ifdef UAP_CFG80211 +#ifdef UAP_SUPPORT +/** Is UAP CFG80211 enabled in module param */ +#define IS_UAP_CFG80211(x) (x & UAP_CFG80211_MASK) +#endif +#endif +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +/** Is UAP or STA CFG80211 enabled in module param */ +#define IS_STA_OR_UAP_CFG80211(x) (x & (STA_CFG80211_MASK | UAP_CFG80211_MASK)) +#endif + +#ifdef STA_WEXT +/** Is STA WEXT enabled in module param */ +#define IS_STA_WEXT(x) (x & STA_WEXT_MASK) +#endif /* STA_WEXT */ +#ifdef UAP_WEXT +/** Is UAP WEXT enabled in module param */ +#define IS_UAP_WEXT(x) (x & UAP_WEXT_MASK) +#endif /* UAP_WEXT */ +#if defined(STA_WEXT) || defined(UAP_WEXT) +/** Is UAP or STA WEXT enabled in module param */ +#define IS_STA_OR_UAP_WEXT(x) (x & (STA_WEXT_MASK | UAP_WEXT_MASK)) +#endif + +#ifdef STA_SUPPORT +/** Driver mode STA bit */ +#define DRV_MODE_STA MBIT(0) +/** Maximum STA BSS */ +#define MAX_STA_BSS 1 +/** Default STA BSS */ +#define DEF_STA_BSS 1 +#endif +#ifdef UAP_SUPPORT +/** Driver mode uAP bit */ +#define DRV_MODE_UAP MBIT(1) +/** Maximum uAP BSS */ +#define MAX_UAP_BSS 2 +/** Default uAP BSS */ +#define DEF_UAP_BSS 1 +#endif +#if defined(WIFI_DIRECT_SUPPORT) +/** Driver mode WIFIDIRECT bit */ +#define DRV_MODE_WIFIDIRECT MBIT(2) +/** Maximum WIFIDIRECT BSS */ +#define MAX_WIFIDIRECT_BSS 2 +/** Default WIFIDIRECT BSS */ +#define DEF_WIFIDIRECT_BSS 1 +#if defined(STA_CFG80211) && defined(UAP_CFG80211) +#define DEF_VIRTUAL_BSS 0 +#endif +#endif /* WIFI_DIRECT_SUPPORT && V14_FEATURE */ +/** Driver mode NAN bit */ +#define DRV_MODE_NAN MBIT(4) +/** Maximum NAN BSS */ +#define MAX_NAN_BSS 1 +/** Default NAN BSS */ +#define DEF_NAN_BSS 1 + +#define DRV_MODE_WLAN (MBIT(0)|MBIT(1)|MBIT(2)|MBIT(3)|MBIT(4)) + +/** + * the maximum number of adapter supported + **/ +#define MAX_MLAN_ADAPTER 2 + +typedef struct _moal_drv_mode { + /** driver mode */ + t_u16 drv_mode; + /** total number of interfaces */ + t_u16 intf_num; + /** attribute of bss */ + mlan_bss_attr *bss_attr; + /** name of firmware image */ + char *fw_name; +} moal_drv_mode; + +#ifdef PROC_DEBUG +/** Debug data */ +struct debug_data { + /** Name */ + char name[32]; + /** Size */ + t_u32 size; + /** Address */ + t_ptr addr; +}; + +/** Private debug data */ +struct debug_data_priv { + /** moal_private handle */ + moal_private *priv; + /** Debug items */ + struct debug_data *items; + /** numbre of item */ + int num_of_items; +}; +#endif + +/** Maximum IP address buffer length */ +#define IPADDR_MAX_BUF 20 +/** IP address operation: Remove */ +#define IPADDR_OP_REMOVE 0 + +#define DROP_TCP_ACK 1 +#define HOLD_TCP_ACK 2 +struct tcp_sess { + struct list_head link; + /** tcp session info */ + t_u32 src_ip_addr; + t_u32 dst_ip_addr; + t_u16 src_tcp_port; + t_u16 dst_tcp_port; + /** tx ack packet info */ + t_u32 ack_seq; + /** tcp ack buffer */ + void *ack_skb; + /** priv structure */ + void *priv; + /** pmbuf */ + void *pmbuf; + /** timer for ack */ + moal_drv_timer ack_timer __ATTRIB_ALIGN__; + /** timer is set */ + BOOLEAN is_timer_set; +}; + +struct tx_status_info { + struct list_head link; + /** cookie */ + t_u64 tx_cookie; + /** seq_num */ + t_u8 tx_seq_num; + /** skb */ + void *tx_skb; +}; + +#define MAX_NUM_ETHER_TYPE 8 +typedef struct { + /** number of protocols in protocol array*/ + t_u8 protocol_num; + /** protocols supported */ + t_u16 protocols[MAX_NUM_ETHER_TYPE]; +} __ATTRIB_PACK__ dot11_protocol; +typedef struct { + /** Data rate in unit of 0.5Mbps */ + t_u16 datarate; + /** Channel number to transmit the frame */ + t_u8 channel; + /** Bandwidth to transmit the frame */ + t_u8 bw; + /** Power to be used for transmission */ + t_u8 power; + /** Priority of the packet to be transmitted */ + t_u8 priority; + /** retry time of tx transmission*/ + t_u8 retry_limit; + /** Reserved fields*/ + t_u8 reserved[1]; +} __ATTRIB_PACK__ dot11_txcontrol; + +typedef struct { + /** Data rate of received paccket*/ + t_u16 datarate; + /** Channel on which packet was received*/ + t_u8 channel; + /** Rx antenna*/ + t_u8 antenna; + /** RSSI */ + t_u8 rssi; + /** Reserved */ + t_u8 reserved[3]; +} __ATTRIB_PACK__ dot11_rxcontrol; + +#define OKC_WAIT_TARGET_PMKSA_TIMEOUT (4 * HZ / 1000) +#define PMKID_LEN 16 +struct pmksa_entry { + struct list_head link; + u8 bssid[ETH_ALEN]; + u8 pmkid[PMKID_LEN]; +}; + +/** default rssi low threshold */ +#define TDLS_RSSI_LOW_THRESHOLD 55 +/** default rssi high threshold */ +#define TDLS_RSSI_HIGH_THRESHOLD 50 +/** TDLS idle time */ +#define TDLS_IDLE_TIME (10*HZ) +/** TDLS max failure count */ +#define TDLS_MAX_FAILURE_COUNT 4 +/** TDLS tear down reason */ +#define TDLS_TEARN_DOWN_REASON_UNSPECIFIC 26 + +/** TDLS status */ +typedef enum _tdlsStatus_e { + TDLS_NOT_SETUP = 0, + TDLS_SETUP_INPROGRESS, + TDLS_SETUP_COMPLETE, + TDLS_SETUP_FAILURE, + TDLS_TEAR_DOWN, + TDLS_SWITCHING_CHANNEL, + TDLS_IN_BASE_CHANNEL, + TDLS_IN_OFF_CHANNEL, +} tdlsStatus_e; + +/** tdls peer_info */ +struct tdls_peer { + struct list_head link; + /** MAC address information */ + t_u8 peer_addr[ETH_ALEN]; + /** rssi */ + int rssi; + /** jiffies with rssi */ + long rssi_jiffies; + /** link status */ + tdlsStatus_e link_status; + /** num of set up failure */ + t_u8 num_failure; +}; + +/** Number of samples in histogram (/proc/mwlan/mlan0/histogram).*/ +#define HIST_MAX_SAMPLES 1048576 +#define RX_RATE_MAX 76 + +/** SRN MAX */ +#define SNR_MAX 256 +/** NOISE FLR MAX */ +#define NOISE_FLR_MAX 256 +/** SIG STRENTGH MAX */ +#define SIG_STRENGTH_MAX 256 +/** historgram data */ +typedef struct _hgm_data { + /** snr */ + atomic_t snr[SNR_MAX]; + /** noise flr */ + atomic_t noise_flr[NOISE_FLR_MAX]; + /** sig_str */ + atomic_t sig_str[SIG_STRENGTH_MAX]; + /** num sample */ + atomic_t num_samples; + /** rx rate */ + atomic_t rx_rate[0]; +} hgm_data; + +/** max antenna number */ +#define MAX_ANTENNA_NUM 1 + +/* wlan_hist_proc_data */ +typedef struct _wlan_hist_proc_data { + /** antenna */ + u8 ant_idx; + /** Private structure */ + struct _moal_private *priv; +} wlan_hist_proc_data; + +/** Private structure for MOAL */ +struct _moal_private { + /** Handle structure */ + moal_handle *phandle; + /** Tx timeout count */ + t_u32 num_tx_timeout; + /** BSS index */ + t_u8 bss_index; + /** BSS type */ + t_u8 bss_type; + /** BSS role */ + t_u8 bss_role; + /** bss virtual flag */ + t_u8 bss_virtual; + /** MAC address information */ + t_u8 current_addr[ETH_ALEN]; + /** Media connection status */ + BOOLEAN media_connected; + /** Statistics of tcp ack tx dropped */ + t_u32 tcp_ack_drop_cnt; + /** Statistics of tcp ack tx in total from kernel */ + t_u32 tcp_ack_cnt; +#ifdef UAP_SUPPORT + /** uAP started or not */ + BOOLEAN bss_started; + /** host based uap flag */ + BOOLEAN uap_host_based; + /** uAP skip CAC*/ + BOOLEAN skip_cac; + /** tx block flag */ + BOOLEAN uap_tx_blocked; +#if defined(DFS_TESTING_SUPPORT) + /** user cac period */ + t_u32 user_cac_period_msec; + /** channel under nop */ + BOOLEAN chan_under_nop; +#endif +#ifdef UAP_CFG80211 +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + /** current working channel */ + struct cfg80211_chan_def chan; +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) + /** switch channel */ + struct cfg80211_chan_def csa_chan; + /** beacon after channel switch */ + struct cfg80211_beacon_data beacon_after; + /** CSA work queue */ + struct workqueue_struct *csa_workqueue; + /** csa work */ + struct delayed_work csa_work; +#endif +#endif +#endif + /** IP addr type */ + t_u32 ip_addr_type; + /** IP addr */ + t_u8 ip_addr[IPADDR_LEN]; +#ifdef STA_SUPPORT + /** scan type */ + t_u8 scan_type; + /** extended capabilities */ + ExtCap_t extended_capabilities; + /** bg_scan_start */ + t_u8 bg_scan_start; + /** bg_scan reported */ + t_u8 bg_scan_reported; + /** bg_scan config */ + wlan_bgscan_cfg scan_cfg; + /** sched scaning flag */ + t_u8 sched_scanning; +#ifdef STA_CFG80211 + /** roaming enabled flag */ + t_u8 roaming_enabled; + /** rssi low threshold */ + int rssi_low; + /** channel for connect */ + struct ieee80211_channel conn_chan; + /** bssid for connect */ + t_u8 conn_bssid[ETH_ALEN]; + /** ssid for connect */ + t_u8 conn_ssid[MLAN_MAX_SSID_LENGTH]; + /** key data */ + t_u8 conn_wep_key[MAX_WEP_KEY_SIZE]; + /** connection param */ + struct cfg80211_connect_params sme_current; + /** roaming required flag */ + t_u8 roaming_required; +#endif + t_u8 wait_target_ap_pmkid; + wait_queue_head_t okc_wait_q __ATTRIB_ALIGN__; + struct list_head pmksa_cache_list; + spinlock_t pmksa_list_lock; + struct pmksa_entry *target_ap_pmksa; + t_u8 okc_ie_len; + t_u8 *okc_roaming_ie; +#endif + /** Net device pointer */ + struct net_device *netdev; + /** Net device statistics structure */ + struct net_device_stats stats; +#if defined(STA_CFG80211) || defined(UAP_CFG80211) + /** Wireless device pointer */ + struct wireless_dev *wdev; + /** Wireless device */ + struct wireless_dev w_dev; + /** Net device pointer */ + struct net_device *pa_netdev; + /** channel parameter for UAP/GO */ + t_u16 channel; +#ifdef UAP_SUPPORT + /** wep key */ + wep_key uap_wep_key[4]; + /** cipher */ + t_u32 cipher; +#endif + /** pmk saved flag */ + t_u8 pmk_saved; + /** pmk */ + mlan_pmk_t pmk; + /** beacon ie index */ + t_u16 beacon_index; + /** proberesp ie index */ + t_u16 proberesp_index; + /** proberesp_p2p_index */ + t_u16 proberesp_p2p_index; + /** assocresp ie index */ + t_u16 assocresp_index; + /** assocresp qos map ie index */ + t_u16 assocresp_qos_map_index; + /** probereq index for mgmt ie */ + t_u16 probereq_index; + /** mgmt_subtype_mask */ + t_u32 mgmt_subtype_mask; + /** beacon wps index for mgmt ie */ + t_u16 beacon_wps_index; + /** beacon/proberesp vendor ie index */ + t_u16 proberesp_vendor_index; +#endif +#ifdef STA_CFG80211 +#ifdef STA_SUPPORT + /** CFG80211 association description */ + t_u8 cfg_bssid[ETH_ALEN]; + /** Disconnect request from CFG80211 */ + bool cfg_disconnect; + /** connect request from CFG80211 */ + bool cfg_connect; + /** lock for cfg connect */ + spinlock_t connect_lock; + /** assoc status */ + t_u32 assoc_status; + /** rssi_threshold */ + s32 cqm_rssi_thold; + /** rssi_high_threshold */ + s32 cqm_rssi_high_thold; + /** rssi hysteresis */ + u32 cqm_rssi_hyst; + /** last rssi_low */ + u8 last_rssi_low; + /** last rssi_high */ + u8 last_rssi_high; + /** mrvl rssi threshold */ + u8 mrvl_rssi_low; + /** last event */ + u32 last_event; + /** fake scan flag */ + u8 fake_scan_complete; + /**ft ie*/ + t_u8 ft_ie[MAX_IE_SIZE]; + /**ft ie len*/ + t_u8 ft_ie_len; + /**mobility domain value*/ + t_u16 ft_md; + /**ft capability*/ + t_u8 ft_cap; + /**set true during ft connection*/ + t_bool ft_pre_connect; + /**ft roaming triggered by driver or not*/ + t_bool ft_roaming_triggered_by_driver; + /**target ap mac address for Fast Transition*/ + t_u8 target_ap_bssid[ETH_ALEN]; + /** IOCTL wait queue for FT*/ + wait_queue_head_t ft_wait_q __ATTRIB_ALIGN__; + /** ft wait condition */ + t_bool ft_wait_condition; +#endif /* STA_SUPPORT */ +#endif /* STA_CFG80211 */ +#ifdef CONFIG_PROC_FS + /** Proc entry */ + struct proc_dir_entry *proc_entry; + /** Proc entry name */ + char proc_entry_name[IFNAMSIZ]; + /** proc entry for hist */ + struct proc_dir_entry *hist_entry; + /** ant_hist_proc_data */ + wlan_hist_proc_data hist_proc[MAX_ANTENNA_NUM]; +#endif /* CONFIG_PROC_FS */ +#ifdef STA_SUPPORT + /** Nickname */ + t_u8 nick_name[16]; + /** AdHoc link sensed flag */ + BOOLEAN is_adhoc_link_sensed; + /** Current WEP key index */ + t_u16 current_key_index; +#ifdef REASSOCIATION + mlan_ssid_bssid prev_ssid_bssid; + /** Re-association required */ + BOOLEAN reassoc_required; + /** Flag of re-association on/off */ + BOOLEAN reassoc_on; + /** Set asynced essid flag */ + BOOLEAN set_asynced_essid_flag; +#endif /* REASSOCIATION */ + /** Report scan result */ + t_u8 report_scan_result; + /** wpa_version */ + t_u8 wpa_version; + /** key mgmt */ + t_u8 key_mgmt; + /** rx_filter */ + t_u8 rx_filter; +#endif /* STA_SUPPORT */ + /** Rate index */ + t_u16 rate_index; +#if defined(STA_WEXT) || defined(UAP_WEXT) + /** IW statistics */ + struct iw_statistics w_stats; +#endif +#ifdef UAP_WEXT + /** Pairwise Cipher used for WPA/WPA2 mode */ + t_u16 pairwise_cipher; + /** Group Cipher */ + t_u16 group_cipher; + /** Protocol stored during uap wext configuratoin */ + t_u16 uap_protocol; + /** Key Mgmt whether PSK or 1x */ + t_u16 uap_key_mgmt; + /** Beacon IE length from hostapd */ + t_u16 bcn_ie_len; + /** Beacon IE buffer from hostapd */ + t_u8 bcn_ie_buf[MAX_IE_SIZE]; +#endif + + /** dscp mapping */ + t_u8 dscp_map[64]; +#ifdef PROC_DEBUG + /** MLAN debug info */ + struct debug_data_priv items_priv; +#endif + + /** tcp session queue */ + struct list_head tcp_sess_queue; + /** TCP Ack enhance flag */ + t_u8 enable_tcp_ack_enh; + /** TCP session spin lock */ + spinlock_t tcp_sess_lock; + /** tcp list */ + struct list_head tdls_list; + /** tdls spin lock */ + spinlock_t tdls_lock; + /** auto tdls flag */ + t_u8 enable_auto_tdls; + /** check tx packet for tdls peer */ + t_u8 tdls_check_tx; +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) + atomic_t wmm_tx_pending[4]; +#endif + /** per interface extra headroom */ + t_u16 extra_tx_head_len; + /** TX status spin lock */ + spinlock_t tx_stat_lock; + /** tx_seq_num */ + t_u8 tx_seq_num; + /** tx status queue */ + struct list_head tx_stat_queue; + + /** rx hgm data */ + hgm_data *hist_data[3]; + BOOLEAN assoc_with_mac; + t_u8 gtk_data_ready; + mlan_ds_misc_gtk_rekey_data gtk_rekey_data; + dot11_protocol tx_protocols; + dot11_protocol rx_protocols; +}; + +/** channel_field.flags */ +#define CHANNEL_FLAGS_TURBO 0x0010 +#define CHANNEL_FLAGS_CCK 0x0020 +#define CHANNEL_FLAGS_OFDM 0x0040 +#define CHANNEL_FLAGS_2GHZ 0x0080 +#define CHANNEL_FLAGS_5GHZ 0x0100 +#define CHANNEL_FLAGS_ONLY_PASSIVSCAN_ALLOW 0x0200 +#define CHANNEL_FLAGS_DYNAMIC_CCK_OFDM 0x0400 +#define CHANNEL_FLAGS_GFSK 0x0800 +struct channel_field { + t_u16 frequency; + t_u16 flags; +} __packed; + +/** mcs_field.known */ +#define MCS_KNOWN_BANDWIDTH 0x01 +#define MCS_KNOWN_MCS_INDEX_KNOWN 0x02 +#define MCS_KNOWN_GUARD_INTERVAL 0x04 +#define MCS_KNOWN_HT_FORMAT 0x08 +#define MCS_KNOWN_FEC_TYPE 0x10 +#define MCS_KNOWN_STBC_KNOWN 0x20 +#define MCS_KNOWN_NESS_KNOWN 0x40 +#define MCS_KNOWN_NESS_DATA 0x80 +/** bandwidth */ +#define RX_BW_20 0 +#define RX_BW_40 1 +#define RX_BW_20L 2 +#define RX_BW_20U 3 +/** mcs_field.flags +The flags field is any combination of the following: +0x03 bandwidth - 0: 20, 1: 40, 2: 20L, 3: 20U +0x04 guard interval - 0: long GI, 1: short GI +0x08 HT format - 0: mixed, 1: greenfield +0x10 FEC type - 0: BCC, 1: LDPC +0x60 Number of STBC streams +0x80 Ness - bit 0 (LSB) of Number of extension spatial streams */ +struct mcs_field { + t_u8 known; + t_u8 flags; + t_u8 mcs; +} __packed; + +/** radiotap_body.flags */ +#define RADIOTAP_FLAGS_DURING_CFG 0x01 +#define RADIOTAP_FLAGS_SHORT_PREAMBLE 0x02 +#define RADIOTAP_FLAGS_WEP_ENCRYPTION 0x04 +#define RADIOTAP_FLAGS_WITH_FRAGMENT 0x08 +#define RADIOTAP_FLAGS_INCLUDE_FCS 0x10 +#define RADIOTAP_FLAGS_PAD_BTW_HEADER_PAYLOAD 0x20 +#define RADIOTAP_FLAGS_FAILED_FCS_CHECK 0x40 +#define RADIOTAP_FLAGS_USE_SGI_HT 0x80 +struct radiotap_body { + t_u64 timestamp; + t_u8 flags; + t_u8 rate; + struct channel_field channel; + t_s8 antenna_signal; + t_s8 antenna_noise; + t_u8 antenna; + struct mcs_field mcs; +} __packed; + +struct radiotap_header { + struct ieee80211_radiotap_header hdr; + struct radiotap_body body; +} __packed; + +/** Roam offload config parameters */ +typedef struct woal_priv_fw_roam_offload_cfg { + /* User set passphrase */ + t_u8 userset_passphrase; + /* BSSID for fw roaming/auto_reconnect */ + t_u8 bssid[MLAN_MAC_ADDR_LENGTH]; + /* Retry_count for fw roaming/auto_reconnect */ + t_u8 retry_count; + /* Condition to trigger roaming + * Bit0 : RSSI low trigger + * Bit1 : Pre-beacon lost trigger + * Bit2 : Link Lost trigger + * Bit3 : Deauth by ext-AP trigger + * Bit4 ~ Bit15 : Reserved + * value 0 : no trigger + * value 0xff : invalid + */ + t_u16 trigger_condition; + /* SSID List(White list) */ + mlan_ds_misc_ssid_list ssid_list; + /* Black list(BSSID list) */ + mlan_ds_misc_roam_offload_aplist black_list; + + /* RSSI paramters set flag */ + t_u8 rssi_param_set_flag; + /* MAX_RSSI for fw roaming */ + t_u8 max_rssi; + /* MIN_RSSI for fw roaming */ + t_u8 min_rssi; + /* Step_RSSI for fw roaming */ + t_u8 step_rssi; + + /* BAND and RSSI_HYSTERESIS set flag */ + t_u8 band_rssi_flag; + mlan_ds_misc_band_rssi band_rssi; + + /* BGSCAN params set flag */ + t_u8 bgscan_set_flag; + mlan_ds_misc_bgscan_cfg bgscan_cfg; + + /* EES mode params set flag */ + t_u8 ees_param_set_flag; + mlan_ds_misc_ees_cfg ees_cfg; + + /* Beacon miss threshold */ + t_u8 bcn_miss_threshold; + + /* Beacon miss threshold */ + t_u8 pre_bcn_miss_threshold; + + /* scan repeat count */ + t_u16 repeat_count; +} woal_roam_offload_cfg; + +int woal_set_clear_pmk(moal_private *priv, t_u8 action); +mlan_status woal_config_fw_roaming(moal_private *priv, t_u8 cfg_mode, + woal_roam_offload_cfg * roam_offload_cfg); +int woal_enable_fw_roaming(moal_private *priv, int data); + +#define GTK_REKEY_OFFLOAD_DISABLE 0 +#define GTK_REKEY_OFFLOAD_ENABLE 1 +#define GTK_REKEY_OFFLOAD_SUSPEND 2 + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) +/** Monitor Band Channel Config */ +typedef struct _netmon_band_chan_cfg { + t_u32 band; + t_u32 channel; + t_u32 chan_bandwidth; +} netmon_band_chan_cfg; +#endif + +typedef struct _monitor_iface { + /* The priv data of interface on which the monitor iface is based */ + moal_private *priv; + struct wireless_dev wdev; + /** 0 - Disabled + * 1 - Channel Specified sniffer mode + * 2 - In-Channel sniffer mode + */ + int sniffer_mode; + int radiotap_enabled; + /* The net_device on which the monitor iface is based. */ + struct net_device *base_ndev; + struct net_device *mon_ndev; + char ifname[IFNAMSIZ]; + int flag; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + struct cfg80211_chan_def chandef; + /** Netmon Band Channel Config */ + netmon_band_chan_cfg band_chan_cfg; +#endif + /** Monitor device statistics structure */ + struct net_device_stats stats; +} monitor_iface; + +/** Handle data structure for MOAL */ +struct _moal_handle { + /** MLAN adapter structure */ + t_void *pmlan_adapter; + /** Private pointer */ + moal_private *priv[MLAN_MAX_BSS_NUM]; + /** Priv number */ + t_u8 priv_num; + /** Bss attr */ + moal_drv_mode drv_mode; + + /** Monitor interface */ + monitor_iface *mon_if; + + /** set mac address flag */ + t_u8 set_mac_addr; + /** MAC address */ + t_u8 mac_addr[ETH_ALEN]; +#ifdef CONFIG_PROC_FS + /** Proc top level directory entry */ + struct proc_dir_entry *proc_mwlan; +#endif + /** Firmware */ + const struct firmware *firmware; + /** Firmware request start time */ + struct timeval req_fw_time; + /** Init config file */ + const struct firmware *init_cfg_data; + /** Init config file */ + const struct firmware *user_data; + /** Init user configure wait queue token */ + t_u16 init_user_conf_wait_flag; + /** Init user configure file wait queue */ + wait_queue_head_t init_user_conf_wait_q __ATTRIB_ALIGN__; + /** dpd config file */ + const struct firmware *dpd_data; + /** txpwr data file */ + const struct firmware *txpwr_data; + /** Hotplug device */ + struct device *hotplug_device; + /** STATUS variables */ + MOAL_HARDWARE_STATUS hardware_status; + BOOLEAN fw_reload; + /** POWER MANAGEMENT AND PnP SUPPORT */ + BOOLEAN surprise_removed; + /** Firmware release number */ + t_u32 fw_release_number; + /** ECSA support */ + t_u8 fw_ecsa_enable; + /** FW ROAMING support */ + t_u8 fw_roam_enable; + /** FW ROAMING capability in fw */ + t_u8 fw_roaming_support; + /** Retry count for auto reconnect based on FW ROAMING*/ + t_u16 auto_reconnect_retry_count; + /** The SSID for auto reconnect FW ROAMING*/ + mlan_802_11_ssid auto_reconnect_ssid; + /** The BSSID for auto reconnect FW ROAMING*/ + mlan_802_11_mac_addr auto_reconnect_bssid; + /** The parameters for FW ROAMING*/ + woal_roam_offload_cfg fw_roam_params; + /** The keys for FW ROAMING*/ + mlan_ds_passphrase ssid_passphrase[MAX_SEC_SSID_NUM]; + + /** Getlog support */ + t_u8 fw_getlog_enable; + /** Init wait queue token */ + t_u16 init_wait_q_woken; + /** Init wait queue */ + wait_queue_head_t init_wait_q __ATTRIB_ALIGN__; +#if defined(SDIO_SUSPEND_RESUME) + /** Device suspend flag */ + BOOLEAN is_suspended; +#ifdef SDIO_SUSPEND_RESUME + /** suspend notify flag */ + BOOLEAN suspend_notify_req; +#endif + /** Host Sleep activated flag */ + t_u8 hs_activated; + /** Host Sleep activated event wait queue token */ + t_u16 hs_activate_wait_q_woken; + /** Host Sleep activated event wait queue */ + wait_queue_head_t hs_activate_wait_q __ATTRIB_ALIGN__; + /** auto_arp and ipv6 offload enable/disable flag */ + t_u8 hs_auto_arp; +#endif + /** Card pointer */ + t_void *card; + /** Rx pending in MLAN */ + atomic_t rx_pending; + /** Tx packet pending count in mlan */ + atomic_t tx_pending; + /** IOCTL pending count in mlan */ + atomic_t ioctl_pending; + /** lock count */ + atomic_t lock_count; + /** Malloc count */ + atomic_t malloc_count; + /** vmalloc count */ + atomic_t vmalloc_count; + /** mlan buffer alloc count */ + atomic_t mbufalloc_count; +#if defined(SDIO_SUSPEND_RESUME) + /** hs skip count */ + t_u32 hs_skip_count; + /** hs force count */ + t_u32 hs_force_count; + /** suspend_fail flag */ + BOOLEAN suspend_fail; +#endif +#ifdef REASSOCIATION + /** Re-association thread */ + moal_thread reassoc_thread; + /** Re-association timer set flag */ + BOOLEAN is_reassoc_timer_set; + /** Re-association timer */ + moal_drv_timer reassoc_timer __ATTRIB_ALIGN__; + /** */ + struct semaphore reassoc_sem; + /** Bitmap for re-association on/off */ + t_u8 reassoc_on; +#endif /* REASSOCIATION */ + /** Driver workqueue */ + struct workqueue_struct *workqueue; + /** main work */ + struct work_struct main_work; + /** Driver workqueue */ + struct workqueue_struct *rx_workqueue; + /** main work */ + struct work_struct rx_work; +#if defined(STA_CFG80211) || defined(UAP_CFG80211) + struct wiphy *wiphy; + /** Country code for regulatory domain */ + t_u8 country_code[COUNTRY_CODE_LEN]; + /** band */ + enum ieee80211_band band; + /** first scan done flag */ + t_u8 first_scan_done; + /** scan channel gap */ + t_u16 scan_chan_gap; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + /** remain on channel flag */ + t_u8 remain_on_channel; + /** bss index for remain on channel */ + t_u8 remain_bss_index; + /** remain_on_channel timer set flag */ + BOOLEAN is_remain_timer_set; + /** remani_on_channel_timer */ + moal_drv_timer remain_timer __ATTRIB_ALIGN__; + /** ieee802_11_channel */ + struct ieee80211_channel chan; +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 8, 0) + /** channel type */ + enum nl80211_channel_type channel_type; +#endif + /** cookie */ + t_u64 cookie; +#endif +#ifdef WIFI_DIRECT_SUPPORT + /** NoA duration */ + t_u32 noa_duration; + /** NoA interval */ + t_u32 noa_interval; + /** miracast mode */ + t_u8 miracast_mode; + /** scan time in miracast mode */ + t_u16 miracast_scan_time; + + /** GO timer set flag */ + BOOLEAN is_go_timer_set; + /** GO timer */ + moal_drv_timer go_timer __ATTRIB_ALIGN__; +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) + /** cfg80211_suspend status */ + t_u8 cfg80211_suspend; +#endif +#endif + /** Read SDIO registers for debugging */ + t_u32 sdio_reg_dbg; + /** Netlink kernel socket */ + struct sock *nl_sk; + /** Netlink kernel socket number */ + t_u32 netlink_num; + /** w_stats wait queue token */ + BOOLEAN meas_wait_q_woken; + /** w_stats wait queue */ + wait_queue_head_t meas_wait_q __ATTRIB_ALIGN__; + /** Measurement start jiffes */ + long meas_start_jiffies; + /** CAC checking period flag */ + BOOLEAN cac_period; + /** CAC timer jiffes */ + long cac_timer_jiffies; + /** BSS START command delay executing flag */ + BOOLEAN delay_bss_start; + /** SSID,BSSID parameter of delay executing */ + mlan_ssid_bssid delay_ssid_bssid; +#ifdef UAP_CFG80211 +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) + /* CAC channel info */ + struct cfg80211_chan_def dfs_channel; + /* time set flag */ + BOOLEAN is_cac_timer_set; + /** cac_timer */ + moal_drv_timer cac_timer __ATTRIB_ALIGN__; + /** cac bss index */ + t_u8 cac_bss_index; +#endif +#endif +#if defined(UAP_SUPPORT) +/** channel switch wait queue token */ + BOOLEAN chsw_wait_q_woken; + /** channel switch wait queue */ + wait_queue_head_t chsw_wait_q __ATTRIB_ALIGN__; +#endif +#ifdef DFS_TESTING_SUPPORT + /** cac period length, valid only when dfs testing is enabled */ + long cac_period_jiffies; +#endif + /** handle index - for multiple card supports */ + t_u8 handle_idx; +#ifdef SDIO_MMC_DEBUG + /** cmd53 write state */ + u8 cmd53w; + /** cmd53 read state */ + u8 cmd53r; +#endif +#ifdef STA_SUPPORT + /** Scan pending on blocked flag */ + t_u8 scan_pending_on_block; + /** Scan Private pointer */ + moal_private *scan_priv; + /** Async scan semaphore */ + struct semaphore async_sem; +#ifdef STA_CFG80211 + /** CFG80211 scan request description */ + struct cfg80211_scan_request *scan_request; +#endif +#endif + /** main state */ + t_u8 main_state; + /** driver state */ + t_u8 driver_state; + /** ioctl timeout */ + t_u8 ioctl_timeout; + /** FW dump state */ + t_u8 fw_dump; + /** cmd52 function */ + t_u8 cmd52_func; + /** cmd52 register */ + t_u8 cmd52_reg; + /** cmd52 value */ + t_u8 cmd52_val; +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) + /** spinlock to stop_queue/wake_queue*/ + spinlock_t queue_lock; +#endif + /** Driver spin lock */ + spinlock_t driver_lock; + /** Driver ioctl spin lock */ + spinlock_t ioctl_lock; + /** lock for scan_request */ + spinlock_t scan_req_lock; + /** Card specific driver version */ + t_s8 driver_version[MLAN_MAX_VER_STR_LEN]; + char *fwdump_fname; +#ifdef ANDROID_KERNEL +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) + struct wakeup_source ws; +#else + struct wake_lock wake_lock; +#endif +#endif + t_u16 dfs_repeater_mode; + t_u8 histogram_table_num; + struct notifier_block woal_notifier; +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + struct ieee80211_regdomain *regd; +#endif +#endif + struct net_device napi_dev; + struct napi_struct napi_rx; +}; +/** + * @brief set trans_start for each TX queue. + * + * @param dev A pointer to net_device structure + * + * @return N/A + */ +static inline void +woal_set_trans_start(struct net_device *dev) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31) + unsigned int i; + for (i = 0; i < dev->num_tx_queues; i++) + netdev_get_tx_queue(dev, i)->trans_start = jiffies; +#endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 7, 0) + dev->trans_start = jiffies; +#else + netif_trans_update(dev); +#endif +} + +/** + * @brief Start queue + * + * @param dev A pointer to net_device structure + * + * @return N/A + */ +static inline void +woal_start_queue(struct net_device *dev) +{ +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 29) + netif_start_queue(dev); +#else + if (dev->reg_state == NETREG_REGISTERED) + netif_tx_wake_all_queues(dev); + else + netif_tx_start_all_queues(dev); +#endif +} + +/** + * @brief Stop queue + * + * @param dev A pointer to net_device structure + * + * @return N/A + */ +static inline void +woal_stop_queue(struct net_device *dev) +{ +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) + unsigned long flags; + moal_private *priv = (moal_private *)netdev_priv(dev); + spin_lock_irqsave(&priv->phandle->queue_lock, flags); + woal_set_trans_start(dev); + if (!netif_queue_stopped(dev)) + netif_tx_stop_all_queues(dev); + spin_unlock_irqrestore(&priv->phandle->queue_lock, flags); +#else + woal_set_trans_start(dev); + if (!netif_queue_stopped(dev)) + netif_stop_queue(dev); +#endif +} + +/** + * @brief wake queue + * + * @param dev A pointer to net_device structure + * + * @return N/A + */ +static inline void +woal_wake_queue(struct net_device *dev) +{ +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) + unsigned long flags; + moal_private *priv = (moal_private *)netdev_priv(dev); + spin_lock_irqsave(&priv->phandle->queue_lock, flags); + if (netif_queue_stopped(dev)) + netif_tx_wake_all_queues(dev); + spin_unlock_irqrestore(&priv->phandle->queue_lock, flags); +#else + if (netif_queue_stopped(dev)) + netif_wake_queue(dev); +#endif +} + +/** Debug Macro definition*/ +#ifdef DEBUG_LEVEL1 +extern t_u32 drvdbg; + +#define LOG_CTRL(level) (0) + +#ifdef DEBUG_LEVEL2 +#define PRINTM_MINFO(level, msg...) do { \ + woal_print(level, msg); \ + if (drvdbg & MINFO) printk(KERN_DEBUG msg); \ +} while (0) +#define PRINTM_MWARN(level, msg...) do { \ + woal_print(level, msg); \ + if (drvdbg & MWARN) printk(KERN_DEBUG msg); \ +} while (0) +#define PRINTM_MENTRY(level, msg...) do { \ + woal_print(level, msg); \ + if (drvdbg & MENTRY) printk(KERN_DEBUG msg); \ +} while (0) +#else +#define PRINTM_MINFO(level, msg...) do {} while (0) +#define PRINTM_MWARN(level, msg...) do {} while (0) +#define PRINTM_MENTRY(level, msg...) do {} while (0) +#endif /* DEBUG_LEVEL2 */ + +#define PRINTM_MFW_D(level, msg...) do { \ + woal_print(level, msg); \ + if (drvdbg & MFW_D) printk(KERN_DEBUG msg); \ +} while (0) +#define PRINTM_MCMD_D(level, msg...) do { \ + woal_print(level, msg); \ + if (drvdbg & MCMD_D) printk(KERN_DEBUG msg); \ +} while (0) +#define PRINTM_MDAT_D(level, msg...) do { \ + woal_print(level, msg); \ + if (drvdbg & MDAT_D) printk(KERN_DEBUG msg); \ +} while (0) +#define PRINTM_MIF_D(level, msg...) do { \ + woal_print(level, msg); \ + if (drvdbg & MIF_D) printk(KERN_DEBUG msg); \ +} while (0) + +#define PRINTM_MIOCTL(level, msg...) do { \ + woal_print(level, msg); \ + if (drvdbg & MIOCTL) printk(KERN_DEBUG msg); \ +} while (0) +#define PRINTM_MINTR(level, msg...) do { \ + woal_print(level, msg); \ + if (drvdbg & MINTR) printk(KERN_DEBUG msg); \ +} while (0) +#define PRINTM_MEVENT(level, msg...) do { \ + woal_print(level, msg); \ + if (drvdbg & MEVENT) printk(msg); \ +} while (0) +#define PRINTM_MCMND(level, msg...) do { \ + woal_print(level, msg); \ + if (drvdbg & MCMND) printk(KERN_DEBUG msg); \ +} while (0) +#define PRINTM_MDATA(level, msg...) do { \ + woal_print(level, msg); \ + if (drvdbg & MDATA) printk(KERN_DEBUG msg); \ +} while (0) +#define PRINTM_MERROR(level, msg...) do { \ + woal_print(level, msg); \ + if (drvdbg & MERROR) printk(KERN_ERR msg); \ +} while (0) +#define PRINTM_MFATAL(level, msg...) do { \ + woal_print(level, msg); \ + if (drvdbg & MFATAL) printk(KERN_ERR msg); \ +} while (0) +#define PRINTM_MMSG(level, msg...) do { \ + woal_print(level, msg); \ + if (drvdbg & MMSG) printk(KERN_ALERT msg); \ +} while (0) + +static inline void +woal_print(t_u32 level, char *fmt, ...) +{ +} + +#define PRINTM(level, msg...) PRINTM_##level(level, msg) + +#else + +#define PRINTM(level, msg...) do {} while (0) + +#endif /* DEBUG_LEVEL1 */ + +/** Wait until a condition becomes true */ +#define MASSERT(cond) \ +do { \ + if (!(cond)) { \ + PRINTM(MFATAL, "ASSERT: %s: %i\n", __func__, __LINE__); \ + panic("Assert failed: Panic!"); \ + } \ +} while (0) + +/** Log entry point for debugging */ +#define ENTER() PRINTM(MENTRY, "Enter: %s\n", \ + __func__) +/** Log exit point for debugging */ +#define LEAVE() PRINTM(MENTRY, "Leave: %s\n", \ + __func__) + +#ifdef DEBUG_LEVEL1 +#define DBG_DUMP_BUF_LEN 64 +#define MAX_DUMP_PER_LINE 16 + +static inline void +hexdump(t_u32 level, char *prompt, t_u8 *buf, int len) +{ + int i; + char dbgdumpbuf[DBG_DUMP_BUF_LEN]; + char *ptr = dbgdumpbuf; + + if (drvdbg & level) + printk(KERN_DEBUG "%s:\n", prompt); + for (i = 1; i <= len; i++) { + ptr += snprintf(ptr, 4, "%02x ", *buf); + buf++; + if (i % MAX_DUMP_PER_LINE == 0) { + *ptr = 0; + if (drvdbg & level) + printk(KERN_DEBUG "%s\n", dbgdumpbuf); + ptr = dbgdumpbuf; + } + } + if (len % MAX_DUMP_PER_LINE) { + *ptr = 0; + if (drvdbg & level) + printk(KERN_DEBUG "%s\n", dbgdumpbuf); + } +} + +#define DBG_HEXDUMP_MERROR(x, y, z) do { \ + if ((drvdbg & MERROR) || LOG_CTRL(MERROR)) \ + hexdump(MERROR, x, y, z); \ +} while (0) +#define DBG_HEXDUMP_MCMD_D(x, y, z) do { \ + if ((drvdbg & MCMD_D) || LOG_CTRL(MCMD_D)) \ + hexdump(MCMD_D, x, y, z); \ +} while (0) +#define DBG_HEXDUMP_MDAT_D(x, y, z) do { \ + if ((drvdbg & MDAT_D) || LOG_CTRL(MDAT_D)) \ + hexdump(MDAT_D, x, y, z); \ +} while (0) +#define DBG_HEXDUMP_MIF_D(x, y, z) do { \ + if ((drvdbg & MIF_D) || LOG_CTRL(MIF_D)) \ + hexdump(MIF_D, x, y, z); \ +} while (0) +#define DBG_HEXDUMP_MEVT_D(x, y, z) do { \ + if ((drvdbg & MEVT_D) || LOG_CTRL(MEVT_D)) \ + hexdump(MEVT_D, x, y, z); \ +} while (0) +#define DBG_HEXDUMP_MFW_D(x, y, z) do { \ + if ((drvdbg & MFW_D) || LOG_CTRL(MFW_D)) \ + hexdump(MFW_D, x, y, z); \ +} while (0) +#define DBG_HEXDUMP(level, x, y, z) DBG_HEXDUMP_##level(x, y, z) + +#else +/** Do nothing since debugging is not turned on */ +#define DBG_HEXDUMP(level, x, y, z) do {} while (0) +#endif + +#ifdef DEBUG_LEVEL2 +#define HEXDUMP(x, y, z) do { \ + if ((drvdbg & MINFO) || LOG_CTRL(MINFO)) \ + hexdump(MINFO, x, y, z); \ +} while (0) +#else +/** Do nothing since debugging is not turned on */ +#define HEXDUMP(x, y, z) do {} while (0) +#endif + +#ifdef BIG_ENDIAN_SUPPORT +/** Convert from 16 bit little endian format to CPU format */ +#define woal_le16_to_cpu(x) le16_to_cpu(x) +/** Convert from 32 bit little endian format to CPU format */ +#define woal_le32_to_cpu(x) le32_to_cpu(x) +/** Convert from 64 bit little endian format to CPU format */ +#define woal_le64_to_cpu(x) le64_to_cpu(x) +/** Convert to 16 bit little endian format from CPU format */ +#define woal_cpu_to_le16(x) cpu_to_le16(x) +/** Convert to 32 bit little endian format from CPU format */ +#define woal_cpu_to_le32(x) cpu_to_le32(x) +/** Convert to 64 bit little endian format from CPU format */ +#define woal_cpu_to_le64(x) cpu_to_le64(x) +#else +/** Do nothing */ +#define woal_le16_to_cpu(x) x +/** Do nothing */ +#define woal_le32_to_cpu(x) x +/** Do nothing */ +#define woal_le64_to_cpu(x) x +/** Do nothing */ +#define woal_cpu_to_le16(x) x +/** Do nothing */ +#define woal_cpu_to_le32(x) x +/** Do nothing */ +#define woal_cpu_to_le64(x) x +#endif + +/** + * @brief This function returns first available priv + * based on the BSS role + * + * @param handle A pointer to moal_handle + * @param bss_role BSS role or MLAN_BSS_ROLE_ANY + * + * @return Pointer to moal_private + */ +static inline moal_private * +woal_get_priv(moal_handle *handle, mlan_bss_role bss_role) +{ + int i; + + for (i = 0; i < MIN(handle->priv_num, MLAN_MAX_BSS_NUM); i++) { + if (handle->priv[i]) { + if (bss_role == MLAN_BSS_ROLE_ANY || + GET_BSS_ROLE(handle->priv[i]) == bss_role) + return handle->priv[i]; + } + } + return NULL; +} + +/** + * @brief This function returns first available priv + * based on the BSS type + * + * @param handle A pointer to moal_handle + * @param bss_type BSS type or MLAN_BSS_TYPE_ANY + * + * @return Pointer to moal_private + */ +static inline moal_private * +woal_get_priv_bss_type(moal_handle *handle, mlan_bss_type bss_type) +{ + int i; + + for (i = 0; i < MIN(handle->priv_num, MLAN_MAX_BSS_NUM); i++) { + if (handle->priv[i]) { + if (bss_type == MLAN_BSS_TYPE_ANY || + handle->priv[i]->bss_type == bss_type) + return handle->priv[i]; + } + } + return NULL; +} + +/* CAC Measure report default time 60 seconds */ +#define MEAS_REPORT_TIME (60 * HZ) + +/** Max line length allowed in init config file */ +#define MAX_LINE_LEN 256 +/** Max MAC address string length allowed */ +#define MAX_MAC_ADDR_LEN 18 +/** Max register type/offset/value etc. parameter length allowed */ +#define MAX_PARAM_LEN 12 + +/** HostCmd_CMD_CFG_DATA for CAL data */ +#define HostCmd_CMD_CFG_DATA 0x008f +/** HostCmd action set */ +#define HostCmd_ACT_GEN_SET 0x0001 +/** HostCmd CAL data header length */ +#define CFG_DATA_HEADER_LEN 6 + +typedef struct _HostCmd_DS_GEN { + t_u16 command; + t_u16 size; + t_u16 seq_num; + t_u16 result; +} HostCmd_DS_GEN; + +typedef struct _HostCmd_DS_802_11_CFG_DATA { + /** Action */ + t_u16 action; + /** Type */ + t_u16 type; + /** Data length */ + t_u16 data_len; + /** Data */ + t_u8 data[1]; +} __ATTRIB_PACK__ HostCmd_DS_802_11_CFG_DATA; + +/** combo scan header */ +#define WEXT_CSCAN_HEADER "CSCAN S\x01\x00\x00S\x00" +/** combo scan header size */ +#define WEXT_CSCAN_HEADER_SIZE 12 +/** combo scan ssid section */ +#define WEXT_CSCAN_SSID_SECTION 'S' +/** commbo scan channel section */ +#define WEXT_CSCAN_CHANNEL_SECTION 'C' +/** commbo scan passive dwell section */ +#define WEXT_CSCAN_PASV_DWELL_SECTION 'P' +/** commbo scan home dwell section */ +#define WEXT_CSCAN_HOME_DWELL_SECTION 'H' +/** BGSCAN RSSI section */ +#define WEXT_BGSCAN_RSSI_SECTION 'R' +/** BGSCAN SCAN INTERVAL SECTION */ +#define WEXT_BGSCAN_INTERVAL_SECTION 'T' +/** BGSCAN REPEAT SECTION */ +#define WEXT_BGSCAN_REPEAT_SECTION 'E' +/** Min BGSCAN interval 30 second */ +#define MIN_BGSCAN_INTERVAL 30000 +/** default repeat count */ +#define DEF_REPEAT_COUNT 6 + +/** default rssi low threshold */ +#define DEFAULT_RSSI_LOW_THRESHOLD 70 +/** RSSI HYSTERSIS */ +#define RSSI_HYSTERESIS 6 +/** lowest rssi threshold */ +#define LOWEST_RSSI_THRESHOLD 82 +/** delta rssi */ +#define DELTA_RSSI 10 + +/** NL80211 scan configuration header */ +#define NL80211_SCANCFG_HEADER "SCAN-CFG " +/** NL80211 scan configuration header length */ +#define NL80211_SCANCFG_HEADER_SIZE 9 +/** NL80211 scan configuration active scan section */ +#define NL80211_SCANCFG_ACTV_DWELL_SECTION 'A' +/** NL80211 scan configuration passive scan section */ +#define NL80211_SCANCFG_PASV_DWELL_SECTION 'P' +/** NL80211 scan configuration specific scan section */ +#define NL80211_SCANCFG_SPCF_DWELL_SECTION 'S' + +/** band AUTO */ +#define WIFI_FREQUENCY_BAND_AUTO 0 +/** band 5G */ +#define WIFI_FREQUENCY_BAND_5GHZ 1 +/** band 2G */ +#define WIFI_FREQUENCY_BAND_2GHZ 2 +/** All band */ +#define WIFI_FREQUENCY_ALL_BAND 3 + +/** Rx filter: IPV4 multicast */ +#define RX_FILTER_IPV4_MULTICAST 1 +/** Rx filter: broadcast */ +#define RX_FILTER_BROADCAST 2 +/** Rx filter: unicast */ +#define RX_FILTER_UNICAST 4 +/** Rx filter: IPV6 multicast */ +#define RX_FILTER_IPV6_MULTICAST 8 + +/** Convert ASCII string to hex value */ +int woal_ascii2hex(t_u8 *d, char *s, t_u32 dlen); +/** parse ie */ +const t_u8 *woal_parse_ie_tlv(const t_u8 *ie, int len, t_u8 id); +/** Convert mac address from string to t_u8 buffer */ +void woal_mac2u8(t_u8 *mac_addr, char *buf); +/** Extract token from string */ +char *woal_strsep(char **s, char delim, char esc); +/** Return int value of a given ASCII string */ +mlan_status woal_atoi(int *data, char *a); +/** Return hex value of a given ASCII string */ +int woal_atox(char *a); +/** Allocate buffer */ +pmlan_buffer woal_alloc_mlan_buffer(moal_handle *handle, int size); +/** Allocate IOCTL request buffer */ +pmlan_ioctl_req woal_alloc_mlan_ioctl_req(int size); +/** Free buffer */ +void woal_free_mlan_buffer(moal_handle *handle, pmlan_buffer pmbuf); +/** Get private structure of a BSS by index */ +moal_private *woal_bss_index_to_priv(moal_handle *handle, t_u8 bss_index); +/* Functions in interface module */ +/** Add card */ +moal_handle *woal_add_card(void *card); +/** Remove card */ +mlan_status woal_remove_card(void *card); +/** broadcast event */ +mlan_status woal_broadcast_event(moal_private *priv, t_u8 *payload, t_u32 len); +#ifdef CONFIG_PROC_FS +/** switch driver mode */ +mlan_status woal_switch_drv_mode(moal_handle *handle, t_u32 mode); +#endif + +/** Interrupt handler */ +mlan_status woal_interrupt(moal_handle *handle); + +/** check if any interface is up */ +t_u8 woal_is_any_interface_active(moal_handle *handle); +/** Get version */ +void woal_get_version(moal_handle *handle, char *version, int maxlen); +/** Get Driver Version */ +int woal_get_driver_version(moal_private *priv, struct ifreq *req); +/** Get extended driver version */ +int woal_get_driver_verext(moal_private *priv, struct ifreq *ireq); +/** check driver status */ +t_u8 woal_check_driver_status(moal_handle *handle); +/** Mgmt frame forward registration */ +int woal_reg_rx_mgmt_ind(moal_private *priv, t_u16 action, + t_u32 *pmgmt_subtype_mask, t_u8 wait_option); +#ifdef DEBUG_LEVEL1 +/** Set driver debug bit masks */ +int woal_set_drvdbg(moal_private *priv, t_u32 drvdbg); +#endif + +mlan_status woal_set_get_tx_bf_cap(moal_private *priv, t_u16 action, + t_u32 *tx_bf_cap); +/** Set/Get TX beamforming configurations */ +mlan_status woal_set_get_tx_bf_cfg(moal_private *priv, t_u16 action, + mlan_ds_11n_tx_bf_cfg *bf_cfg); +/** Request MAC address setting */ +mlan_status woal_request_set_mac_address(moal_private *priv); +/** Request multicast list setting */ +void woal_request_set_multicast_list(moal_private *priv, + struct net_device *dev); +/** Request IOCTL action */ +mlan_status woal_request_ioctl(moal_private *priv, mlan_ioctl_req *req, + t_u8 wait_option); +#ifdef CONFIG_PROC_FS +mlan_status woal_request_soft_reset(moal_handle *handle); +#endif +void woal_request_fw_reload(moal_handle *handle, t_u8 mode); +/** driver initial the fw reset */ +#define FW_RELOAD_SDIO_INBAND_RESET 1 +/** out band reset trigger reset, no interface re-emulation */ +#define FW_RELOAD_NO_EMULATION 2 +/** out band reset with interface re-emulation */ +#define FW_RELOAD_WITH_EMULATION 3 + +#ifdef PROC_DEBUG +/** Get debug information */ +mlan_status woal_get_debug_info(moal_private *priv, t_u8 wait_option, + mlan_debug_info *debug_info); +/** Set debug information */ +mlan_status woal_set_debug_info(moal_private *priv, t_u8 wait_option, + mlan_debug_info *debug_info); +#endif +/** Disconnect */ +mlan_status woal_disconnect(moal_private *priv, t_u8 wait_option, t_u8 *mac, + t_u16 reason_code); +/** associate */ +mlan_status woal_bss_start(moal_private *priv, t_u8 wait_option, + mlan_ssid_bssid *ssid_bssid); +/** Request firmware information */ +mlan_status woal_request_get_fw_info(moal_private *priv, t_u8 wait_option, + mlan_fw_info *fw_info); +#ifdef STA_SUPPORT +/** Request Exented Capability information */ +int woal_request_extcap(moal_private *priv, t_u8 *buf, t_u8 len); +#endif +mlan_status woal_set_get_dtim_period(moal_private *priv, + t_u32 action, t_u8 wait_option, + t_u8 *value); +/** Set/get Host Sleep parameters */ +mlan_status woal_set_get_hs_params(moal_private *priv, t_u16 action, + t_u8 wait_option, mlan_ds_hs_cfg *hscfg); +/** Cancel Host Sleep configuration */ +mlan_status woal_cancel_hs(moal_private *priv, t_u8 wait_option); +#if defined(SDIO_SUSPEND_RESUME) +/** Enable Host Sleep configuration */ +int woal_enable_hs(moal_private *priv); +/** hs active timeout 2 second */ +#define HS_ACTIVE_TIMEOUT (2 * HZ) +#endif +/** Get wakeup reason */ +mlan_status woal_get_wakeup_reason(moal_private *priv, + mlan_ds_hs_wakeup_reason *wakeup_reason); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 1, 0) +void woal_create_dump_dir(moal_handle *phandle, char *dir_buf, int buf_size); +#endif +mlan_status woal_save_dump_info_to_file(char *dir_name, char *file_name, + t_u8 *buf, t_u32 buf_len); +void woal_dump_drv_info(moal_handle *phandle, t_u8 *dir_name); + +void woal_dump_firmware_info_v3(moal_handle *phandle); +/* Store the FW dumps received from events in a file */ +void woal_store_firmware_dump(moal_handle *phandle, mlan_event *pmevent); + +/** get deep sleep */ +int woal_get_deep_sleep(moal_private *priv, t_u32 *data); +/** set deep sleep */ +int woal_set_deep_sleep(moal_private *priv, t_u8 wait_option, + BOOLEAN bdeep_sleep, t_u16 idletime); +/** process hang */ +void woal_process_hang(moal_handle *handle); +/** Get BSS information */ +mlan_status woal_get_bss_info(moal_private *priv, t_u8 wait_option, + mlan_bss_info *bss_info); +void woal_process_ioctl_resp(moal_private *priv, mlan_ioctl_req *req); +/** Set/Get generic element */ +mlan_status woal_set_get_gen_ie(moal_private *priv, t_u32 action, t_u8 *ie, + int *ie_len, t_u8 wait_option); +char *region_code_2_string(t_u8 region_code); +t_bool woal_is_etsi_country(t_u8 *country_code); +t_u8 woal_is_valid_alpha2(char *alpha2); +#ifdef STA_SUPPORT +void woal_send_disconnect_to_system(moal_private *priv); +void woal_send_mic_error_event(moal_private *priv, t_u32 event); +void woal_ioctl_get_bss_resp(moal_private *priv, mlan_ds_bss *bss); +void woal_ioctl_get_info_resp(moal_private *priv, mlan_ds_get_info *info); +mlan_status woal_get_assoc_rsp(moal_private *priv, + mlan_ds_misc_assoc_rsp *assoc_rsp, + t_u8 wait_option); +/** Get signal information */ +mlan_status woal_get_signal_info(moal_private *priv, t_u8 wait_option, + mlan_ds_get_signal *signal); +/** Get mode */ +t_u32 woal_get_mode(moal_private *priv, t_u8 wait_option); +mlan_status woal_get_sta_channel(moal_private *priv, t_u8 wait_option, + chan_band_info * channel); +#ifdef STA_WEXT +/** Get data rates */ +mlan_status woal_get_data_rates(moal_private *priv, t_u8 wait_option, + moal_802_11_rates *m_rates); +void woal_send_iwevcustom_event(moal_private *priv, char *str); +/** Get channel list */ +mlan_status woal_get_channel_list(moal_private *priv, t_u8 wait_option, + mlan_chan_list *chanlist); +mlan_status woal_11d_check_ap_channel(moal_private *priv, t_u8 wait_option, + mlan_ssid_bssid *ssid_bssid); +#endif +/** Set/Get retry count */ +mlan_status woal_set_get_retry(moal_private *priv, t_u32 action, + t_u8 wait_option, int *value); +/** Set/Get RTS threshold */ +mlan_status woal_set_get_rts(moal_private *priv, t_u32 action, t_u8 wait_option, + int *value); +/** Set/Get fragment threshold */ +mlan_status woal_set_get_frag(moal_private *priv, t_u32 action, + t_u8 wait_option, int *value); +/** Set/Get TX power */ +mlan_status woal_set_get_tx_power(moal_private *priv, t_u32 action, + mlan_power_cfg_t *pwr); +/** Set/Get power IEEE management */ +mlan_status woal_set_get_power_mgmt(moal_private *priv, t_u32 action, + int *disabled, int type, t_u8 wait_option); +/** Get data rate */ +mlan_status woal_set_get_data_rate(moal_private *priv, t_u8 action, + mlan_rate_cfg_t *datarate); +/** Request a network scan */ +mlan_status woal_request_scan(moal_private *priv, t_u8 wait_option, + mlan_802_11_ssid *req_ssid); +/** Set radio on/off */ +int woal_set_radio(moal_private *priv, t_u8 option); +/** Set region code */ +mlan_status woal_set_region_code(moal_private *priv, char *region); +/** Set authentication mode */ +mlan_status woal_set_auth_mode(moal_private *priv, t_u8 wait_option, + t_u32 auth_mode); +/** Set encryption mode */ +mlan_status woal_set_encrypt_mode(moal_private *priv, t_u8 wait_option, + t_u32 encrypt_mode); +/** Enable wep key */ +mlan_status woal_enable_wep_key(moal_private *priv, t_u8 wait_option); +/** Set WPA enable */ +mlan_status woal_set_wpa_enable(moal_private *priv, t_u8 wait_option, + t_u32 enable); + +/** cancel scan command */ +mlan_status woal_cancel_scan(moal_private *priv, t_u8 wait_option); +/** Find best network to connect */ +mlan_status woal_find_best_network(moal_private *priv, t_u8 wait_option, + mlan_ssid_bssid *ssid_bssid); +/** Set Ad-Hoc channel */ +mlan_status woal_change_adhoc_chan(moal_private *priv, int channel, + t_u8 wait_option); + +/** Get scan table */ +mlan_status woal_get_scan_table(moal_private *priv, t_u8 wait_option, + mlan_scan_resp *scanresp); +/** Get authentication mode */ +mlan_status woal_get_auth_mode(moal_private *priv, t_u8 wait_option, + t_u32 *auth_mode); +/** Get encryption mode */ +mlan_status woal_get_encrypt_mode(moal_private *priv, t_u8 wait_option, + t_u32 *encrypt_mode); +/** Get WPA state */ +mlan_status woal_get_wpa_enable(moal_private *priv, t_u8 wait_option, + t_u32 *enable); +#endif /**STA_SUPPORT */ +mlan_status woal_set_11d(moal_private *priv, t_u8 wait_option, t_u8 enable); + +#if defined(STA_SUPPORT) || defined(UAP_SUPPORT) +/** Get statistics information */ +mlan_status woal_get_stats_info(moal_private *priv, t_u8 wait_option, + mlan_ds_get_stats *stats); +#endif /**STA_SUPPORT||UAP_SUPPORT*/ + +mlan_status woal_set_wapi_enable(moal_private *priv, t_u8 wait_option, + t_u32 enable); + +/** Initialize priv */ +void woal_init_priv(moal_private *priv, t_u8 wait_option); +/** Reset interface(s) */ +int woal_reset_intf(moal_private *priv, t_u8 wait_option, int all_intf); +#define TLV_TYPE_MGMT_IE (0x169) +#define MGMT_MASK_ASSOC_REQ 0x01 +#define MGMT_MASK_REASSOC_REQ 0x04 +#define MGMT_MASK_ASSOC_RESP 0x02 +#define MGMT_MASK_REASSOC_RESP 0x08 +#define MGMT_MASK_PROBE_REQ 0x10 +#define MGMT_MASK_PROBE_RESP 0x20 +#define MGMT_MASK_BEACON 0x100 +#define MGMT_MASK_ASSOC_RESP_QOS_MAP 0x4000 +#define MGMT_MASK_BEACON_WPS_P2P 0x8000 +/** common ioctl for uap, station */ +int woal_custom_ie_ioctl(struct net_device *dev, struct ifreq *req); +int woal_send_host_packet(struct net_device *dev, struct ifreq *req); +/** Private command ID to pass mgmt frame */ +#define WOAL_MGMT_FRAME_TX_IOCTL (SIOCDEVPRIVATE + 12) +/** common ioctl for TDLS */ +int woal_tdls_config_ioctl(struct net_device *dev, struct ifreq *req); + +int woal_get_bss_type(struct net_device *dev, struct ifreq *req); +#if defined(STA_WEXT) || defined(UAP_WEXT) +int woal_host_command(moal_private *priv, struct iwreq *wrq); +#endif +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) +mlan_status woal_bss_role_cfg(moal_private *priv, t_u8 action, + t_u8 wait_option, t_u8 *bss_role); +#if defined(STA_CFG80211) && defined(UAP_CFG80211) +void woal_go_timer_func(void *context); +#endif +#if defined(STA_WEXT) || defined(UAP_WEXT) +int woal_set_get_bss_role(moal_private *priv, struct iwreq *wrq); +#endif +#endif +#if defined(WIFI_DIRECT_SUPPORT) || defined(UAP_SUPPORT) +/** hostcmd ioctl for uap, wifidirect */ +int woal_hostcmd_ioctl(struct net_device *dev, struct ifreq *req); +#endif + +mlan_status woal_set_remain_channel_ioctl(moal_private *priv, t_u8 wait_option, + mlan_ds_remain_chan *pchan); +void woal_remain_timer_func(void *context); + +#if defined(WIFI_DIRECT_SUPPORT) +mlan_status woal_wifi_direct_mode_cfg(moal_private *priv, t_u16 action, + t_u16 *mode); +mlan_status woal_p2p_config(moal_private *priv, t_u32 action, + mlan_ds_wifi_direct_config *p2p_config); +#endif /* WIFI_DIRECT_SUPPORT */ + +int woal_11h_cancel_chan_report_ioctl(moal_private *priv, t_u8 wait_option); + +#ifdef CONFIG_PROC_FS +/** Initialize proc fs */ +void woal_proc_init(moal_handle *handle); +/** Clean up proc fs */ +void woal_proc_exit(moal_handle *handle); +/** Create proc entry */ +void woal_create_proc_entry(moal_private *priv); +/** Remove proc entry */ +void woal_proc_remove(moal_private *priv); +/** string to number */ +int woal_string_to_number(char *s); +#endif + +#ifdef PROC_DEBUG +/** Create debug proc fs */ +void woal_debug_entry(moal_private *priv); +/** Remove debug proc fs */ +void woal_debug_remove(moal_private *priv); +#endif /* PROC_DEBUG */ + +/** check pm info */ +mlan_status woal_get_pm_info(moal_private *priv, mlan_ds_ps_info *pm_info); +/** get mlan debug info */ +void woal_mlan_debug_info(moal_private *priv); + +#ifdef REASSOCIATION +int woal_reassociation_thread(void *data); +void woal_reassoc_timer_func(void *context); +#endif /* REASSOCIATION */ + +t_void woal_main_work_queue(struct work_struct *work); +t_void woal_rx_work_queue(struct work_struct *work); + +int woal_hard_start_xmit(struct sk_buff *skb, struct net_device *dev); +#ifdef STA_SUPPORT +mlan_status woal_init_sta_dev(struct net_device *dev, moal_private *priv); +#endif +#ifdef UAP_SUPPORT +mlan_status woal_init_uap_dev(struct net_device *dev, moal_private *priv); +#endif +mlan_status woal_update_drv_tbl(moal_handle *handle, int drv_mode_local); +moal_private *woal_add_interface(moal_handle *handle, t_u8 bss_num, + t_u8 bss_type); +void woal_remove_interface(moal_handle *handle, t_u8 bss_index); +void woal_set_multicast_list(struct net_device *dev); +mlan_status woal_request_fw(moal_handle *handle); +int woal_11h_channel_check_ioctl(moal_private *priv, t_u8 wait_option); +void woal_cancel_cac_block(moal_private *priv); +void woal_moal_debug_info(moal_private *priv, moal_handle *handle, u8 flag); + +#ifdef STA_SUPPORT +mlan_status woal_get_powermode(moal_private *priv, int *powermode); +mlan_status woal_set_scan_type(moal_private *priv, t_u32 scan_type); +mlan_status woal_enable_ext_scan(moal_private *priv, t_u8 enable); +mlan_status woal_set_powermode(moal_private *priv, char *powermode); +int woal_find_essid(moal_private *priv, mlan_ssid_bssid *ssid_bssid, + t_u8 wait_option); +mlan_status woal_request_userscan(moal_private *priv, t_u8 wait_option, + wlan_user_scan_cfg *scan_cfg); +mlan_status woal_do_scan(moal_private *priv, wlan_user_scan_cfg *scan_cfg); +int woal_set_combo_scan(moal_private *priv, char *buf, int length); +mlan_status woal_set_scan_time(moal_private *priv, t_u16 active_scan_time, + t_u16 passive_scan_time, + t_u16 specific_scan_time); +mlan_status woal_get_band(moal_private *priv, int *band); +mlan_status woal_set_band(moal_private *priv, char *pband); +mlan_status woal_add_rxfilter(moal_private *priv, char *rxfilter); +mlan_status woal_remove_rxfilter(moal_private *priv, char *rxfilter); +mlan_status woal_priv_qos_cfg(moal_private *priv, t_u32 action, char *qos_cfg); +mlan_status woal_set_sleeppd(moal_private *priv, char *psleeppd); +int woal_set_scan_cfg(moal_private *priv, char *buf, int length); +void woal_update_dscp_mapping(moal_private *priv); + +/* EVENT: BCN_RSSI_LOW */ +#define EVENT_BCN_RSSI_LOW 0x0001 +/* EVENT: PRE_BCN_LOST */ +#define EVENT_PRE_BCN_LOST 0x0002 +mlan_status woal_set_rssi_low_threshold(moal_private *priv, char *rssi, + t_u8 wait_option); +mlan_status woal_set_rssi_threshold(moal_private *priv, t_u32 event_id, + t_u8 wait_option); +/* EVENT: BG_SCAN_REPORT */ +#define EVENT_BG_SCAN_REPORT 0x0004 +mlan_status woal_set_bg_scan(moal_private *priv, char *buf, int length); +mlan_status woal_stop_bg_scan(moal_private *priv, t_u8 wait_option); +void woal_reconfig_bgscan(moal_handle *handle); +#ifdef STA_CFG80211 +void woal_config_bgscan_and_rssi(moal_private *priv, t_u8 set_rssi); +void woal_start_roaming(moal_private *priv); +#endif +#ifdef STA_CFG80211 +void woal_save_conn_params(moal_private *priv, + struct cfg80211_connect_params *sme); +void woal_clear_conn_params(moal_private *priv); +#endif +mlan_status woal_request_bgscan(moal_private *priv, t_u8 wait_option, + wlan_bgscan_cfg *scan_cfg); +#endif + +void woal_flush_tcp_sess_queue(moal_private *priv); +void woal_flush_tdls_list(moal_private *priv); +void wlan_scan_create_brief_table_entry(t_u8 **ppbuffer, + BSSDescriptor_t *pbss_desc); +int wlan_get_scan_table_ret_entry(BSSDescriptor_t *pbss_desc, t_u8 **ppbuffer, + int *pspace_left); +BOOLEAN woal_ssid_valid(mlan_802_11_ssid *pssid); +int woal_is_connected(moal_private *priv, mlan_ssid_bssid *ssid_bssid); +int woal_priv_hostcmd(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen, + t_u8 wait_option); +void woal_flush_tx_stat_queue(moal_private *priv); +struct tx_status_info *woal_get_tx_info(moal_private *priv, t_u8 tx_seq_num); +void woal_remove_tx_info(moal_private *priv, t_u8 tx_seq_num); + +mlan_status woal_request_country_power_table(moal_private *priv, char *region); +mlan_status woal_mc_policy_cfg(moal_private *priv, t_u16 *enable, + t_u8 wait_option, t_u8 action); +#ifdef RX_PACKET_COALESCE +mlan_status woal_rx_pkt_coalesce_cfg(moal_private *priv, t_u16 *enable, + t_u8 wait_option, t_u8 action); +#endif +int woal_hexval(char chr); +mlan_status woal_pmic_configure(moal_handle *handle, t_u8 wait_option); +void woal_hist_data_reset(moal_private *priv); +void woal_hist_do_reset(moal_private *priv, void *data); +void woal_hist_reset_table(moal_private *priv, t_u8 antenna); +void woal_hist_data_add(moal_private *priv, t_u8 rx_rate, t_s8 snr, t_s8 nflr, + t_u8 antenna); +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) +mlan_status woal_set_net_monitor(moal_private *priv, t_u8 wait_option, + t_u8 enable, t_u8 filter, + netmon_band_chan_cfg * band_chan_cfg); +#endif +mlan_status woal_delba_all(moal_private *priv, t_u8 wait_option); + +monitor_iface *woal_prepare_mon_if(moal_private *priv, + const char *name, + unsigned char name_assign_type, + int sniffer_mode); + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 1, 0) +mlan_status woal_set_rekey_data(moal_private *priv, + mlan_ds_misc_gtk_rekey_data * gtk_rekey, + t_u8 action); +#endif +void woal_ioctl_get_misc_conf(moal_private *priv, mlan_ds_misc_cfg *info); +#endif /* _MOAL_MAIN_H */ diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_priv.c b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_priv.c new file mode 100644 index 000000000000..628018414d39 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_priv.c @@ -0,0 +1,7265 @@ +/** @file moal_priv.c + * + * @brief This file contains standard ioctl functions + * + * Copyright (C) 2008-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +/************************************************************************ +Change log: + 10/30/2008: initial version +************************************************************************/ + +#include "moal_main.h" +#include "moal_sdio.h" + +#include "moal_eth_ioctl.h" + +/******************************************************** + Local Variables +********************************************************/ +/** Bands supported in Infra mode */ +static t_u8 SupportedInfraBand[] = { + BAND_B, + BAND_B | BAND_G, BAND_G, + BAND_GN, BAND_B | BAND_G | BAND_GN, BAND_G | BAND_GN, + BAND_A, BAND_B | BAND_A, BAND_B | BAND_G | BAND_A, BAND_G | BAND_A, + BAND_A | BAND_B | BAND_G | BAND_AN | BAND_GN, + BAND_A | BAND_G | BAND_AN | BAND_GN, BAND_A | BAND_AN, +}; + +/** Bands supported in Ad-Hoc mode */ +static t_u8 SupportedAdhocBand[] = { + BAND_B, BAND_B | BAND_G, BAND_G, + BAND_GN, BAND_B | BAND_G | BAND_GN, BAND_G | BAND_GN, + BAND_A, + BAND_AN, BAND_A | BAND_AN, +}; + +/******************************************************** + Global Variables +********************************************************/ + +extern int cfg80211_wext; + +/******************************************************** + Local Functions +********************************************************/ + +/** + * @brief Associated to a specific indexed entry in the ScanTable + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int +woal_associate_ssid_bssid(moal_private *priv, struct iwreq *wrq) +{ + mlan_ssid_bssid ssid_bssid; +#ifdef REASSOCIATION + mlan_bss_info bss_info; +#endif + char buf[64]; + t_u8 buflen; + t_u8 mac_idx; + t_u8 i; + + ENTER(); + + memset(&ssid_bssid, 0, sizeof(ssid_bssid)); + mac_idx = 0; + buflen = MIN(wrq->u.data.length, (sizeof(buf) - 1)); + memset(buf, 0, sizeof(buf)); + + if (buflen < (3 * ETH_ALEN) + 2) { + PRINTM(MERROR, + "Associate: Insufficient length in IOCTL input\n"); + + /* buffer should be at least 3 characters per BSSID octet "00:" + ** plus a space separater and at least 1 char in the SSID + */ + LEAVE(); + return -EINVAL; + } + + if (copy_from_user(buf, wrq->u.data.pointer, buflen) != 0) { + /* copy_from_user failed */ + PRINTM(MERROR, "Associate: copy from user failed\n"); + LEAVE(); + return -EINVAL; + } + + for (i = 0; (i < buflen) && (buf[i] == ' '); i++) { + /* Skip white space */ + } + + /* Copy/Convert the BSSID */ + for (; (i < buflen) && (mac_idx < ETH_ALEN) && (buf[i] != ' '); i++) { + if (buf[i] == ':') { + mac_idx++; + } else { + if (mac_idx < ETH_ALEN) + ssid_bssid.bssid[mac_idx] = + (t_u8)woal_atox(buf + i); + + while ((i < buflen) && (isxdigit(buf[i + 1]))) { + /* Skip entire hex value */ + i++; + } + } + } + + /* Skip one space between the BSSID and start of the SSID */ + i++; + + /* Copy the SSID */ + ssid_bssid.ssid.ssid_len = buflen - i - 1; + memcpy(ssid_bssid.ssid.ssid, buf + i, sizeof(ssid_bssid.ssid.ssid)); + + PRINTM(MCMND, "iwpriv assoc: AP=[" MACSTR "], ssid(%d)=[%s]\n", + MAC2STR(ssid_bssid.bssid), + (int)ssid_bssid.ssid.ssid_len, ssid_bssid.ssid.ssid); + + if (MLAN_STATUS_SUCCESS != woal_bss_start(priv, + MOAL_IOCTL_WAIT, + &ssid_bssid)) { + LEAVE(); + return -EFAULT; + } +#ifdef REASSOCIATION + memset(&bss_info, 0x00, sizeof(bss_info)); + if (MLAN_STATUS_SUCCESS == woal_get_bss_info(priv, + MOAL_IOCTL_WAIT, + &bss_info)) { + memcpy(&priv->prev_ssid_bssid.ssid, + &bss_info.ssid, sizeof(mlan_802_11_ssid)); + memcpy(&priv->prev_ssid_bssid.bssid, + &bss_info.bssid, MLAN_MAC_ADDR_LENGTH); + } +#endif /* REASSOCIATION */ + + LEAVE(); + return 0; +} + +/** + * @brief Copy Rates + * + * @param dest A pointer to destination buffer + * @param pos The position for copy + * @param src A pointer to source buffer + * @param len Length of the source buffer + * + * @return Number of rates copied + */ +static inline int +woal_copy_rates(t_u8 *dest, int pos, t_u8 *src, int len) +{ + int i; + + for (i = 0; i < len && src[i]; i++, pos++) { + if (pos >= MLAN_SUPPORTED_RATES) + break; + dest[pos] = src[i]; + } + return pos; +} + +/** + * @brief Performs warm reset + * + * @param priv A pointer to moal_private structure + * + * @return 0/MLAN_STATUS_SUCCESS --success, otherwise fail + */ +static int +woal_warm_reset(moal_private *priv) +{ + int ret = 0; + int intf_num; + moal_handle *handle = priv->phandle; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *misc = NULL; +#if defined(WIFI_DIRECT_SUPPORT) +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) +#if defined(STA_WEXT) || defined(UAP_WEXT) + t_u8 bss_role = MLAN_BSS_ROLE_STA; +#endif +#endif +#endif /* WIFI_DIRECT_SUPPORT && V14_FEATURE */ + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + woal_cancel_cac_block(priv); + + /* Reset all interfaces */ + ret = woal_reset_intf(priv, MOAL_IOCTL_WAIT, MTRUE); + + /* Initialize private structures */ + for (intf_num = 0; intf_num < handle->priv_num; intf_num++) { + woal_init_priv(handle->priv[intf_num], MOAL_IOCTL_WAIT); +#if defined(WIFI_DIRECT_SUPPORT) +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) +#if defined(STA_WEXT) || defined(UAP_WEXT) + if ((handle->priv[intf_num]->bss_type == + MLAN_BSS_TYPE_WIFIDIRECT) && + (GET_BSS_ROLE(handle->priv[intf_num]) == + MLAN_BSS_ROLE_UAP)) { + if (MLAN_STATUS_SUCCESS != + woal_bss_role_cfg(handle->priv[intf_num], + MLAN_ACT_SET, MOAL_IOCTL_WAIT, + &bss_role)) { + ret = -EFAULT; + goto done; + } + } +#endif /* STA_WEXT || UAP_WEXT */ +#endif /* STA_SUPPORT && UAP_SUPPORT */ +#endif /* WIFI_DIRECT_SUPPORT && V14_FEATURE */ + } + + /* Restart the firmware */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req) { + misc = (mlan_ds_misc_cfg *)req->pbuf; + misc->sub_command = MLAN_OID_MISC_WARM_RESET; + req->req_id = MLAN_IOCTL_MISC_CFG; + req->action = MLAN_ACT_SET; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + if (status != MLAN_STATUS_PENDING) + kfree(req); + goto done; + } + kfree(req); + } + + /* Enable interfaces */ + for (intf_num = 0; intf_num < handle->priv_num; intf_num++) { + netif_device_attach(handle->priv[intf_num]->netdev); + woal_start_queue(handle->priv[intf_num]->netdev); + } + +done: + LEAVE(); + return ret; +} + +/** + * @brief Get signal + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int +woal_get_signal(moal_private *priv, struct iwreq *wrq) +{ +/** Input data size */ +#define IN_DATA_SIZE 2 +/** Output data size */ +#define OUT_DATA_SIZE 12 + int ret = 0; + int in_data[IN_DATA_SIZE]; + int out_data[OUT_DATA_SIZE]; + mlan_ds_get_signal signal; + int data_length = 0; + int buflen = 0; + + ENTER(); + + memset(in_data, 0, sizeof(in_data)); + memset(out_data, 0, sizeof(out_data)); + buflen = MIN(wrq->u.data.length, IN_DATA_SIZE); + + if (priv->media_connected == MFALSE) { + PRINTM(MERROR, "Can not get RSSI in disconnected state\n"); + ret = -ENOTSUPP; + goto done; + } + + if (wrq->u.data.length) { + if (sizeof(int) * wrq->u.data.length > sizeof(in_data)) { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + if (copy_from_user + (in_data, wrq->u.data.pointer, sizeof(int) * buflen)) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + } + + switch (wrq->u.data.length) { + case 0: /* No checking, get everything */ + break; + case 2: /* Check subtype range */ + if (in_data[1] < 1 || in_data[1] > 4) { + ret = -EINVAL; + goto done; + } + /* Fall through */ + case 1: /* Check type range */ + if (in_data[0] < 1 || in_data[0] > 3) { + ret = -EINVAL; + goto done; + } + break; + default: + ret = -EINVAL; + goto done; + } + + memset(&signal, 0, sizeof(mlan_ds_get_signal)); + if (MLAN_STATUS_SUCCESS != + woal_get_signal_info(priv, MOAL_IOCTL_WAIT, &signal)) { + ret = -EFAULT; + goto done; + } + PRINTM(MINFO, "RSSI Beacon Last : %d\n", (int)signal.bcn_rssi_last); + PRINTM(MINFO, "RSSI Beacon Average: %d\n", (int)signal.bcn_rssi_avg); + PRINTM(MINFO, "RSSI Data Last : %d\n", (int)signal.data_rssi_last); + PRINTM(MINFO, "RSSI Data Average : %d\n", (int)signal.data_rssi_avg); + PRINTM(MINFO, "SNR Beacon Last : %d\n", (int)signal.bcn_snr_last); + PRINTM(MINFO, "SNR Beacon Average : %d\n", (int)signal.bcn_snr_avg); + PRINTM(MINFO, "SNR Data Last : %d\n", (int)signal.data_snr_last); + PRINTM(MINFO, "SNR Data Average : %d\n", (int)signal.data_snr_avg); + PRINTM(MINFO, "NF Beacon Last : %d\n", (int)signal.bcn_nf_last); + PRINTM(MINFO, "NF Beacon Average : %d\n", (int)signal.bcn_nf_avg); + PRINTM(MINFO, "NF Data Last : %d\n", (int)signal.data_nf_last); + PRINTM(MINFO, "NF Data Average : %d\n", (int)signal.data_nf_avg); + + /* Check type */ + switch (in_data[0]) { + case 0: /* Send everything */ + out_data[data_length++] = signal.bcn_rssi_last; + out_data[data_length++] = signal.bcn_rssi_avg; + out_data[data_length++] = signal.data_rssi_last; + out_data[data_length++] = signal.data_rssi_avg; + out_data[data_length++] = signal.bcn_snr_last; + out_data[data_length++] = signal.bcn_snr_avg; + out_data[data_length++] = signal.data_snr_last; + out_data[data_length++] = signal.data_snr_avg; + out_data[data_length++] = signal.bcn_nf_last; + out_data[data_length++] = signal.bcn_nf_avg; + out_data[data_length++] = signal.data_nf_last; + out_data[data_length++] = signal.data_nf_avg; + break; + case 1: /* RSSI */ + /* Check subtype */ + switch (in_data[1]) { + case 0: /* Everything */ + out_data[data_length++] = signal.bcn_rssi_last; + out_data[data_length++] = signal.bcn_rssi_avg; + out_data[data_length++] = signal.data_rssi_last; + out_data[data_length++] = signal.data_rssi_avg; + break; + case 1: /* bcn last */ + out_data[data_length++] = signal.bcn_rssi_last; + break; + case 2: /* bcn avg */ + out_data[data_length++] = signal.bcn_rssi_avg; + break; + case 3: /* data last */ + out_data[data_length++] = signal.data_rssi_last; + break; + case 4: /* data avg */ + out_data[data_length++] = signal.data_rssi_avg; + break; + default: + break; + } + break; + case 2: /* SNR */ + /* Check subtype */ + switch (in_data[1]) { + case 0: /* Everything */ + out_data[data_length++] = signal.bcn_snr_last; + out_data[data_length++] = signal.bcn_snr_avg; + out_data[data_length++] = signal.data_snr_last; + out_data[data_length++] = signal.data_snr_avg; + break; + case 1: /* bcn last */ + out_data[data_length++] = signal.bcn_snr_last; + break; + case 2: /* bcn avg */ + out_data[data_length++] = signal.bcn_snr_avg; + break; + case 3: /* data last */ + out_data[data_length++] = signal.data_snr_last; + break; + case 4: /* data avg */ + out_data[data_length++] = signal.data_snr_avg; + break; + default: + break; + } + break; + case 3: /* NF */ + /* Check subtype */ + switch (in_data[1]) { + case 0: /* Everything */ + out_data[data_length++] = signal.bcn_nf_last; + out_data[data_length++] = signal.bcn_nf_avg; + out_data[data_length++] = signal.data_nf_last; + out_data[data_length++] = signal.data_nf_avg; + break; + case 1: /* bcn last */ + out_data[data_length++] = signal.bcn_nf_last; + break; + case 2: /* bcn avg */ + out_data[data_length++] = signal.bcn_nf_avg; + break; + case 3: /* data last */ + out_data[data_length++] = signal.data_nf_last; + break; + case 4: /* data avg */ + out_data[data_length++] = signal.data_nf_avg; + break; + default: + break; + } + break; + default: + break; + } + + wrq->u.data.length = data_length; + if (copy_to_user(wrq->u.data.pointer, out_data, + wrq->u.data.length * sizeof(out_data[0]))) { + PRINTM(MERROR, "Copy to user failed\n"); + ret = -EFAULT; + goto done; + } +done: + LEAVE(); + return ret; +} + +/** + * @brief Get/Set DeepSleep mode + * + * @param priv Pointer to the moal_private driver data struct + * @param wreq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int +woal_deep_sleep_ioctl(moal_private *priv, struct iwreq *wrq) +{ + int ret = 0; + int user_data_len; + t_u32 deep_sleep = DEEP_SLEEP_OFF; + t_u32 data[2]; + int copy_len; + t_u16 idletime = DEEP_SLEEP_IDLE_TIME; + + ENTER(); + + user_data_len = wrq->u.data.length; + copy_len = MIN(sizeof(data), sizeof(int) * user_data_len); + if (user_data_len == 1 || user_data_len == 2) { + if (copy_from_user(&data, wrq->u.data.pointer, copy_len)) { + PRINTM(MERROR, "Copy from user failed\n"); + LEAVE(); + return -EFAULT; + } + deep_sleep = data[0]; + if (deep_sleep == DEEP_SLEEP_OFF) { + PRINTM(MINFO, "Exit Deep Sleep Mode\n"); + ret = woal_set_deep_sleep(priv, MOAL_IOCTL_WAIT, MFALSE, + 0); + if (ret != MLAN_STATUS_SUCCESS) { + LEAVE(); + return -EINVAL; + } + } else if (deep_sleep == DEEP_SLEEP_ON) { + PRINTM(MINFO, "Enter Deep Sleep Mode\n"); + if (user_data_len == 2) + idletime = data[1]; + else + idletime = 0; + ret = woal_set_deep_sleep(priv, MOAL_IOCTL_WAIT, MTRUE, + idletime); + if (ret != MLAN_STATUS_SUCCESS) { + LEAVE(); + return -EINVAL; + } + } else { + PRINTM(MERROR, "Unknown option = %u\n", deep_sleep); + LEAVE(); + return -EINVAL; + } + } else if (user_data_len > 2) { + PRINTM(MERROR, "Invalid number of arguments %d\n", + user_data_len); + LEAVE(); + return -EINVAL; + } else { /* Display Deep Sleep settings */ + PRINTM(MINFO, "Get Deep Sleep Mode\n"); + if (MLAN_STATUS_SUCCESS != woal_get_deep_sleep(priv, data)) { + LEAVE(); + return -EFAULT; + } + if (data[0] == 0) + wrq->u.data.length = 1; + else + wrq->u.data.length = 2; + } + + /* Copy the Deep Sleep setting to user */ + if (copy_to_user + (wrq->u.data.pointer, data, wrq->u.data.length * sizeof(int))) { + PRINTM(MERROR, "Copy to user failed\n"); + LEAVE(); + return -EINVAL; + } + + LEAVE(); + return 0; +} + +/** + * @brief Set/Get Usr 11n configuration request + * + * @param priv Pointer to the moal_private driver data struct + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int +woal_11n_htcap_cfg(moal_private *priv, struct iwreq *wrq) +{ + int data[2], copy_len; + mlan_ioctl_req *req = NULL; + mlan_ds_11n_cfg *cfg_11n = NULL; + int ret = 0; + int data_length = wrq->u.data.length; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (data_length > 2) { + PRINTM(MERROR, "Invalid number of arguments\n"); + ret = -EINVAL; + goto done; + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + cfg_11n = (mlan_ds_11n_cfg *)req->pbuf; + cfg_11n->sub_command = MLAN_OID_11N_HTCAP_CFG; + req->req_id = MLAN_IOCTL_11N_CFG; + + copy_len = MIN(sizeof(data), sizeof(int) * data_length); + if (data_length == 0) { + /* Get 11n tx parameters from MLAN */ + req->action = MLAN_ACT_GET; + cfg_11n->param.htcap_cfg.misc_cfg = BAND_SELECT_BG; + } else { + if (copy_from_user(data, wrq->u.data.pointer, copy_len)) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + + cfg_11n->param.htcap_cfg.htcap = data[0]; + PRINTM(MINFO, "SET: htcapinfo:0x%x\n", data[0]); + cfg_11n->param.htcap_cfg.misc_cfg = BAND_SELECT_BOTH; + if (data_length == 2) { + if (data[1] != BAND_SELECT_BG && + data[1] != BAND_SELECT_A && + data[1] != BAND_SELECT_BOTH) { + PRINTM(MERROR, "Invalid band selection\n"); + ret = -EINVAL; + goto done; + } + cfg_11n->param.htcap_cfg.misc_cfg = data[1]; + PRINTM(MINFO, "SET: htcapinfo band:0x%x\n", data[1]); + } + /* Update 11n tx parameters in MLAN */ + req->action = MLAN_ACT_SET; + } + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + data[0] = cfg_11n->param.htcap_cfg.htcap; + + if (req->action == MLAN_ACT_GET) { + data_length = 1; + cfg_11n->param.htcap_cfg.htcap = 0; + cfg_11n->param.htcap_cfg.misc_cfg = BAND_SELECT_A; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + if (cfg_11n->param.htcap_cfg.htcap != data[0]) { + data_length = 2; + data[1] = cfg_11n->param.htcap_cfg.htcap; + PRINTM(MINFO, "GET: htcapinfo for 2.4GHz:0x%x\n", + data[0]); + PRINTM(MINFO, "GET: htcapinfo for 5GHz:0x%x\n", + data[1]); + } else + PRINTM(MINFO, "GET: htcapinfo:0x%x\n", data[0]); + } + + if (copy_to_user(wrq->u.data.pointer, data, sizeof(data))) { + PRINTM(MERROR, "Copy to user failed\n"); + ret = -EFAULT; + goto done; + } + + wrq->u.data.length = data_length; + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Enable/Disable amsdu_aggr_ctrl + * + * @param priv Pointer to the moal_private driver data struct + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int +woal_11n_amsdu_aggr_ctrl(moal_private *priv, struct iwreq *wrq) +{ + int data[2], copy_len; + mlan_ioctl_req *req = NULL; + mlan_ds_11n_cfg *cfg_11n = NULL; + int ret = 0; + int data_length = wrq->u.data.length; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if ((data_length != 0) && (data_length != 1)) { + PRINTM(MERROR, "Invalid number of arguments\n"); + ret = -EINVAL; + goto done; + } + copy_len = MIN(sizeof(data), sizeof(int) * data_length); + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + cfg_11n = (mlan_ds_11n_cfg *)req->pbuf; + cfg_11n->sub_command = MLAN_OID_11N_CFG_AMSDU_AGGR_CTRL; + req->req_id = MLAN_IOCTL_11N_CFG; + + if (data_length == 0) { + /* Get 11n tx parameters from MLAN */ + req->action = MLAN_ACT_GET; + } else if (data_length == 1) { + if (copy_from_user(data, wrq->u.data.pointer, copy_len)) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + cfg_11n->param.amsdu_aggr_ctrl.enable = data[0]; + /* Update 11n tx parameters in MLAN */ + req->action = MLAN_ACT_SET; + } + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + data[0] = cfg_11n->param.amsdu_aggr_ctrl.enable; + data[1] = cfg_11n->param.amsdu_aggr_ctrl.curr_buf_size; + + if (copy_to_user(wrq->u.data.pointer, data, sizeof(data))) { + PRINTM(MERROR, "Copy to user failed\n"); + ret = -EFAULT; + goto done; + } + wrq->u.data.length = 2; +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get 11n configuration request + * + * @param priv Pointer to the moal_private driver data struct + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int +woal_11n_tx_cfg(moal_private *priv, struct iwreq *wrq) +{ + int data[2], copy_len; + mlan_ioctl_req *req = NULL; + mlan_ds_11n_cfg *cfg_11n = NULL; + int ret = 0; + int data_length = wrq->u.data.length; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (data_length > 2) { + PRINTM(MERROR, "Invalid number of arguments\n"); + ret = -EINVAL; + goto done; + } + copy_len = MIN(sizeof(data), sizeof(int) * data_length); + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + cfg_11n = (mlan_ds_11n_cfg *)req->pbuf; + cfg_11n->sub_command = MLAN_OID_11N_CFG_TX; + req->req_id = MLAN_IOCTL_11N_CFG; + + if (data_length == 0) { + /* Get 11n tx parameters from MLAN */ + req->action = MLAN_ACT_GET; + cfg_11n->param.tx_cfg.misc_cfg = BAND_SELECT_BG; + } else { + if (copy_from_user(data, wrq->u.data.pointer, copy_len)) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + + cfg_11n->param.tx_cfg.httxcap = data[0]; + PRINTM(MINFO, "SET: httxcap:0x%x\n", data[0]); + cfg_11n->param.tx_cfg.misc_cfg = BAND_SELECT_BOTH; + if (data_length == 2) { + if (data[1] != BAND_SELECT_BG && + data[1] != BAND_SELECT_A && + data[1] != BAND_SELECT_BOTH) { + PRINTM(MERROR, "Invalid band selection\n"); + ret = -EINVAL; + goto done; + } + cfg_11n->param.tx_cfg.misc_cfg = data[1]; + PRINTM(MINFO, "SET: httxcap band:0x%x\n", data[1]); + } + /* Update 11n tx parameters in MLAN */ + req->action = MLAN_ACT_SET; + } + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + data[0] = cfg_11n->param.tx_cfg.httxcap; + + if (req->action == MLAN_ACT_GET) { + data_length = 1; + cfg_11n->param.tx_cfg.httxcap = 0; + cfg_11n->param.tx_cfg.misc_cfg = BAND_SELECT_A; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + if (cfg_11n->param.tx_cfg.httxcap != data[0]) { + data_length = 2; + data[1] = cfg_11n->param.tx_cfg.httxcap; + PRINTM(MINFO, "GET: httxcap for 2.4GHz:0x%x\n", + data[0]); + PRINTM(MINFO, "GET: httxcap for 5GHz:0x%x\n", data[1]); + } else + PRINTM(MINFO, "GET: httxcap:0x%x\n", data[0]); + } + + if (copy_to_user(wrq->u.data.pointer, data, sizeof(data))) { + PRINTM(MERROR, "Copy to user failed\n"); + ret = -EFAULT; + goto done; + } + wrq->u.data.length = data_length; + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Enable/Disable TX Aggregation + * + * @param priv Pointer to the moal_private driver data struct + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int +woal_11n_prio_tbl(moal_private *priv, struct iwreq *wrq) +{ + int data[MAX_NUM_TID * 2], i, j, copy_len; + mlan_ioctl_req *req = NULL; + mlan_ds_11n_cfg *cfg_11n = NULL; + int ret = 0; + int data_length = wrq->u.data.length; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if ((wrq->u.data.pointer == NULL)) { + LEAVE(); + return -EINVAL; + } + copy_len = MIN(sizeof(data), sizeof(int) * data_length); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); + if (req == NULL) { + LEAVE(); + return -ENOMEM; + } + cfg_11n = (mlan_ds_11n_cfg *)req->pbuf; + cfg_11n->sub_command = MLAN_OID_11N_CFG_AGGR_PRIO_TBL; + req->req_id = MLAN_IOCTL_11N_CFG; + + if (data_length == 0) { + /* Get aggr priority table from MLAN */ + req->action = MLAN_ACT_GET; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto error; + } + wrq->u.data.length = MAX_NUM_TID * 2; + for (i = 0, j = 0; i < (wrq->u.data.length); i = i + 2, ++j) { + data[i] = cfg_11n->param.aggr_prio_tbl.ampdu[j]; + data[i + 1] = cfg_11n->param.aggr_prio_tbl.amsdu[j]; + } + + if (copy_to_user(wrq->u.data.pointer, data, + sizeof(int) * wrq->u.data.length)) { + PRINTM(MERROR, "Copy to user failed\n"); + ret = -EFAULT; + goto error; + } + } else if (data_length == 16) { + if (copy_from_user(data, wrq->u.data.pointer, copy_len)) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto error; + } + for (i = 0, j = 0; i < (data_length); i = i + 2, ++j) { + if ((data[i] > 7 && data[i] != 0xff) || + (data[i + 1] > 7 && data[i + 1] != 0xff)) { + PRINTM(MERROR, + "Invalid priority, valid value 0-7 or 0xff.\n"); + ret = -EFAULT; + goto error; + } + cfg_11n->param.aggr_prio_tbl.ampdu[j] = data[i]; + cfg_11n->param.aggr_prio_tbl.amsdu[j] = data[i + 1]; + } + + /* Update aggr priority table in MLAN */ + req->action = MLAN_ACT_SET; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto error; + } + } else { + PRINTM(MERROR, "Invalid number of arguments\n"); + ret = -EINVAL; + goto error; + } + +error: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get add BA Reject parameters + * + * @param priv Pointer to the moal_private driver data struct + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int +woal_addba_reject(moal_private *priv, struct iwreq *wrq) +{ + int data[MAX_NUM_TID], ret = 0, i, copy_len; + mlan_ioctl_req *req = NULL; + mlan_ds_11n_cfg *cfg_11n = NULL; + int data_length = wrq->u.data.length; + mlan_status status = MLAN_STATUS_SUCCESS; + ENTER(); + + copy_len = MIN(sizeof(data), sizeof(int) * data_length); + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); + if (req == NULL) { + LEAVE(); + return -ENOMEM; + } + cfg_11n = (mlan_ds_11n_cfg *)req->pbuf; + cfg_11n->sub_command = MLAN_OID_11N_CFG_ADDBA_REJECT; + req->req_id = MLAN_IOCTL_11N_CFG; + + if (data_length == 0) { + PRINTM(MERROR, "Addba reject moal\n"); + /* Get aggr priority table from MLAN */ + req->action = MLAN_ACT_GET; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto error; + } + + wrq->u.data.length = MAX_NUM_TID; + for (i = 0; i < (wrq->u.data.length); ++i) + data[i] = cfg_11n->param.addba_reject[i]; + + if (copy_to_user(wrq->u.data.pointer, data, + sizeof(int) * wrq->u.data.length)) { + PRINTM(MERROR, "Copy to user failed\n"); + ret = -EFAULT; + goto error; + } + } else if (data_length == 8) { + if (copy_from_user(data, wrq->u.data.pointer, copy_len)) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto error; + } + for (i = 0; i < (data_length); ++i) { + if (data[i] != 0 && data[i] != 1) { + PRINTM(MERROR, + "addba reject only takes argument as 0 or 1\n"); + ret = -EFAULT; + goto error; + } + cfg_11n->param.addba_reject[i] = data[i]; + } + + /* Update aggr priority table in MLAN */ + req->action = MLAN_ACT_SET; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto error; + } + } else { + PRINTM(MERROR, "Invalid number of arguments\n"); + ret = -EINVAL; + goto error; + } +error: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get add BA parameters + * + * @param priv Pointer to the moal_private driver data struct + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int +woal_addba_para_updt(moal_private *priv, struct iwreq *wrq) +{ + int data[5], ret = 0, copy_len; + mlan_ioctl_req *req = NULL; + mlan_ds_11n_cfg *cfg_11n = NULL; + int data_length = wrq->u.data.length; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + copy_len = MIN(sizeof(data), sizeof(int) * data_length); + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); + if (req == NULL) { + LEAVE(); + return -ENOMEM; + } + cfg_11n = (mlan_ds_11n_cfg *)req->pbuf; + cfg_11n->sub_command = MLAN_OID_11N_CFG_ADDBA_PARAM; + req->req_id = MLAN_IOCTL_11N_CFG; + + if (data_length == 0) { + /* Get Add BA parameters from MLAN */ + req->action = MLAN_ACT_GET; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto error; + } + data[0] = cfg_11n->param.addba_param.timeout; + data[1] = cfg_11n->param.addba_param.txwinsize; + data[2] = cfg_11n->param.addba_param.rxwinsize; + data[3] = cfg_11n->param.addba_param.txamsdu; + data[4] = cfg_11n->param.addba_param.rxamsdu; + PRINTM(MINFO, + "GET: timeout:%d txwinsize:%d rxwinsize:%d txamsdu=%d, rxamsdu=%d\n", + data[0], data[1], data[2], data[3], data[4]); + wrq->u.data.length = 5; + if (copy_to_user(wrq->u.data.pointer, data, + wrq->u.data.length * sizeof(int))) { + PRINTM(MERROR, "Copy to user failed\n"); + ret = -EFAULT; + goto error; + } + } else if (data_length == 5) { + if (copy_from_user(data, wrq->u.data.pointer, copy_len)) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto error; + } + if (data[0] < 0 || data[0] > MLAN_DEFAULT_BLOCK_ACK_TIMEOUT) { + PRINTM(MERROR, "Incorrect addba timeout value.\n"); + ret = -EFAULT; + goto error; + } + if (data[1] <= 0 || data[1] > MLAN_AMPDU_MAX_TXWINSIZE || + data[2] <= 0 || data[2] > MLAN_AMPDU_MAX_RXWINSIZE) { + PRINTM(MERROR, "Incorrect Tx/Rx window size.\n"); + ret = -EFAULT; + goto error; + } + cfg_11n->param.addba_param.timeout = data[0]; + cfg_11n->param.addba_param.txwinsize = data[1]; + cfg_11n->param.addba_param.rxwinsize = data[2]; + if (data[3] < 0 || data[3] > 1 || data[4] < 0 || data[4] > 1) { + PRINTM(MERROR, "Incorrect Tx/Rx amsdu.\n"); + ret = -EFAULT; + goto error; + } + cfg_11n->param.addba_param.txamsdu = data[3]; + cfg_11n->param.addba_param.rxamsdu = data[4]; + PRINTM(MINFO, + "SET: timeout:%d txwinsize:%d rxwinsize:%d txamsdu=%d rxamsdu=%d\n", + data[0], data[1], data[2], data[3], data[4]); + /* Update Add BA parameters in MLAN */ + req->action = MLAN_ACT_SET; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = MLAN_STATUS_FAILURE; + goto error; + } + } else { + PRINTM(MERROR, "Invalid number of arguments\n"); + ret = -EINVAL; + goto error; + } + +error: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get Transmit buffer size + * + * @param priv Pointer to the moal_private driver data struct + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int +woal_txbuf_cfg(moal_private *priv, struct iwreq *wrq) +{ + int buf_size; + mlan_ioctl_req *req = NULL; + mlan_ds_11n_cfg *cfg_11n = NULL; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + cfg_11n = (mlan_ds_11n_cfg *)req->pbuf; + cfg_11n->sub_command = MLAN_OID_11N_CFG_MAX_TX_BUF_SIZE; + req->req_id = MLAN_IOCTL_11N_CFG; + + if (wrq->u.data.length == 0) { + /* Get Tx buffer size from MLAN */ + req->action = MLAN_ACT_GET; + } else { + ret = -EINVAL; + PRINTM(MERROR, + "Don't support set Tx buffer size after driver loaded!\n"); + goto done; + } + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + buf_size = cfg_11n->param.tx_buf_size; + if (copy_to_user(wrq->u.data.pointer, &buf_size, sizeof(buf_size))) { + PRINTM(MERROR, "Copy to user failed\n"); + ret = -EFAULT; + goto done; + } + wrq->u.data.length = 1; +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get Host Sleep configuration + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * @param invoke_hostcmd MTRUE --invoke HostCmd, otherwise MFALSE + * + * @return 0 --success, otherwise fail + */ +static int +woal_hs_cfg(moal_private *priv, struct iwreq *wrq, BOOLEAN invoke_hostcmd) +{ + int data[3], copy_len; + int ret = 0; + mlan_ds_hs_cfg hscfg; + t_u16 action; + mlan_bss_info bss_info; + int data_length = wrq->u.data.length; + + ENTER(); + + memset(data, 0, sizeof(data)); + memset(&hscfg, 0, sizeof(mlan_ds_hs_cfg)); + copy_len = MIN(sizeof(data), sizeof(int) * data_length); + + if (data_length == 0) { + action = MLAN_ACT_GET; + } else { + action = MLAN_ACT_SET; + if (data_length >= 1 && data_length <= 3) { + if (copy_from_user(data, wrq->u.data.pointer, copy_len)) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + } else { + PRINTM(MERROR, "Invalid arguments\n"); + ret = -EINVAL; + goto done; + } + } + + /* HS config is blocked if HS is already activated */ + if (data_length && + (data[0] != HOST_SLEEP_CFG_CANCEL || invoke_hostcmd == MFALSE)) { + memset(&bss_info, 0, sizeof(bss_info)); + woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info); + if (bss_info.is_hs_configured) { + PRINTM(MERROR, "HS already configured\n"); + ret = -EFAULT; + goto done; + } + } + + /* Do a GET first if some arguments are not provided */ + if (data_length >= 1 && data_length < 3) { + woal_set_get_hs_params(priv, MLAN_ACT_GET, MOAL_IOCTL_WAIT, + &hscfg); + } + + if (data_length) + hscfg.conditions = data[0]; + if (data_length >= 2) + hscfg.gpio = data[1]; + if (data_length == 3) + hscfg.gap = data[2]; + + if ((invoke_hostcmd == MTRUE) && (action == MLAN_ACT_SET)) { + /* Need to issue an extra IOCTL first to set up parameters */ + hscfg.is_invoke_hostcmd = MFALSE; + if (MLAN_STATUS_SUCCESS != + woal_set_get_hs_params(priv, MLAN_ACT_SET, MOAL_IOCTL_WAIT, + &hscfg)) { + ret = -EFAULT; + goto done; + } + } + hscfg.is_invoke_hostcmd = invoke_hostcmd; + if (MLAN_STATUS_SUCCESS != + woal_set_get_hs_params(priv, action, MOAL_IOCTL_WAIT, &hscfg)) { + ret = -EFAULT; + goto done; + } + + if (action == MLAN_ACT_GET) { + data[0] = hscfg.conditions; + data[1] = hscfg.gpio; + data[2] = hscfg.gap; + wrq->u.data.length = 3; + if (copy_to_user + (wrq->u.data.pointer, data, + sizeof(int) * wrq->u.data.length)) { + PRINTM(MERROR, "Copy to user failed\n"); + ret = -EFAULT; + goto done; + } + } +done: + LEAVE(); + return ret; +} + +/** + * @brief Set Host Sleep parameters + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int +woal_hs_setpara(moal_private *priv, struct iwreq *wrq) +{ + int ret = 0; + int data_length = wrq->u.data.length; + + ENTER(); + + if (data_length >= 1 && data_length <= 3) { + ret = woal_hs_cfg(priv, wrq, MFALSE); + goto done; + } else { + PRINTM(MERROR, "Invalid arguments\n"); + ret = -EINVAL; + goto done; + } +done: + LEAVE(); + return ret; +} + +/** + * @brief Get/Set inactivity timeout extend + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int +woal_inactivity_timeout_ext(moal_private *priv, struct iwreq *wrq) +{ + int data[4], copy_len; + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_pm_cfg *pmcfg = NULL; + pmlan_ds_inactivity_to inac_to = NULL; + int data_length = wrq->u.data.length; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + copy_len = MIN(sizeof(data), sizeof(int) * data_length); + memset(data, 0, sizeof(data)); + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + pmcfg = (mlan_ds_pm_cfg *)req->pbuf; + inac_to = &pmcfg->param.inactivity_to; + pmcfg->sub_command = MLAN_OID_PM_CFG_INACTIVITY_TO; + req->req_id = MLAN_IOCTL_PM_CFG; + + if ((data_length != 0 && data_length != 3 && data_length != 4) || + sizeof(int) * data_length > sizeof(data)) { + PRINTM(MERROR, "Invalid number of parameters\n"); + ret = -EINVAL; + goto done; + } + + req->action = MLAN_ACT_GET; + if (data_length) { + if (copy_from_user(data, wrq->u.data.pointer, copy_len)) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + req->action = MLAN_ACT_SET; + inac_to->timeout_unit = data[0]; + inac_to->unicast_timeout = data[1]; + inac_to->mcast_timeout = data[2]; + inac_to->ps_entry_timeout = data[3]; + } + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + /* Copy back current values regardless of GET/SET */ + data[0] = inac_to->timeout_unit; + data[1] = inac_to->unicast_timeout; + data[2] = inac_to->mcast_timeout; + data[3] = inac_to->ps_entry_timeout; + + if (req->action == MLAN_ACT_GET) { + wrq->u.data.length = 4; + if (copy_to_user + (wrq->u.data.pointer, data, + wrq->u.data.length * sizeof(int))) { + PRINTM(MERROR, "Copy to user failed\n"); + ret = -EFAULT; + goto done; + } + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get system clock + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int +woal_ecl_sys_clock(moal_private *priv, struct iwreq *wrq) +{ + int data[64], copy_len; + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *cfg = NULL; + int data_length = wrq->u.data.length; + int i = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + memset(data, 0, sizeof(data)); + copy_len = MIN(sizeof(data), sizeof(int) * data_length); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + cfg = (mlan_ds_misc_cfg *)req->pbuf; + cfg->sub_command = MLAN_OID_MISC_SYS_CLOCK; + req->req_id = MLAN_IOCTL_MISC_CFG; + + if (!data_length) + req->action = MLAN_ACT_GET; + else if (data_length <= MLAN_MAX_CLK_NUM) { + req->action = MLAN_ACT_SET; + if (copy_from_user(data, wrq->u.data.pointer, copy_len)) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + } else { + PRINTM(MERROR, "Invalid arguments\n"); + ret = -EINVAL; + goto done; + } + + if (req->action == MLAN_ACT_GET) { + /* Get configurable clocks */ + cfg->param.sys_clock.sys_clk_type = MLAN_CLK_CONFIGURABLE; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + /* Current system clock */ + data[0] = (int)cfg->param.sys_clock.cur_sys_clk; + wrq->u.data.length = 1; + + data_length = + MIN(cfg->param.sys_clock.sys_clk_num, MLAN_MAX_CLK_NUM); + + /* Configurable clocks */ + for (i = 0; i < data_length; i++) { + data[i + wrq->u.data.length] = + (int)cfg->param.sys_clock.sys_clk[i]; + } + wrq->u.data.length += data_length; + + /* Get supported clocks */ + cfg->param.sys_clock.sys_clk_type = MLAN_CLK_SUPPORTED; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + data_length = + MIN(cfg->param.sys_clock.sys_clk_num, MLAN_MAX_CLK_NUM); + + /* Supported clocks */ + for (i = 0; i < data_length; i++) { + data[i + wrq->u.data.length] = + (int)cfg->param.sys_clock.sys_clk[i]; + } + + wrq->u.data.length += data_length; + + if (copy_to_user + (wrq->u.data.pointer, data, + sizeof(int) * wrq->u.data.length)) { + PRINTM(MERROR, "Copy to user failed\n"); + ret = -EFAULT; + goto done; + } + } else { + /* Set configurable clocks */ + cfg->param.sys_clock.sys_clk_type = MLAN_CLK_CONFIGURABLE; + cfg->param.sys_clock.sys_clk_num = + MIN(MLAN_MAX_CLK_NUM, data_length); + for (i = 0; i < cfg->param.sys_clock.sys_clk_num; i++) + cfg->param.sys_clock.sys_clk[i] = (t_u16)data[i]; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get Band and Adhoc-band setting + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int +woal_band_cfg(moal_private *priv, struct iwreq *wrq) +{ + int ret = 0; + unsigned int i; + int data[4]; + int user_data_len = wrq->u.data.length, copy_len; + t_u32 infra_band = 0; + t_u32 adhoc_band = 0; + t_u32 adhoc_channel = 0; + t_u32 adhoc_chan_bandwidth = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_radio_cfg *radio_cfg = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (sizeof(int) * user_data_len > sizeof(data)) { + PRINTM(MERROR, "Too many arguments\n"); + LEAVE(); + return -EINVAL; + } + + if (user_data_len > 0) { + if (priv->media_connected == MTRUE) { + LEAVE(); + return -EOPNOTSUPP; + } + } + + copy_len = MIN(sizeof(data), sizeof(int) * user_data_len); + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_radio_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto error; + } + radio_cfg = (mlan_ds_radio_cfg *)req->pbuf; + radio_cfg->sub_command = MLAN_OID_BAND_CFG; + req->req_id = MLAN_IOCTL_RADIO_CFG; + + if (wrq->u.data.length == 0) { + /* Get config_bands, adhoc_start_band and adhoc_channel values from MLAN */ + req->action = MLAN_ACT_GET; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto error; + } + /* Infra Band */ + data[0] = radio_cfg->param.band_cfg.config_bands; + /* Adhoc Band */ + data[1] = radio_cfg->param.band_cfg.adhoc_start_band; + /* Adhoc Channel */ + data[2] = radio_cfg->param.band_cfg.adhoc_channel; + wrq->u.data.length = 3; + if (radio_cfg->param.band_cfg.adhoc_start_band & BAND_GN + || radio_cfg->param.band_cfg.adhoc_start_band & BAND_AN) { + /* secondary bandwidth */ + data[3] = + radio_cfg->param.band_cfg.adhoc_chan_bandwidth; + wrq->u.data.length = 4; + } + + if (copy_to_user + (wrq->u.data.pointer, data, + sizeof(int) * wrq->u.data.length)) { + ret = -EFAULT; + goto error; + } + } else { + if (copy_from_user(data, wrq->u.data.pointer, copy_len)) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto error; + } + + /* To support only */ + infra_band = data[0]; + for (i = 0; i < sizeof(SupportedInfraBand); i++) + if (infra_band == SupportedInfraBand[i]) + break; + if (i == sizeof(SupportedInfraBand)) { + ret = -EINVAL; + goto error; + } + + /* Set Adhoc band */ + if (user_data_len >= 2) { + adhoc_band = data[1]; + for (i = 0; i < sizeof(SupportedAdhocBand); i++) + if (adhoc_band == SupportedAdhocBand[i]) + break; + if (i == sizeof(SupportedAdhocBand)) { + ret = -EINVAL; + goto error; + } + } + + /* Set Adhoc channel */ + if (user_data_len >= 3) { + adhoc_channel = data[2]; + if (adhoc_channel == 0) { + /* Check if specified adhoc channel is non-zero */ + ret = -EINVAL; + goto error; + } + } + if (user_data_len == 4) { + if (!(adhoc_band & (BAND_GN | BAND_AN))) { + PRINTM(MERROR, + "11n is not enabled for adhoc, can not set HT/VHT channel bandwidth\n"); + ret = -EINVAL; + goto error; + } + adhoc_chan_bandwidth = data[3]; + if ((adhoc_chan_bandwidth != CHANNEL_BW_20MHZ) && + (adhoc_chan_bandwidth != CHANNEL_BW_40MHZ_ABOVE) && + (adhoc_chan_bandwidth != CHANNEL_BW_40MHZ_BELOW) + ) { + PRINTM(MERROR, + "Invalid secondary channel bandwidth, only allowed 0, 1, 3 or 4\n"); + ret = -EINVAL; + goto error; + } + } + /* Set config_bands and adhoc_start_band values to MLAN */ + req->action = MLAN_ACT_SET; + radio_cfg->param.band_cfg.config_bands = infra_band; + radio_cfg->param.band_cfg.adhoc_start_band = adhoc_band; + radio_cfg->param.band_cfg.adhoc_channel = adhoc_channel; + radio_cfg->param.band_cfg.adhoc_chan_bandwidth = + adhoc_chan_bandwidth; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto error; + } + } + +error: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Read/Write adapter registers value + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int +woal_reg_read_write(moal_private *priv, struct iwreq *wrq) +{ + int data[3], copy_len; + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_reg_mem *reg = NULL; + int data_length = wrq->u.data.length; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + memset(data, 0, sizeof(data)); + copy_len = MIN(sizeof(data), sizeof(int) * data_length); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_reg_mem)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + reg = (mlan_ds_reg_mem *)req->pbuf; + reg->sub_command = MLAN_OID_REG_RW; + req->req_id = MLAN_IOCTL_REG_MEM; + + if (data_length == 2) { + req->action = MLAN_ACT_GET; + } else if (data_length == 3) { + req->action = MLAN_ACT_SET; + } else { + ret = -EINVAL; + goto done; + } + if (copy_from_user(data, wrq->u.data.pointer, copy_len)) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + reg->param.reg_rw.type = (t_u32)data[0]; + reg->param.reg_rw.offset = (t_u32)data[1]; + if (data_length == 3) + reg->param.reg_rw.value = (t_u32)data[2]; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (req->action == MLAN_ACT_GET) { + if (copy_to_user + (wrq->u.data.pointer, ®->param.reg_rw.value, + sizeof(int))) { + PRINTM(MERROR, "Copy to user failed\n"); + ret = -EFAULT; + goto done; + } + wrq->u.data.length = 1; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Read the EEPROM contents of the card + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int +woal_read_eeprom(moal_private *priv, struct iwreq *wrq) +{ + int data[2], copy_len; + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_reg_mem *reg = NULL; + int data_length = wrq->u.data.length; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + memset(data, 0, sizeof(data)); + copy_len = MIN(sizeof(data), sizeof(int) * data_length); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_reg_mem)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + reg = (mlan_ds_reg_mem *)req->pbuf; + reg->sub_command = MLAN_OID_EEPROM_RD; + req->req_id = MLAN_IOCTL_REG_MEM; + + if (data_length == 2) { + req->action = MLAN_ACT_GET; + } else { + ret = -EINVAL; + goto done; + } + if (copy_from_user(data, wrq->u.data.pointer, copy_len)) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + + reg->param.rd_eeprom.offset = (t_u16)data[0]; + reg->param.rd_eeprom.byte_count = (t_u16)data[1]; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (req->action == MLAN_ACT_GET) { + wrq->u.data.length = reg->param.rd_eeprom.byte_count; + if (copy_to_user + (wrq->u.data.pointer, reg->param.rd_eeprom.value, + MIN(wrq->u.data.length, MAX_EEPROM_DATA))) { + PRINTM(MERROR, "Copy to user failed\n"); + ret = -EFAULT; + goto done; + } + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Read/Write device memory value + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int +woal_mem_read_write(moal_private *priv, struct iwreq *wrq) +{ + t_u32 data[2]; + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_reg_mem *reg_mem = NULL; + int data_length = wrq->u.data.length, copy_len; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + memset(data, 0, sizeof(data)); + copy_len = MIN(sizeof(data), sizeof(int) * data_length); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_reg_mem)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + reg_mem = (mlan_ds_reg_mem *)req->pbuf; + reg_mem->sub_command = MLAN_OID_MEM_RW; + req->req_id = MLAN_IOCTL_REG_MEM; + + if (data_length == 1) { + PRINTM(MINFO, "MEM_RW: GET\n"); + req->action = MLAN_ACT_GET; + } else if (data_length == 2) { + PRINTM(MINFO, "MEM_RW: SET\n"); + req->action = MLAN_ACT_SET; + } else { + ret = -EINVAL; + goto done; + } + if (copy_from_user(data, wrq->u.data.pointer, copy_len)) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + + reg_mem->param.mem_rw.addr = (t_u32)data[0]; + if (data_length == 2) + reg_mem->param.mem_rw.value = (t_u32)data[1]; + + PRINTM(MINFO, "MEM_RW: Addr=0x%x, Value=0x%x\n", + (int)reg_mem->param.mem_rw.addr, + (int)reg_mem->param.mem_rw.value); + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (req->action == MLAN_ACT_GET) { + if (copy_to_user + (wrq->u.data.pointer, ®_mem->param.mem_rw.value, + sizeof(int))) { + PRINTM(MERROR, "Copy to user failed\n"); + ret = -EFAULT; + goto done; + } + wrq->u.data.length = 1; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get network monitor configurations + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int +woal_net_monitor_ioctl(moal_private *priv, struct iwreq *wrq) +{ + int user_data_len = wrq->u.data.length; + int data[5] = { 0 }, copy_len; + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *misc = NULL; + mlan_ds_misc_net_monitor *net_mon = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + copy_len = MIN(sizeof(data), sizeof(int) * user_data_len); + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + LEAVE(); + return -ENOMEM; + } + misc = (mlan_ds_misc_cfg *)req->pbuf; + net_mon = (mlan_ds_misc_net_monitor *)&misc->param.net_mon; + misc->sub_command = MLAN_OID_MISC_NET_MONITOR; + req->req_id = MLAN_IOCTL_MISC_CFG; + + if (!user_data_len) { + req->action = MLAN_ACT_GET; + } else if (user_data_len == 1 || user_data_len == 4 + || user_data_len == 5) { + if (copy_from_user(data, wrq->u.data.pointer, copy_len)) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + if (data[0] != MTRUE && data[0] != MFALSE) { + PRINTM(MERROR, + "NET_MON: Activity should be enable(=1)/disable(=0)\n"); + ret = -EINVAL; + goto done; + } + net_mon->enable_net_mon = data[0]; + if (data[0] == MTRUE) { + int i; + if (user_data_len != 4 && user_data_len != 5) { + PRINTM(MERROR, + "NET_MON: Invalid number of args!\n"); + ret = -EINVAL; + goto done; + } + /* Supported filter flags */ + if (!data[1] || data[1] & + ~(MLAN_NETMON_DATA_FRAME | + MLAN_NETMON_MANAGEMENT_FRAME | + MLAN_NETMON_CONTROL_FRAME)) { + PRINTM(MERROR, + "NET_MON: Invalid filter flag\n"); + ret = -EINVAL; + goto done; + } + /* Supported bands */ + for (i = 0; i < sizeof(SupportedAdhocBand); i++) + if (data[2] == SupportedAdhocBand[i]) + break; + if (i == sizeof(SupportedAdhocBand)) { + PRINTM(MERROR, "NET_MON: Invalid band\n"); + ret = -EINVAL; + goto done; + } + /* Supported channel */ + if (data[3] < 1 || data[3] > MLAN_MAX_CHANNEL) { + PRINTM(MERROR, + "NET_MON: Invalid channel number\n"); + ret = -EINVAL; + goto done; + } + if (user_data_len == 5) { + /* Secondary channel offset */ + if (!(data[2] & (BAND_GN | BAND_AN))) { + PRINTM(MERROR, + "No 11n in band, can not set " + "secondary channel offset\n"); + ret = -EINVAL; + goto done; + } + if ((data[4] != CHANNEL_BW_20MHZ) && + (data[4] != CHANNEL_BW_40MHZ_ABOVE) && + (data[4] != CHANNEL_BW_40MHZ_BELOW) + ) { + PRINTM(MERROR, + "Invalid secondary channel bandwidth, " + "only allowed 0, 1, 3 or 4\n"); + ret = -EINVAL; + goto done; + } + net_mon->chan_bandwidth = data[4]; + } + net_mon->filter_flag = data[1]; + net_mon->band = data[2]; + net_mon->channel = data[3]; + } + req->action = MLAN_ACT_SET; + } else { + PRINTM(MERROR, "NET_MON: Invalid number of args!\n"); + ret = -EINVAL; + goto done; + } + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + data[0] = net_mon->enable_net_mon; + data[1] = net_mon->filter_flag; + data[2] = net_mon->band; + data[3] = net_mon->channel; + data[4] = net_mon->chan_bandwidth; + wrq->u.data.length = 5; + if (copy_to_user + (wrq->u.data.pointer, data, sizeof(int) * wrq->u.data.length)) { + PRINTM(MERROR, "Copy to user failed\n"); + ret = -EFAULT; + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Get LOG + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int +woal_get_log(moal_private *priv, struct iwreq *wrq) +{ + int ret = 0; + mlan_ds_get_stats stats; + char *buf = NULL; + int i = 0; + + ENTER(); + + PRINTM(MINFO, " GET STATS\n"); + buf = kmalloc(GETLOG_BUFSIZE, GFP_KERNEL); + if (!buf) { + PRINTM(MERROR, "kmalloc failed!\n"); + ret = -ENOMEM; + goto done; + } + + memset(&stats, 0, sizeof(mlan_ds_get_stats)); + if (MLAN_STATUS_SUCCESS != + woal_get_stats_info(priv, MOAL_IOCTL_WAIT, &stats)) { + ret = -EFAULT; + goto done; + } + + if (wrq->u.data.pointer) { + sprintf(buf, "\n" + "mcasttxframe %u\n" + "failed %u\n" + "retry %u\n" + "multiretry %u\n" + "framedup %u\n" + "rtssuccess %u\n" + "rtsfailure %u\n" + "ackfailure %u\n" + "rxfrag %u\n" + "mcastrxframe %u\n" + "fcserror %u\n" + "txframe %u\n" + "wepicverrcnt-1 %u\n" + "wepicverrcnt-2 %u\n" + "wepicverrcnt-3 %u\n" + "wepicverrcnt-4 %u\n" + "beacon_rcnt %u\n" + "beacon_mcnt %u\n", + stats.mcast_tx_frame, + stats.failed, + stats.retry, + stats.multi_retry, + stats.frame_dup, + stats.rts_success, + stats.rts_failure, + stats.ack_failure, + stats.rx_frag, + stats.mcast_rx_frame, + stats.fcs_error, + stats.tx_frame, + stats.wep_icv_error[0], + stats.wep_icv_error[1], + stats.wep_icv_error[2], + stats.wep_icv_error[3], + stats.bcn_rcv_cnt, stats.bcn_miss_cnt); + if (priv->phandle->fw_getlog_enable) { + sprintf(buf + strlen(buf), "tx_frag_cnt %u\n", + stats.tx_frag_cnt); + sprintf(buf + strlen(buf), "qos_tx_frag_cnt "); + for (i = 0; i < 8; i++) { + sprintf(buf + strlen(buf), "%u ", + stats.qos_tx_frag_cnt[i]); + } + sprintf(buf + strlen(buf), "\nqos_failed_cnt "); + for (i = 0; i < 8; i++) { + sprintf(buf + strlen(buf), "%u ", + stats.qos_failed_cnt[i]); + } + sprintf(buf + strlen(buf), "\nqos_retry_cnt "); + for (i = 0; i < 8; i++) { + sprintf(buf + strlen(buf), "%u ", + stats.qos_retry_cnt[i]); + } + sprintf(buf + strlen(buf), "\nqos_multi_retry_cnt "); + for (i = 0; i < 8; i++) { + sprintf(buf + strlen(buf), "%u ", + stats.qos_multi_retry_cnt[i]); + } + sprintf(buf + strlen(buf), "\nqos_frm_dup_cnt "); + for (i = 0; i < 8; i++) { + sprintf(buf + strlen(buf), "%u ", + stats.qos_frm_dup_cnt[i]); + } + sprintf(buf + strlen(buf), "\nqos_rts_suc_cnt "); + for (i = 0; i < 8; i++) { + sprintf(buf + strlen(buf), "%u ", + stats.qos_rts_suc_cnt[i]); + } + sprintf(buf + strlen(buf), + "\nqos_rts_failure_cnt "); + for (i = 0; i < 8; i++) { + sprintf(buf + strlen(buf), "%u ", + stats.qos_rts_failure_cnt[i]); + } + sprintf(buf + strlen(buf), "\nqos_ack_failure_cnt "); + for (i = 0; i < 8; i++) { + sprintf(buf + strlen(buf), "%u ", + stats.qos_ack_failure_cnt[i]); + } + sprintf(buf + strlen(buf), "\nqos_rx_frag_cnt "); + for (i = 0; i < 8; i++) { + sprintf(buf + strlen(buf), "%u ", + stats.qos_rx_frag_cnt[i]); + } + sprintf(buf + strlen(buf), "\nqos_tx_frm_cnt "); + for (i = 0; i < 8; i++) { + sprintf(buf + strlen(buf), "%u ", + stats.qos_tx_frm_cnt[i]); + } + sprintf(buf + strlen(buf), "\nqos_discarded_frm_cnt "); + for (i = 0; i < 8; i++) { + sprintf(buf + strlen(buf), "%u ", + stats.qos_discarded_frm_cnt[i]); + } + sprintf(buf + strlen(buf), "\nqos_mpdus_rx_cnt "); + for (i = 0; i < 8; i++) { + sprintf(buf + strlen(buf), "%u ", + stats.qos_mpdus_rx_cnt[i]); + } + sprintf(buf + strlen(buf), "\nqos_retries_rx_cnt "); + for (i = 0; i < 8; i++) { + sprintf(buf + strlen(buf), "%u ", + stats.qos_retries_rx_cnt[i]); + } + sprintf(buf + strlen(buf), + "\nmgmt_ccmp_replays %u\n" + "tx_amsdu_cnt %u\n" + "failed_amsdu_cnt %u\n" + "retry_amsdu_cnt %u\n" + "multi_retry_amsdu_cnt %u\n" + "tx_octets_in_amsdu_cnt %llu\n" + "amsdu_ack_failure_cnt %u\n" + "rx_amsdu_cnt %u\n" + "rx_octets_in_amsdu_cnt %llu\n" + "tx_ampdu_cnt %u\n" + "tx_mpdus_in_ampdu_cnt %u\n" + "tx_octets_in_ampdu_cnt %llu\n" + "ampdu_rx_cnt %u\n" + "mpdu_in_rx_ampdu_cnt %u\n" + "rx_octets_in_ampdu_cnt %llu\n" + "ampdu_delimiter_crc_error_cnt %u\n", + stats.mgmt_ccmp_replays, stats.tx_amsdu_cnt, + stats.failed_amsdu_cnt, stats.retry_amsdu_cnt, + stats.multi_retry_amsdu_cnt, + stats.tx_octets_in_amsdu_cnt, + stats.amsdu_ack_failure_cnt, stats.rx_amsdu_cnt, + stats.rx_octets_in_amsdu_cnt, + stats.tx_ampdu_cnt, stats.tx_mpdus_in_ampdu_cnt, + stats.tx_octets_in_ampdu_cnt, + stats.ampdu_rx_cnt, stats.mpdu_in_rx_ampdu_cnt, + stats.rx_octets_in_ampdu_cnt, + stats.ampdu_delimiter_crc_error_cnt); + + } + wrq->u.data.length = strlen(buf) + 1; + if (copy_to_user(wrq->u.data.pointer, buf, wrq->u.data.length)) { + PRINTM(MERROR, "Copy to user failed\n"); + ret = -EFAULT; + } + } +done: + kfree(buf); + LEAVE(); + return ret; +} + +/** + * @brief Deauthenticate + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int +woal_deauth(moal_private *priv, struct iwreq *wrq) +{ + int ret = 0; + struct sockaddr saddr; + + ENTER(); + if (wrq->u.data.length) { + /* Deauth mentioned BSSID */ + if (copy_from_user(&saddr, wrq->u.data.pointer, sizeof(saddr))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + if (MLAN_STATUS_SUCCESS != + woal_disconnect(priv, MOAL_IOCTL_WAIT, + (t_u8 *)saddr.sa_data, + DEF_DEAUTH_REASON_CODE)) { + ret = -EFAULT; + goto done; + } + } else { + if (MLAN_STATUS_SUCCESS != + woal_disconnect(priv, MOAL_IOCTL_WAIT, NULL, + DEF_DEAUTH_REASON_CODE)) + ret = -EFAULT; + } +done: + LEAVE(); + return ret; +} + +/** + * @brief Set/Get TX power configurations + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int +woal_tx_power_cfg(moal_private *priv, struct iwreq *wrq) +{ + int data[5], user_data_len, copy_len; + int ret = 0; + mlan_bss_info bss_info; + mlan_ds_power_cfg *pcfg = NULL; + mlan_ioctl_req *req = NULL; + int power_data[MAX_POWER_TABLE_SIZE]; + int i, power_ext_len = 0; + int *ptr = power_data; + mlan_power_group *pwr_grp = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + ENTER(); + + memset(&bss_info, 0, sizeof(bss_info)); + woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info); + + memset(data, 0, sizeof(data)); + user_data_len = wrq->u.data.length; + copy_len = MIN(sizeof(data), sizeof(int) * user_data_len); + + if (user_data_len) { + if (sizeof(int) * user_data_len > sizeof(data)) { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + if (copy_from_user(data, wrq->u.data.pointer, copy_len)) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + switch (user_data_len) { + case 1: + if (data[0] != 0xFF) + ret = -EINVAL; + break; + case 2: + case 4: + if (data[0] == 0xFF) { + ret = -EINVAL; + break; + } + if (data[1] < bss_info.min_power_level) { + PRINTM(MERROR, + "The set powercfg rate value %d dBm is out of range (%d dBm-%d dBm)!\n", + data[1], (int)bss_info.min_power_level, + (int)bss_info.max_power_level); + ret = -EINVAL; + break; + } + if (user_data_len == 4) { + if (data[1] > data[2]) { + PRINTM(MERROR, + "Min power should be less than maximum!\n"); + ret = -EINVAL; + break; + } + if (data[3] < 0) { + PRINTM(MERROR, + "Step should not less than 0!\n"); + ret = -EINVAL; + break; + } + if (data[2] > bss_info.max_power_level) { + PRINTM(MERROR, + "The set powercfg rate value %d dBm is out of range (%d dBm-%d dBm)!\n", + data[2], + (int)bss_info.min_power_level, + (int)bss_info.max_power_level); + ret = -EINVAL; + break; + } + if (data[3] > data[2] - data[1]) { + PRINTM(MERROR, + "Step should not greater than power difference!\n"); + ret = -EINVAL; + break; + } + } + break; + default: + ret = -EINVAL; + break; + } + if (ret) + goto done; + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_power_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + pcfg = (mlan_ds_power_cfg *)req->pbuf; + pcfg->sub_command = MLAN_OID_POWER_CFG_EXT; + req->req_id = MLAN_IOCTL_POWER_CFG; + if (!user_data_len) + req->action = MLAN_ACT_GET; + else { + req->action = MLAN_ACT_SET; + if (data[0] == 0xFF) + pcfg->param.power_ext.power_group[0].rate_format = + TX_PWR_CFG_AUTO_CTRL_OFF; + else { + pcfg->param.power_ext.power_group[0].power_step = 0; + pcfg->param.power_ext.power_group[0].first_rate_ind = + data[0]; + pcfg->param.power_ext.power_group[0].last_rate_ind = + data[0]; + if (data[0] <= 11) { + pcfg->param.power_ext.power_group[0]. + rate_format = MLAN_RATE_FORMAT_LG; + pcfg->param.power_ext.power_group[0].bandwidth = + MLAN_HT_BW20; + } else if (data[0] <= 27) { + pcfg->param.power_ext.power_group[0]. + rate_format = MLAN_RATE_FORMAT_HT; + pcfg->param.power_ext.power_group[0].bandwidth = + MLAN_HT_BW20; + pcfg->param.power_ext.power_group[0]. + first_rate_ind -= 12; + pcfg->param.power_ext.power_group[0]. + last_rate_ind -= 12; + } else if ((140 <= data[0]) && (data[0] <= 155)) { + pcfg->param.power_ext.power_group[0]. + rate_format = MLAN_RATE_FORMAT_HT; + pcfg->param.power_ext.power_group[0].bandwidth = + MLAN_HT_BW40; + pcfg->param.power_ext.power_group[0]. + first_rate_ind -= 140; + pcfg->param.power_ext.power_group[0]. + last_rate_ind -= 140; + } + if (user_data_len == 2) { + pcfg->param.power_ext.power_group[0].power_min = + data[1]; + pcfg->param.power_ext.power_group[0].power_max = + data[1]; + } else if (user_data_len == 4) { + pcfg->param.power_ext.power_group[0].power_min = + data[1]; + pcfg->param.power_ext.power_group[0].power_max = + data[2]; + pcfg->param.power_ext.power_group[0]. + power_step = data[3]; + } + pcfg->param.power_ext.num_pwr_grp = 1; + } + } + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + if (!user_data_len) { + /* GET operation */ + i = 0; + power_ext_len = 0; + ptr = power_data; + while ((i < pcfg->param.power_ext.num_pwr_grp) && + (power_ext_len < MAX_POWER_TABLE_SIZE)) { + pwr_grp = &pcfg->param.power_ext.power_group[i]; + if (pwr_grp->rate_format == MLAN_RATE_FORMAT_HT) { + if (pwr_grp->bandwidth == MLAN_HT_BW20) { + pwr_grp->first_rate_ind += 12; + pwr_grp->last_rate_ind += 12; + } else if (pwr_grp->bandwidth == MLAN_HT_BW40) { + pwr_grp->first_rate_ind += 140; + pwr_grp->last_rate_ind += 140; + } + } + + if ((pwr_grp->rate_format == MLAN_RATE_FORMAT_LG) || + (pwr_grp->rate_format == MLAN_RATE_FORMAT_HT)) { + *ptr = pwr_grp->first_rate_ind; + ptr++; + *ptr = pwr_grp->last_rate_ind; + ptr++; + *ptr = pwr_grp->power_min; + ptr++; + *ptr = pwr_grp->power_max; + ptr++; + *ptr = pwr_grp->power_step; + ptr++; + power_ext_len += 5; + } + i++; + } + if (copy_to_user(wrq->u.data.pointer, (t_u8 *)power_data, + sizeof(int) * power_ext_len)) { + ret = -EFAULT; + goto done; + } + wrq->u.data.length = power_ext_len; + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Get Tx/Rx data rates + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int +woal_get_txrx_rate(moal_private *priv, struct iwreq *wrq) +{ + int ret = 0; + mlan_ds_rate *rate = NULL; + mlan_ioctl_req *req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_rate)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + rate = (mlan_ds_rate *)req->pbuf; + rate->sub_command = MLAN_OID_GET_DATA_RATE; + req->req_id = MLAN_IOCTL_RATE; + req->action = MLAN_ACT_GET; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (copy_to_user + (wrq->u.data.pointer, (t_u8 *)&rate->param.data_rate, + sizeof(int) * 2)) { + ret = -EFAULT; + goto done; + } + wrq->u.data.length = 2; +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Turn on/off the sdio clock + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return 0/MLAN_STATUS_SUCCESS --success, otherwise fail + */ +static int +woal_sdio_clock_ioctl(moal_private *priv, struct iwreq *wrq) +{ + int ret = 0; + int data = 2; + /* Initialize the clock state as on */ + static int clock_state = 1; + + ENTER(); + + if (wrq->u.data.length) { + if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + } else { + wrq->u.data.length = sizeof(clock_state) / sizeof(int); + if (copy_to_user + (wrq->u.data.pointer, &clock_state, sizeof(int))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + + goto done; + } + switch (data) { + case CMD_DISABLED: + PRINTM(MINFO, "SDIO clock is turned off\n"); + ret = woal_sdio_set_bus_clock(priv->phandle, MFALSE); + clock_state = data; + break; + case CMD_ENABLED: + PRINTM(MINFO, "SDIO clock is turned on\n"); + ret = woal_sdio_set_bus_clock(priv->phandle, MTRUE); + clock_state = data; + break; + default: + ret = -EINVAL; + PRINTM(MINFO, "sdioclock: wrong parameter\n"); + break; + } +done: + LEAVE(); + return ret; +} + +/** + * @brief Set/Get beacon interval + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int +woal_beacon_interval(moal_private *priv, struct iwreq *wrq) +{ + int ret = 0; + mlan_ds_bss *bss = NULL; + mlan_ioctl_req *req = NULL; + int bcn = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (wrq->u.data.length) { + if (copy_from_user(&bcn, wrq->u.data.pointer, sizeof(int))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + if ((bcn < MLAN_MIN_BEACON_INTERVAL) || + (bcn > MLAN_MAX_BEACON_INTERVAL)) { + ret = -EINVAL; + goto done; + } + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + bss = (mlan_ds_bss *)req->pbuf; + bss->sub_command = MLAN_OID_IBSS_BCN_INTERVAL; + req->req_id = MLAN_IOCTL_BSS; + if (!wrq->u.data.length) + req->action = MLAN_ACT_GET; + else { + req->action = MLAN_ACT_SET; + bss->param.bcn_interval = bcn; + } + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (copy_to_user + (wrq->u.data.pointer, (t_u8 *)&bss->param.bcn_interval, + sizeof(int))) { + ret = -EFAULT; + goto done; + } + wrq->u.data.length = 1; +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get ATIM window + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int +woal_atim_window(moal_private *priv, struct iwreq *wrq) +{ + int ret = 0; + mlan_ds_bss *bss = NULL; + mlan_ioctl_req *req = NULL; + int atim = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (wrq->u.data.length) { + if (copy_from_user(&atim, wrq->u.data.pointer, sizeof(int))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + if ((atim < 0) || (atim > MLAN_MAX_ATIM_WINDOW)) { + ret = -EINVAL; + goto done; + } + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + bss = (mlan_ds_bss *)req->pbuf; + bss->sub_command = MLAN_OID_IBSS_ATIM_WINDOW; + req->req_id = MLAN_IOCTL_BSS; + if (!wrq->u.data.length) + req->action = MLAN_ACT_GET; + else { + req->action = MLAN_ACT_SET; + bss->param.atim_window = atim; + } + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (copy_to_user + (wrq->u.data.pointer, (t_u8 *)&bss->param.atim_window, + sizeof(int))) { + ret = -EFAULT; + goto done; + } + wrq->u.data.length = 1; +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get TX data rate + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int +woal_set_get_txrate(moal_private *priv, struct iwreq *wrq) +{ + int ret = 0; + mlan_ds_rate *rate = NULL; + mlan_ioctl_req *req = NULL; + int rateindex = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + if (wrq->u.data.length) { + if (copy_from_user + (&rateindex, wrq->u.data.pointer, sizeof(int))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + } + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_rate)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + rate = (mlan_ds_rate *)req->pbuf; + rate->param.rate_cfg.rate_type = MLAN_RATE_INDEX; + rate->sub_command = MLAN_OID_RATE_CFG; + req->req_id = MLAN_IOCTL_RATE; + if (!wrq->u.data.length) + req->action = MLAN_ACT_GET; + else { + req->action = MLAN_ACT_SET; + if (rateindex == AUTO_RATE) + rate->param.rate_cfg.is_rate_auto = 1; + else { + if ((rateindex != MLAN_RATE_INDEX_MCS32) && + ((rateindex < 0) || + (rateindex > MLAN_RATE_INDEX_MCS7))) { + ret = -EINVAL; + goto done; + } + } + rate->param.rate_cfg.rate = rateindex; + } + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } else { + if (wrq->u.data.length) + priv->rate_index = rateindex; + } + if (!wrq->u.data.length) { + if (rate->param.rate_cfg.is_rate_auto) + rateindex = AUTO_RATE; + else + rateindex = rate->param.rate_cfg.rate; + wrq->u.data.length = 1; + if (copy_to_user(wrq->u.data.pointer, &rateindex, sizeof(int))) + ret = -EFAULT; + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get region code + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int +woal_set_get_regioncode(moal_private *priv, struct iwreq *wrq) +{ + int ret = 0; + mlan_ds_misc_cfg *cfg = NULL; + mlan_ioctl_req *req = NULL; + int region = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (wrq->u.data.length) { + if (copy_from_user(®ion, wrq->u.data.pointer, sizeof(int))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + cfg = (mlan_ds_misc_cfg *)req->pbuf; + cfg->sub_command = MLAN_OID_MISC_REGION; + req->req_id = MLAN_IOCTL_MISC_CFG; + if (!wrq->u.data.length) + req->action = MLAN_ACT_GET; + else { + req->action = MLAN_ACT_SET; + cfg->param.region_code = region; + } + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (!wrq->u.data.length) { + wrq->u.data.length = 1; + if (copy_to_user + (wrq->u.data.pointer, &cfg->param.region_code, sizeof(int))) + ret = -EFAULT; + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get radio + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int +woal_set_get_radio(moal_private *priv, struct iwreq *wrq) +{ + int ret = 0; + mlan_bss_info bss_info; + int option = 0; + + ENTER(); + + memset(&bss_info, 0, sizeof(bss_info)); + + if (wrq->u.data.length) { + /* Set radio */ + if (copy_from_user(&option, wrq->u.data.pointer, sizeof(int))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + if (MLAN_STATUS_SUCCESS != woal_set_radio(priv, (t_u8)option)) + ret = -EFAULT; + } else { + /* Get radio status */ + woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info); + wrq->u.data.length = 1; + if (copy_to_user + (wrq->u.data.pointer, &bss_info.radio_on, + sizeof(bss_info.radio_on))) { + PRINTM(MERROR, "Copy to user failed\n"); + ret = -EFAULT; + } + } +done: + LEAVE(); + return ret; +} + +#ifdef DEBUG_LEVEL1 +/** + * @brief Get/Set the bit mask of driver debug message control + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to wrq structure + * + * @return 0 --success, otherwise fail + */ +static int +woal_drv_dbg(moal_private *priv, struct iwreq *wrq) +{ + int data[4], copy_len; + int ret = 0; + int data_length = wrq->u.data.length; + ENTER(); + + copy_len = MIN(sizeof(data), sizeof(int) * data_length); + if (!data_length) { + data[0] = drvdbg; + /* Return the current driver debug bit masks */ + if (copy_to_user(wrq->u.data.pointer, data, sizeof(int))) { + PRINTM(MERROR, "Copy to user failed\n"); + ret = -EFAULT; + goto drvdbgexit; + } + wrq->u.data.length = 1; + } else if (data_length < 3) { + /* Get the driver debug bit masks from user */ + if (copy_from_user(data, wrq->u.data.pointer, copy_len)) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto drvdbgexit; + } + drvdbg = data[0]; + /* Set the driver debug bit masks into mlan */ + woal_set_drvdbg(priv, drvdbg); + } else { + PRINTM(MERROR, "Invalid parameter number\n"); + goto drvdbgexit; + } + + printk(KERN_ALERT "drvdbg = 0x%08x\n", drvdbg); +#ifdef DEBUG_LEVEL2 + printk(KERN_ALERT "MINFO (%08x) %s\n", MINFO, + (drvdbg & MINFO) ? "X" : ""); + printk(KERN_ALERT "MWARN (%08x) %s\n", MWARN, + (drvdbg & MWARN) ? "X" : ""); + printk(KERN_ALERT "MENTRY (%08x) %s\n", MENTRY, + (drvdbg & MENTRY) ? "X" : ""); +#endif + printk(KERN_ALERT "MMPA_D (%08x) %s\n", MMPA_D, + (drvdbg & MMPA_D) ? "X" : ""); + printk(KERN_ALERT "MIF_D (%08x) %s\n", MIF_D, + (drvdbg & MIF_D) ? "X" : ""); + printk(KERN_ALERT "MFW_D (%08x) %s\n", MFW_D, + (drvdbg & MFW_D) ? "X" : ""); + printk(KERN_ALERT "MEVT_D (%08x) %s\n", MEVT_D, + (drvdbg & MEVT_D) ? "X" : ""); + printk(KERN_ALERT "MCMD_D (%08x) %s\n", MCMD_D, + (drvdbg & MCMD_D) ? "X" : ""); + printk(KERN_ALERT "MDAT_D (%08x) %s\n", MDAT_D, + (drvdbg & MDAT_D) ? "X" : ""); + printk(KERN_ALERT "MIOCTL (%08x) %s\n", MIOCTL, + (drvdbg & MIOCTL) ? "X" : ""); + printk(KERN_ALERT "MINTR (%08x) %s\n", MINTR, + (drvdbg & MINTR) ? "X" : ""); + printk(KERN_ALERT "MEVENT (%08x) %s\n", MEVENT, + (drvdbg & MEVENT) ? "X" : ""); + printk(KERN_ALERT "MCMND (%08x) %s\n", MCMND, + (drvdbg & MCMND) ? "X" : ""); + printk(KERN_ALERT "MDATA (%08x) %s\n", MDATA, + (drvdbg & MDATA) ? "X" : ""); + printk(KERN_ALERT "MERROR (%08x) %s\n", MERROR, + (drvdbg & MERROR) ? "X" : ""); + printk(KERN_ALERT "MFATAL (%08x) %s\n", MFATAL, + (drvdbg & MFATAL) ? "X" : ""); + printk(KERN_ALERT "MMSG (%08x) %s\n", MMSG, + (drvdbg & MMSG) ? "X" : ""); + +drvdbgexit: + LEAVE(); + return ret; +} +#endif /* DEBUG_LEVEL1 */ + +/** + * @brief Set/Get QoS configuration + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int +woal_set_get_qos_cfg(moal_private *priv, struct iwreq *wrq) +{ + int ret = 0; + mlan_ds_wmm_cfg *cfg = NULL; + mlan_ioctl_req *req = NULL; + int data = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_wmm_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + cfg = (mlan_ds_wmm_cfg *)req->pbuf; + cfg->sub_command = MLAN_OID_WMM_CFG_QOS; + req->req_id = MLAN_IOCTL_WMM_CFG; + if (wrq->u.data.length) { + if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + req->action = MLAN_ACT_SET; + cfg->param.qos_cfg = (t_u8)data; + } else + req->action = MLAN_ACT_GET; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + if (!wrq->u.data.length) { + data = (int)cfg->param.qos_cfg; + if (copy_to_user(wrq->u.data.pointer, &data, sizeof(int))) { + ret = -EFAULT; + goto done; + } + wrq->u.data.length = 1; + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get WWS mode + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int +woal_wws_cfg(moal_private *priv, struct iwreq *wrq) +{ + int ret = 0; + mlan_ds_misc_cfg *wws = NULL; + mlan_ioctl_req *req = NULL; + int data = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + wws = (mlan_ds_misc_cfg *)req->pbuf; + wws->sub_command = MLAN_OID_MISC_WWS; + req->req_id = MLAN_IOCTL_MISC_CFG; + if (wrq->u.data.length) { + if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + if (data != CMD_DISABLED && data != CMD_ENABLED) { + ret = -EINVAL; + goto done; + } + req->action = MLAN_ACT_SET; + wws->param.wws_cfg = data; + } else + req->action = MLAN_ACT_GET; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + if (!wrq->u.data.length) { + data = wws->param.wws_cfg; + if (copy_to_user(wrq->u.data.pointer, &data, sizeof(int))) { + ret = -EFAULT; + goto done; + } + wrq->u.data.length = 1; + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get sleep period + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int +woal_sleep_pd(moal_private *priv, struct iwreq *wrq) +{ + int ret = 0; + mlan_ds_pm_cfg *pm_cfg = NULL; + mlan_ioctl_req *req = NULL; + int data = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + pm_cfg = (mlan_ds_pm_cfg *)req->pbuf; + pm_cfg->sub_command = MLAN_OID_PM_CFG_SLEEP_PD; + req->req_id = MLAN_IOCTL_PM_CFG; + if (wrq->u.data.length) { + if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + if ((data <= MAX_SLEEP_PERIOD && data >= MIN_SLEEP_PERIOD) || + (data == 0) + || (data == SLEEP_PERIOD_RESERVED_FF) + ) { + req->action = MLAN_ACT_SET; + pm_cfg->param.sleep_period = data; + } else { + ret = -EINVAL; + goto done; + } + } else + req->action = MLAN_ACT_GET; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + if (!wrq->u.data.length) { + data = pm_cfg->param.sleep_period; + if (copy_to_user(wrq->u.data.pointer, &data, sizeof(int))) { + ret = -EFAULT; + goto done; + } + wrq->u.data.length = 1; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get module configuration + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int +woal_fw_wakeup_method(moal_private *priv, struct iwreq *wrq) +{ + int ret = 0, data[2]; + mlan_ds_pm_cfg *pm_cfg = NULL; + mlan_ioctl_req *req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + pm_cfg = (mlan_ds_pm_cfg *)req->pbuf; + + if (wrq->u.data.length > 2) { + ret = -EINVAL; + goto done; + } + if (!wrq->u.data.length) { + req->action = MLAN_ACT_GET; + } else { + req->action = MLAN_ACT_SET; + if (copy_from_user + (data, wrq->u.data.pointer, + sizeof(int) * wrq->u.data.length)) { + PRINTM(MINFO, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + if (data[0] != FW_WAKEUP_METHOD_INTERFACE && + data[0] != FW_WAKEUP_METHOD_GPIO) { + PRINTM(MERROR, "Invalid FW wake up method:%d\n", + data[0]); + ret = -EINVAL; + goto done; + } + if (data[0] == FW_WAKEUP_METHOD_GPIO) { + if (wrq->u.data.length == 1) { + PRINTM(MERROR, + "Please provide gpio pin number for FW_WAKEUP_METHOD gpio\n"); + ret = -EINVAL; + goto done; + } + pm_cfg->param.fw_wakeup_params.gpio_pin = data[1]; + } + + pm_cfg->param.fw_wakeup_params.method = data[0]; + } + + pm_cfg->sub_command = MLAN_OID_PM_CFG_FW_WAKEUP_METHOD; + req->req_id = MLAN_IOCTL_PM_CFG; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + data[0] = ((mlan_ds_pm_cfg *)req->pbuf)->param.fw_wakeup_params.method; + data[1] = + ((mlan_ds_pm_cfg *)req->pbuf)->param.fw_wakeup_params.gpio_pin; + + if (data[0] == FW_WAKEUP_METHOD_INTERFACE) + wrq->u.data.length = 1; + else + wrq->u.data.length = 2; + if (copy_to_user + (wrq->u.data.pointer, data, sizeof(int) * wrq->u.data.length)) { + ret = -EFAULT; + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Configure sleep parameters + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int +woal_sleep_params_ioctl(moal_private *priv, struct iwreq *wrq) +{ + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_pm_cfg *pm = NULL; + mlan_ds_sleep_params *psleep_params = NULL; + int data[6] = { 0 }, i, copy_len; + int data_length = wrq->u.data.length; +#ifdef DEBUG_LEVEL1 + char err_str[][35] = { {"sleep clock error in ppm"}, + {"wakeup offset in usec"}, + {"clock stabilization time in usec"}, + {"value of reserved for debug"} + }; +#endif + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + copy_len = MIN(sizeof(data), sizeof(int) * data_length); + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg)); + if (req == NULL) { + LEAVE(); + return -ENOMEM; + } + + pm = (mlan_ds_pm_cfg *)req->pbuf; + pm->sub_command = MLAN_OID_PM_CFG_SLEEP_PARAMS; + req->req_id = MLAN_IOCTL_PM_CFG; + psleep_params = (pmlan_ds_sleep_params)&pm->param.sleep_params; + + if (data_length == 0) { + req->action = MLAN_ACT_GET; + } else if (data_length == 6) { + if (copy_from_user(data, wrq->u.data.pointer, copy_len)) { + /* copy_from_user failed */ + PRINTM(MERROR, "S_PARAMS: copy from user failed\n"); + LEAVE(); + return -EINVAL; + } +#define MIN_VAL 0x0000 +#define MAX_VAL 0xFFFF + for (i = 0; i < 6; i++) { + if ((i == 3) || (i == 4)) { + /* These two cases are handled below the loop */ + continue; + } + if (data[i] < MIN_VAL || data[i] > MAX_VAL) { + PRINTM(MERROR, "Invalid %s (0-65535)!\n", + err_str[i]); + ret = -EINVAL; + goto done; + } + } + if (data[3] < 0 || data[3] > 2) { + PRINTM(MERROR, + "Invalid control periodic calibration (0-2)!\n"); + ret = -EINVAL; + goto done; + } + if (data[4] < 0 || data[4] > 2) { + PRINTM(MERROR, + "Invalid control of external sleep clock (0-2)!\n"); + ret = -EINVAL; + goto done; + } + req->action = MLAN_ACT_SET; + psleep_params->error = data[0]; + psleep_params->offset = data[1]; + psleep_params->stable_time = data[2]; + psleep_params->cal_control = data[3]; + psleep_params->ext_sleep_clk = data[4]; + psleep_params->reserved = data[5]; + } else { + ret = -EINVAL; + goto done; + } + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + data[0] = psleep_params->error; + data[1] = psleep_params->offset; + data[2] = psleep_params->stable_time; + data[3] = psleep_params->cal_control; + data[4] = psleep_params->ext_sleep_clk; + data[5] = psleep_params->reserved; + wrq->u.data.length = 6; + + if (copy_to_user(wrq->u.data.pointer, data, sizeof(int) * + wrq->u.data.length)) { + PRINTM(MERROR, "QCONFIG: copy to user failed\n"); + ret = -EFAULT; + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Control Coalescing status Enable/Disable + * + * @param priv Pointer to the moal_private driver data struct + * @param wrq Pointer to user data + * + * @return 0 --success, otherwise fail + */ +static int +woal_coalescing_status_ioctl(moal_private *priv, struct iwreq *wrq) +{ + int ret = 0; + mlan_ds_misc_cfg *pcoal = NULL; + mlan_ioctl_req *req = NULL; + char buf[8]; + struct iwreq *wreq = (struct iwreq *)wrq; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + pcoal = (mlan_ds_misc_cfg *)req->pbuf; + + memset(buf, 0, sizeof(buf)); + if (!wrq->u.data.length) { + req->action = MLAN_ACT_GET; + } else { + req->action = MLAN_ACT_SET; + if (copy_from_user(buf, wrq->u.data.pointer, + MIN(sizeof(buf) - 1, wreq->u.data.length))) { + PRINTM(MINFO, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + if (buf[0] == 1) + pcoal->param.coalescing_status = + MLAN_MISC_COALESCING_ENABLE; + else + pcoal->param.coalescing_status = + MLAN_MISC_COALESCING_DISABLE; + } + + req->req_id = MLAN_IOCTL_MISC_CFG; + pcoal->sub_command = MLAN_OID_MISC_COALESCING_STATUS; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + buf[0] = ((mlan_ds_misc_cfg *)req->pbuf)->param.coalescing_status; + + if (copy_to_user(wrq->u.data.pointer, buf, wrq->u.data.length)) { + ret = -EFAULT; + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/get user provisioned local power constraint + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * @return 0 --success, otherwise fail + */ +static int +woal_set_get_11h_local_pwr_constraint(moal_private *priv, struct iwreq *wrq) +{ + int ret = 0, data = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_11h_cfg *ds_11hcfg = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11h_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + ds_11hcfg = (mlan_ds_11h_cfg *)req->pbuf; + if (wrq->u.data.length) { + if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int))) { + PRINTM(MINFO, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + ds_11hcfg->param.usr_local_power_constraint = (t_s8)data; + req->action = MLAN_ACT_SET; + } else + req->action = MLAN_ACT_GET; + + ds_11hcfg->sub_command = MLAN_OID_11H_LOCAL_POWER_CONSTRAINT; + req->req_id = MLAN_IOCTL_11H_CFG; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + /* Copy response to user */ + if (req->action == MLAN_ACT_GET) { + data = (int)ds_11hcfg->param.usr_local_power_constraint; + if (copy_to_user(wrq->u.data.pointer, &data, sizeof(int))) { + PRINTM(MINFO, "Copy to user failed\n"); + ret = -EFAULT; + goto done; + } + wrq->u.data.length = 1; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/get MAC control configuration + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * @return 0 --success, otherwise fail + */ +static int +woal_mac_control_ioctl(moal_private *priv, struct iwreq *wrq) +{ + int ret = 0, data = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *cfg = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + cfg = (mlan_ds_misc_cfg *)req->pbuf; + if (wrq->u.data.length) { + if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int))) { + PRINTM(MINFO, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + /* Validation will be done later */ + cfg->param.mac_ctrl = data; + req->action = MLAN_ACT_SET; + } else + req->action = MLAN_ACT_GET; + + cfg->sub_command = MLAN_OID_MISC_MAC_CONTROL; + req->req_id = MLAN_IOCTL_MISC_CFG; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + /* Copy response to user */ + data = (int)cfg->param.mac_ctrl; + if (copy_to_user(wrq->u.data.pointer, &data, sizeof(int))) { + PRINTM(MINFO, "Copy to user failed\n"); + ret = -EFAULT; + goto done; + } + wrq->u.data.length = 1; + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Get thermal reading + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * @return 0 --success, otherwise fail + */ +static int +woal_thermal_ioctl(moal_private *priv, struct iwreq *wrq) +{ + int ret = 0, data = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *cfg = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + cfg = (mlan_ds_misc_cfg *)req->pbuf; + if (wrq->u.data.length) { + PRINTM(MERROR, "Set is not supported for this command\n"); + ret = -EINVAL; + goto done; + } + req->action = MLAN_ACT_GET; + + cfg->sub_command = MLAN_OID_MISC_THERMAL; + req->req_id = MLAN_IOCTL_MISC_CFG; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + /* Copy response to user */ + data = (int)cfg->param.thermal; + if (copy_to_user(wrq->u.data.pointer, &data, sizeof(int))) { + PRINTM(MINFO, "Copy to user failed\n"); + ret = -EFAULT; + goto done; + } + wrq->u.data.length = 1; + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +#if defined(REASSOCIATION) +/** + * @brief Set/Get reassociation settings + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int +woal_set_get_reassoc(moal_private *priv, struct iwreq *wrq) +{ + moal_handle *handle = priv->phandle; + int ret = 0; + int data = 0; + + ENTER(); + + if (wrq->u.data.length) { + if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + if (data == 0) { + handle->reassoc_on &= ~MBIT(priv->bss_index); + priv->reassoc_on = MFALSE; + priv->reassoc_required = MFALSE; + if (!handle->reassoc_on && + handle->is_reassoc_timer_set == MTRUE) { + woal_cancel_timer(&handle->reassoc_timer); + handle->is_reassoc_timer_set = MFALSE; + } + } else { + handle->reassoc_on |= MBIT(priv->bss_index); + priv->reassoc_on = MTRUE; + } + } else { + data = (int)(priv->reassoc_on); + if (copy_to_user(wrq->u.data.pointer, &data, sizeof(int))) { + ret = -EFAULT; + goto done; + } + wrq->u.data.length = 1; + } + +done: + LEAVE(); + return ret; +} +#endif /* REASSOCIATION */ + +/** + * @brief implement WMM enable command + * + * @param priv Pointer to the moal_private driver data struct + * @param wrq Pointer to the iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int +woal_wmm_enable_ioctl(moal_private *priv, struct iwreq *wrq) +{ + int ret = 0; + mlan_ds_wmm_cfg *wmm = NULL; + mlan_ioctl_req *req = NULL; + int data = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_wmm_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + wmm = (mlan_ds_wmm_cfg *)req->pbuf; + req->req_id = MLAN_IOCTL_WMM_CFG; + wmm->sub_command = MLAN_OID_WMM_CFG_ENABLE; + + if (wrq->u.data.length) { + /* Set WMM configuration */ + if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + if ((data < CMD_DISABLED) || (data > CMD_ENABLED)) { + ret = -EINVAL; + goto done; + } + req->action = MLAN_ACT_SET; + if (data == CMD_DISABLED) + wmm->param.wmm_enable = MFALSE; + else + wmm->param.wmm_enable = MTRUE; + } else { + /* Get WMM status */ + req->action = MLAN_ACT_GET; + } + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (wrq->u.data.pointer) { + if (copy_to_user + (wrq->u.data.pointer, &wmm->param.wmm_enable, + sizeof(wmm->param.wmm_enable))) { + PRINTM(MERROR, "Copy to user failed\n"); + ret = -EFAULT; + goto done; + } + wrq->u.data.length = 1; + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Implement 802.11D enable command + * + * @param priv Pointer to the moal_private driver data struct + * @param wrq Pointer to the iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int +woal_11d_enable_ioctl(moal_private *priv, struct iwreq *wrq) +{ + int ret = 0; + mlan_ds_11d_cfg *pcfg_11d = NULL; + mlan_ioctl_req *req = NULL; + int data = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11d_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + pcfg_11d = (mlan_ds_11d_cfg *)req->pbuf; + req->req_id = MLAN_IOCTL_11D_CFG; + pcfg_11d->sub_command = MLAN_OID_11D_CFG_ENABLE; + if (wrq->u.data.length) { + /* Set 11D configuration */ + if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + if ((data < CMD_DISABLED) || (data > CMD_ENABLED)) { + ret = -EINVAL; + goto done; + } + if (data == CMD_DISABLED) + pcfg_11d->param.enable_11d = MFALSE; + else + pcfg_11d->param.enable_11d = MTRUE; + req->action = MLAN_ACT_SET; + } else { + req->action = MLAN_ACT_GET; + } + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (wrq->u.data.pointer) { + if (copy_to_user + (wrq->u.data.pointer, &pcfg_11d->param.enable_11d, + sizeof(pcfg_11d->param.enable_11d))) { + PRINTM(MERROR, "Copy to user failed\n"); + ret = -EFAULT; + goto done; + } + wrq->u.data.length = 1; + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Implement 802.11D clear chan table command + * + * @param priv Pointer to the moal_private driver data struct + * @param wrq Pointer to the iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int +woal_11d_clr_chan_table(moal_private *priv, struct iwreq *wrq) +{ + int ret = 0; + mlan_ds_11d_cfg *pcfg_11d = NULL; + mlan_ioctl_req *req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11d_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + pcfg_11d = (mlan_ds_11d_cfg *)req->pbuf; + req->req_id = MLAN_IOCTL_11D_CFG; + pcfg_11d->sub_command = MLAN_OID_11D_CLR_CHAN_TABLE; + req->action = MLAN_ACT_SET; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Control WPS Session Enable/Disable + * + * @param priv Pointer to the moal_private driver data struct + * @param wrq Pointer to the iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int +woal_wps_cfg_ioctl(moal_private *priv, struct iwreq *wrq) +{ + int ret = 0; + mlan_ds_wps_cfg *pwps = NULL; + mlan_ioctl_req *req = NULL; + char buf[8]; + struct iwreq *wreq = (struct iwreq *)wrq; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + PRINTM(MINFO, "WOAL_WPS_SESSION\n"); + + memset(buf, 0, sizeof(buf)); + if (copy_from_user(buf, wreq->u.data.pointer, + MIN(sizeof(buf) - 1, wreq->u.data.length))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_wps_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + pwps = (mlan_ds_wps_cfg *)req->pbuf; + req->req_id = MLAN_IOCTL_WPS_CFG; + req->action = MLAN_ACT_SET; + pwps->sub_command = MLAN_OID_WPS_CFG_SESSION; + if (buf[0] == 1) + pwps->param.wps_session = MLAN_WPS_CFG_SESSION_START; + else + pwps->param.wps_session = MLAN_WPS_CFG_SESSION_END; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set WPA passphrase and SSID + * + * @param priv A pointer to moal_private structure + * @param wrq Pointer to the iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int +woal_passphrase(moal_private *priv, struct iwreq *wrq) +{ + t_u16 len = 0; + char buf[256]; + char *begin = NULL, *end = NULL, *opt = NULL; + int ret = 0, action = -1, i; + mlan_ds_sec_cfg *sec = NULL; + mlan_ioctl_req *req = NULL; + t_u8 zero_mac[] = { 0, 0, 0, 0, 0, 0 }; + t_u8 *mac = NULL; + int data_length = wrq->u.data.length, copy_len; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (!data_length || data_length >= sizeof(buf)) { + PRINTM(MERROR, + "Argument missing or too long for setpassphrase\n"); + ret = -EINVAL; + goto done; + } + memset(buf, 0, sizeof(buf)); + copy_len = data_length; + + if (copy_from_user(buf, wrq->u.data.pointer, copy_len)) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + + /* Parse the buf to get the cmd_action */ + begin = buf; + end = woal_strsep(&begin, ';', '/'); + if (!end) { + PRINTM(MERROR, "Invalid option\n"); + ret = -EINVAL; + goto done; + } + action = woal_atox(end); + if (action < 0 || action > 2 || end[1] != '\0') { + PRINTM(MERROR, "Invalid action argument %s\n", end); + ret = -EINVAL; + goto done; + } + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + sec = (mlan_ds_sec_cfg *)req->pbuf; + sec->sub_command = MLAN_OID_SEC_CFG_PASSPHRASE; + req->req_id = MLAN_IOCTL_SEC_CFG; + if (action == 0) + req->action = MLAN_ACT_GET; + else + req->action = MLAN_ACT_SET; + while (begin) { + end = woal_strsep(&begin, ';', '/'); + opt = woal_strsep(&end, '=', '/'); + if (!opt || !end || !end[0]) { + PRINTM(MERROR, "Invalid option\n"); + ret = -EINVAL; + break; + } else if (!strnicmp(opt, "ssid", strlen(opt))) { + if (strlen(end) > MLAN_MAX_SSID_LENGTH) { + PRINTM(MERROR, + "SSID length exceeds max length\n"); + ret = -EFAULT; + break; + } + sec->param.passphrase.ssid.ssid_len = strlen(end); + strncpy((char *)sec->param.passphrase.ssid.ssid, end, + MIN(strlen(end), MLAN_MAX_SSID_LENGTH)); + PRINTM(MINFO, "ssid=%s, len=%d\n", + sec->param.passphrase.ssid.ssid, + (int)sec->param.passphrase.ssid.ssid_len); + } else if (!strnicmp(opt, "bssid", strlen(opt))) { + woal_mac2u8(sec->param.passphrase.bssid, end); + } else if (!strnicmp(opt, "psk", strlen(opt)) && + req->action == MLAN_ACT_SET) { + if (strlen(end) != MLAN_PMK_HEXSTR_LENGTH) { + PRINTM(MERROR, "Invalid PMK length\n"); + ret = -EINVAL; + break; + } + woal_ascii2hex((t_u8 *)(sec->param.passphrase.psk.pmk. + pmk), end, + MLAN_PMK_HEXSTR_LENGTH / 2); + sec->param.passphrase.psk_type = MLAN_PSK_PMK; + } else if (!strnicmp(opt, "passphrase", strlen(opt)) && + req->action == MLAN_ACT_SET) { + if (strlen(end) < MLAN_MIN_PASSPHRASE_LENGTH || + strlen(end) > MLAN_MAX_PASSPHRASE_LENGTH) { + PRINTM(MERROR, + "Invalid length for passphrase\n"); + ret = -EINVAL; + break; + } + sec->param.passphrase.psk_type = MLAN_PSK_PASSPHRASE; + memcpy(sec->param.passphrase.psk.passphrase.passphrase, + end, + sizeof(sec->param.passphrase.psk.passphrase. + passphrase)); + sec->param.passphrase.psk.passphrase.passphrase_len = + strlen(end); + PRINTM(MINFO, "passphrase=%s, len=%d\n", + sec->param.passphrase.psk.passphrase.passphrase, + (int)sec->param.passphrase.psk.passphrase. + passphrase_len); + } else { + PRINTM(MERROR, "Invalid option %s\n", opt); + ret = -EINVAL; + break; + } + } + if (ret) + goto done; + + if (action == 2) + sec->param.passphrase.psk_type = MLAN_PSK_CLEAR; + else if (action == 0) + sec->param.passphrase.psk_type = MLAN_PSK_QUERY; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + if (action == 0) { + memset(buf, 0, sizeof(buf)); + if (sec->param.passphrase.ssid.ssid_len) { + len += sprintf(buf + len, "ssid:"); + memcpy(buf + len, sec->param.passphrase.ssid.ssid, + sec->param.passphrase.ssid.ssid_len); + len += sec->param.passphrase.ssid.ssid_len; + len += sprintf(buf + len, " "); + } + if (memcmp + (&sec->param.passphrase.bssid, zero_mac, + sizeof(zero_mac))) { + mac = (t_u8 *)&sec->param.passphrase.bssid; + len += sprintf(buf + len, "bssid:"); + for (i = 0; i < ETH_ALEN - 1; ++i) + len += sprintf(buf + len, "%02x:", mac[i]); + len += sprintf(buf + len, "%02x ", mac[i]); + } + if (sec->param.passphrase.psk_type == MLAN_PSK_PMK) { + len += sprintf(buf + len, "psk:"); + for (i = 0; i < MLAN_MAX_KEY_LENGTH; ++i) + len += sprintf(buf + len, "%02x", + sec->param.passphrase.psk.pmk. + pmk[i]); + len += sprintf(buf + len, "\n"); + } + if (sec->param.passphrase.psk_type == MLAN_PSK_PASSPHRASE) { + len += sprintf(buf + len, "passphrase:%s\n", + sec->param.passphrase.psk.passphrase. + passphrase); + } + if (wrq->u.data.pointer) { + if (copy_to_user + (wrq->u.data.pointer, buf, MIN(len, sizeof(buf)))) { + PRINTM(MERROR, "Copy to user failed, len %d\n", + len); + ret = -EFAULT; + goto done; + } + wrq->u.data.length = len; + } + + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Get esupp mode + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int +woal_get_esupp_mode(moal_private *priv, struct iwreq *wrq) +{ + int ret = 0; + mlan_ds_sec_cfg *sec = NULL; + mlan_ioctl_req *req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + sec = (mlan_ds_sec_cfg *)req->pbuf; + sec->sub_command = MLAN_OID_SEC_CFG_ESUPP_MODE; + req->req_id = MLAN_IOCTL_SEC_CFG; + req->action = MLAN_ACT_GET; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (copy_to_user + (wrq->u.data.pointer, (t_u8 *)&sec->param.esupp_mode, + sizeof(int) * 3)) { + ret = -EFAULT; + goto done; + } + wrq->u.data.length = 3; +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** AES key length */ +#define AES_KEY_LEN 16 +/** + * @brief Adhoc AES control + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to the iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int +woal_adhoc_aes_ioctl(moal_private *priv, struct iwreq *wrq) +{ + static char buf[256]; + int ret = 0, action = -1; + unsigned int i; + t_u8 key_ascii[32]; + t_u8 key_hex[16]; + t_u8 *tmp = NULL; + mlan_bss_info bss_info; + mlan_ds_sec_cfg *sec = NULL; + mlan_ioctl_req *req = NULL; + t_u8 bcast_addr[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + int data_length = wrq->u.data.length, copy_len; + mlan_status status = MLAN_STATUS_SUCCESS; + ENTER(); + + memset(key_ascii, 0x00, sizeof(key_ascii)); + memset(key_hex, 0x00, sizeof(key_hex)); + memset(buf, 0x00, sizeof(buf)); + + /* Get current BSS information */ + memset(&bss_info, 0, sizeof(bss_info)); + woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info); + if (bss_info.bss_mode != MLAN_BSS_MODE_IBSS || + bss_info.media_connected == MTRUE) { + PRINTM(MERROR, "STA is connected or not in IBSS mode.\n"); + ret = -EOPNOTSUPP; + goto done; + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + copy_len = data_length; + + if (data_length > 0) { + if (data_length >= sizeof(buf)) { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + if (copy_from_user(buf, wrq->u.data.pointer, copy_len)) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + + if (data_length == 1) { + /* Get Adhoc AES Key */ + req->req_id = MLAN_IOCTL_SEC_CFG; + req->action = MLAN_ACT_GET; + sec = (mlan_ds_sec_cfg *)req->pbuf; + sec->sub_command = MLAN_OID_SEC_CFG_ENCRYPT_KEY; + sec->param.encrypt_key.key_len = AES_KEY_LEN; + sec->param.encrypt_key.key_index = + MLAN_KEY_INDEX_UNICAST; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + memcpy(key_hex, sec->param.encrypt_key.key_material, + sizeof(key_hex)); + HEXDUMP("Adhoc AES Key (HEX)", key_hex, + sizeof(key_hex)); + + wrq->u.data.length = sizeof(key_ascii) + 1; + + tmp = key_ascii; + for (i = 0; i < sizeof(key_hex); i++) + tmp += sprintf((char *)tmp, "%02x", key_hex[i]); + } else if (data_length >= 2) { + /* Parse the buf to get the cmd_action */ + action = woal_atox(buf); + if (action < 1 || action > 2) { + PRINTM(MERROR, "Invalid action argument %d\n", + action); + ret = -EINVAL; + goto done; + } + + req->req_id = MLAN_IOCTL_SEC_CFG; + req->action = MLAN_ACT_SET; + sec = (mlan_ds_sec_cfg *)req->pbuf; + sec->sub_command = MLAN_OID_SEC_CFG_ENCRYPT_KEY; + + if (action == 1) { + /* Set Adhoc AES Key */ + memcpy(key_ascii, &buf[2], sizeof(key_ascii)); + woal_ascii2hex(key_hex, (char *)key_ascii, + sizeof(key_hex)); + HEXDUMP("Adhoc AES Key (HEX)", key_hex, + sizeof(key_hex)); + + sec->param.encrypt_key.key_len = AES_KEY_LEN; + sec->param.encrypt_key.key_index = + MLAN_KEY_INDEX_UNICAST; + sec->param.encrypt_key.key_flags = + KEY_FLAG_SET_TX_KEY | + KEY_FLAG_GROUP_KEY; + memcpy(sec->param.encrypt_key.mac_addr, + (u8 *)bcast_addr, ETH_ALEN); + memcpy(sec->param.encrypt_key.key_material, + key_hex, sec->param.encrypt_key.key_len); + + status = woal_request_ioctl(priv, req, + MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + } else { + /* Clear Adhoc AES Key */ + sec->param.encrypt_key.key_len = AES_KEY_LEN; + sec->param.encrypt_key.key_index = + MLAN_KEY_INDEX_UNICAST; + sec->param.encrypt_key.key_flags = + KEY_FLAG_REMOVE_KEY; + memcpy(sec->param.encrypt_key.mac_addr, + (u8 *)bcast_addr, ETH_ALEN); + memset(sec->param.encrypt_key.key_material, 0, + sizeof(sec->param.encrypt_key. + key_material)); + + status = woal_request_ioctl(priv, req, + MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + } + } + + HEXDUMP("Adhoc AES Key (ASCII)", key_ascii, sizeof(key_ascii)); + wrq->u.data.length = sizeof(key_ascii); + if (wrq->u.data.pointer) { + if (copy_to_user(wrq->u.data.pointer, &key_ascii, + sizeof(key_ascii))) { + PRINTM(MERROR, "copy_to_user failed\n"); + ret = -EFAULT; + goto done; + } + } + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief arpfilter ioctl function + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * @return 0 --success, otherwise fail + */ +static int +woal_arp_filter(moal_private *priv, struct iwreq *wrq) +{ + int ret = 0; + mlan_ds_misc_cfg *misc = NULL; + mlan_ioctl_req *req = NULL; + int data_length = wrq->u.data.length, copy_len; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + copy_len = + MIN(sizeof(misc->param.gen_ie.ie_data), + sizeof(int) * data_length); + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + misc = (mlan_ds_misc_cfg *)req->pbuf; + misc->sub_command = MLAN_OID_MISC_GEN_IE; + req->req_id = MLAN_IOCTL_MISC_CFG; + req->action = MLAN_ACT_SET; + misc->param.gen_ie.type = MLAN_IE_TYPE_ARP_FILTER; + misc->param.gen_ie.len = data_length; + + /* get the whole command from user */ + if (copy_from_user + (misc->param.gen_ie.ie_data, wrq->u.data.pointer, copy_len)) { + PRINTM(MERROR, "copy from user failed\n"); + ret = -EFAULT; + goto done; + } + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/get IP address + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * @return 0 --success, otherwise fail + */ +static int +woal_set_get_ip_addr(moal_private *priv, struct iwreq *wrq) +{ + char buf[IPADDR_MAX_BUF]; + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_misc_cfg *misc = NULL; + int ret = 0, op_code = 0, data_length = wrq->u.data.length; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + memset(buf, 0, IPADDR_MAX_BUF); + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (ioctl_req == NULL) { + ret = -ENOMEM; + goto done; + } + misc = (mlan_ds_misc_cfg *)ioctl_req->pbuf; + + if (data_length <= 1) { /* GET */ + ioctl_req->action = MLAN_ACT_GET; + } else { + if (copy_from_user(buf, wrq->u.data.pointer, + MIN(IPADDR_MAX_BUF - 1, data_length))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + /* Make sure we have the operation argument */ + if (data_length > 2 && buf[1] != ';') { + PRINTM(MERROR, + "No operation argument. Separate with ';'\n"); + ret = -EINVAL; + goto done; + } else { + buf[1] = '\0'; + } + ioctl_req->action = MLAN_ACT_SET; + /* only one IP is supported in current firmware */ + memset(misc->param.ipaddr_cfg.ip_addr[0], 0, IPADDR_LEN); + in4_pton(&buf[2], MIN((IPADDR_MAX_BUF - 3), (data_length - 2)), + misc->param.ipaddr_cfg.ip_addr[0], ' ', NULL); + /* only one IP is supported in current firmware */ + misc->param.ipaddr_cfg.ip_addr_num = 1; + misc->param.ipaddr_cfg.ip_addr_type = IPADDR_TYPE_IPV4; + } + if (woal_atoi(&op_code, buf) != MLAN_STATUS_SUCCESS) { + ret = -EINVAL; + goto done; + } + misc->param.ipaddr_cfg.op_code = (t_u32)op_code; + ioctl_req->req_id = MLAN_IOCTL_MISC_CFG; + misc->sub_command = MLAN_OID_MISC_IP_ADDR; + + /* Send ioctl to mlan */ + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (ioctl_req->action == MLAN_ACT_GET) { + snprintf(buf, IPADDR_MAX_BUF, "%d;%d.%d.%d.%d", + misc->param.ipaddr_cfg.op_code, + misc->param.ipaddr_cfg.ip_addr[0][0], + misc->param.ipaddr_cfg.ip_addr[0][1], + misc->param.ipaddr_cfg.ip_addr[0][2], + misc->param.ipaddr_cfg.ip_addr[0][3]); + wrq->u.data.length = IPADDR_MAX_BUF; + if (copy_to_user(wrq->u.data.pointer, buf, IPADDR_MAX_BUF)) { + PRINTM(MERROR, "Copy to user failed\n"); + ret = -EFAULT; + } + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get Transmit beamforming capabilities + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return 0 -- success, otherwise fail + */ +static int +woal_tx_bf_cap_ioctl(moal_private *priv, struct iwreq *wrq) +{ + int ret = 0, data_length = wrq->u.data.length; + mlan_ioctl_req *req = NULL; + mlan_ds_11n_cfg *bf_cfg = NULL; + int bf_cap = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (data_length > 1) { + PRINTM(MERROR, "Invalid no of arguments!\n"); + ret = -EINVAL; + goto done; + } + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + bf_cfg = (mlan_ds_11n_cfg *)req->pbuf; + req->req_id = MLAN_IOCTL_11N_CFG; + bf_cfg->sub_command = MLAN_OID_11N_CFG_TX_BF_CAP; + req->action = MLAN_ACT_GET; + if (data_length) { /* SET */ + if (copy_from_user(&bf_cap, wrq->u.data.pointer, sizeof(int))) { + PRINTM(MERROR, "copy from user failed\n"); + ret = -EFAULT; + goto done; + } + bf_cfg->param.tx_bf_cap = bf_cap; + req->action = MLAN_ACT_SET; + } + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + bf_cap = bf_cfg->param.tx_bf_cap; + if (copy_to_user(wrq->u.data.pointer, &bf_cap, sizeof(int))) { + ret = -EFAULT; + goto done; + } + wrq->u.data.length = 1; + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/* Maximum input output characters in group WOAL_SET_GET_256_CHAR */ +#define MAX_IN_OUT_CHAR 256 +/** Tx BF Global conf argument index */ +#define BF_ENABLE_PARAM 1 +#define SOUND_ENABLE_PARAM 2 +#define FB_TYPE_PARAM 3 +#define SNR_THRESHOLD_PARAM 4 +#define SOUND_INTVL_PARAM 5 +#define BF_MODE_PARAM 6 +#define MAX_TX_BF_GLOBAL_ARGS 6 +#define BF_CFG_ACT_GET 0 +#define BF_CFG_ACT_SET 1 + +/** + * @brief Set/Get Transmit beamforming configuration + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return 0 -- success, otherwise fail + */ +static int +woal_tx_bf_cfg_ioctl(moal_private *priv, struct iwreq *wrq) +{ + int ret = 0, data_length = wrq->u.data.length; + int bf_action = 0, interval = 0; + int snr = 0, i, tmp_val; + t_u8 buf[MAX_IN_OUT_CHAR], char_count = 0; + t_u8 *str, *token, *pos; + t_u16 action = 0; + + mlan_ds_11n_tx_bf_cfg bf_cfg; + mlan_trigger_sound_args *bf_sound = NULL; + mlan_tx_bf_peer_args *tx_bf_peer = NULL; + mlan_snr_thr_args *bf_snr = NULL; + mlan_bf_periodicity_args *bf_periodicity = NULL; + mlan_bf_global_cfg_args *bf_global = NULL; + + ENTER(); + + memset(&bf_cfg, 0, sizeof(bf_cfg)); + /* Pointer to corresponding buffer */ + bf_sound = bf_cfg.body.bf_sound; + tx_bf_peer = bf_cfg.body.tx_bf_peer; + bf_snr = bf_cfg.body.bf_snr; + bf_periodicity = bf_cfg.body.bf_periodicity; + bf_global = &bf_cfg.body.bf_global_cfg; + + /* Total characters in buffer */ + char_count = data_length - 1; + memset(buf, 0, sizeof(buf)); + if (char_count) { + if (data_length > sizeof(buf)) { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + if (copy_from_user(buf, wrq->u.data.pointer, data_length)) { + PRINTM(MERROR, "copy from user failed\n"); + ret = -EFAULT; + goto done; + } + + if (char_count > 1 && buf[1] != ';') { + PRINTM(MERROR, + "No action argument. Separate with ';'\n"); + ret = -EINVAL; + goto done; + } + /* Replace ';' with NULL in the string to separate args */ + for (i = 0; i < char_count; i++) { + if (buf[i] == ';') + buf[i] = '\0'; + } + /* The first byte represents the beamforming action */ + if (woal_atoi(&bf_action, &buf[0]) != MLAN_STATUS_SUCCESS) { + ret = -EINVAL; + goto done; + } + switch (bf_action) { + case BF_GLOBAL_CONFIGURATION: + if (char_count == 1) { + action = MLAN_ACT_GET; + bf_cfg.action = BF_CFG_ACT_GET; + } else { + action = MLAN_ACT_SET; + bf_cfg.action = BF_CFG_ACT_SET; + /* Eliminate action field */ + token = &buf[2]; + for (i = 1, str = &buf[2]; token != NULL; i++) { + token = strstr(str, " "); + pos = str; + if (token != NULL) { + *token = '\0'; + str = token + 1; + } + woal_atoi(&tmp_val, pos); + switch (i) { + case BF_ENABLE_PARAM: + bf_global->bf_enbl = + (t_u8)tmp_val; + break; + case SOUND_ENABLE_PARAM: + bf_global->sounding_enbl = + (t_u8)tmp_val; + break; + case FB_TYPE_PARAM: + bf_global->fb_type = + (t_u8)tmp_val; + break; + case SNR_THRESHOLD_PARAM: + bf_global->snr_threshold = + (t_u8)tmp_val; + break; + case SOUND_INTVL_PARAM: + bf_global->sounding_interval = + (t_u16)tmp_val; + break; + case BF_MODE_PARAM: + bf_global->bf_mode = + (t_u8)tmp_val; + break; + default: + PRINTM(MERROR, + "Invalid Argument\n"); + ret = -EINVAL; + goto done; + } + } + } + break; + case TRIGGER_SOUNDING_FOR_PEER: + /* + * First arg = 2 BfAction + * Second arg = 17 MAC "00:50:43:20:BF:64" + */ + if (char_count != 19) { + PRINTM(MERROR, "Invalid argument\n"); + ret = -EINVAL; + goto done; + } + woal_mac2u8(bf_sound->peer_mac, &buf[2]); + action = MLAN_ACT_SET; + bf_cfg.action = BF_CFG_ACT_SET; + break; + case SET_GET_BF_PERIODICITY: + /* + * First arg = 2 BfAction + * Second arg = 18 MAC "00:50:43:20:BF:64;" + * Third arg = 1 (min char) TX BF interval + * 10 (max char) u32 maximum value 4294967295 + */ + if (char_count < 19 || char_count > 30) { + PRINTM(MERROR, "Invalid argument\n"); + ret = -EINVAL; + goto done; + } + + woal_mac2u8(bf_periodicity->peer_mac, &buf[2]); + if (char_count == 19) { + action = MLAN_ACT_GET; + bf_cfg.action = BF_CFG_ACT_GET; + } else { + action = MLAN_ACT_SET; + bf_cfg.action = BF_CFG_ACT_SET; + if (woal_atoi(&interval, &buf[20]) != + MLAN_STATUS_SUCCESS) { + ret = -EINVAL; + goto done; + } + bf_periodicity->interval = interval; + } + break; + case TX_BF_FOR_PEER_ENBL: + /* + * Handle only SET operation here + * First arg = 2 BfAction + * Second arg = 18 MAC "00:50:43:20:BF:64;" + * Third arg = 2 enable/disable bf + * Fourth arg = 2 enable/disable sounding + * Fifth arg = 1 FB Type + */ + if (char_count != 25 && char_count != 1) { + PRINTM(MERROR, "Invalid argument\n"); + ret = -EINVAL; + goto done; + } + if (char_count == 1) { + action = MLAN_ACT_GET; + bf_cfg.action = BF_CFG_ACT_GET; + } else { + woal_mac2u8(tx_bf_peer->peer_mac, &buf[2]); + woal_atoi(&tmp_val, &buf[20]); + tx_bf_peer->bf_enbl = (t_u8)tmp_val; + woal_atoi(&tmp_val, &buf[22]); + tx_bf_peer->sounding_enbl = (t_u8)tmp_val; + woal_atoi(&tmp_val, &buf[24]); + tx_bf_peer->fb_type = (t_u8)tmp_val; + action = MLAN_ACT_SET; + bf_cfg.action = BF_CFG_ACT_SET; + } + break; + case SET_SNR_THR_PEER: + /* + * First arg = 2 BfAction + * Second arg = 18 MAC "00:50:43:20:BF:64;" + * Third arg = 1/2 SNR u8 - can be 1/2 charerters + */ + if (char_count != 1 && + !(char_count == 21 || char_count == 22)) { + PRINTM(MERROR, "Invalid argument\n"); + ret = -EINVAL; + goto done; + } + if (char_count == 1) { + action = MLAN_ACT_GET; + bf_cfg.action = BF_CFG_ACT_GET; + } else { + woal_mac2u8(bf_snr->peer_mac, &buf[2]); + if (woal_atoi(&snr, &buf[20]) != + MLAN_STATUS_SUCCESS) { + ret = -EINVAL; + goto done; + } + bf_snr->snr = snr; + action = MLAN_ACT_SET; + bf_cfg.action = BF_CFG_ACT_SET; + } + break; + default: + ret = -EINVAL; + goto done; + } + + /* Save the value */ + bf_cfg.bf_action = bf_action; + if (MLAN_STATUS_SUCCESS != + woal_set_get_tx_bf_cfg(priv, action, &bf_cfg)) { + ret = -EFAULT; + goto done; + } + } else { + ret = -EINVAL; + goto done; + } + + if (action == MLAN_ACT_GET) { + data_length = 0; + memset(buf, 0, sizeof(buf)); + switch (bf_action) { + case BF_GLOBAL_CONFIGURATION: + data_length += + sprintf(buf + data_length, "%d ", + (int)bf_global->bf_enbl); + data_length += + sprintf(buf + data_length, "%d ", + (int)bf_global->sounding_enbl); + data_length += + sprintf(buf + data_length, "%d ", + (int)bf_global->fb_type); + data_length += + sprintf(buf + data_length, "%d ", + (int)bf_global->snr_threshold); + data_length += + sprintf(buf + data_length, "%d ", + (int)bf_global->sounding_interval); + data_length += + sprintf(buf + data_length, "%d ", + (int)bf_global->bf_mode); + break; + case SET_GET_BF_PERIODICITY: + data_length += sprintf(buf + data_length, + "%02x:%02x:%02x:%02x:%02x:%02x", + bf_periodicity->peer_mac[0], + bf_periodicity->peer_mac[1], + bf_periodicity->peer_mac[2], + bf_periodicity->peer_mac[3], + bf_periodicity->peer_mac[4], + bf_periodicity->peer_mac[5]); + data_length += sprintf(buf + data_length, "%c", ' '); + data_length += + sprintf(buf + data_length, "%d", + bf_periodicity->interval); + break; + case TX_BF_FOR_PEER_ENBL: + for (i = 0; i < bf_cfg.no_of_peers; i++) { + data_length += sprintf(buf + data_length, + "%02x:%02x:%02x:%02x:%02x:%02x", + tx_bf_peer->peer_mac[0], + tx_bf_peer->peer_mac[1], + tx_bf_peer->peer_mac[2], + tx_bf_peer->peer_mac[3], + tx_bf_peer->peer_mac[4], + tx_bf_peer->peer_mac[5]); + data_length += + sprintf(buf + data_length, "%c", ' '); + data_length += + sprintf(buf + data_length, "%d;", + tx_bf_peer->bf_enbl); + data_length += + sprintf(buf + data_length, "%d;", + tx_bf_peer->sounding_enbl); + data_length += + sprintf(buf + data_length, "%d ", + tx_bf_peer->fb_type); + tx_bf_peer++; + } + break; + case SET_SNR_THR_PEER: + for (i = 0; i < bf_cfg.no_of_peers; i++) { + data_length += sprintf(buf + data_length, + "%02x:%02x:%02x:%02x:%02x:%02x", + bf_snr->peer_mac[0], + bf_snr->peer_mac[1], + bf_snr->peer_mac[2], + bf_snr->peer_mac[3], + bf_snr->peer_mac[4], + bf_snr->peer_mac[5]); + data_length += + sprintf(buf + data_length, "%c", ';'); + data_length += + sprintf(buf + data_length, "%d", + bf_snr->snr); + data_length += + sprintf(buf + data_length, "%c", ' '); + bf_snr++; + } + break; + } + buf[data_length] = '\0'; + } + + wrq->u.data.length = data_length; + if (copy_to_user(wrq->u.data.pointer, buf, wrq->u.data.length)) { + ret = -EFAULT; + goto done; + } + +done: + LEAVE(); + return ret; +} + +/** + * @brief Retrieve the scan response/beacon table + * + * @param wrq A pointer to iwreq structure + * @param scan_resp A pointer to mlan_scan_resp structure + * @param scan_start argument + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +static int +moal_ret_get_scan_table_ioctl(struct iwreq *wrq, + mlan_scan_resp *scan_resp, t_u32 scan_start) +{ + pBSSDescriptor_t pbss_desc, scan_table; + wlan_ioctl_get_scan_table_info *prsp_info; + int ret_code; + int ret_len; + int space_left; + t_u8 *pcurrent; + t_u8 *pbuffer_end; + t_u32 num_scans_done; + + ENTER(); + + num_scans_done = 0; + ret_code = MLAN_STATUS_SUCCESS; + + prsp_info = (wlan_ioctl_get_scan_table_info *)wrq->u.data.pointer; + pcurrent = (t_u8 *)prsp_info->scan_table_entry_buf; + + pbuffer_end = wrq->u.data.pointer + wrq->u.data.length - 1; + space_left = pbuffer_end - pcurrent; + scan_table = (BSSDescriptor_t *)(scan_resp->pscan_table); + + PRINTM(MINFO, "GetScanTable: scan_start req = %d\n", scan_start); + PRINTM(MINFO, "GetScanTable: length avail = %d\n", wrq->u.data.length); + + if (!scan_start) { + PRINTM(MINFO, "GetScanTable: get current BSS Descriptor\n"); + + /* Use to get current association saved descriptor */ + pbss_desc = scan_table; + + ret_code = wlan_get_scan_table_ret_entry(pbss_desc, + &pcurrent, + &space_left); + + if (ret_code == MLAN_STATUS_SUCCESS) + num_scans_done = 1; + } else { + scan_start--; + + while (space_left + && (scan_start + num_scans_done < + scan_resp->num_in_scan_table) + && (ret_code == MLAN_STATUS_SUCCESS)) { + + pbss_desc = + (scan_table + (scan_start + num_scans_done)); + + PRINTM(MINFO, + "GetScanTable: get current BSS Descriptor [%d]\n", + scan_start + num_scans_done); + + ret_code = wlan_get_scan_table_ret_entry(pbss_desc, + &pcurrent, + &space_left); + + if (ret_code == MLAN_STATUS_SUCCESS) + num_scans_done++; + } + } + + prsp_info->scan_number = num_scans_done; + ret_len = pcurrent - (t_u8 *)wrq->u.data.pointer; + + wrq->u.data.length = ret_len; + + /* Return ret_code (EFAULT or E2BIG) in the case where no scan results were + * successfully encoded. + */ + LEAVE(); + return num_scans_done ? MLAN_STATUS_SUCCESS : ret_code; +} + +/** + * @brief Get scan table ioctl + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail + */ +static mlan_status +woal_get_scan_table_ioctl(moal_private *priv, struct iwreq *wrq) +{ + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_scan *scan = NULL; + int scan_start = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_scan)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + scan = (mlan_ds_scan *)req->pbuf; + req->req_id = MLAN_IOCTL_SCAN; + req->action = MLAN_ACT_GET; + + /* get the whole command from user */ + if (copy_from_user + (&scan_start, wrq->u.data.pointer, sizeof(scan_start))) { + PRINTM(MERROR, "copy from user failed\n"); + ret = -EFAULT; + goto done; + } + if (scan_start > 0) + scan->sub_command = MLAN_OID_SCAN_NORMAL; + else + scan->sub_command = MLAN_OID_SCAN_GET_CURRENT_BSS; + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status == MLAN_STATUS_SUCCESS) { + status = moal_ret_get_scan_table_ioctl(wrq, + &scan->param.scan_resp, + scan_start); + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return status; +} + +/** + * @brief Set user scan ext -- Async mode, without wait + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return 0 -- success, otherwise fail + */ +static int +woal_set_user_scan_ext_ioctl(moal_private *priv, struct iwreq *wrq) +{ + int ret = 0; + wlan_user_scan_cfg scan_req; + ENTER(); + memset(&scan_req, 0x00, sizeof(scan_req)); + if (copy_from_user + (&scan_req, wrq->u.data.pointer, + MIN(wrq->u.data.length, sizeof(scan_req)))) { + PRINTM(MINFO, "Copy from user failed\n"); + LEAVE(); + return -EFAULT; + } + if (MLAN_STATUS_FAILURE == woal_do_scan(priv, &scan_req)) + ret = -EFAULT; + LEAVE(); + return ret; +} + +/** + * @brief Set user scan + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail + */ +static mlan_status +woal_set_user_scan_ioctl(moal_private *priv, struct iwreq *wrq) +{ + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_scan *scan = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + union iwreq_data wrqu; + moal_handle *handle = priv->phandle; + + ENTER(); + + if (handle->scan_pending_on_block == MTRUE) { + PRINTM(MINFO, "scan already in processing...\n"); + LEAVE(); + return ret; + } + if (MOAL_ACQ_SEMAPHORE_BLOCK(&handle->async_sem)) { + PRINTM(MERROR, "Acquire semaphore error, request_scan\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + handle->scan_pending_on_block = MTRUE; + handle->scan_priv = priv; + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_scan) + + wrq->u.data.length); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + scan = (mlan_ds_scan *)req->pbuf; + scan->sub_command = MLAN_OID_SCAN_USER_CONFIG; + req->req_id = MLAN_IOCTL_SCAN; + req->action = MLAN_ACT_SET; + + if (copy_from_user(scan->param.user_scan.scan_cfg_buf, + wrq->u.data.pointer, wrq->u.data.length)) { + PRINTM(MINFO, "Copy from user failed\n"); + LEAVE(); + return -EFAULT; + } + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status == MLAN_STATUS_SUCCESS) { + memset(&wrqu, 0, sizeof(union iwreq_data)); + wireless_send_event(priv->netdev, SIOCGIWSCAN, &wrqu, NULL); + } + handle->scan_pending_on_block = MFALSE; + handle->scan_priv = NULL; + MOAL_REL_SEMAPHORE(&handle->async_sem); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return status; +} + +/** + * @brief Cmd52 read/write register + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +static int +woal_cmd52rdwr_ioctl(moal_private *priv, struct iwreq *wrq) +{ + t_u8 rw = 0, func, data = 0; + int buf[3], reg, ret = MLAN_STATUS_SUCCESS; + int data_length = wrq->u.data.length; + + ENTER(); + + if (data_length < 2 || data_length > 3) { + PRINTM(MERROR, "Invalid number of arguments\n"); + ret = -EINVAL; + goto done; + } + + if (copy_from_user(buf, wrq->u.data.pointer, sizeof(int) * data_length)) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + + func = (t_u8)buf[0]; + if (func > 7) { + PRINTM(MERROR, "Invalid function number!\n"); + ret = -EINVAL; + goto done; + } + reg = (t_u32)buf[1]; + if (data_length == 2) { + rw = 0; /* CMD52 read */ + PRINTM(MINFO, "Cmd52 read, func=%d, reg=0x%08X\n", func, reg); + } + if (data_length == 3) { + rw = 1; /* CMD52 write */ + data = (t_u8)buf[2]; + PRINTM(MINFO, "Cmd52 write, func=%d, reg=0x%08X, data=0x%02X\n", + func, reg, data); + } + + if (!rw) { + sdio_claim_host(((struct sdio_mmc_card *)priv->phandle->card)-> + func); + if (func) + data = sdio_readb(((struct sdio_mmc_card *)priv-> + phandle->card)->func, reg, &ret); + else + data = sdio_f0_readb(((struct sdio_mmc_card *)priv-> + phandle->card)->func, reg, &ret); + sdio_release_host(((struct sdio_mmc_card *)priv->phandle-> + card)->func); + if (ret) { + PRINTM(MERROR, + "sdio_readb: reading register 0x%X failed\n", + reg); + goto done; + } + } else { + sdio_claim_host(((struct sdio_mmc_card *)priv->phandle->card)-> + func); + if (func) + sdio_writeb(((struct sdio_mmc_card *)priv->phandle-> + card)->func, data, reg, &ret); + else + sdio_f0_writeb(((struct sdio_mmc_card *)priv->phandle-> + card)->func, data, reg, &ret); + sdio_release_host(((struct sdio_mmc_card *)priv->phandle-> + card)->func); + if (ret) { + PRINTM(MERROR, + "sdio_writeb: writing register 0x%X failed\n", + reg); + goto done; + } + } + + buf[0] = data; + wrq->u.data.length = 1; + if (copy_to_user(wrq->u.data.pointer, buf, sizeof(int))) { + PRINTM(MERROR, "Copy to user failed\n"); + ret = -EFAULT; + goto done; + } + +done: + LEAVE(); + return ret; +} + +/** + * @brief Cmd53 read/write register + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +static int +woal_cmd53rdwr_ioctl(moal_private *priv, struct iwreq *wrq) +{ + t_u8 *buf = NULL; + t_u8 rw, func, mode; + t_u16 blklen = 0, blknum = 0; + int reg = 0, pattern_len = 0, pos = 0, ret = MLAN_STATUS_SUCCESS; + t_u32 total_len = 0; + t_u8 *data = NULL; + + ENTER(); + + buf = kmalloc(WOAL_2K_BYTES, GFP_KERNEL); + if (!buf) { + PRINTM(MERROR, "Cannot allocate buffer for command!\n"); + ret = -EFAULT; + goto done; + } + data = kmalloc(WOAL_2K_BYTES, GFP_KERNEL); + if (!data) { + PRINTM(MERROR, "Cannot allocate buffer for command!\n"); + ret = -EFAULT; + goto done; + } + if (wrq->u.data.length > WOAL_2K_BYTES) { + PRINTM(MERROR, "Data lengh is too large!\n"); + ret = -EINVAL; + goto done; + } + if (copy_from_user(buf, wrq->u.data.pointer, wrq->u.data.length)) { + PRINTM(MINFO, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + + rw = buf[0]; /* read/write (0/1) */ + func = buf[1]; /* func (0/1/2) */ + reg = buf[5]; /* address */ + reg = (reg << 8) + buf[4]; + reg = (reg << 8) + buf[3]; + reg = (reg << 8) + buf[2]; + mode = buf[6]; /* byte mode/block mode (0/1) */ + blklen = buf[8]; /* block size */ + blklen = (blklen << 8) + buf[7]; + blknum = buf[10]; /* block number or byte number */ + blknum = (blknum << 8) + buf[9]; + + if (mode != BYTE_MODE) + mode = BLOCK_MODE; + total_len = (mode == BLOCK_MODE) ? blknum * blklen : blknum; + if (total_len > WOAL_2K_BYTES) { + PRINTM(MERROR, "Total data length is too large!\n"); + ret = -EINVAL; + goto done; + } + PRINTM(MINFO, + "CMD53 read/write, func = %d, addr = %#x, mode = %d, block size = %d, block(byte) number = %d\n", + func, reg, mode, blklen, blknum); + + if (!rw) { + sdio_claim_host(((struct sdio_mmc_card *)priv->phandle->card)-> + func); + if (sdio_readsb + (((struct sdio_mmc_card *)priv->phandle->card)->func, data, + reg, total_len)) + PRINTM(MERROR, + "sdio_readsb: reading memory 0x%x failed\n", + reg); + sdio_release_host(((struct sdio_mmc_card *)priv->phandle-> + card)->func); + + if (copy_to_user(wrq->u.data.pointer, data, total_len)) { + PRINTM(MINFO, "Copy to user failed\n"); + ret = -EFAULT; + goto done; + } + wrq->u.data.length = total_len; + } else { + pattern_len = wrq->u.data.length - 11; + if (pattern_len > total_len) + pattern_len = total_len; + memset(data, 0, WOAL_2K_BYTES); + + /* Copy/duplicate the pattern to data buffer */ + for (pos = 0; pos < total_len; pos++) + data[pos] = buf[11 + (pos % pattern_len)]; + + sdio_claim_host(((struct sdio_mmc_card *)priv->phandle->card)-> + func); + if (sdio_writesb + (((struct sdio_mmc_card *)priv->phandle->card)->func, reg, + data, total_len)) + PRINTM(MERROR, + "sdio_writesb: writing memory 0x%x failed\n", + reg); + sdio_release_host(((struct sdio_mmc_card *)priv->phandle-> + card)->func); + } + +done: + kfree(buf); + kfree(data); + LEAVE(); + return ret; +} + +#if defined(SDIO_MULTI_PORT_TX_AGGR) || defined(SDIO_MULTI_PORT_RX_AGGR) +/** + * @brief Set SDIO Multi-point aggregation control parameters + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return 0/MLAN_STATUS_PENDING --success, otherwise fail + */ +static int +woal_do_sdio_mpa_ctrl(moal_private *priv, struct iwreq *wrq) +{ + int data[6], data_length = wrq->u.data.length, copy_len; + int ret = 0; + mlan_ds_misc_cfg *misc = NULL; + mlan_ioctl_req *req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (sizeof(int) * wrq->u.data.length > sizeof(data)) { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + copy_len = MIN(sizeof(data), sizeof(int) * data_length); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + misc = (mlan_ds_misc_cfg *)req->pbuf; + memset(misc, 0, sizeof(mlan_ds_misc_cfg)); + + misc->sub_command = MLAN_OID_MISC_SDIO_MPA_CTRL; + req->req_id = MLAN_IOCTL_MISC_CFG; + + /* Get the values first, then modify these values if + * user had modified them */ + + req->action = MLAN_ACT_GET; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "woal_request_ioctl returned %d\n", ret); + ret = -EFAULT; + goto done; + } + + if (data_length == 0) { + data[0] = misc->param.mpa_ctrl.tx_enable; + data[1] = misc->param.mpa_ctrl.rx_enable; + data[2] = misc->param.mpa_ctrl.tx_buf_size; + data[3] = misc->param.mpa_ctrl.rx_buf_size; + data[4] = misc->param.mpa_ctrl.tx_max_ports; + data[5] = misc->param.mpa_ctrl.rx_max_ports; + + PRINTM(MINFO, "Get Param: %d %d %d %d %d %d\n", data[0], + data[1], data[2], data[3], data[4], data[5]); + + if (copy_to_user(wrq->u.data.pointer, data, sizeof(data))) { + ret = -EFAULT; + goto done; + } + wrq->u.data.length = ARRAY_SIZE(data); + goto done; + } + + if (copy_from_user(data, wrq->u.data.pointer, copy_len)) { + PRINTM(MINFO, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + + switch (data_length) { + case 6: + misc->param.mpa_ctrl.rx_max_ports = data[5]; + case 5: + misc->param.mpa_ctrl.tx_max_ports = data[4]; + case 4: + misc->param.mpa_ctrl.rx_buf_size = data[3]; + case 3: + misc->param.mpa_ctrl.tx_buf_size = data[2]; + case 2: + misc->param.mpa_ctrl.rx_enable = data[1]; + case 1: + /* Set cmd */ + req->action = MLAN_ACT_SET; + + PRINTM(MINFO, "Set Param: %d %d %d %d %d %d\n", data[0], + data[1], data[2], data[3], data[4], data[5]); + + misc->param.mpa_ctrl.tx_enable = data[0]; + break; + default: + PRINTM(MERROR, "Default case error\n"); + ret = -EINVAL; + goto done; + } + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "woal_request_ioctl returned %d\n", ret); + ret = -EFAULT; + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} +#endif /* SDIO_MULTI_PORT_TX_AGGR || SDIO_MULTI_PORT_RX_AGGR */ + +/** + * @brief Set/Get scan configuration parameters + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int +woal_set_get_scan_cfg(moal_private *priv, struct iwreq *wrq) +{ + int ret = 0; + int arg_len = 7; + int data[arg_len], copy_len; + mlan_ds_scan *scan = NULL; + mlan_ioctl_req *req = NULL; + int data_length = wrq->u.data.length; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + copy_len = MIN(sizeof(data), sizeof(int) * data_length); + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_scan)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + if (data_length > arg_len) { + ret = -EINVAL; + goto done; + } + scan = (mlan_ds_scan *)req->pbuf; + scan->sub_command = MLAN_OID_SCAN_CONFIG; + req->req_id = MLAN_IOCTL_SCAN; + memset(data, 0, sizeof(data)); + + if (data_length) { + if (copy_from_user(data, wrq->u.data.pointer, copy_len)) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + if ((data[0] < 0) || (data[0] > MLAN_SCAN_TYPE_PASSIVE)) { + PRINTM(MERROR, "Invalid argument for scan type\n"); + ret = -EINVAL; + goto done; + } + if ((data[1] < 0) || (data[1] > MLAN_SCAN_MODE_ANY)) { + PRINTM(MERROR, "Invalid argument for scan mode\n"); + ret = -EINVAL; + goto done; + } + if ((data[2] < 0) || (data[2] > MAX_PROBES)) { + PRINTM(MERROR, "Invalid argument for scan probes\n"); + ret = -EINVAL; + goto done; + } + if (((data[3] < 0) || + (data[3] > MRVDRV_MAX_ACTIVE_SCAN_CHAN_TIME)) || + ((data[4] < 0) || + (data[4] > MRVDRV_MAX_ACTIVE_SCAN_CHAN_TIME)) || + ((data[5] < 0) || + (data[5] > MRVDRV_MAX_PASSIVE_SCAN_CHAN_TIME))) { + PRINTM(MERROR, "Invalid argument for scan time\n"); + ret = -EINVAL; + goto done; + } + if ((data[6] < 0) || (data[6] > 1)) { + PRINTM(MERROR, "Invalid argument for extended scan\n"); + ret = -EINVAL; + goto done; + } + req->action = MLAN_ACT_SET; + memcpy(&scan->param.scan_cfg, data, sizeof(data)); + } else + req->action = MLAN_ACT_GET; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + if (!data_length) { + memcpy(data, &scan->param.scan_cfg, sizeof(data)); + if (copy_to_user(wrq->u.data.pointer, data, sizeof(data))) { + ret = -EFAULT; + goto done; + } + wrq->u.data.length = ARRAY_SIZE(data); + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get PS configuration parameters + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int +woal_set_get_ps_cfg(moal_private *priv, struct iwreq *wrq) +{ + int data[7], copy_len, ret = 0; + mlan_ds_pm_cfg *pm_cfg = NULL; + mlan_ioctl_req *req = NULL; + int allowed = 3; + int i = 3; + int data_length = wrq->u.data.length; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + allowed++; /* For ad-hoc awake period parameter */ + allowed++; /* For beacon missing timeout parameter */ + allowed += 2; /* For delay to PS and PS mode parameters */ + copy_len = MIN(sizeof(data), sizeof(int) * data_length); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + if (data_length > allowed) { + ret = -EINVAL; + goto done; + } + pm_cfg = (mlan_ds_pm_cfg *)req->pbuf; + pm_cfg->sub_command = MLAN_OID_PM_CFG_PS_CFG; + req->req_id = MLAN_IOCTL_PM_CFG; + memset(data, 0, sizeof(data)); + + if (data_length) { + if (copy_from_user(data, wrq->u.data.pointer, copy_len)) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + if ((data[0] < PS_NULL_DISABLE)) { + PRINTM(MERROR, + "Invalid argument for PS null interval\n"); + ret = -EINVAL; + goto done; + } + if ((data[1] != MRVDRV_IGNORE_MULTIPLE_DTIM) + && (data[1] != MRVDRV_MATCH_CLOSEST_DTIM) + && ((data[1] < MRVDRV_MIN_MULTIPLE_DTIM) + || (data[1] > MRVDRV_MAX_MULTIPLE_DTIM))) { + PRINTM(MERROR, "Invalid argument for multiple DTIM\n"); + ret = -EINVAL; + goto done; + } + + if ((data[2] < MRVDRV_MIN_LISTEN_INTERVAL) + && (data[2] != MRVDRV_LISTEN_INTERVAL_DISABLE)) { + PRINTM(MERROR, + "Invalid argument for listen interval\n"); + ret = -EINVAL; + goto done; + } + + if ((data[i] != SPECIAL_ADHOC_AWAKE_PD) && + ((data[i] < MIN_ADHOC_AWAKE_PD) || + (data[i] > MAX_ADHOC_AWAKE_PD))) { + PRINTM(MERROR, + "Invalid argument for adhoc awake period\n"); + ret = -EINVAL; + goto done; + } + i++; + if ((data[i] != DISABLE_BCN_MISS_TO) && + ((data[i] < MIN_BCN_MISS_TO) || + (data[i] > MAX_BCN_MISS_TO))) { + PRINTM(MERROR, + "Invalid argument for beacon miss timeout\n"); + ret = -EINVAL; + goto done; + } + i++; + if (data_length < allowed - 1) + data[i] = DELAY_TO_PS_UNCHANGED; + else if ((data[i] < MIN_DELAY_TO_PS) || + (data[i] > MAX_DELAY_TO_PS)) { + PRINTM(MERROR, "Invalid argument for delay to PS\n"); + ret = -EINVAL; + goto done; + } + i++; + if ((data[i] != PS_MODE_UNCHANGED) && (data[i] != PS_MODE_AUTO) + && (data[i] != PS_MODE_POLL) && (data[i] != PS_MODE_NULL)) { + PRINTM(MERROR, "Invalid argument for PS mode\n"); + ret = -EINVAL; + goto done; + } + i++; + req->action = MLAN_ACT_SET; + memcpy(&pm_cfg->param.ps_cfg, data, + MIN(sizeof(pm_cfg->param.ps_cfg), sizeof(data))); + } else + req->action = MLAN_ACT_GET; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + memcpy(data, &pm_cfg->param.ps_cfg, + MIN((sizeof(int) * allowed), sizeof(pm_cfg->param.ps_cfg))); + if (copy_to_user(wrq->u.data.pointer, data, sizeof(int) * allowed)) { + ret = -EFAULT; + goto done; + } + wrq->u.data.length = allowed; + + if (req->action == MLAN_ACT_SET) { + pm_cfg = (mlan_ds_pm_cfg *)req->pbuf; + pm_cfg->sub_command = MLAN_OID_PM_CFG_IEEE_PS; + pm_cfg->param.ps_mode = 1; + req->req_id = MLAN_IOCTL_PM_CFG; + req->action = MLAN_ACT_SET; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Private IOCTL entry to send an ADDTS TSPEC + * + * Receive a ADDTS command from the application. The command structure + * contains a TSPEC and timeout in milliseconds. The timeout is performed + * in the firmware after the ADDTS command frame is sent. + * + * The TSPEC is received in the API as an opaque block. The firmware will + * send the entire data block, including the bytes after the TSPEC. This + * is done to allow extra IEs to be packaged with the TSPEC in the ADDTS + * action frame. + * + * The IOCTL structure contains two return fields: + * - The firmware command result, which indicates failure and timeouts + * - The IEEE Status code which contains the corresponding value from + * any ADDTS response frame received. + * + * In addition, the opaque TSPEC data block passed in is replaced with the + * TSPEC received in the ADDTS response frame. In case of failure, the + * AP may modify the TSPEC on return and in the case of success, the + * medium time is returned as calculated by the AP. Along with the TSPEC, + * any IEs that are sent in the ADDTS response are also returned and can be + * parsed using the IOCTL length as an indicator of extra elements. + * + * The return value to the application layer indicates a driver execution + * success or failure. A successful return could still indicate a firmware + * failure or AP negotiation failure via the commandResult field copied + * back to the application. + * + * @param priv Pointer to the mlan_private driver data struct + * @param wrq A pointer to iwreq structure containing the + * wlan_ioctl_wmm_addts_req_t struct for this ADDTS request + * + * @return 0 if successful; IOCTL error code otherwise + */ +static int +woal_wmm_addts_req_ioctl(moal_private *priv, struct iwreq *wrq) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_wmm_cfg *cfg = NULL; + wlan_ioctl_wmm_addts_req_t addts_ioctl; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_wmm_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + req->req_id = MLAN_IOCTL_WMM_CFG; + cfg = (mlan_ds_wmm_cfg *)req->pbuf; + cfg->sub_command = MLAN_OID_WMM_CFG_ADDTS; + + memset(&addts_ioctl, 0x00, sizeof(addts_ioctl)); + + if (wrq->u.data.length) { + if (copy_from_user(&addts_ioctl, wrq->u.data.pointer, + MIN(wrq->u.data.length, + sizeof(addts_ioctl)))) { + PRINTM(MERROR, "TSPEC: ADDTS copy from user failed\n"); + ret = -EFAULT; + goto done; + } + + cfg->param.addts.timeout = addts_ioctl.timeout_ms; + cfg->param.addts.ie_data_len = addts_ioctl.ie_data_len; + + if (cfg->param.addts.ie_data_len > + sizeof(cfg->param.addts.ie_data)) { + PRINTM(MERROR, "IE data length too large\n"); + ret = -EFAULT; + goto done; + } + + memcpy(cfg->param.addts.ie_data, + addts_ioctl.ie_data, cfg->param.addts.ie_data_len); + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + addts_ioctl.cmd_result = cfg->param.addts.result; + addts_ioctl.ieee_status_code = + (t_u8)cfg->param.addts.status_code; + addts_ioctl.ie_data_len = cfg->param.addts.ie_data_len; + + memcpy(addts_ioctl.ie_data, + cfg->param.addts.ie_data, cfg->param.addts.ie_data_len); + + wrq->u.data.length = (sizeof(addts_ioctl) + - sizeof(addts_ioctl.ie_data) + + cfg->param.addts.ie_data_len); + + if (copy_to_user(wrq->u.data.pointer, + &addts_ioctl, wrq->u.data.length)) { + PRINTM(MERROR, "TSPEC: ADDTS copy to user failed\n"); + ret = -EFAULT; + goto done; + } + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Private IOCTL entry to send a DELTS TSPEC + * + * Receive a DELTS command from the application. The command structure + * contains a TSPEC and reason code along with space for a command result + * to be returned. The information is packaged is sent to the wlan_cmd.c + * firmware command prep and send routines for execution in the firmware. + * + * The reason code is not used for WMM implementations but is indicated in + * the 802.11e specification. + * + * The return value to the application layer indicates a driver execution + * success or failure. A successful return could still indicate a firmware + * failure via the cmd_result field copied back to the application. + * + * @param priv Pointer to the mlan_private driver data struct + * @param wrq A pointer to iwreq structure containing the + * wlan_ioctl_wmm_delts_req_t struct for this DELTS request + * + * @return 0 if successful; IOCTL error code otherwise + */ +static int +woal_wmm_delts_req_ioctl(moal_private *priv, struct iwreq *wrq) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_wmm_cfg *cfg = NULL; + wlan_ioctl_wmm_delts_req_t delts_ioctl; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_wmm_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + req->req_id = MLAN_IOCTL_WMM_CFG; + cfg = (mlan_ds_wmm_cfg *)req->pbuf; + cfg->sub_command = MLAN_OID_WMM_CFG_DELTS; + + memset(&delts_ioctl, 0x00, sizeof(delts_ioctl)); + + if (wrq->u.data.length) { + if (copy_from_user(&delts_ioctl, wrq->u.data.pointer, + MIN(wrq->u.data.length, + sizeof(delts_ioctl)))) { + PRINTM(MERROR, "TSPEC: DELTS copy from user failed\n"); + ret = -EFAULT; + goto done; + } + + cfg->param.delts.status_code = + (t_u32)delts_ioctl.ieee_reason_code; + cfg->param.delts.ie_data_len = (t_u8)delts_ioctl.ie_data_len; + + if ((cfg->param.delts.ie_data_len) > + sizeof(cfg->param.delts.ie_data)) { + PRINTM(MERROR, "IE data length too large\n"); + ret = -EFAULT; + goto done; + } + + memcpy(cfg->param.delts.ie_data, + delts_ioctl.ie_data, cfg->param.delts.ie_data_len); + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + /* Return the firmware command result back to the application layer */ + delts_ioctl.cmd_result = cfg->param.delts.result; + wrq->u.data.length = sizeof(delts_ioctl); + + if (copy_to_user(wrq->u.data.pointer, + &delts_ioctl, wrq->u.data.length)) { + PRINTM(MERROR, "TSPEC: DELTS copy to user failed\n"); + ret = -EFAULT; + goto done; + } + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Private IOCTL entry to get/set a specified AC Queue's parameters + * + * Receive a AC Queue configuration command which is used to get, set, or + * default the parameters associated with a specific WMM AC Queue. + * + * @param priv Pointer to the mlan_private driver data struct + * @param wrq A pointer to iwreq structure containing the + * wlan_ioctl_wmm_queue_config_t struct + * + * @return 0 if successful; IOCTL error code otherwise + */ +static int +woal_wmm_queue_config_ioctl(moal_private *priv, struct iwreq *wrq) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_wmm_cfg *pwmm = NULL; + mlan_ds_wmm_queue_config *pqcfg = NULL; + wlan_ioctl_wmm_queue_config_t qcfg_ioctl; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_wmm_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + req->req_id = MLAN_IOCTL_WMM_CFG; + pwmm = (mlan_ds_wmm_cfg *)req->pbuf; + pwmm->sub_command = MLAN_OID_WMM_CFG_QUEUE_CONFIG; + + memset(&qcfg_ioctl, 0x00, sizeof(qcfg_ioctl)); + pqcfg = (mlan_ds_wmm_queue_config *)&pwmm->param.q_cfg; + + if (wrq->u.data.length) { + if (copy_from_user(&qcfg_ioctl, wrq->u.data.pointer, + MIN(wrq->u.data.length, + sizeof(qcfg_ioctl)))) { + PRINTM(MERROR, "QCONFIG: copy from user failed\n"); + ret = -EFAULT; + goto done; + } + + pqcfg->action = qcfg_ioctl.action; + pqcfg->access_category = qcfg_ioctl.access_category; + pqcfg->msdu_lifetime_expiry = qcfg_ioctl.msdu_lifetime_expiry; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + memset(&qcfg_ioctl, 0x00, sizeof(qcfg_ioctl)); + qcfg_ioctl.action = pqcfg->action; + qcfg_ioctl.access_category = pqcfg->access_category; + qcfg_ioctl.msdu_lifetime_expiry = pqcfg->msdu_lifetime_expiry; + wrq->u.data.length = sizeof(qcfg_ioctl); + + if (copy_to_user + (wrq->u.data.pointer, &qcfg_ioctl, wrq->u.data.length)) { + PRINTM(MERROR, "QCONFIG: copy to user failed\n"); + ret = -EFAULT; + goto done; + } + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Private IOCTL entry to get and start/stop queue stats on a WMM AC + * + * Receive a AC Queue statistics command from the application for a specific + * WMM AC. The command can: + * - Turn stats on + * - Turn stats off + * - Collect and clear the stats + * + * @param priv Pointer to the moal_private driver data struct + * @param wrq A pointer to iwreq structure containing the + * wlan_ioctl_wmm_queue_stats_t struct + * + * @return 0 if successful; IOCTL error code otherwise + */ +static int +woal_wmm_queue_stats_ioctl(moal_private *priv, struct iwreq *wrq) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_wmm_cfg *pwmm = NULL; + mlan_ds_wmm_queue_stats *pqstats = NULL; + wlan_ioctl_wmm_queue_stats_t qstats_ioctl; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_wmm_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + req->req_id = MLAN_IOCTL_WMM_CFG; + pwmm = (mlan_ds_wmm_cfg *)req->pbuf; + pwmm->sub_command = MLAN_OID_WMM_CFG_QUEUE_STATS; + + memset(&qstats_ioctl, 0x00, sizeof(qstats_ioctl)); + pqstats = (mlan_ds_wmm_queue_stats *)&pwmm->param.q_stats; + + if (wrq->u.data.length) { + if (copy_from_user(&qstats_ioctl, wrq->u.data.pointer, + MIN(wrq->u.data.length, + sizeof(qstats_ioctl)))) { + PRINTM(MERROR, "QSTATS: copy from user failed\n"); + ret = -EFAULT; + goto done; + } + + memcpy((void *)pqstats, (void *)&qstats_ioctl, + sizeof(qstats_ioctl)); + PRINTM(MINFO, "QSTATS: IOCTL [%d,%d]\n", qstats_ioctl.action, + qstats_ioctl.user_priority); + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + memset(&qstats_ioctl, 0x00, sizeof(qstats_ioctl)); + memcpy((void *)&qstats_ioctl, (void *)pqstats, + sizeof(qstats_ioctl)); + wrq->u.data.length = sizeof(qstats_ioctl); + + if (copy_to_user + (wrq->u.data.pointer, &qstats_ioctl, wrq->u.data.length)) { + PRINTM(MERROR, "QSTATS: copy to user failed\n"); + ret = -EFAULT; + goto done; + } + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Private IOCTL entry to get the status of the WMM queues + * + * Return the following information for each WMM AC: + * - WMM IE Acm Required + * - Firmware Flow Required + * - Firmware Flow Established + * - Firmware Queue Enabled + * - Firmware Delivery Enabled + * - Firmware Trigger Enabled + * + * @param priv Pointer to the moal_private driver data struct + * @param wrq A pointer to iwreq structure containing the + * wlan_ioctl_wmm_queue_status_t struct for request + * + * @return 0 if successful; IOCTL error code otherwise + */ +static int +woal_wmm_queue_status_ioctl(moal_private *priv, struct iwreq *wrq) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_wmm_cfg *pwmm = NULL; + wlan_ioctl_wmm_queue_status_t qstatus_ioctl; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_wmm_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + req->req_id = MLAN_IOCTL_WMM_CFG; + pwmm = (mlan_ds_wmm_cfg *)req->pbuf; + pwmm->sub_command = MLAN_OID_WMM_CFG_QUEUE_STATUS; + + if (wrq->u.data.length == sizeof(qstatus_ioctl)) { + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + memset(&qstatus_ioctl, 0x00, sizeof(qstatus_ioctl)); + memcpy((void *)&qstatus_ioctl, (void *)&pwmm->param.q_status, + sizeof(qstatus_ioctl)); + wrq->u.data.length = sizeof(qstatus_ioctl); + } else { + wrq->u.data.length = 0; + } + + if (copy_to_user + (wrq->u.data.pointer, &qstatus_ioctl, wrq->u.data.length)) { + PRINTM(MERROR, "QSTATUS: copy to user failed\n"); + ret = -EFAULT; + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Private IOCTL entry to get the status of the WMM Traffic Streams + * + * @param priv Pointer to the moal_private driver data struct + * @param wrq A pointer to iwreq structure containing the + * wlan_ioctl_wmm_ts_status_t struct for request + * + * @return 0 if successful; IOCTL error code otherwise + */ +static int +woal_wmm_ts_status_ioctl(moal_private *priv, struct iwreq *wrq) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_wmm_cfg *pwmm = NULL; + wlan_ioctl_wmm_ts_status_t ts_status_ioctl; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_wmm_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + req->req_id = MLAN_IOCTL_WMM_CFG; + pwmm = (mlan_ds_wmm_cfg *)req->pbuf; + pwmm->sub_command = MLAN_OID_WMM_CFG_TS_STATUS; + + memset(&ts_status_ioctl, 0x00, sizeof(ts_status_ioctl)); + + if (wrq->u.data.length == sizeof(ts_status_ioctl)) { + if (copy_from_user(&ts_status_ioctl, wrq->u.data.pointer, + MIN(wrq->u.data.length, + sizeof(ts_status_ioctl)))) { + PRINTM(MERROR, "TS_STATUS: copy from user failed\n"); + ret = -EFAULT; + goto done; + } + + memset(&pwmm->param.ts_status, 0x00, sizeof(ts_status_ioctl)); + pwmm->param.ts_status.tid = ts_status_ioctl.tid; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + memset(&ts_status_ioctl, 0x00, sizeof(ts_status_ioctl)); + memcpy((void *)&ts_status_ioctl, (void *)&pwmm->param.ts_status, + sizeof(ts_status_ioctl)); + wrq->u.data.length = sizeof(ts_status_ioctl); + } else { + wrq->u.data.length = 0; + } + + if (copy_to_user + (wrq->u.data.pointer, &ts_status_ioctl, wrq->u.data.length)) { + PRINTM(MERROR, "TS_STATUS: copy to user failed\n"); + ret = -EFAULT; + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Private IOCTL entry to get the By-passed TX packet from upper layer + * + * @param priv Pointer to the moal_private driver data struct + * @param wrq A pointer to iwreq structure containing the packet + * + * @return 0 if successful; IOCTL error code otherwise + */ +static int +woal_bypassed_packet_ioctl(moal_private *priv, struct iwreq *wrq) +{ + int ret = 0; + struct sk_buff *skb = NULL; + struct ethhdr *eth; + t_u16 moreLen = 0, copyLen = 0; + ENTER(); + +#define MLAN_BYPASS_PKT_EXTRA_OFFSET (4) + + copyLen = wrq->u.data.length; + moreLen = MLAN_MIN_DATA_HEADER_LEN + MLAN_BYPASS_PKT_EXTRA_OFFSET + + sizeof(mlan_buffer); + + skb = alloc_skb(copyLen + moreLen, GFP_KERNEL); + if (skb == NULL) { + PRINTM(MERROR, "kmalloc no memory !!\n"); + LEAVE(); + return -ENOMEM; + } + + skb_reserve(skb, moreLen); + + if (copy_from_user(skb_put(skb, copyLen), wrq->u.data.pointer, copyLen)) { + PRINTM(MERROR, "PortBlock: copy from user failed\n"); + dev_kfree_skb_any(skb); + ret = -EFAULT; + goto done; + } + + eth = (struct ethhdr *)skb->data; + eth->h_proto = __constant_htons(eth->h_proto); + skb->dev = priv->netdev; + + HEXDUMP("Bypass TX Data", skb->data, MIN(skb->len, 100)); + + woal_hard_start_xmit(skb, priv->netdev); +done: + LEAVE(); + return ret; +} + +/** + * @brief Set/Get auth type + * + * @param priv Pointer to the moal_private driver data struct + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int +woal_auth_type(moal_private *priv, struct iwreq *wrq) +{ + int auth_type; + t_u32 auth_mode; + int ret = 0; + + ENTER(); + if (wrq->u.data.length == 0) { + if (MLAN_STATUS_SUCCESS != + woal_get_auth_mode(priv, MOAL_IOCTL_WAIT, &auth_mode)) { + ret = -EFAULT; + goto done; + } + auth_type = auth_mode; + if (copy_to_user + (wrq->u.data.pointer, &auth_type, sizeof(auth_type))) { + PRINTM(MERROR, "Copy to user failed\n"); + ret = -EFAULT; + goto done; + } + wrq->u.data.length = 1; + } else { + if (copy_from_user + (&auth_type, wrq->u.data.pointer, sizeof(auth_type))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + PRINTM(MINFO, "SET: auth_type %d\n", auth_type); + if (((auth_type < MLAN_AUTH_MODE_OPEN) || + (auth_type > MLAN_AUTH_MODE_SHARED)) + && (auth_type != MLAN_AUTH_MODE_AUTO)) { + ret = -EINVAL; + goto done; + } + auth_mode = auth_type; + if (MLAN_STATUS_SUCCESS != + woal_set_auth_mode(priv, MOAL_IOCTL_WAIT, auth_mode)) { + ret = -EFAULT; + goto done; + } + } +done: + LEAVE(); + return ret; +} + +/** + * @brief Set/Get Port Control mode + * + * @param priv Pointer to the moal_private driver data struct + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int +woal_port_ctrl(moal_private *priv, struct iwreq *wrq) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_sec_cfg *sec = NULL; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + ENTER(); + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + sec = (mlan_ds_sec_cfg *)req->pbuf; + sec->sub_command = MLAN_OID_SEC_CFG_PORT_CTRL_ENABLED; + req->req_id = MLAN_IOCTL_SEC_CFG; + + if (wrq->u.data.length) { + if (copy_from_user(&sec->param.port_ctrl_enabled, + wrq->u.data.pointer, sizeof(int)) != 0) { + PRINTM(MERROR, "port_ctrl:Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + req->action = MLAN_ACT_SET; + } else { + req->action = MLAN_ACT_GET; + } + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (!wrq->u.data.length) { + if (copy_to_user + (wrq->u.data.pointer, &sec->param.port_ctrl_enabled, + sizeof(int))) { + PRINTM(MERROR, "port_ctrl:Copy to user failed\n"); + ret = -EFAULT; + goto done; + } + wrq->u.data.length = 1; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +#if defined(DFS_TESTING_SUPPORT) +/** + * @brief Set/Get DFS Testing settings + * + * @param priv Pointer to the moal_private driver data struct + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int +woal_dfs_testing(moal_private *priv, struct iwreq *wrq) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_11h_cfg *ds_11hcfg = NULL; + int ret = 0; + int data[4], copy_len; + int data_length = wrq->u.data.length; + mlan_status status = MLAN_STATUS_SUCCESS; + ENTER(); + + copy_len = MIN(sizeof(data), sizeof(int) * data_length); + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11h_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + ds_11hcfg = (mlan_ds_11h_cfg *)req->pbuf; + ds_11hcfg->sub_command = MLAN_OID_11H_DFS_TESTING; + req->req_id = MLAN_IOCTL_11H_CFG; + + if (!data_length) { + req->action = MLAN_ACT_GET; + } else if (data_length == 4) { + if (copy_from_user(data, wrq->u.data.pointer, copy_len)) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + if ((unsigned)data[0] > 0xFFFF) { + PRINTM(MERROR, "The maximum user CAC is 65535 msec.\n"); + ret = -EINVAL; + goto done; + } + if ((unsigned)data[1] > 0xFFFF) { + PRINTM(MERROR, "The maximum user NOP is 65535 sec.\n"); + ret = -EINVAL; + goto done; + } + if ((unsigned)data[3] > 0xFF) { + PRINTM(MERROR, + "The maximum user fixed channel is 255.\n"); + ret = -EINVAL; + goto done; + } + ds_11hcfg->param.dfs_testing.usr_cac_period_msec = + (t_u16)data[0]; + ds_11hcfg->param.dfs_testing.usr_nop_period_sec = + (t_u16)data[1]; + ds_11hcfg->param.dfs_testing.usr_no_chan_change = + data[2] ? 1 : 0; + ds_11hcfg->param.dfs_testing.usr_fixed_new_chan = (t_u8)data[3]; + priv->phandle->cac_period_jiffies = (t_u16)data[0] * HZ / 1000; + req->action = MLAN_ACT_SET; + } else { + PRINTM(MERROR, "Invalid number of args!\n"); + ret = -EINVAL; + goto done; + } + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (!data_length) { + data[0] = ds_11hcfg->param.dfs_testing.usr_cac_period_msec; + data[1] = ds_11hcfg->param.dfs_testing.usr_nop_period_sec; + data[2] = ds_11hcfg->param.dfs_testing.usr_no_chan_change; + data[3] = ds_11hcfg->param.dfs_testing.usr_fixed_new_chan; + if (copy_to_user(wrq->u.data.pointer, &data, sizeof(int) * 4)) { + PRINTM(MERROR, "Copy to user failed\n"); + ret = -EFAULT; + goto done; + } + wrq->u.data.length = 4; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} +#endif /* DFS_SUPPORT && DFS_TESTING_SUPPORT */ + +/** + * @brief Set/Get Mgmt Frame passthru mask + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return 0 -- success, otherwise fail + */ +static int +woal_mgmt_frame_passthru_ctrl(moal_private *priv, struct iwreq *wrq) +{ + int ret = 0, data_length = wrq->u.data.length; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *mgmt_cfg = NULL; + int mask = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (data_length > 1) { + PRINTM(MERROR, "Invalid no of arguments!\n"); + ret = -EINVAL; + goto done; + } + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + mgmt_cfg = (mlan_ds_misc_cfg *)req->pbuf; + req->req_id = MLAN_IOCTL_MISC_CFG; + mgmt_cfg->sub_command = MLAN_OID_MISC_RX_MGMT_IND; + + if (data_length) { /* SET */ + if (copy_from_user(&mask, wrq->u.data.pointer, sizeof(int))) { + PRINTM(MERROR, "copy from user failed\n"); + ret = -EFAULT; + goto done; + } + mgmt_cfg->param.mgmt_subtype_mask = mask; + req->action = MLAN_ACT_SET; + } else { + req->action = MLAN_ACT_GET; + } + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + mask = mgmt_cfg->param.mgmt_subtype_mask; + if (copy_to_user(wrq->u.data.pointer, &mask, sizeof(int))) { + ret = -EFAULT; + goto done; + } + wrq->u.data.length = 1; + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get CFP table codes + * + * @param priv Pointer to the moal_private driver data struct + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int +woal_cfp_code(moal_private *priv, struct iwreq *wrq) +{ + int ret = 0; + int data[2], copy_len; + int data_length = wrq->u.data.length; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *misc_cfg = NULL; + mlan_ds_misc_cfp_code *cfp_code = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (data_length > 2) { + PRINTM(MERROR, "Invalid number of argument!\n"); + ret = -EINVAL; + goto done; + } + copy_len = MIN(sizeof(data), sizeof(int) * data_length); + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + misc_cfg = (mlan_ds_misc_cfg *)req->pbuf; + cfp_code = &misc_cfg->param.cfp_code; + misc_cfg->sub_command = MLAN_OID_MISC_CFP_CODE; + req->req_id = MLAN_IOCTL_MISC_CFG; + + if (!data_length) { + req->action = MLAN_ACT_GET; + } else { + if (copy_from_user(data, wrq->u.data.pointer, copy_len)) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + cfp_code->cfp_code_bg = data[0]; + if (data_length == 2) + cfp_code->cfp_code_a = data[1]; + req->action = MLAN_ACT_SET; + } + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (!data_length) { + data[0] = cfp_code->cfp_code_bg; + data[1] = cfp_code->cfp_code_a; + data_length = 2; + if (copy_to_user + (wrq->u.data.pointer, &data, sizeof(int) * data_length)) { + PRINTM(MERROR, "Copy to user failed\n"); + ret = -EFAULT; + goto done; + } + wrq->u.data.length = data_length; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get Tx/Rx antenna + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int +woal_set_get_tx_rx_ant(moal_private *priv, struct iwreq *wrq) +{ + int ret = 0; + mlan_ds_radio_cfg *radio = NULL; + mlan_ioctl_req *req = NULL; + int data[3] = { 0 }; + mlan_status status = MLAN_STATUS_SUCCESS; + int copy_len; + + ENTER(); + + if (wrq->u.data.length * sizeof(int) > sizeof(data)) { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EFAULT; + goto done; + } + copy_len = MIN(sizeof(data), wrq->u.data.length * sizeof(int)); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_radio_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + radio = (mlan_ds_radio_cfg *)req->pbuf; + radio->sub_command = MLAN_OID_ANT_CFG; + req->req_id = MLAN_IOCTL_RADIO_CFG; + if (wrq->u.data.length) { + if (copy_from_user(data, wrq->u.data.pointer, copy_len)) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + + radio->param.ant_cfg_1x1.antenna = data[0]; + if (wrq->u.data.length == 2) + radio->param.ant_cfg_1x1.evaluate_time = data[1]; + req->action = MLAN_ACT_SET; + } else + req->action = MLAN_ACT_GET; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + if (!wrq->u.data.length) { + wrq->u.data.length = 1; + data[0] = (int)radio->param.ant_cfg_1x1.antenna; + data[1] = (int)radio->param.ant_cfg_1x1.evaluate_time; + data[2] = (int)radio->param.ant_cfg_1x1.current_antenna; + if (data[0] == 0xffff && data[2] > 0) + wrq->u.data.length = 3; + else if (data[0] == 0xffff) + wrq->u.data.length = 2; + if (copy_to_user + (wrq->u.data.pointer, data, + wrq->u.data.length * sizeof(int))) { + ret = -EFAULT; + goto done; + } + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Configure gpio independent reset + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int +woal_ind_rst_ioctl(moal_private *priv, struct iwreq *wrq) +{ + int data[2], data_length = wrq->u.data.length, copy_len; + int ret = 0; + mlan_ds_misc_cfg *misc = NULL; + mlan_ioctl_req *req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (sizeof(int) * wrq->u.data.length > sizeof(data)) { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + copy_len = MIN(sizeof(data), sizeof(int) * data_length); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + misc = (mlan_ds_misc_cfg *)req->pbuf; + memset(misc, 0, sizeof(mlan_ds_misc_cfg)); + + misc->sub_command = MLAN_OID_MISC_IND_RST_CFG; + req->req_id = MLAN_IOCTL_MISC_CFG; + + if (data_length == 0) { + req->action = MLAN_ACT_GET; + } else if ((data_length == 1) || (data_length == 2)) { + req->action = MLAN_ACT_SET; + + if (copy_from_user(data, wrq->u.data.pointer, copy_len)) { + /* copy_from_user failed */ + PRINTM(MERROR, "S_PARAMS: copy from user failed\n"); + LEAVE(); + return -EINVAL; + } + + /* ir_mode */ + if (data[0] < 0 || data[0] > 2) { + PRINTM(MERROR, "Invalid ir mode parameter (0/1/2)!\n"); + ret = -EINVAL; + goto done; + } + misc->param.ind_rst_cfg.ir_mode = data[0]; + + /* gpio_pin */ + if (data_length == 2) { + if ((data[1] != 0xFF) && (data[1] < 0 || data[1] > 15)) { + PRINTM(MERROR, + "Invalid gpio pin no (0-15 , 0xFF for default)!\n"); + ret = -EINVAL; + goto done; + } + misc->param.ind_rst_cfg.gpio_pin = data[1]; + } + + } else { + ret = -EINVAL; + goto done; + } + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + data[0] = misc->param.ind_rst_cfg.ir_mode; + data[1] = misc->param.ind_rst_cfg.gpio_pin; + wrq->u.data.length = 2; + + if (copy_to_user(wrq->u.data.pointer, data, sizeof(int) * + wrq->u.data.length)) { + PRINTM(MERROR, "QCONFIG: copy to user failed\n"); + ret = -EFAULT; + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/******************************************************** + Global Functions +********************************************************/ +/** + * @brief ioctl function - entry point + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @param cmd Command + * + * @return 0 --success, otherwise fail + */ +int +woal_wext_do_ioctl(struct net_device *dev, struct ifreq *req, int cmd) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + struct iwreq *wrq = (struct iwreq *)req; + int ret = 0; + + if (!IS_STA_WEXT(cfg80211_wext)) + return -EOPNOTSUPP; + + ENTER(); + + PRINTM(MINFO, "woal_wext_do_ioctl: ioctl cmd = 0x%x\n", cmd); + switch (cmd) { + case WOAL_SETONEINT_GETWORDCHAR: + switch (wrq->u.data.flags) { + case WOAL_VERSION: /* Get driver version */ + ret = woal_get_driver_version(priv, req); + break; + case WOAL_VEREXT: /* Get extended driver version */ + ret = woal_get_driver_verext(priv, req); + break; + default: + ret = -EOPNOTSUPP; + break; + } + break; + case WOAL_SETNONE_GETNONE: + switch (wrq->u.data.flags) { + case WOAL_WARMRESET: + ret = woal_warm_reset(priv); + break; + case WOAL_11D_CLR_CHAN_TABLE: + ret = woal_11d_clr_chan_table(priv, wrq); + break; + default: + ret = -EOPNOTSUPP; + break; + } + break; + case WOAL_SETONEINT_GETONEINT: + switch (wrq->u.data.flags) { + case WOAL_SET_GET_TXRATE: + ret = woal_set_get_txrate(priv, wrq); + break; + case WOAL_SET_GET_REGIONCODE: + ret = woal_set_get_regioncode(priv, wrq); + break; + case WOAL_SET_RADIO: + ret = woal_set_get_radio(priv, wrq); + break; + case WOAL_WMM_ENABLE: + ret = woal_wmm_enable_ioctl(priv, wrq); + break; + case WOAL_11D_ENABLE: + ret = woal_11d_enable_ioctl(priv, wrq); + break; + case WOAL_SET_GET_QOS_CFG: + ret = woal_set_get_qos_cfg(priv, wrq); + break; +#if defined(REASSOCIATION) + case WOAL_SET_GET_REASSOC: + ret = woal_set_get_reassoc(priv, wrq); + break; +#endif /* REASSOCIATION */ + case WOAL_TXBUF_CFG: + ret = woal_txbuf_cfg(priv, wrq); + break; + case WOAL_SET_GET_WWS_CFG: + ret = woal_wws_cfg(priv, wrq); + break; + case WOAL_SLEEP_PD: + ret = woal_sleep_pd(priv, wrq); + break; + case WOAL_FW_WAKEUP_METHOD: + ret = woal_fw_wakeup_method(priv, wrq); + break; + case WOAL_AUTH_TYPE: + ret = woal_auth_type(priv, wrq); + break; + case WOAL_PORT_CTRL: + ret = woal_port_ctrl(priv, wrq); + break; + case WOAL_COALESCING_STATUS: + ret = woal_coalescing_status_ioctl(priv, wrq); + break; +#if defined(WIFI_DIRECT_SUPPORT) +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) + case WOAL_SET_GET_BSS_ROLE: + ret = woal_set_get_bss_role(priv, wrq); + break; +#endif +#endif + case WOAL_SET_GET_11H_LOCAL_PWR_CONSTRAINT: + ret = woal_set_get_11h_local_pwr_constraint(priv, wrq); + break; + case WOAL_MAC_CONTROL: + ret = woal_mac_control_ioctl(priv, wrq); + break; + case WOAL_THERMAL: + ret = woal_thermal_ioctl(priv, wrq); + break; + default: + ret = -EOPNOTSUPP; + break; + } + break; + + case WOAL_SET_GET_SIXTEEN_INT: + switch ((int)wrq->u.data.flags) { + case WOAL_TX_POWERCFG: + ret = woal_tx_power_cfg(priv, wrq); + break; +#ifdef DEBUG_LEVEL1 + case WOAL_DRV_DBG: + ret = woal_drv_dbg(priv, wrq); + break; +#endif + case WOAL_BEACON_INTERVAL: + ret = woal_beacon_interval(priv, wrq); + break; + case WOAL_ATIM_WINDOW: + ret = woal_atim_window(priv, wrq); + break; + case WOAL_SIGNAL: + ret = woal_get_signal(priv, wrq); + break; + case WOAL_DEEP_SLEEP: + ret = woal_deep_sleep_ioctl(priv, wrq); + break; + case WOAL_11N_TX_CFG: + ret = woal_11n_tx_cfg(priv, wrq); + break; + case WOAL_11N_AMSDU_AGGR_CTRL: + ret = woal_11n_amsdu_aggr_ctrl(priv, wrq); + break; + case WOAL_11N_HTCAP_CFG: + ret = woal_11n_htcap_cfg(priv, wrq); + break; + case WOAL_PRIO_TBL: + ret = woal_11n_prio_tbl(priv, wrq); + break; + case WOAL_ADDBA_UPDT: + ret = woal_addba_para_updt(priv, wrq); + break; + case WOAL_ADDBA_REJECT: + ret = woal_addba_reject(priv, wrq); + break; + case WOAL_TX_BF_CAP: + ret = woal_tx_bf_cap_ioctl(priv, wrq); + break; + case WOAL_HS_CFG: + ret = woal_hs_cfg(priv, wrq, MTRUE); + break; + case WOAL_HS_SETPARA: + ret = woal_hs_setpara(priv, wrq); + break; + case WOAL_REG_READ_WRITE: + ret = woal_reg_read_write(priv, wrq); + break; + case WOAL_INACTIVITY_TIMEOUT_EXT: + ret = woal_inactivity_timeout_ext(priv, wrq); + break; + case WOAL_SDIO_CLOCK: + ret = woal_sdio_clock_ioctl(priv, wrq); + break; + case WOAL_CMD_52RDWR: + ret = woal_cmd52rdwr_ioctl(priv, wrq); + break; + case WOAL_BAND_CFG: + ret = woal_band_cfg(priv, wrq); + break; + case WOAL_SCAN_CFG: + ret = woal_set_get_scan_cfg(priv, wrq); + break; + case WOAL_PS_CFG: + ret = woal_set_get_ps_cfg(priv, wrq); + break; + case WOAL_MEM_READ_WRITE: + ret = woal_mem_read_write(priv, wrq); + break; +#if defined(SDIO_MULTI_PORT_TX_AGGR) || defined(SDIO_MULTI_PORT_RX_AGGR) + case WOAL_SDIO_MPA_CTRL: + ret = woal_do_sdio_mpa_ctrl(priv, wrq); + break; +#endif + case WOAL_SLEEP_PARAMS: + ret = woal_sleep_params_ioctl(priv, wrq); + break; + case WOAL_NET_MONITOR: + ret = woal_net_monitor_ioctl(priv, wrq); + break; +#if defined(DFS_TESTING_SUPPORT) + case WOAL_DFS_TESTING: + ret = woal_dfs_testing(priv, wrq); + break; +#endif + case WOAL_MGMT_FRAME_CTRL: + ret = woal_mgmt_frame_passthru_ctrl(priv, wrq); + break; + case WOAL_CFP_CODE: + ret = woal_cfp_code(priv, wrq); + break; + case WOAL_SET_GET_TX_RX_ANT: + ret = woal_set_get_tx_rx_ant(priv, wrq); + break; + case WOAL_IND_RST_CFG: + ret = woal_ind_rst_ioctl(priv, wrq); + break; + default: + ret = -EINVAL; + break; + } + break; + + case WOALGETLOG: + ret = woal_get_log(priv, wrq); + break; + + case WOAL_SET_GET_256_CHAR: + switch (wrq->u.data.flags) { + case WOAL_PASSPHRASE: + ret = woal_passphrase(priv, wrq); + break; + case WOAL_ADHOC_AES: + ret = woal_adhoc_aes_ioctl(priv, wrq); + break; + case WOAL_ASSOCIATE: + ret = woal_associate_ssid_bssid(priv, wrq); + break; + case WOAL_WMM_QUEUE_STATUS: + ret = woal_wmm_queue_status_ioctl(priv, wrq); + break; + + case WOAL_WMM_TS_STATUS: + ret = woal_wmm_ts_status_ioctl(priv, wrq); + break; + case WOAL_IP_ADDRESS: + ret = woal_set_get_ip_addr(priv, wrq); + break; + case WOAL_TX_BF_CFG: + ret = woal_tx_bf_cfg_ioctl(priv, wrq); + break; + default: + ret = -EINVAL; + break; + } + break; + + case WOAL_SETADDR_GETNONE: + switch ((int)wrq->u.data.flags) { + case WOAL_DEAUTH: + ret = woal_deauth(priv, wrq); + break; + default: + ret = -EINVAL; + break; + } + break; + + case WOAL_SETNONE_GETTWELVE_CHAR: + /* + * We've not used IW_PRIV_TYPE_FIXED so sub-ioctl number is + * in flags of iwreq structure, otherwise it will be in + * mode member of iwreq structure. + */ + switch ((int)wrq->u.data.flags) { + case WOAL_WPS_SESSION: + ret = woal_wps_cfg_ioctl(priv, wrq); + break; + default: + ret = -EINVAL; + break; + } + break; + case WOAL_SETNONE_GET_FOUR_INT: + switch ((int)wrq->u.data.flags) { + case WOAL_DATA_RATE: + ret = woal_get_txrx_rate(priv, wrq); + break; + case WOAL_ESUPP_MODE: + ret = woal_get_esupp_mode(priv, wrq); + break; + default: + ret = -EINVAL; + break; + } + break; + + case WOAL_SET_GET_64_INT: + switch ((int)wrq->u.data.flags) { + case WOAL_ECL_SYS_CLOCK: + ret = woal_ecl_sys_clock(priv, wrq); + break; + } + + break; + + case WOAL_HOST_CMD: + ret = woal_host_command(priv, wrq); + break; + case WOAL_ARP_FILTER: + ret = woal_arp_filter(priv, wrq); + break; + case WOAL_SET_INTS_GET_CHARS: + switch ((int)wrq->u.data.flags) { + case WOAL_READ_EEPROM: + ret = woal_read_eeprom(priv, wrq); + break; + } + break; + case WOAL_SET_GET_2K_BYTES: + switch ((int)wrq->u.data.flags) { + case WOAL_CMD_53RDWR: + ret = woal_cmd53rdwr_ioctl(priv, wrq); + break; + case WOAL_SET_USER_SCAN: + ret = woal_set_user_scan_ioctl(priv, wrq); + break; + case WOAL_GET_SCAN_TABLE: + ret = woal_get_scan_table_ioctl(priv, wrq); + break; + case WOAL_SET_USER_SCAN_EXT: + ret = woal_set_user_scan_ext_ioctl(priv, wrq); + break; + case WOAL_WMM_ADDTS: + ret = woal_wmm_addts_req_ioctl(priv, wrq); + break; + case WOAL_WMM_DELTS: + ret = woal_wmm_delts_req_ioctl(priv, wrq); + break; + case WOAL_WMM_QUEUE_CONFIG: + ret = woal_wmm_queue_config_ioctl(priv, wrq); + break; + case WOAL_WMM_QUEUE_STATS: + ret = woal_wmm_queue_stats_ioctl(priv, wrq); + break; + case WOAL_BYPASSED_PACKET: + ret = woal_bypassed_packet_ioctl(priv, wrq); + break; + default: + ret = -EINVAL; + break; + } + break; + +#ifdef UAP_WEXT + case WOAL_FROYO_START: + break; + case WOAL_FROYO_WL_FW_RELOAD: + break; + case WOAL_FROYO_STOP: + if (IS_UAP_WEXT(cfg80211_wext) && MLAN_STATUS_SUCCESS != + woal_disconnect(priv, MOAL_IOCTL_WAIT, NULL, + DEF_DEAUTH_REASON_CODE)) { + ret = -EFAULT; + } + break; +#endif + default: + ret = -EINVAL; + break; + } + + LEAVE(); + return ret; +} + +/** + * @brief Get data rates + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * @param m_rates A pointer to moal_802_11_rates structure + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail + */ +mlan_status +woal_get_data_rates(moal_private *priv, t_u8 wait_option, + moal_802_11_rates *m_rates) +{ + int ret = 0; + mlan_ds_rate *rate = NULL; + mlan_ioctl_req *req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + ENTER(); + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_rate)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + rate = (mlan_ds_rate *)req->pbuf; + rate->sub_command = MLAN_OID_SUPPORTED_RATES; + req->req_id = MLAN_IOCTL_RATE; + req->action = MLAN_ACT_GET; + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, wait_option); + if (status == MLAN_STATUS_SUCCESS) { + if (m_rates) + m_rates->num_of_rates = + woal_copy_rates(m_rates->rates, + m_rates->num_of_rates, + rate->param.rates, + MLAN_SUPPORTED_RATES); + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return status; +} + +/** + * @brief Get channel list + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * @param chan_list A pointer to mlan_chan_list structure + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail + */ +mlan_status +woal_get_channel_list(moal_private *priv, t_u8 wait_option, + mlan_chan_list *chan_list) +{ + int ret = 0; + mlan_ds_bss *bss = NULL; + mlan_ioctl_req *req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + ENTER(); + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + bss = (mlan_ds_bss *)req->pbuf; + bss->sub_command = MLAN_OID_BSS_CHANNEL_LIST; + req->req_id = MLAN_IOCTL_BSS; + req->action = MLAN_ACT_GET; + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, wait_option); + if (status == MLAN_STATUS_SUCCESS) { + if (chan_list) { + memcpy(chan_list, &bss->param.chanlist, + sizeof(mlan_chan_list)); + } + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return status; +} + +/** + * @brief Handle get info resp + * + * @param priv Pointer to moal_private structure + * @param info Pointer to mlan_ds_get_info structure + * + * @return N/A + */ +void +woal_ioctl_get_info_resp(moal_private *priv, mlan_ds_get_info *info) +{ + ENTER(); + switch (info->sub_command) { + case MLAN_OID_GET_STATS: + priv->w_stats.discard.fragment = info->param.stats.fcs_error; + priv->w_stats.discard.retries = info->param.stats.retry; + priv->w_stats.discard.misc = info->param.stats.ack_failure; + break; + case MLAN_OID_GET_SIGNAL: + if (info->param.signal.selector & BCN_RSSI_AVG_MASK) + priv->w_stats.qual.level = + info->param.signal.bcn_rssi_avg; + if (info->param.signal.selector & BCN_NF_AVG_MASK) + priv->w_stats.qual.noise = + info->param.signal.bcn_nf_avg; + break; + default: + break; + } + LEAVE(); +} + +/** + * @brief Handle get BSS resp + * + * @param priv Pointer to moal_private structure + * @param bss Pointer to mlan_ds_bss structure + * + * @return N/A + */ +void +woal_ioctl_get_bss_resp(moal_private *priv, mlan_ds_bss *bss) +{ + t_u32 mode = 0; + + ENTER(); + + switch (bss->sub_command) { + case MLAN_OID_BSS_MODE: + if (bss->param.bss_mode == MLAN_BSS_MODE_INFRA) + mode = IW_MODE_INFRA; + else if (bss->param.bss_mode == MLAN_BSS_MODE_IBSS) + mode = IW_MODE_ADHOC; + else + mode = IW_MODE_AUTO; + priv->w_stats.status = mode; + break; + default: + break; + } + + LEAVE(); + return; +} diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_priv.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_priv.h new file mode 100644 index 000000000000..fd0e6f5f6f46 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_priv.h @@ -0,0 +1,770 @@ + +/** @file moal_priv.h + * + * @brief This file contains definition for extended private IOCTL call. + * + * Copyright (C) 2008-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +/******************************************************** +Change log: + 10/31/2008: initial version +********************************************************/ + +#ifndef _WOAL_PRIV_H_ +#define _WOAL_PRIV_H_ + +/** 2K bytes */ +#define WOAL_2K_BYTES 2000 + +/** PRIVATE CMD ID */ +#define WOAL_IOCTL (SIOCIWFIRSTPRIV) /* 0x8BE0 defined in wireless.h */ + +/** Private command ID to set one int/get word char */ +#define WOAL_SETONEINT_GETWORDCHAR (WOAL_IOCTL + 1) +/** Private command ID to get version */ +#define WOAL_VERSION 1 +/** Private command ID to get extended version */ +#define WOAL_VEREXT 2 + +/** Private command ID to set/get none */ +#define WOAL_SETNONE_GETNONE (WOAL_IOCTL + 2) +/** Private command ID for warm reset */ +#define WOAL_WARMRESET 1 + +/** + * Linux Kernels later 3.9 use CONFIG_PM_RUNTIME instead of + * CONFIG_USB_SUSPEND + */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) +#ifdef CONFIG_PM +#ifndef CONFIG_USB_SUSPEND +#define CONFIG_USB_SUSPEND +#endif +#endif +#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) */ +#ifdef CONFIG_PM_RUNTIME +#ifndef CONFIG_USB_SUSPEND +#define CONFIG_USB_SUSPEND +#endif +#endif +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) */ +#endif + +/** Private command ID to clear 11d chan table */ +#define WOAL_11D_CLR_CHAN_TABLE 4 + +/** Private command ID to set/get sixteen int */ +#define WOAL_SET_GET_SIXTEEN_INT (WOAL_IOCTL + 3) +/** Private command ID to set/get TX power configurations */ +#define WOAL_TX_POWERCFG 1 +#ifdef DEBUG_LEVEL1 +/** Private command ID to set/get driver debug */ +#define WOAL_DRV_DBG 2 +#endif +/** Private command ID to set/get beacon interval */ +#define WOAL_BEACON_INTERVAL 3 +/** Private command ID to set/get ATIM window */ +#define WOAL_ATIM_WINDOW 4 +/** Private command ID to get RSSI */ +#define WOAL_SIGNAL 5 +/** Private command ID to set/get Deep Sleep mode */ +#define WOAL_DEEP_SLEEP 7 +/** Private command ID for 11n ht configration */ +#define WOAL_11N_TX_CFG 8 +/** Private command ID for 11n usr ht configration */ +#define WOAL_11N_HTCAP_CFG 9 +/** Private command ID for TX Aggregation */ +#define WOAL_PRIO_TBL 10 +/** Private command ID for Updating ADDBA variables */ +#define WOAL_ADDBA_UPDT 11 +/** Private command ID to set/get Host Sleep configuration */ +#define WOAL_HS_CFG 12 +/** Private command ID to set Host Sleep parameters */ +#define WOAL_HS_SETPARA 13 +/** Private command ID to read/write registers */ +#define WOAL_REG_READ_WRITE 14 +/** Private command ID to set/get band/adhocband */ +#define WOAL_BAND_CFG 15 +/** Private command ID for TX Aggregation */ +#define WOAL_11N_AMSDU_AGGR_CTRL 17 +/** Private command ID to set/get Inactivity timeout */ +#define WOAL_INACTIVITY_TIMEOUT_EXT 18 +/** Private command ID to turn on/off sdio clock */ +#define WOAL_SDIO_CLOCK 19 +/** Private command ID to read/write Command 52 */ +#define WOAL_CMD_52RDWR 20 +/** Private command ID to set/get scan configuration parameter */ +#define WOAL_SCAN_CFG 21 +/** Private command ID to set/get PS configuration parameter */ +#define WOAL_PS_CFG 22 +/** Private command ID to read/write memory */ +#define WOAL_MEM_READ_WRITE 23 +#if defined(SDIO_MULTI_PORT_TX_AGGR) || defined(SDIO_MULTI_PORT_RX_AGGR) +/** Private command ID to control SDIO MP-A */ +#define WOAL_SDIO_MPA_CTRL 25 +#endif +/** Private command ID for Updating ADDBA variables */ +#define WOAL_ADDBA_REJECT 27 +/** Private command ID to set/get sleep parameters */ +#define WOAL_SLEEP_PARAMS 28 +/** Private command ID to set/get network monitor */ +#define WOAL_NET_MONITOR 30 +/** Private command ID to set/get TX BF capabilities */ +#define WOAL_TX_BF_CAP 31 +#if defined(DFS_TESTING_SUPPORT) +/** Private command ID to set/get dfs testing settings */ +#define WOAL_DFS_TESTING 33 +#endif +/** Private command ID to set/get CFP table codes */ +#define WOAL_CFP_CODE 34 +/** Private command ID to set/get tx/rx antenna */ +#define WOAL_SET_GET_TX_RX_ANT 35 +/** Private command ID to set/get management frame passthru mask */ +#define WOAL_MGMT_FRAME_CTRL 36 + +/** Private command ID to configure gpio independent reset */ +#define WOAL_IND_RST_CFG 37 + +/** Private command ID to set one int/get one int */ +#define WOAL_SETONEINT_GETONEINT (WOAL_IOCTL + 5) +/** Private command ID to set/get Tx rate */ +#define WOAL_SET_GET_TXRATE 1 +/** Private command ID to set/get region code */ +#define WOAL_SET_GET_REGIONCODE 2 +/** Private command ID to turn on/off radio */ +#define WOAL_SET_RADIO 3 +/** Private command ID to enable WMM */ +#define WOAL_WMM_ENABLE 4 +/** Private command ID to enable 802.11D */ +#define WOAL_11D_ENABLE 5 +/** Private command ID to set/get QoS configuration */ +#define WOAL_SET_GET_QOS_CFG 7 +#if defined(REASSOCIATION) +/** Private command ID to set/get reassociation setting */ +#define WOAL_SET_GET_REASSOC 9 +#endif /* REASSOCIATION */ +/** Private command ID for Updating Transmit buffer configration */ +#define WOAL_TXBUF_CFG 10 +/** Private command ID to set/get WWS mode */ +#define WOAL_SET_GET_WWS_CFG 12 +/** Private command ID to set/get sleep period */ +#define WOAL_SLEEP_PD 13 +/** Private command ID to set/get firmware wakeup method */ +#define WOAL_FW_WAKEUP_METHOD 15 +/** Private command ID to set/get auth type */ +#define WOAL_AUTH_TYPE 18 +/** Private command ID to set/get port control */ +#define WOAL_PORT_CTRL 19 +/** Private command ID for coalesced status */ +#define WOAL_COALESCING_STATUS 20 +#if defined(WIFI_DIRECT_SUPPORT) +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) +/** Private command ID for set/get BSS role */ +#define WOAL_SET_GET_BSS_ROLE 21 +#endif +#endif +/** Private command ID for set/get 11h local power constraint */ +#define WOAL_SET_GET_11H_LOCAL_PWR_CONSTRAINT 22 +/** Private command ID to set/get MAC control */ +#define WOAL_MAC_CONTROL 24 +/** Private command ID to get thermal value */ +#define WOAL_THERMAL 25 + +/** Private command ID to get log */ +#define WOALGETLOG (WOAL_IOCTL + 7) + +/** Private command ID to set a wext address variable */ +#define WOAL_SETADDR_GETNONE (WOAL_IOCTL + 8) +/** Private command ID to send deauthentication */ +#define WOAL_DEAUTH 1 + +/** Private command to get/set 256 chars */ +#define WOAL_SET_GET_256_CHAR (WOAL_IOCTL + 9) +/** Private command to read/write passphrase */ +#define WOAL_PASSPHRASE 1 +/** Private command to get/set Ad-Hoc AES */ +#define WOAL_ADHOC_AES 2 +#define WOAL_ASSOCIATE 3 +/** Private command ID to get WMM queue status */ +#define WOAL_WMM_QUEUE_STATUS 4 +/** Private command ID to get Traffic stream status */ +#define WOAL_WMM_TS_STATUS 5 +#define WOAL_IP_ADDRESS 7 +/** Private command ID to set/get TX bemaforming */ +#define WOAL_TX_BF_CFG 8 + +/** Get log buffer size */ +#define GETLOG_BUFSIZE 512 + +/** Private command ID to set none/get twelve chars*/ +#define WOAL_SETNONE_GETTWELVE_CHAR (WOAL_IOCTL + 11) +/** Private command ID for WPS session */ +#define WOAL_WPS_SESSION 1 + +/** Private command ID to set none/get four int */ +#define WOAL_SETNONE_GET_FOUR_INT (WOAL_IOCTL + 13) +/** Private command ID to get data rates */ +#define WOAL_DATA_RATE 1 +/** Private command ID to get E-Supplicant mode */ +#define WOAL_ESUPP_MODE 2 + +/** Private command to get/set 64 ints */ +#define WOAL_SET_GET_64_INT (WOAL_IOCTL + 15) +/** Private command ID to set/get ECL system clock */ +#define WOAL_ECL_SYS_CLOCK 1 + +/** Private command ID for hostcmd */ +#define WOAL_HOST_CMD (WOAL_IOCTL + 17) + +/** Private command ID for arpfilter */ +#define WOAL_ARP_FILTER (WOAL_IOCTL + 19) + +/** Private command ID to set ints and get chars */ +#define WOAL_SET_INTS_GET_CHARS (WOAL_IOCTL + 21) +/** Private command ID to read EEPROM data */ +#define WOAL_READ_EEPROM 1 + +/** Private command ID to set/get 2K bytes */ +#define WOAL_SET_GET_2K_BYTES (WOAL_IOCTL + 23) + +/** Private command ID to read/write Command 53 */ +#define WOAL_CMD_53RDWR 2 + +/** Private command ID for setuserscan */ +#define WOAL_SET_USER_SCAN 3 +/** Private command ID for getscantable */ +#define WOAL_GET_SCAN_TABLE 4 +/** Private command ID for setuserscanext: async without wait */ +#define WOAL_SET_USER_SCAN_EXT 5 + +/** Private command ID to request ADDTS */ +#define WOAL_WMM_ADDTS 7 +/** Private command ID to request DELTS */ +#define WOAL_WMM_DELTS 8 +/** Private command ID to queue configuration */ +#define WOAL_WMM_QUEUE_CONFIG 9 +/** Private command ID to queue stats */ +#define WOAL_WMM_QUEUE_STATS 10 +/** Private command ID to Bypass auth packet */ +#define WOAL_BYPASSED_PACKET 11 + +#ifdef UAP_WEXT +/** The following command IDs are for Froyo app */ +/** Private command ID to start driver */ +#define WOAL_FROYO_START (WOAL_IOCTL + 28) +/** Private command ID to reload FW */ +#define WOAL_FROYO_WL_FW_RELOAD (WOAL_IOCTL + 29) +/** Private command ID to stop driver */ +#define WOAL_FROYO_STOP (WOAL_IOCTL + 30) +#endif + +/** + * iwpriv ioctl handlers + */ +static const struct iw_priv_args woal_private_args[] = { + { + WOAL_SETONEINT_GETWORDCHAR, + IW_PRIV_TYPE_INT | 1, + IW_PRIV_TYPE_CHAR | 128, + ""}, + { + WOAL_VERSION, + IW_PRIV_TYPE_INT | 1, + IW_PRIV_TYPE_CHAR | 128, + "version"}, + { + WOAL_VEREXT, + IW_PRIV_TYPE_INT | 1, + IW_PRIV_TYPE_CHAR | 128, + "verext"}, + { + WOAL_SETNONE_GETNONE, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_NONE, + ""}, + { + WOAL_WARMRESET, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_NONE, + "warmreset"}, + { + WOAL_SETONEINT_GETONEINT, + IW_PRIV_TYPE_INT | 1, + IW_PRIV_TYPE_INT | 1, + ""}, + { + WOAL_SET_GET_TXRATE, + IW_PRIV_TYPE_INT | 1, + IW_PRIV_TYPE_INT | 1, + "txratecfg"}, + { + WOAL_SET_GET_REGIONCODE, + IW_PRIV_TYPE_INT | 1, + IW_PRIV_TYPE_INT | 1, + "regioncode"}, + { + WOAL_SET_RADIO, + IW_PRIV_TYPE_INT | 1, + IW_PRIV_TYPE_INT | 1, + "radioctrl"}, + { + WOAL_WMM_ENABLE, + IW_PRIV_TYPE_INT | 1, + IW_PRIV_TYPE_INT | 1, + "wmmcfg"}, + { + WOAL_11D_ENABLE, + IW_PRIV_TYPE_INT | 1, + IW_PRIV_TYPE_INT | 1, + "11dcfg"}, + { + WOAL_11D_CLR_CHAN_TABLE, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_NONE, + "11dclrtbl"}, + { + WOAL_SET_GET_QOS_CFG, + IW_PRIV_TYPE_INT | 1, + IW_PRIV_TYPE_INT | 1, + "qoscfg"}, + { + WOAL_SET_GET_WWS_CFG, + IW_PRIV_TYPE_INT | 1, + IW_PRIV_TYPE_INT | 1, + "wwscfg"}, +#if defined(REASSOCIATION) + { + WOAL_SET_GET_REASSOC, + IW_PRIV_TYPE_INT | 1, + IW_PRIV_TYPE_INT | 1, + "reassoctrl"}, +#endif + { + WOAL_TXBUF_CFG, + IW_PRIV_TYPE_INT | 1, + IW_PRIV_TYPE_INT | 1, + "txbufcfg"}, + { + WOAL_SLEEP_PD, + IW_PRIV_TYPE_INT | 1, + IW_PRIV_TYPE_INT | 1, + "sleeppd"}, + { + WOAL_FW_WAKEUP_METHOD, + IW_PRIV_TYPE_INT | 1, + IW_PRIV_TYPE_INT | 1, + "fwwakeupmethod"}, + { + WOAL_AUTH_TYPE, + IW_PRIV_TYPE_INT | 1, + IW_PRIV_TYPE_INT | 1, + "authtype"}, + { + WOAL_PORT_CTRL, + IW_PRIV_TYPE_INT | 1, + IW_PRIV_TYPE_INT | 1, + "port_ctrl"}, + { + WOAL_COALESCING_STATUS, + IW_PRIV_TYPE_INT | 1, + IW_PRIV_TYPE_INT | 1, + "coalesce_status"}, +#if defined(WIFI_DIRECT_SUPPORT) +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) + { + WOAL_SET_GET_BSS_ROLE, + IW_PRIV_TYPE_INT | 1, + IW_PRIV_TYPE_INT | 1, + "bssrole"}, +#endif +#endif + { + WOAL_SET_GET_11H_LOCAL_PWR_CONSTRAINT, + IW_PRIV_TYPE_INT | 1, + IW_PRIV_TYPE_INT | 1, + "powercons"}, + { + WOAL_MAC_CONTROL, + IW_PRIV_TYPE_INT | 1, + IW_PRIV_TYPE_INT | 1, + "macctrl"}, + { + WOAL_THERMAL, + IW_PRIV_TYPE_INT | 1, + IW_PRIV_TYPE_INT | 1, + "thermal"}, + { + WOAL_SET_GET_SIXTEEN_INT, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_INT | 16, + ""}, + { + WOAL_TX_POWERCFG, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_INT | 16, + "txpowercfg"}, +#ifdef DEBUG_LEVEL1 + { + WOAL_DRV_DBG, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_INT | 16, + "drvdbg"}, +#endif + { + WOAL_BEACON_INTERVAL, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_INT | 16, + "bcninterval"}, + { + WOAL_ATIM_WINDOW, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_INT | 16, + "atimwindow"}, + { + WOAL_SIGNAL, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_INT | 16, + "getsignal"}, + { + WOAL_DEEP_SLEEP, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_INT | 16, + "deepsleep", + }, + { + WOAL_11N_TX_CFG, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_INT | 16, + "httxcfg"}, + { + WOAL_11N_HTCAP_CFG, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_INT | 16, + "htcapinfo"}, + { + WOAL_PRIO_TBL, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_INT | 16, + "aggrpriotbl"}, + { + WOAL_11N_AMSDU_AGGR_CTRL, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_INT | 16, + "amsduaggrctrl"}, + { + WOAL_ADDBA_UPDT, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_INT | 16, + "addbapara"}, + { + WOAL_ADDBA_REJECT, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_INT | 16, + "addbareject"}, + { + WOAL_TX_BF_CAP, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_INT | 16, + "httxbfcap"}, + { + WOAL_HS_CFG, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_INT | 16, + "hscfg"}, + { + WOAL_HS_SETPARA, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_INT | 16, + "hssetpara"}, + { + WOAL_REG_READ_WRITE, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_INT | 16, + "regrdwr"}, + { + WOAL_BAND_CFG, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_INT | 16, + "bandcfg"}, + { + WOAL_INACTIVITY_TIMEOUT_EXT, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_INT | 16, + "inactivityto"}, + { + WOAL_SDIO_CLOCK, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_INT | 16, + "sdioclock"}, + { + WOAL_CMD_52RDWR, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_INT | 16, + "sdcmd52rw"}, + { + WOAL_SCAN_CFG, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_INT | 16, + "scancfg"}, + { + WOAL_PS_CFG, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_INT | 16, + "pscfg"}, + { + WOAL_MEM_READ_WRITE, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_INT | 16, + "memrdwr"}, +#if defined(SDIO_MULTI_PORT_TX_AGGR) || defined(SDIO_MULTI_PORT_RX_AGGR) + { + WOAL_SDIO_MPA_CTRL, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_INT | 16, + "mpactrl"}, +#endif + { + WOAL_SLEEP_PARAMS, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_INT | 16, + "sleepparams"}, + { + WOAL_NET_MONITOR, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_INT | 16, + "netmon"}, +#if defined(DFS_TESTING_SUPPORT) + { + WOAL_DFS_TESTING, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_INT | 16, + "dfstesting"}, +#endif + { + WOAL_MGMT_FRAME_CTRL, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_INT | 16, + "mgmtframectrl"}, + { + WOAL_CFP_CODE, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_INT | 16, + "cfpcode"}, + { + WOAL_SET_GET_TX_RX_ANT, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_INT | 16, + "antcfg"}, + { + WOAL_IND_RST_CFG, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_INT | 16, + "indrstcfg"}, + { + WOALGETLOG, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_CHAR | GETLOG_BUFSIZE, + "getlog"}, + { + WOAL_SETADDR_GETNONE, + IW_PRIV_TYPE_ADDR | 1, + IW_PRIV_TYPE_NONE, + ""}, + { + WOAL_DEAUTH, + IW_PRIV_TYPE_ADDR | 1, + IW_PRIV_TYPE_NONE, + "deauth"}, + { + WOAL_SET_GET_256_CHAR, + IW_PRIV_TYPE_CHAR | 256, + IW_PRIV_TYPE_CHAR | 256, + ""}, + { + WOAL_PASSPHRASE, + IW_PRIV_TYPE_CHAR | 256, + IW_PRIV_TYPE_CHAR | 256, + "passphrase"}, + { + WOAL_ADHOC_AES, + IW_PRIV_TYPE_CHAR | 256, + IW_PRIV_TYPE_CHAR | 256, + "adhocaes"}, + { + WOAL_ASSOCIATE, + IW_PRIV_TYPE_CHAR | 256, + IW_PRIV_TYPE_CHAR | 256, + "associate"}, + { + WOAL_WMM_QUEUE_STATUS, + IW_PRIV_TYPE_CHAR | 256, + IW_PRIV_TYPE_CHAR | 256, + "qstatus"}, + { + WOAL_WMM_TS_STATUS, + IW_PRIV_TYPE_CHAR | 256, + IW_PRIV_TYPE_CHAR | 256, + "ts_status"}, + { + WOAL_IP_ADDRESS, + IW_PRIV_TYPE_CHAR | 256, + IW_PRIV_TYPE_CHAR | 256, + "ipaddr"}, + { + WOAL_TX_BF_CFG, + IW_PRIV_TYPE_CHAR | 256, + IW_PRIV_TYPE_CHAR | 256, + "httxbfcfg"}, + { + WOAL_SETNONE_GETTWELVE_CHAR, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_CHAR | 12, + ""}, + { + WOAL_WPS_SESSION, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_CHAR | 12, + "wpssession"}, + { + WOAL_SETNONE_GET_FOUR_INT, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_INT | 4, + ""}, + { + WOAL_DATA_RATE, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_INT | 4, + "getdatarate"}, + { + WOAL_ESUPP_MODE, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_INT | 4, + "esuppmode"}, + { + WOAL_SET_GET_64_INT, + IW_PRIV_TYPE_INT | 64, + IW_PRIV_TYPE_INT | 64, + ""}, + { + WOAL_ECL_SYS_CLOCK, + IW_PRIV_TYPE_INT | 64, + IW_PRIV_TYPE_INT | 64, + "sysclock"}, + { + WOAL_HOST_CMD, + IW_PRIV_TYPE_BYTE | 2047, + IW_PRIV_TYPE_BYTE | 2047, + "hostcmd"}, + { + WOAL_ARP_FILTER, + IW_PRIV_TYPE_BYTE | 2047, + IW_PRIV_TYPE_BYTE | 2047, + "arpfilter"}, + { + WOAL_SET_INTS_GET_CHARS, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_BYTE | 256, + ""}, + { + WOAL_READ_EEPROM, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_BYTE | 256, + "rdeeprom"}, + { + WOAL_SET_GET_2K_BYTES, + IW_PRIV_TYPE_BYTE | WOAL_2K_BYTES, + IW_PRIV_TYPE_BYTE | WOAL_2K_BYTES, + ""}, + { + WOAL_CMD_53RDWR, + IW_PRIV_TYPE_BYTE | WOAL_2K_BYTES, + IW_PRIV_TYPE_BYTE | WOAL_2K_BYTES, + "sdcmd53rw"}, + { + WOAL_SET_USER_SCAN, + IW_PRIV_TYPE_BYTE | WOAL_2K_BYTES, + IW_PRIV_TYPE_BYTE | WOAL_2K_BYTES, + "setuserscan"}, + { + WOAL_GET_SCAN_TABLE, + IW_PRIV_TYPE_BYTE | WOAL_2K_BYTES, + IW_PRIV_TYPE_BYTE | WOAL_2K_BYTES, + "getscantable"}, + { + WOAL_SET_USER_SCAN_EXT, + IW_PRIV_TYPE_BYTE | WOAL_2K_BYTES, + IW_PRIV_TYPE_BYTE | WOAL_2K_BYTES, + "setuserscanext"}, + { + WOAL_WMM_ADDTS, + IW_PRIV_TYPE_BYTE | WOAL_2K_BYTES, + IW_PRIV_TYPE_BYTE | WOAL_2K_BYTES, + "addts"}, + { + WOAL_WMM_DELTS, + IW_PRIV_TYPE_BYTE | WOAL_2K_BYTES, + IW_PRIV_TYPE_BYTE | WOAL_2K_BYTES, + "delts"}, + { + WOAL_WMM_QUEUE_CONFIG, + IW_PRIV_TYPE_BYTE | WOAL_2K_BYTES, + IW_PRIV_TYPE_BYTE | WOAL_2K_BYTES, + "qconfig"}, + { + WOAL_WMM_QUEUE_STATS, + IW_PRIV_TYPE_BYTE | WOAL_2K_BYTES, + IW_PRIV_TYPE_BYTE | WOAL_2K_BYTES, + "qstats"}, + { + WOAL_BYPASSED_PACKET, + IW_PRIV_TYPE_BYTE | WOAL_2K_BYTES, + IW_PRIV_TYPE_BYTE | WOAL_2K_BYTES, + "pb_bypass"}, +#ifdef UAP_WEXT + { + WOAL_FROYO_START, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_NONE, + "START"}, + { + WOAL_FROYO_STOP, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_NONE, + "STOP"}, + { + WOAL_FROYO_WL_FW_RELOAD, + IW_PRIV_TYPE_CHAR | 256, + IW_PRIV_TYPE_CHAR | 256, + "WL_FW_RELOAD"}, +#endif +}; + +/** moal_802_11_rates */ +typedef struct _moal_802_11_rates { + /** Num of rates */ + t_u8 num_of_rates; + /** Rates */ + t_u8 rates[MLAN_SUPPORTED_RATES]; +} moal_802_11_rates; + +#if defined(STA_WEXT) || defined(UAP_WEXT) +int woal_wext_do_ioctl(struct net_device *dev, struct ifreq *req, int cmd); +#endif + +#endif /* _WOAL_PRIV_H_ */ diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_proc.c b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_proc.c new file mode 100644 index 000000000000..ca66c862e403 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_proc.c @@ -0,0 +1,778 @@ +/** @file moal_proc.c + * + * @brief This file contains functions for proc file. + * + * Copyright (C) 2008-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +/******************************************************** +Change log: + 10/21/2008: initial version +********************************************************/ + +#include "moal_main.h" +#ifdef UAP_SUPPORT +#include "moal_uap.h" +#endif +#include "moal_sdio.h" + +/******************************************************** + Local Variables +********************************************************/ +#ifdef CONFIG_PROC_FS +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) +#define PROC_DIR NULL +#define MWLAN_PROC_DIR "mwlan/" +#define MWLAN_PROC "mwlan" +/** Proc top level directory entry */ +struct proc_dir_entry *proc_mwlan; +int proc_dir_entry_use_count; +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) +#define PROC_DIR (&proc_root) +#else +#define PROC_DIR proc_net +#endif + +#ifdef STA_SUPPORT +static char *szModes[] = { + "Unknown", + "Managed", + "Ad-hoc", + "Auto", +}; +#endif + +extern int drv_mode; + +/******************************************************** + Global Variables +********************************************************/ + +/******************************************************** + Local Functions +********************************************************/ +/** + * @brief Proc read function for info + * + * @param sfp pointer to seq_file structure + * @param data void pointer to data + * + * @return Number of output data + */ +static int +woal_info_proc_read(struct seq_file *sfp, void *data) +{ + struct net_device *netdev = (struct net_device *)sfp->private; + char fmt[MLAN_MAX_VER_STR_LEN]; + moal_private *priv = (moal_private *)netdev_priv(netdev); +#ifdef STA_SUPPORT + int i = 0; + moal_handle *handle = NULL; + mlan_bss_info info; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) + struct dev_mc_list *mcptr = netdev->mc_list; + int mc_count = netdev->mc_count; +#else + struct netdev_hw_addr *mcptr = NULL; + int mc_count = netdev_mc_count(netdev); +#endif /* < 2.6.35 */ +#else +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) + int i = 0; +#endif /* >= 2.6.29 */ +#endif +#ifdef UAP_SUPPORT + mlan_ds_uap_stats ustats; +#endif + + ENTER(); + + if (priv == NULL) + goto exit; +#ifdef STA_SUPPORT + handle = priv->phandle; + if (handle == NULL) + goto exit; +#endif + + if (!MODULE_GET) { + LEAVE(); + return 0; + } + + memset(fmt, 0, sizeof(fmt)); +#ifdef UAP_SUPPORT + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) { + seq_printf(sfp, "driver_name = " "\"uap\"\n"); + woal_uap_get_version(priv, fmt, sizeof(fmt) - 1); + if (MLAN_STATUS_SUCCESS != + woal_uap_get_stats(priv, MOAL_IOCTL_WAIT, &ustats)) { + MODULE_PUT; + LEAVE(); + return -EFAULT; + } + } +#endif /* UAP_SUPPORT */ +#ifdef STA_SUPPORT + memset(&info, 0, sizeof(info)); + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) { + woal_get_version(handle, fmt, sizeof(fmt) - 1); + if (MLAN_STATUS_SUCCESS != + woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &info)) { + MODULE_PUT; + LEAVE(); + return -EFAULT; + } + seq_printf(sfp, "driver_name = " "\"wlan\"\n"); + } +#endif + seq_printf(sfp, "driver_version = %s", fmt); + seq_printf(sfp, "\ninterface_name=\"%s\"\n", netdev->name); +#if defined(WIFI_DIRECT_SUPPORT) + if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT) { + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) + seq_printf(sfp, "bss_mode = \"WIFIDIRECT-Client\"\n"); + else + seq_printf(sfp, "bss_mode = \"WIFIDIRECT-GO\"\n"); + } +#endif +#ifdef STA_SUPPORT + if (priv->bss_type == MLAN_BSS_TYPE_STA) + seq_printf(sfp, "bss_mode =\"%s\"\n", szModes[info.bss_mode]); +#endif + seq_printf(sfp, "media_state=\"%s\"\n", + ((priv->media_connected == + MFALSE) ? "Disconnected" : "Connected")); + seq_printf(sfp, "mac_address=\"%02x:%02x:%02x:%02x:%02x:%02x\"\n", + netdev->dev_addr[0], netdev->dev_addr[1], + netdev->dev_addr[2], netdev->dev_addr[3], + netdev->dev_addr[4], netdev->dev_addr[5]); +#ifdef STA_SUPPORT + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) { + seq_printf(sfp, "multicast_count=\"%d\"\n", mc_count); + seq_printf(sfp, "essid=\"%s\"\n", info.ssid.ssid); + seq_printf(sfp, "bssid=\"%02x:%02x:%02x:%02x:%02x:%02x\"\n", + info.bssid[0], info.bssid[1], + info.bssid[2], info.bssid[3], + info.bssid[4], info.bssid[5]); + seq_printf(sfp, "channel=\"%d\"\n", (int)info.bss_chan); + seq_printf(sfp, "region_code = \"%02x\"\n", + (t_u8)info.region_code); + + /* + * Put out the multicast list + */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) + for (i = 0; i < netdev->mc_count; i++) { + seq_printf(sfp, + "multicast_address[%d]=\"%02x:%02x:%02x:%02x:%02x:%02x\"\n", + i, + mcptr->dmi_addr[0], mcptr->dmi_addr[1], + mcptr->dmi_addr[2], mcptr->dmi_addr[3], + mcptr->dmi_addr[4], mcptr->dmi_addr[5]); + + mcptr = mcptr->next; + } +#else + netdev_for_each_mc_addr(mcptr, netdev) + seq_printf(sfp, + "multicast_address[%d]=\"%02x:%02x:%02x:%02x:%02x:%02x\"\n", + i++, + mcptr->addr[0], mcptr->addr[1], + mcptr->addr[2], mcptr->addr[3], + mcptr->addr[4], mcptr->addr[5]); +#endif /* < 2.6.35 */ + } +#endif + seq_printf(sfp, "num_tx_bytes = %lu\n", priv->stats.tx_bytes); + seq_printf(sfp, "num_rx_bytes = %lu\n", priv->stats.rx_bytes); + seq_printf(sfp, "num_tx_pkts = %lu\n", priv->stats.tx_packets); + seq_printf(sfp, "num_rx_pkts = %lu\n", priv->stats.rx_packets); + seq_printf(sfp, "num_tx_pkts_dropped = %lu\n", priv->stats.tx_dropped); + seq_printf(sfp, "num_rx_pkts_dropped = %lu\n", priv->stats.rx_dropped); + seq_printf(sfp, "num_tx_pkts_err = %lu\n", priv->stats.tx_errors); + seq_printf(sfp, "num_rx_pkts_err = %lu\n", priv->stats.rx_errors); + seq_printf(sfp, "carrier %s\n", + ((netif_carrier_ok(priv->netdev)) ? "on" : "off")); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) + for (i = 0; i < netdev->num_tx_queues; i++) { + seq_printf(sfp, "tx queue %d: %s\n", i, + ((netif_tx_queue_stopped + (netdev_get_tx_queue(netdev, 0))) ? "stopped" : + "started")); + } +#else + seq_printf(sfp, "tx queue %s\n", + ((netif_queue_stopped(priv->netdev)) ? "stopped" : + "started")); +#endif +#ifdef UAP_SUPPORT + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) { + seq_printf(sfp, "tkip_mic_failures = %u\n", + ustats.tkip_mic_failures); + seq_printf(sfp, "ccmp_decrypt_errors = %u\n", + ustats.ccmp_decrypt_errors); + seq_printf(sfp, "wep_undecryptable_count = %u\n", + ustats.wep_undecryptable_count); + seq_printf(sfp, "wep_icv_error_count = %u\n", + ustats.wep_icv_error_count); + seq_printf(sfp, "decrypt_failure_count = %u\n", + ustats.decrypt_failure_count); + seq_printf(sfp, "mcast_tx_count = %u\n", ustats.mcast_tx_count); + seq_printf(sfp, "failed_count = %u\n", ustats.failed_count); + seq_printf(sfp, "retry_count = %u\n", ustats.retry_count); + seq_printf(sfp, "multiple_retry_count = %u\n", + ustats.multi_retry_count); + seq_printf(sfp, "frame_duplicate_count = %u\n", + ustats.frame_dup_count); + seq_printf(sfp, "rts_success_count = %u\n", + ustats.rts_success_count); + seq_printf(sfp, "rts_failure_count = %u\n", + ustats.rts_failure_count); + seq_printf(sfp, "ack_failure_count = %u\n", + ustats.ack_failure_count); + seq_printf(sfp, "rx_fragment_count = %u\n", + ustats.rx_fragment_count); + seq_printf(sfp, "mcast_rx_frame_count = %u\n", + ustats.mcast_rx_frame_count); + seq_printf(sfp, "fcs_error_count = %u\n", + ustats.fcs_error_count); + seq_printf(sfp, "tx_frame_count = %u\n", ustats.tx_frame_count); + seq_printf(sfp, "rsna_tkip_cm_invoked = %u\n", + ustats.rsna_tkip_cm_invoked); + seq_printf(sfp, "rsna_4way_hshk_failures = %u\n", + ustats.rsna_4way_hshk_failures); + } +#endif /* UAP_SUPPORT */ +exit: + LEAVE(); + MODULE_PUT; + return 0; +} + +static int +woal_info_proc_open(struct inode *inode, struct file *file) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) + return single_open(file, woal_info_proc_read, PDE_DATA(inode)); +#else + return single_open(file, woal_info_proc_read, PDE(inode)->data); +#endif +} + +static const struct file_operations info_proc_fops = { + .owner = THIS_MODULE, + .open = woal_info_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +#define CMD52_STR_LEN 50 +/* + * @brief Parse cmd52 string + * + * @param buffer A pointer user buffer + * @param len Length user buffer + * @param func Parsed func number + * @param reg Parsed reg value + * @param val Parsed value to set + * @return BT_STATUS_SUCCESS + */ +static int +parse_cmd52_string(const char *buffer, size_t len, int *func, int *reg, + int *val) +{ + int ret = MLAN_STATUS_SUCCESS; + char *string = NULL; + char *pos = NULL; + gfp_t flag; + + ENTER(); + flag = (in_atomic() || irqs_disabled())? GFP_ATOMIC : GFP_KERNEL; + string = kzalloc(CMD52_STR_LEN, flag); + if (string == NULL) + return -ENOMEM; + + memcpy(string, buffer + strlen("sdcmd52rw="), + MIN((CMD52_STR_LEN - 1), (len - strlen("sdcmd52rw=")))); + string = strstrip(string); + + *func = -1; + *reg = -1; + *val = -1; + + /* Get func */ + pos = strsep(&string, " \t"); + if (pos) + *func = woal_string_to_number(pos); + + /* Get reg */ + pos = strsep(&string, " \t"); + if (pos) + *reg = woal_string_to_number(pos); + + /* Get val (optional) */ + pos = strsep(&string, " \t"); + if (pos) + *val = woal_string_to_number(pos); + kfree(string); + LEAVE(); + return ret; +} + +/** + * @brief config proc write function + * + * @param f file pointer + * @param buf pointer to data buffer + * @param count data number to write + * @param off Offset + * + * @return number of data + */ +static ssize_t +woal_config_write(struct file *f, const char __user * buf, size_t count, + loff_t * off) +{ + char databuf[101]; + char *line = NULL; + t_u32 config_data = 0; + struct seq_file *sfp = f->private_data; + moal_handle *handle = (moal_handle *)sfp->private; + + int func = 0, reg = 0, val = 0; + int copy_len; + moal_private *priv = NULL; + + ENTER(); + if (!MODULE_GET) { + LEAVE(); + return 0; + } + + if (count >= sizeof(databuf)) { + MODULE_PUT; + LEAVE(); + return (int)count; + } + memset(databuf, 0, sizeof(databuf)); + copy_len = MIN((sizeof(databuf) - 1), count); + if (copy_from_user(databuf, buf, copy_len)) { + MODULE_PUT; + LEAVE(); + return 0; + } + line = databuf; + if (!strncmp(databuf, "soft_reset", strlen("soft_reset"))) { + line += strlen("soft_reset") + 1; + config_data = (t_u32)woal_string_to_number(line); + PRINTM(MINFO, "soft_reset: %d\n", (int)config_data); + if (woal_request_soft_reset(handle) == MLAN_STATUS_SUCCESS) + handle->hardware_status = HardwareStatusReset; + else + PRINTM(MERROR, "Could not perform soft reset\n"); + } + if (!strncmp(databuf, "drv_mode", strlen("drv_mode"))) { + line += strlen("drv_mode") + 1; + config_data = (t_u32)woal_string_to_number(line); + PRINTM(MINFO, "drv_mode: %d\n", (int)config_data); + if (config_data != (t_u32)drv_mode) + if (woal_switch_drv_mode(handle, config_data) != + MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "Could not switch drv mode\n"); + } + } + if (!strncmp(databuf, "sdcmd52rw=", strlen("sdcmd52rw=")) && + count > strlen("sdcmd52rw=")) { + parse_cmd52_string((const char *)databuf, (size_t) count, &func, + ®, &val); + woal_sdio_read_write_cmd52(handle, func, reg, val); + } + if (!strncmp(databuf, "debug_dump", strlen("debug_dump"))) { + priv = woal_get_priv(handle, MLAN_BSS_ROLE_ANY); + if (priv) { + PRINTM(MERROR, "Recevie debug_dump command\n"); +#ifdef DEBUG_LEVEL1 + drvdbg &= ~MFW_D; +#endif + woal_mlan_debug_info(priv); + woal_moal_debug_info(priv, NULL, MFALSE); + + woal_dump_firmware_info_v3(handle); + } + } + + if (!strncmp(databuf, "fwdump_file=", strlen("fwdump_file="))) { + int len = copy_len - strlen("fwdump_file="); + gfp_t flag; + if (len) { + kfree(handle->fwdump_fname); + flag = (in_atomic() || + irqs_disabled())? GFP_ATOMIC : GFP_KERNEL; + handle->fwdump_fname = kzalloc(len, flag); + if (handle->fwdump_fname) + memcpy(handle->fwdump_fname, + databuf + strlen("fwdump_file="), + len - 1); + } + } + if (!strncmp(databuf, "fw_reload", strlen("fw_reload"))) { + if (!strncmp(databuf, "fw_reload=", strlen("fw_reload="))) { + line += strlen("fw_reload") + 1; + config_data = (t_u32)woal_string_to_number(line); + } else + config_data = FW_RELOAD_SDIO_INBAND_RESET; + PRINTM(MMSG, "Request fw_reload=%d\n", config_data); + woal_request_fw_reload(handle, config_data); + } + MODULE_PUT; + LEAVE(); + return (int)count; +} + +/** + * @brief config proc read function + * + * @param sfp pointer to seq_file structure + * @param data Void pointer to data + * + * @return number of output data + */ +static int +woal_config_read(struct seq_file *sfp, void *data) +{ + moal_handle *handle = (moal_handle *)sfp->private; + + ENTER(); + + if (!MODULE_GET) { + LEAVE(); + return 0; + } + + seq_printf(sfp, "hardware_status=%d\n", (int)handle->hardware_status); + seq_printf(sfp, "netlink_num=%d\n", (int)handle->netlink_num); + seq_printf(sfp, "drv_mode=%d\n", (int)drv_mode); + seq_printf(sfp, "sdcmd52rw=%d 0x%0x 0x%02X\n", handle->cmd52_func, + handle->cmd52_reg, handle->cmd52_val); + + MODULE_PUT; + LEAVE(); + return 0; +} + +static int +woal_config_proc_open(struct inode *inode, struct file *file) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) + return single_open(file, woal_config_read, PDE_DATA(inode)); +#else + return single_open(file, woal_config_read, PDE(inode)->data); +#endif +} + +static const struct file_operations config_proc_fops = { + .owner = THIS_MODULE, + .open = woal_config_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .write = woal_config_write, +}; + +/******************************************************** + Global Functions +********************************************************/ +/** + * @brief Convert string to number + * + * @param s Pointer to numbered string + * + * @return Converted number from string s + */ +int +woal_string_to_number(char *s) +{ + int r = 0; + int base = 0; + int pn = 1; + + if (!strncmp(s, "-", 1)) { + pn = -1; + s++; + } + if (!strncmp(s, "0x", 2) || !strncmp(s, "0X", 2)) { + base = 16; + s += 2; + } else + base = 10; + + for (s = s; *s; s++) { + if ((*s >= '0') && (*s <= '9')) + r = (r * base) + (*s - '0'); + else if ((*s >= 'A') && (*s <= 'F')) + r = (r * base) + (*s - 'A' + 10); + else if ((*s >= 'a') && (*s <= 'f')) + r = (r * base) + (*s - 'a' + 10); + else + break; + } + + return r * pn; +} + +/** + * @brief Create the top level proc directory + * + * @param handle Pointer to woal_handle + * + * @return N/A + */ +void +woal_proc_init(moal_handle *handle) +{ + struct proc_dir_entry *r; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) + struct proc_dir_entry *pde = PROC_DIR; +#endif + char config_proc_dir[20]; + + ENTER(); + + PRINTM(MINFO, "Create Proc Interface\n"); + if (!handle->proc_mwlan) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) + /* Check if directory already exists */ + for (pde = pde->subdir; pde; pde = pde->next) { + if (pde->namelen && !strcmp(MWLAN_PROC, pde->name)) { + /* Directory exists */ + PRINTM(MWARN, + "proc interface already exists!\n"); + handle->proc_mwlan = pde; + break; + } + } + if (pde == NULL) { + handle->proc_mwlan = proc_mkdir(MWLAN_PROC, PROC_DIR); + if (!handle->proc_mwlan) + PRINTM(MERROR, + "Cannot create proc interface!\n"); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24) + else + atomic_set(&handle->proc_mwlan->count, 1); +#endif + } +#else + if (!proc_mwlan) { + handle->proc_mwlan = proc_mkdir(MWLAN_PROC, PROC_DIR); + if (!handle->proc_mwlan) { + PRINTM(MERROR, + "Cannot create proc interface!\n"); + } + } else { + handle->proc_mwlan = proc_mwlan; + } +#endif + if (handle->proc_mwlan) { + if (handle->handle_idx) + sprintf(config_proc_dir, "config%d", + handle->handle_idx); + else + strcpy(config_proc_dir, "config"); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) + r = proc_create_data(config_proc_dir, 0644, + handle->proc_mwlan, + &config_proc_fops, handle); + if (r == NULL) +#else + r = create_proc_entry(config_proc_dir, 0644, + handle->proc_mwlan); + if (r) { + r->data = handle; + r->proc_fops = &config_proc_fops; + } else +#endif + PRINTM(MMSG, "Fail to create proc config\n"); + proc_dir_entry_use_count++; + } +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 26) + proc_mwlan = handle->proc_mwlan; +#endif + } + + LEAVE(); +} + +/** + * @brief Remove the top level proc directory + * + * @param handle pointer moal_handle + * + * @return N/A + */ +void +woal_proc_exit(moal_handle *handle) +{ + ENTER(); + + PRINTM(MINFO, "Remove Proc Interface\n"); + if (handle->proc_mwlan) { + char config_proc_dir[20]; + if (handle->handle_idx) + sprintf(config_proc_dir, "config%d", + handle->handle_idx); + else + strcpy(config_proc_dir, "config"); + remove_proc_entry(config_proc_dir, handle->proc_mwlan); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0) + /* Remove only if we are the only instance using this */ + if (atomic_read(&(handle->proc_mwlan->count)) > 1) { + PRINTM(MWARN, "More than one interface using proc!\n"); + } else { +#endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24) + atomic_dec(&(handle->proc_mwlan->count)); +#endif + if (!--proc_dir_entry_use_count) { + remove_proc_entry(MWLAN_PROC, PROC_DIR); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 26) + proc_mwlan = NULL; +#endif + } + + handle->proc_mwlan = NULL; +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0) + } +#endif + } + + LEAVE(); +} + +/** + * @brief Create proc file for interface + * + * @param priv pointer moal_private + * + * @return N/A + */ +void +woal_create_proc_entry(moal_private *priv) +{ + struct proc_dir_entry *r; + struct net_device *dev = priv->netdev; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) + char proc_dir_name[22]; +#endif + + ENTER(); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) + if (!priv->proc_entry) { + memset(proc_dir_name, 0, sizeof(proc_dir_name)); + strcpy(proc_dir_name, MWLAN_PROC_DIR); + + if (strlen(dev->name) > + ((sizeof(proc_dir_name) - 1) - strlen(MWLAN_PROC_DIR))) { + PRINTM(MERROR, + "Failed to create proc entry, device name is too long\n"); + LEAVE(); + return; + } + strcat(proc_dir_name, dev->name); + /* Try to create mwlan/mlanX first */ + priv->proc_entry = proc_mkdir(proc_dir_name, PROC_DIR); + if (priv->proc_entry) { + /* Success. Continue normally */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0) + if (!priv->phandle->proc_mwlan) { + priv->phandle->proc_mwlan = + priv->proc_entry->parent; + } + atomic_inc(&(priv->phandle->proc_mwlan->count)); +#endif + } else { + /* Failure. mwlan may not exist. Try to create that first */ + priv->phandle->proc_mwlan = + proc_mkdir(MWLAN_PROC, PROC_DIR); + if (!priv->phandle->proc_mwlan) { + /* Failure. Something broken */ + LEAVE(); + return; + } else { + /* Success. Now retry creating mlanX */ + priv->proc_entry = + proc_mkdir(proc_dir_name, PROC_DIR); +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0) + atomic_inc(&(priv->phandle->proc_mwlan->count)); +#endif + } + } +#else + if (priv->phandle->proc_mwlan && !priv->proc_entry) { + priv->proc_entry = + proc_mkdir(dev->name, priv->phandle->proc_mwlan); +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0) + atomic_inc(&(priv->phandle->proc_mwlan->count)); +#endif /* < 3.10.0 */ +#endif /* < 2.6.26 */ + strcpy(priv->proc_entry_name, dev->name); + if (priv->proc_entry) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) + r = proc_create_data("info", 0, priv->proc_entry, + &info_proc_fops, dev); + if (r == NULL) +#else + r = create_proc_entry("info", 0, priv->proc_entry); + if (r) { + r->data = dev; + r->proc_fops = &info_proc_fops; + } else +#endif + PRINTM(MMSG, "Fail to create proc info\n"); + } + } + + LEAVE(); +} + +/** + * @brief Remove proc file + * + * @param priv Pointer moal_private + * + * @return N/A + */ +void +woal_proc_remove(moal_private *priv) +{ + ENTER(); + if (priv->phandle->proc_mwlan && priv->proc_entry) { + remove_proc_entry("info", priv->proc_entry); + remove_proc_entry(priv->proc_entry_name, + priv->phandle->proc_mwlan); +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0) + atomic_dec(&(priv->phandle->proc_mwlan->count)); +#endif + priv->proc_entry = NULL; + } + LEAVE(); +} +#endif diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_sdio.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_sdio.h new file mode 100644 index 000000000000..e8c84ee70d80 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_sdio.h @@ -0,0 +1,143 @@ +/** @file moal_sdio.h + * + * @brief This file contains definitions for SDIO interface. + * driver. + * + * Copyright (C) 2008-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ +/**************************************************** +Change log: +****************************************************/ + +#ifndef _MOAL_SDIO_H +#define _MOAL_SDIO_H + +#include +#include +#include +#include +#include + +#include "moal_main.h" + +#ifndef BLOCK_MODE +/** Block mode */ +#define BLOCK_MODE 1 +#endif + +#ifndef BYTE_MODE +/** Byte Mode */ +#define BYTE_MODE 0 +#endif + +#ifndef FIXED_ADDRESS +/** Fixed address mode */ +#define FIXED_ADDRESS 0 +#endif + +#define SD8977_V0 0x0 +#define SD8977_V1 0x8 +#define SD8977_V2 0x9 +#define SD8977_V0_FW_NAME "mrvl/sdsd8977_combo.bin" +#define SD8977_V1_FW_NAME "mrvl/sdsd8977_combo_v1.bin" +#define SD8977_V2_FW_NAME "mrvl/sdsd8977_combo_v2.bin" +#define SD8977_WLAN_V2_FW_NAME "mrvl/sd8977_wlan_v2.bin" +#define SD8977_WLAN_V1_FW_NAME "mrvl/sd8977_wlan_v1.bin" +#define SD8977_WLAN_V0_FW_NAME "mrvl/sd8977_wlan.bin" + +#ifdef STA_SUPPORT +/** Default firmware name */ + +#define DEFAULT_FW_NAME "mrvl/sdsd8977_combo.bin" + +#ifndef DEFAULT_FW_NAME +#define DEFAULT_FW_NAME "" +#endif +#endif /* STA_SUPPORT */ + +#ifdef UAP_SUPPORT +/** Default firmware name */ + +#define DEFAULT_AP_FW_NAME "mrvl/sdsd8977_combo.bin" +#define DEFAULT_WLAN_FW_NAME "mrvl/sd8977_wlan.bin" + +#ifndef DEFAULT_AP_FW_NAME +#define DEFAULT_AP_FW_NAME "" +#endif +#endif /* UAP_SUPPORT */ + +/** Default firmaware name */ + +#define DEFAULT_AP_STA_FW_NAME "mrvl/sdsd8977_combo.bin" +#define DEFAULT_WLAN_FW_NAME "mrvl/sd8977_wlan.bin" + +#ifndef DEFAULT_AP_STA_FW_NAME +#define DEFAULT_AP_STA_FW_NAME "" +#endif + +/******************************************************** + Global Functions +********************************************************/ + +/** Function to write register */ +mlan_status woal_write_reg(moal_handle *handle, t_u32 reg, t_u32 data); +/** Function to read register */ +mlan_status woal_read_reg(moal_handle *handle, t_u32 reg, t_u32 *data); +/** Function to write data to IO memory */ +mlan_status woal_write_data_sync(moal_handle *handle, mlan_buffer *pmbuf, + t_u32 port, t_u32 timeout); +/** Function to read data from IO memory */ +mlan_status woal_read_data_sync(moal_handle *handle, mlan_buffer *pmbuf, + t_u32 port, t_u32 timeout); + +/** Register to bus driver function */ +mlan_status woal_bus_register(void); +/** Unregister from bus driver function */ +void woal_bus_unregister(void); + +/** Register device function */ +mlan_status woal_register_dev(moal_handle *handle); +/** Unregister device function */ +void woal_unregister_dev(moal_handle *handle); + +int woal_sdio_set_bus_clock(moal_handle *handle, t_u8 option); + +#ifdef SDIO_SUSPEND_RESUME +#ifdef MMC_PM_FUNC_SUSPENDED +/** Notify SDIO bus driver that WLAN is suspended */ +void woal_wlan_is_suspended(moal_handle *handle); +#endif +/** SDIO Suspend */ +int woal_sdio_suspend(struct device *dev); +/** SDIO Resume */ +int woal_sdio_resume(struct device *dev); +#endif /* SDIO_SUSPEND_RESUME */ + +/** Structure: SDIO MMC card */ +struct sdio_mmc_card { + /** sdio_func structure pointer */ + struct sdio_func *func; + /** moal_handle structure pointer */ + moal_handle *handle; + /** saved host clock value */ + unsigned int host_clock; +}; + +/** cmd52 read write */ +int woal_sdio_read_write_cmd52(moal_handle *handle, int func, int reg, int val); + +#endif /* _MOAL_SDIO_H */ diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_sdio_mmc.c b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_sdio_mmc.c new file mode 100644 index 000000000000..506baddd8990 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_sdio_mmc.c @@ -0,0 +1,1007 @@ +/** @file moal_sdio_mmc.c + * + * @brief This file contains SDIO MMC IF (interface) module + * related functions. + * + * Copyright (C) 2008-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ +/**************************************************** +Change log: + 02/25/09: Initial creation - + This file supports SDIO MMC only +****************************************************/ + +#include + +#include "moal_sdio.h" + +/** define marvell vendor id */ +#define MARVELL_VENDOR_ID 0x02df + +/* The macros below are hardware platform dependent. + The definition should match the actual platform */ +/** Initialize GPIO port */ +#define GPIO_PORT_INIT() +/** Set GPIO port to high */ +#define GPIO_PORT_TO_HIGH() +/** Set GPIO port to low */ +#define GPIO_PORT_TO_LOW() + +/******************************************************** + Local Variables +********************************************************/ + +/******************************************************** + Global Variables +********************************************************/ + +#ifdef SDIO_SUSPEND_RESUME +/** PM keep power */ +extern int pm_keep_power; +extern int shutdown_hs; +#endif + +extern int disconnect_on_suspend; + +/** Device ID for SD8977 */ +#define SD_DEVICE_ID_8977 (0x9145) + +/** WLAN IDs */ +static const struct sdio_device_id wlan_ids[] = { + {SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8977)}, + {}, +}; + +MODULE_DEVICE_TABLE(sdio, wlan_ids); + +int woal_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id); +void woal_sdio_remove(struct sdio_func *func); + +#ifdef SDIO_SUSPEND_RESUME +#ifdef MMC_PM_KEEP_POWER +int woal_sdio_suspend(struct device *dev); +int woal_sdio_resume(struct device *dev); + +static struct dev_pm_ops wlan_sdio_pm_ops = { + .suspend = woal_sdio_suspend, + .resume = woal_sdio_resume, +}; + +void woal_sdio_shutdown(struct device *dev); +#endif +#endif +static struct sdio_driver REFDATA wlan_sdio = { + .name = "wlan_sdio", + .id_table = wlan_ids, + .probe = woal_sdio_probe, + .remove = woal_sdio_remove, +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) + .drv = { + .owner = THIS_MODULE, +#ifdef SDIO_SUSPEND_RESUME +#ifdef MMC_PM_KEEP_POWER + .pm = &wlan_sdio_pm_ops, + .shutdown = woal_sdio_shutdown, +#endif +#endif + + } +#else +#ifdef SDIO_SUSPEND_RESUME +#ifdef MMC_PM_KEEP_POWER + .drv = { + .pm = &wlan_sdio_pm_ops, + .shutdown = woal_sdio_shutdown, + } +#endif +#endif +#endif +}; + +/******************************************************** + Local Functions +********************************************************/ +/** @brief This function dump the sdio register + * + * @param handle A Pointer to the moal_handle structure + * @return N/A + */ +void +woal_dump_sdio_reg(moal_handle *handle) +{ + int ret = 0; + t_u8 data, i; + int fun0_reg[] = { 0x05, 0x04 }; + t_u8 array_size = 0; + int fun1_reg[] = { 0x03, 0x04, 0x05, 0x60, 0x61 }; + + for (i = 0; i < ARRAY_SIZE(fun0_reg); i++) { + data = sdio_f0_readb(((struct sdio_mmc_card *)handle->card)-> + func, fun0_reg[i], &ret); + PRINTM(MMSG, "fun0: reg 0x%02x=0x%02x ret=%d\n", fun0_reg[i], + data, ret); + } + + array_size = ARRAY_SIZE(fun1_reg); + for (i = 0; i < array_size; i++) { + data = sdio_readb(((struct sdio_mmc_card *)handle->card)->func, + fun1_reg[i], &ret); + PRINTM(MMSG, "fun1: reg 0x%02x=0x%02x ret=%d\n", fun1_reg[i], + data, ret); + } + return; +} + +/******************************************************** + Global Functions +********************************************************/ + +/** + * @brief This function handles the interrupt. + * + * @param func A pointer to the sdio_func structure + * @return N/A + */ +static void +woal_sdio_interrupt(struct sdio_func *func) +{ + moal_handle *handle; + struct sdio_mmc_card *card; + + ENTER(); + + card = sdio_get_drvdata(func); + if (!card || !card->handle) { + PRINTM(MINFO, + "sdio_mmc_interrupt(func = %p) card or handle is NULL, card=%p\n", + func, card); + LEAVE(); + return; + } + handle = card->handle; + + PRINTM(MINFO, "*** IN SDIO IRQ ***\n"); + woal_interrupt(handle); + + LEAVE(); +} + +/** @brief This function handles client driver probe. + * + * @param func A pointer to sdio_func structure. + * @param id A pointer to sdio_device_id structure. + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE/error code + */ +int +woal_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id) +{ + int ret = MLAN_STATUS_SUCCESS; + struct sdio_mmc_card *card = NULL; + + ENTER(); + + PRINTM(MMSG, "vendor=0x%4.04X device=0x%4.04X class=%d function=%d\n", + func->vendor, func->device, func->class, func->num); + + card = kzalloc(sizeof(struct sdio_mmc_card), GFP_KERNEL); + if (!card) { + PRINTM(MFATAL, + "Failed to allocate memory in probe function!\n"); + LEAVE(); + return -ENOMEM; + } + + card->func = func; + +#ifdef MMC_QUIRK_BLKSZ_FOR_BYTE_MODE + /* The byte mode patch is available in kernel MMC driver + * which fixes one issue in MP-A transfer. + * bit1: use func->cur_blksize for byte mode + */ + func->card->quirks |= MMC_QUIRK_BLKSZ_FOR_BYTE_MODE; +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) + func->card->quirks |= MMC_QUIRK_LENIENT_FN0; +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) + /* wait for chip fully wake up */ + if (!func->enable_timeout) + func->enable_timeout = 200; +#endif + sdio_claim_host(func); + ret = sdio_enable_func(func); + if (ret) { + sdio_disable_func(func); + sdio_release_host(func); + kfree(card); + PRINTM(MFATAL, "sdio_enable_func() failed: ret=%d\n", ret); + LEAVE(); + return -EIO; + } + sdio_release_host(func); + if (NULL == woal_add_card(card)) { + PRINTM(MERROR, "woal_add_card failed\n"); + kfree(card); + sdio_claim_host(func); + sdio_disable_func(func); + sdio_release_host(func); + ret = MLAN_STATUS_FAILURE; + } + + LEAVE(); + return ret; +} + +/** @brief This function handles client driver remove. + * + * @param func A pointer to sdio_func structure. + * @return N/A + */ +void +woal_sdio_remove(struct sdio_func *func) +{ + struct sdio_mmc_card *card; + + ENTER(); + + if (func) { + PRINTM(MINFO, "SDIO func=%d\n", func->num); + card = sdio_get_drvdata(func); + if (card) { + woal_remove_card(card); + kfree(card); + } + } + + LEAVE(); +} + +#ifdef SDIO_SUSPEND_RESUME +#ifdef MMC_PM_KEEP_POWER +#ifdef MMC_PM_FUNC_SUSPENDED +/** @brief This function tells lower driver that WLAN is suspended + * + * @param handle A Pointer to the moal_handle structure + * @return N/A + */ +void +woal_wlan_is_suspended(moal_handle *handle) +{ + ENTER(); + if (handle->suspend_notify_req == MTRUE) { + handle->is_suspended = MTRUE; + sdio_func_suspended(((struct sdio_mmc_card *)handle->card)-> + func); + } + LEAVE(); +} +#endif + +#define SHUTDOWN_HOST_SLEEP_DEF_GAP 100 +#define SHUTDOWN_HOST_SLEEP_DEF_GPIO 0x3 +#define SHUTDOWN_HOST_SLEEP_DEF_COND 0x0 + +/** @brief This function handles client driver shutdown + * + * @param dev A pointer to device structure + * @return N/A + */ +void +woal_sdio_shutdown(struct device *dev) +{ + struct sdio_func *func = dev_to_sdio_func(dev); + moal_handle *handle = NULL; + struct sdio_mmc_card *cardp; + mlan_ds_ps_info pm_info; + int timeout = 0; + int i, retry_num = 8; + + ENTER(); + PRINTM(MCMND, "<--- Enter woal_sdio_shutdown --->\n"); + cardp = sdio_get_drvdata(func); + if (!cardp || !cardp->handle) { + PRINTM(MERROR, "Card or moal_handle structure is not valid\n"); + LEAVE(); + return; + } + handle = cardp->handle; + for (i = 0; i < handle->priv_num; i++) + netif_device_detach(handle->priv[i]->netdev); + + if (shutdown_hs) { + memset(&pm_info, 0, sizeof(pm_info)); + for (i = 0; i < retry_num; i++) { + if (MLAN_STATUS_SUCCESS == + woal_get_pm_info(woal_get_priv + (handle, MLAN_BSS_ROLE_ANY), + &pm_info)) { + if (pm_info.is_suspend_allowed == MTRUE) + break; + else + PRINTM(MMSG, + "Shutdown not allowed and retry again\n"); + } + woal_sched_timeout(100); + } + if (pm_info.is_suspend_allowed == MFALSE) { + PRINTM(MMSG, "Shutdown not allowed\n"); + goto done; + } + woal_enable_hs(woal_get_priv(handle, MLAN_BSS_ROLE_ANY)); + + timeout = + wait_event_interruptible_timeout(handle-> + hs_activate_wait_q, + handle-> + hs_activate_wait_q_woken, + HS_ACTIVE_TIMEOUT); + if (handle->hs_activated == MTRUE) + PRINTM(MMSG, "HS actived in shutdown\n"); + else + PRINTM(MMSG, "Fail to enable HS in shutdown\n"); + } else { + for (i = 0; i < MIN(handle->priv_num, MLAN_MAX_BSS_NUM); i++) { + if (handle->priv[i]) { + if (handle->priv[i]->media_connected == MTRUE +#ifdef UAP_SUPPORT + || (GET_BSS_ROLE(handle->priv[i]) == + MLAN_BSS_ROLE_UAP) +#endif + ) { + PRINTM(MIOCTL, + "disconnect on suspend\n"); + woal_disconnect(handle->priv[i], + MOAL_NO_WAIT, NULL, + DEF_DEAUTH_REASON_CODE); + } + } + } + } + +done: + PRINTM(MCMND, "<--- Leave woal_sdio_shutdown --->\n"); + LEAVE(); + return; +} + +/** @brief This function handles client driver suspend + * + * @param dev A pointer to device structure + * @return MLAN_STATUS_SUCCESS or error code + */ +int +woal_sdio_suspend(struct device *dev) +{ + struct sdio_func *func = dev_to_sdio_func(dev); + mmc_pm_flag_t pm_flags = 0; + moal_handle *handle = NULL; + struct sdio_mmc_card *cardp; + int i, retry_num = 8; + int ret = MLAN_STATUS_SUCCESS; + int hs_actived = 0; + mlan_ds_ps_info pm_info; + + ENTER(); + PRINTM(MCMND, "<--- Enter woal_sdio_suspend --->\n"); + pm_flags = sdio_get_host_pm_caps(func); + PRINTM(MCMND, "%s: suspend: PM flags = 0x%x\n", sdio_func_id(func), + pm_flags); + if (!(pm_flags & MMC_PM_KEEP_POWER)) { + PRINTM(MERROR, + "%s: cannot remain alive while host is suspended\n", + sdio_func_id(func)); + LEAVE(); + return -ENOSYS; + } + cardp = sdio_get_drvdata(func); + if (!cardp || !cardp->handle) { + PRINTM(MERROR, "Card or moal_handle structure is not valid\n"); + LEAVE(); + return MLAN_STATUS_SUCCESS; + } + + handle = cardp->handle; + if (handle->is_suspended == MTRUE) { + PRINTM(MWARN, "Device already suspended\n"); + LEAVE(); + return MLAN_STATUS_SUCCESS; + } + if (handle->fw_dump) { + PRINTM(MMSG, "suspend not allowed while FW dump!"); + ret = -EBUSY; + goto done; + } +#ifdef STA_SUPPORT + for (i = 0; i < MIN(handle->priv_num, MLAN_MAX_BSS_NUM); i++) { + if (handle->priv[i] && + (GET_BSS_ROLE(handle->priv[i]) == MLAN_BSS_ROLE_STA)) + woal_cancel_scan(handle->priv[i], MOAL_IOCTL_WAIT); + } +#endif + handle->suspend_fail = MFALSE; + memset(&pm_info, 0, sizeof(pm_info)); + for (i = 0; i < retry_num; i++) { + if (MLAN_STATUS_SUCCESS == + woal_get_pm_info(woal_get_priv(handle, MLAN_BSS_ROLE_ANY), + &pm_info)) { + if (pm_info.is_suspend_allowed == MTRUE) + break; + else + PRINTM(MMSG, + "Suspend not allowed and retry again\n"); + } + woal_sched_timeout(100); + } + if (pm_info.is_suspend_allowed == MFALSE) { + PRINTM(MMSG, "Suspend not allowed\n"); + ret = -EBUSY; + goto done; + } + + for (i = 0; i < handle->priv_num; i++) + netif_device_detach(handle->priv[i]->netdev); + + if (pm_keep_power) { + /* Enable the Host Sleep */ +#ifdef MMC_PM_FUNC_SUSPENDED + handle->suspend_notify_req = MTRUE; +#endif + hs_actived = + woal_enable_hs(woal_get_priv + (handle, MLAN_BSS_ROLE_ANY)); +#ifdef MMC_PM_FUNC_SUSPENDED + handle->suspend_notify_req = MFALSE; +#endif + if (hs_actived) { +#ifdef MMC_PM_SKIP_RESUME_PROBE + PRINTM(MCMND, + "suspend with MMC_PM_KEEP_POWER and MMC_PM_SKIP_RESUME_PROBE\n"); + ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER | + MMC_PM_SKIP_RESUME_PROBE); +#else + PRINTM(MCMND, "suspend with MMC_PM_KEEP_POWER\n"); + ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); +#endif + } else { + PRINTM(MMSG, "HS not actived, suspend fail!"); + handle->suspend_fail = MTRUE; + for (i = 0; i < handle->priv_num; i++) + netif_device_attach(handle->priv[i]->netdev); + ret = -EBUSY; + goto done; + } + } + + /* Indicate device suspended */ + handle->is_suspended = MTRUE; +done: + PRINTM(MCMND, "<--- Leave woal_sdio_suspend --->\n"); + LEAVE(); + return ret; +} + +/** @brief This function handles client driver resume + * + * @param dev A pointer to device structure + * @return MLAN_STATUS_SUCCESS + */ +int +woal_sdio_resume(struct device *dev) +{ + struct sdio_func *func = dev_to_sdio_func(dev); + mmc_pm_flag_t pm_flags = 0; + moal_handle *handle = NULL; + struct sdio_mmc_card *cardp; + int i; + + ENTER(); + PRINTM(MCMND, "<--- Enter woal_sdio_resume --->\n"); + pm_flags = sdio_get_host_pm_caps(func); + PRINTM(MCMND, "%s: resume: PM flags = 0x%x\n", sdio_func_id(func), + pm_flags); + cardp = sdio_get_drvdata(func); + if (!cardp || !cardp->handle) { + PRINTM(MERROR, "Card or moal_handle structure is not valid\n"); + LEAVE(); + return MLAN_STATUS_SUCCESS; + } + handle = cardp->handle; + + if (handle->is_suspended == MFALSE) { + PRINTM(MWARN, "Device already resumed\n"); + LEAVE(); + return MLAN_STATUS_SUCCESS; + } + handle->is_suspended = MFALSE; + if (woal_check_driver_status(handle)) { + PRINTM(MERROR, "Resuem, device is in hang state\n"); + LEAVE(); + return MLAN_STATUS_SUCCESS; + } + for (i = 0; i < handle->priv_num; i++) + netif_device_attach(handle->priv[i]->netdev); + + /* Disable Host Sleep */ + woal_cancel_hs(woal_get_priv(handle, MLAN_BSS_ROLE_ANY), MOAL_NO_WAIT); + PRINTM(MCMND, "<--- Leave woal_sdio_resume --->\n"); + LEAVE(); + return MLAN_STATUS_SUCCESS; +} +#endif +#endif /* SDIO_SUSPEND_RESUME */ + +/** + * @brief This function writes data into card register + * + * @param handle A Pointer to the moal_handle structure + * @param reg Register offset + * @param data Value + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +woal_write_reg(moal_handle *handle, t_u32 reg, t_u32 data) +{ + mlan_status ret = MLAN_STATUS_FAILURE; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) + sdio_claim_host(((struct sdio_mmc_card *)handle->card)->func); +#endif + sdio_writeb(((struct sdio_mmc_card *)handle->card)->func, (t_u8)data, + reg, (int *)&ret); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) + sdio_release_host(((struct sdio_mmc_card *)handle->card)->func); +#endif + return ret; +} + +/** + * @brief This function reads data from card register + * + * @param handle A Pointer to the moal_handle structure + * @param reg Register offset + * @param data Value + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +woal_read_reg(moal_handle *handle, t_u32 reg, t_u32 *data) +{ + mlan_status ret = MLAN_STATUS_FAILURE; + t_u8 val; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) + sdio_claim_host(((struct sdio_mmc_card *)handle->card)->func); +#endif + val = sdio_readb(((struct sdio_mmc_card *)handle->card)->func, reg, + (int *)&ret); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) + sdio_release_host(((struct sdio_mmc_card *)handle->card)->func); +#endif + *data = val; + + return ret; +} + +/** + * @brief This function use SG mode to read/write data into card memory + * + * @param handle A Pointer to the moal_handle structure + * @param pmbuf_list Pointer to a linked list of mlan_buffer structure + * @param port Port + * @param write write flag + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +woal_sdio_rw_mb(moal_handle *handle, pmlan_buffer pmbuf_list, t_u32 port, + t_u8 write) +{ + struct scatterlist sg_list[SDIO_MP_AGGR_DEF_PKT_LIMIT]; + int num_sg = pmbuf_list->use_count; + int i = 0; + mlan_buffer *pmbuf = NULL; + struct mmc_request mmc_req; + struct mmc_command mmc_cmd; + struct mmc_data mmc_dat; + struct sdio_func *func = ((struct sdio_mmc_card *)handle->card)->func; + t_u32 ioport = (port & MLAN_SDIO_IO_PORT_MASK); + t_u32 blkcnt = pmbuf_list->data_len / MLAN_SDIO_BLOCK_SIZE; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) + int status; +#endif + + if (num_sg > SDIO_MP_AGGR_DEF_PKT_LIMIT) { + PRINTM(MERROR, "ERROR: num_sg=%d", num_sg); + return MLAN_STATUS_FAILURE; + } + sg_init_table(sg_list, num_sg); + pmbuf = pmbuf_list->pnext; + for (i = 0; i < num_sg; i++) { + if (pmbuf == pmbuf_list) + break; + sg_set_buf(&sg_list[i], pmbuf->pbuf + pmbuf->data_offset, + pmbuf->data_len); + pmbuf = pmbuf->pnext; + } + memset(&mmc_req, 0, sizeof(struct mmc_request)); + memset(&mmc_cmd, 0, sizeof(struct mmc_command)); + memset(&mmc_dat, 0, sizeof(struct mmc_data)); + + mmc_dat.sg = sg_list; + mmc_dat.sg_len = num_sg; + mmc_dat.blksz = MLAN_SDIO_BLOCK_SIZE; + mmc_dat.blocks = blkcnt; + mmc_dat.flags = write ? MMC_DATA_WRITE : MMC_DATA_READ; + + mmc_cmd.opcode = SD_IO_RW_EXTENDED; + mmc_cmd.arg = write ? 1 << 31 : 0; + mmc_cmd.arg |= (func->num & 0x7) << 28; + mmc_cmd.arg |= 1 << 27; /* block basic */ + mmc_cmd.arg |= 0; /* fix address */ + mmc_cmd.arg |= (ioport & 0x1FFFF) << 9; + mmc_cmd.arg |= blkcnt & 0x1FF; + mmc_cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC; + + mmc_req.cmd = &mmc_cmd; + mmc_req.data = &mmc_dat; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) + sdio_claim_host(((struct sdio_mmc_card *)handle->card)->func); +#endif + mmc_set_data_timeout(&mmc_dat, + ((struct sdio_mmc_card *)handle->card)->func-> + card); + mmc_wait_for_req(((struct sdio_mmc_card *)handle->card)->func->card-> + host, &mmc_req); + + if (mmc_cmd.error || mmc_dat.error) { + PRINTM(MERROR, "CMD53 %s cmd_error = %d data_error=%d\n", + write ? "write" : "read", mmc_cmd.error, mmc_dat.error); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) + /* issue abort cmd52 command through F0 */ + sdio_f0_writeb(((struct sdio_mmc_card *)handle->card)->func, + 0x01, SDIO_CCCR_ABORT, &status); +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) + sdio_release_host(((struct sdio_mmc_card *)handle->card)->func); +#endif + return MLAN_STATUS_FAILURE; + } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) + sdio_release_host(((struct sdio_mmc_card *)handle->card)->func); +#endif + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function writes multiple bytes into card memory + * + * @param handle A Pointer to the moal_handle structure + * @param pmbuf Pointer to mlan_buffer structure + * @param port Port + * @param timeout Time out value + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +woal_write_data_sync(moal_handle *handle, mlan_buffer *pmbuf, t_u32 port, + t_u32 timeout) +{ + mlan_status ret = MLAN_STATUS_FAILURE; + t_u8 *buffer = (t_u8 *)(pmbuf->pbuf + pmbuf->data_offset); + t_u8 blkmode = + (port & MLAN_SDIO_BYTE_MODE_MASK) ? BYTE_MODE : BLOCK_MODE; + t_u32 blksz = (blkmode == BLOCK_MODE) ? MLAN_SDIO_BLOCK_SIZE : 1; + t_u32 blkcnt = + (blkmode == + BLOCK_MODE) ? (pmbuf->data_len / + MLAN_SDIO_BLOCK_SIZE) : pmbuf->data_len; + t_u32 ioport = (port & MLAN_SDIO_IO_PORT_MASK); + int status = 0; + if (pmbuf->use_count > 1) + return woal_sdio_rw_mb(handle, pmbuf, port, MTRUE); +#ifdef SDIO_MMC_DEBUG + handle->cmd53w = 1; +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) + sdio_claim_host(((struct sdio_mmc_card *)handle->card)->func); +#endif + status = sdio_writesb(((struct sdio_mmc_card *)handle->card)->func, + ioport, buffer, blkcnt * blksz); + if (!status) + ret = MLAN_STATUS_SUCCESS; + else { + PRINTM(MERROR, "cmd53 write error=%d\n", status); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) + /* issue abort cmd52 command through F0 */ + sdio_f0_writeb(((struct sdio_mmc_card *)handle->card)->func, + 0x01, SDIO_CCCR_ABORT, &status); +#endif + } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) + sdio_release_host(((struct sdio_mmc_card *)handle->card)->func); +#endif +#ifdef SDIO_MMC_DEBUG + handle->cmd53w = 2; +#endif + return ret; +} + +/** + * @brief This function reads multiple bytes from card memory + * + * @param handle A Pointer to the moal_handle structure + * @param pmbuf Pointer to mlan_buffer structure + * @param port Port + * @param timeout Time out value + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +woal_read_data_sync(moal_handle *handle, mlan_buffer *pmbuf, t_u32 port, + t_u32 timeout) +{ + mlan_status ret = MLAN_STATUS_FAILURE; + t_u8 *buffer = (t_u8 *)(pmbuf->pbuf + pmbuf->data_offset); + t_u8 blkmode = + (port & MLAN_SDIO_BYTE_MODE_MASK) ? BYTE_MODE : BLOCK_MODE; + t_u32 blksz = (blkmode == BLOCK_MODE) ? MLAN_SDIO_BLOCK_SIZE : 1; + t_u32 blkcnt = + (blkmode == + BLOCK_MODE) ? (pmbuf->data_len / + MLAN_SDIO_BLOCK_SIZE) : pmbuf->data_len; + t_u32 ioport = (port & MLAN_SDIO_IO_PORT_MASK); + int status = 0; + if (pmbuf->use_count > 1) + return woal_sdio_rw_mb(handle, pmbuf, port, MFALSE); +#ifdef SDIO_MMC_DEBUG + handle->cmd53r = 1; +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) + sdio_claim_host(((struct sdio_mmc_card *)handle->card)->func); +#endif + status = sdio_readsb(((struct sdio_mmc_card *)handle->card)->func, + buffer, ioport, blkcnt * blksz); + if (!status) { + ret = MLAN_STATUS_SUCCESS; + } else { + PRINTM(MERROR, "cmd53 read error=%d\n", status); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) + /* issue abort cmd52 command through F0 */ + sdio_f0_writeb(((struct sdio_mmc_card *)handle->card)->func, + 0x01, SDIO_CCCR_ABORT, &status); +#endif + } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) + sdio_release_host(((struct sdio_mmc_card *)handle->card)->func); +#endif +#ifdef SDIO_MMC_DEBUG + handle->cmd53r = 2; +#endif + return ret; +} + +/** + * @brief This function registers the IF module in bus driver + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +woal_bus_register(void) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + /* SDIO Driver Registration */ + if (sdio_register_driver(&wlan_sdio)) { + PRINTM(MFATAL, "SDIO Driver Registration Failed \n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + /* init GPIO PORT for wakeup purpose */ + GPIO_PORT_INIT(); + /* set default value */ + GPIO_PORT_TO_HIGH(); + + LEAVE(); + return ret; +} + +/** + * @brief This function de-registers the IF module in bus driver + * + * @return N/A + */ +void +woal_bus_unregister(void) +{ + ENTER(); + + /* SDIO Driver Unregistration */ + sdio_unregister_driver(&wlan_sdio); + + LEAVE(); +} + +/** + * @brief This function de-registers the device + * + * @param handle A pointer to moal_handle structure + * @return N/A + */ +void +woal_unregister_dev(moal_handle *handle) +{ + ENTER(); + if (handle->card) { + /* Release the SDIO IRQ */ + sdio_claim_host(((struct sdio_mmc_card *)handle->card)->func); + sdio_release_irq(((struct sdio_mmc_card *)handle->card)->func); + sdio_disable_func(((struct sdio_mmc_card *)handle->card)->func); + sdio_release_host(((struct sdio_mmc_card *)handle->card)->func); + + sdio_set_drvdata(((struct sdio_mmc_card *)handle->card)->func, + NULL); + + GPIO_PORT_TO_LOW(); + PRINTM(MWARN, "Making the sdio dev card as NULL\n"); + } + + LEAVE(); +} + +/** + * @brief This function registers the device + * + * @param handle A pointer to moal_handle structure + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +woal_register_dev(moal_handle *handle) +{ + int ret = MLAN_STATUS_SUCCESS; + struct sdio_mmc_card *card = handle->card; + struct sdio_func *func; + + ENTER(); + + GPIO_PORT_INIT(); + GPIO_PORT_TO_HIGH(); + + func = card->func; + sdio_claim_host(func); + /* Request the SDIO IRQ */ + ret = sdio_claim_irq(func, woal_sdio_interrupt); + if (ret) { + PRINTM(MFATAL, "sdio_claim_irq failed: ret=%d\n", ret); + goto release_host; + } + + /* Set block size */ + ret = sdio_set_block_size(card->func, MLAN_SDIO_BLOCK_SIZE); + if (ret) { + PRINTM(MERROR, + "sdio_set_block_seize(): cannot set SDIO block size\n"); + ret = MLAN_STATUS_FAILURE; + goto release_irq; + } + + sdio_release_host(func); + sdio_set_drvdata(func, card); + + handle->hotplug_device = &func->dev; + + LEAVE(); + return MLAN_STATUS_SUCCESS; + +release_irq: + sdio_release_irq(func); +release_host: + sdio_release_host(func); + handle->card = NULL; + + LEAVE(); + return MLAN_STATUS_FAILURE; +} + +/** + * @brief This function set bus clock on/off + * + * @param handle A pointer to moal_handle structure + * @param option TRUE--on , FALSE--off + * @return MLAN_STATUS_SUCCESS + */ +int +woal_sdio_set_bus_clock(moal_handle *handle, t_u8 option) +{ + struct sdio_mmc_card *cardp = (struct sdio_mmc_card *)handle->card; + struct mmc_host *host = cardp->func->card->host; + + ENTER(); + if (option == MTRUE) { + /* restore value if non-zero */ + if (cardp->host_clock) + host->ios.clock = cardp->host_clock; + } else { + /* backup value if non-zero, then clear */ + if (host->ios.clock) + cardp->host_clock = host->ios.clock; + host->ios.clock = 0; + } + + host->ops->set_ios(host, &host->ios); + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function updates card reg based on the Cmd52 value in dev structure + * + * @param handle A pointer to moal_handle structure + * @param func A pointer to store func variable + * @param reg A pointer to store reg variable + * @param val A pointer to store val variable + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +int +woal_sdio_read_write_cmd52(moal_handle *handle, int func, int reg, int val) +{ + int ret = MLAN_STATUS_SUCCESS; + struct sdio_mmc_card *card = (struct sdio_mmc_card *)handle->card; + + ENTER(); + /* Save current func and reg for read */ + handle->cmd52_func = func; + handle->cmd52_reg = reg; + sdio_claim_host(card->func); + if (val >= 0) { + /* Perform actual write only if val is provided */ + if (func) + sdio_writeb(card->func, val, reg, &ret); + else + sdio_f0_writeb(card->func, val, reg, &ret); + if (ret) { + PRINTM(MERROR, + "Cannot write value (0x%x) to func %d reg 0x%x\n", + val, func, reg); + } else { + PRINTM(MMSG, "write value (0x%x) to func %d reg 0x%x\n", + (u8)val, func, reg); + handle->cmd52_val = val; + } + } else { + if (func) + val = sdio_readb(card->func, reg, &ret); + else + val = sdio_f0_readb(card->func, reg, &ret); + if (ret) { + PRINTM(MERROR, + "Cannot read value from func %d reg 0x%x\n", + func, reg); + } else { + PRINTM(MMSG, + "read value (0x%x) from func %d reg 0x%x\n", + (u8)val, func, reg); + handle->cmd52_val = val; + } + } + sdio_release_host(card->func); + LEAVE(); + return ret; +} diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_shim.c b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_shim.c new file mode 100644 index 000000000000..2cda9ddec99c --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_shim.c @@ -0,0 +1,2968 @@ +/** @file moal_shim.c + * + * @brief This file contains the callback functions registered to MLAN + * + * Copyright (C) 2008-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +/******************************************************** +Change log: + 10/21/2008: initial version +********************************************************/ + +#include "moal_main.h" +#include "moal_sdio.h" +#ifdef UAP_SUPPORT +#include "moal_uap.h" +#endif +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#include "moal_cfg80211.h" +#include "moal_cfgvendor.h" +#endif +extern int drv_mode; +#include + +/******************************************************** + Local Variables +********************************************************/ +/** moal_lock */ +typedef struct _moal_lock { + /** Lock */ + spinlock_t lock; + /** Flags */ + unsigned long flags; +} moal_lock; + +/******************************************************** + Global Variables +********************************************************/ +extern int cfg80211_wext; + +extern int hw_test; + +#ifdef ANDROID_KERNEL +extern int wakelock_timeout; +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +extern int dfs_offload; +#endif +#endif + +/** napi support*/ +extern int napi; + +typedef MLAN_PACK_START struct { + t_u32 t4; + t_u8 t4_error; + t_u32 t1; + t_u8 t1_error; + t_u64 egress_time; +} MLAN_PACK_END confirm_timestamps; + +/******************************************************** + Local Functions +********************************************************/ + +/******************************************************** + Global Functions +********************************************************/ +/** + * @brief Alloc a buffer + * + * @param pmoal_handle Pointer to the MOAL context + * @param size The size of the buffer to be allocated + * @param flag The type of the buffer to be allocated + * @param ppbuf Pointer to a buffer location to store buffer pointer allocated + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +moal_malloc(IN t_void *pmoal_handle, + IN t_u32 size, IN t_u32 flag, OUT t_u8 **ppbuf) +{ + moal_handle *handle = (moal_handle *)pmoal_handle; + t_u32 mem_flag = (in_interrupt() || irqs_disabled() || + !write_can_lock(&dev_base_lock)) ? GFP_ATOMIC : + GFP_KERNEL; + + if (flag & MLAN_MEM_DMA) + mem_flag |= GFP_DMA; + + *ppbuf = kzalloc(size, mem_flag); + if (*ppbuf == NULL) { + PRINTM(MERROR, "%s: allocate memory (%d bytes) failed!\n", + __func__, (int)size); + return MLAN_STATUS_FAILURE; + } + atomic_inc(&handle->malloc_count); + + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Free a buffer + * + * @param pmoal_handle Pointer to the MOAL context + * @param pbuf Pointer to the buffer to be freed + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +moal_mfree(IN t_void *pmoal_handle, IN t_u8 *pbuf) +{ + moal_handle *handle = (moal_handle *)pmoal_handle; + + if (!pbuf) + return MLAN_STATUS_FAILURE; + kfree(pbuf); + atomic_dec(&handle->malloc_count); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Alloc a vitual-address-continuous buffer + * + * @param pmoal_handle Pointer to the MOAL context + * @param size The size of the buffer to be allocated + * @param ppbuf Pointer to a buffer location to store buffer pointer allocated + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +moal_vmalloc(IN t_void *pmoal_handle, IN t_u32 size, OUT t_u8 **ppbuf) +{ + moal_handle *handle = (moal_handle *)pmoal_handle; + + *ppbuf = vmalloc(size); + if (*ppbuf == NULL) { + PRINTM(MERROR, "%s: vmalloc (%d bytes) failed!", __func__, + (int)size); + return MLAN_STATUS_FAILURE; + } + atomic_inc(&handle->vmalloc_count); + + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Free a buffer allocated by vmalloc + * + * @param pmoal_handle Pointer to the MOAL context + * @param pbuf Pointer to the buffer to be freed + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +moal_vfree(IN t_void *pmoal_handle, IN t_u8 *pbuf) +{ + moal_handle *handle = (moal_handle *)pmoal_handle; + + if (!pbuf) + return MLAN_STATUS_FAILURE; + vfree(pbuf); + atomic_dec(&handle->vmalloc_count); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Fill memory with constant byte + * + * @param pmoal_handle Pointer to the MOAL context + * @param pmem Pointer to the memory area + * @param byte A constant byte + * @param num Number of bytes to fill + * + * @return Pointer to the memory area + */ +t_void * +moal_memset(IN t_void *pmoal_handle, + IN t_void *pmem, IN t_u8 byte, IN t_u32 num) +{ + t_void *p = pmem; + + if (pmem && num) + p = memset(pmem, byte, num); + + return p; +} + +/** + * @brief Copy memory from one area to another + * + * @param pmoal_handle Pointer to the MOAL context + * @param pdest Pointer to the dest memory + * @param psrc Pointer to the src memory + * @param num Number of bytes to move + * + * @return Pointer to the dest memory + */ +t_void * +moal_memcpy(IN t_void *pmoal_handle, + IN t_void *pdest, IN const t_void *psrc, IN t_u32 num) +{ + t_void *p = pdest; + + if (pdest && psrc && num) + p = memcpy(pdest, psrc, num); + + return p; +} + +/** + * @brief Move memory from one area to another + * + * @param pmoal_handle Pointer to the MOAL context + * @param pdest Pointer to the dest memory + * @param psrc Pointer to the src memory + * @param num Number of bytes to move + * + * @return Pointer to the dest memory + */ +t_void * +moal_memmove(IN t_void *pmoal_handle, + IN t_void *pdest, IN const t_void *psrc, IN t_u32 num) +{ + t_void *p = pdest; + + if (pdest && psrc && num) + p = memmove(pdest, psrc, num); + + return p; +} + +/** + * @brief Compare two memory areas + * + * @param pmoal_handle Pointer to the MOAL context + * @param pmem1 Pointer to the first memory + * @param pmem2 Pointer to the second memory + * @param num Number of bytes to compare + * + * @return Compare result returns by memcmp + */ +t_s32 +moal_memcmp(IN t_void *pmoal_handle, + IN const t_void *pmem1, IN const t_void *pmem2, IN t_u32 num) +{ + t_s32 result; + + result = memcmp(pmem1, pmem2, num); + + return result; +} + +/** + * @brief Delay function + * + * @param pmoal_handle Pointer to the MOAL context + * @param delay delay in micro-second + * + * @return N/A + */ +t_void +moal_udelay(IN t_void *pmoal_handle, IN t_u32 delay) +{ + if (delay >= 1000) + mdelay(delay / 1000); + if (delay % 1000) + udelay(delay % 1000); +} + +/** + * @brief Retrieves the current system time + * + * @param pmoal_handle Pointer to the MOAL context + * @param psec Pointer to buf for the seconds of system time + * @param pusec Pointer to buf the micro seconds of system time + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +moal_get_system_time(IN t_void *pmoal_handle, OUT t_u32 *psec, OUT t_u32 *pusec) +{ + struct timeval t; + + do_gettimeofday(&t); + *psec = (t_u32)t.tv_sec; + *pusec = (t_u32)t.tv_usec; + + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Initializes the timer + * + * @param pmoal_handle Pointer to the MOAL context + * @param pptimer Pointer to the timer + * @param callback Pointer to callback function + * @param pcontext Pointer to context + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +moal_init_timer(IN t_void *pmoal_handle, + OUT t_void **pptimer, + IN t_void (*callback) (t_void *pcontext), IN t_void *pcontext) +{ + moal_drv_timer *timer = NULL; + t_u32 mem_flag = (in_interrupt() || irqs_disabled() || + !write_can_lock(&dev_base_lock)) ? GFP_ATOMIC : + GFP_KERNEL; + + timer = kmalloc(sizeof(moal_drv_timer), mem_flag); + if (timer == NULL) + return MLAN_STATUS_FAILURE; + woal_initialize_timer(timer, callback, pcontext); + *pptimer = (t_void *)timer; + + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Free the timer + * + * @param pmoal_handle Pointer to the MOAL context + * @param ptimer Pointer to the timer + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +moal_free_timer(IN t_void *pmoal_handle, IN t_void *ptimer) +{ + moal_drv_timer *timer = (moal_drv_timer *)ptimer; + + if (timer) { + if ((timer->timer_is_canceled == MFALSE) && timer->time_period) { + PRINTM(MWARN, + "mlan try to free timer without stop timer!\n"); + woal_cancel_timer(timer); + } + kfree(timer); + } + + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Start the timer + * + * @param pmoal_handle Pointer to the MOAL context + * @param ptimer Pointer to the timer + * @param periodic Periodic timer + * @param msec Timer value in milliseconds + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +moal_start_timer(IN t_void *pmoal_handle, + IN t_void *ptimer, IN t_u8 periodic, IN t_u32 msec) +{ + if (!ptimer) + return MLAN_STATUS_FAILURE; + + ((moal_drv_timer *)ptimer)->timer_is_periodic = periodic; + woal_mod_timer((moal_drv_timer *)ptimer, msec); + + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Stop the timer + * + * @param pmoal_handle Pointer to the MOAL context + * @param ptimer Pointer to the timer + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +moal_stop_timer(IN t_void *pmoal_handle, IN t_void *ptimer) +{ + if (!ptimer) + return MLAN_STATUS_FAILURE; + woal_cancel_timer((moal_drv_timer *)ptimer); + + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Initializes the lock + * + * @param pmoal_handle Pointer to the MOAL context + * @param pplock Pointer to the lock + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +moal_init_lock(IN t_void *pmoal_handle, OUT t_void **pplock) +{ + moal_handle *handle = (moal_handle *)pmoal_handle; + moal_lock *mlock = NULL; + + mlock = kmalloc(sizeof(moal_lock), GFP_ATOMIC); + if (!mlock) + return MLAN_STATUS_FAILURE; + spin_lock_init(&mlock->lock); + *pplock = (t_void *)mlock; + + atomic_inc(&handle->lock_count); + + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Free the lock + * + * @param pmoal_handle Pointer to the MOAL context + * @param plock Lock + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +moal_free_lock(IN t_void *pmoal_handle, IN t_void *plock) +{ + moal_handle *handle = (moal_handle *)pmoal_handle; + moal_lock *mlock = plock; + + kfree(mlock); + if (mlock) + atomic_dec(&handle->lock_count); + + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Request a spin lock + * + * @param pmoal_handle Pointer to the MOAL context + * @param plock Pointer to the lock + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +moal_spin_lock(IN t_void *pmoal_handle, IN t_void *plock) +{ + moal_lock *mlock = plock; + unsigned long flags = 0; + + if (mlock) { + spin_lock_irqsave(&mlock->lock, flags); + mlock->flags = flags; + return MLAN_STATUS_SUCCESS; + } else { + return MLAN_STATUS_FAILURE; + } +} + +/** + * @brief Request a spin_unlock + * + * @param pmoal_handle Pointer to the MOAL context + * @param plock Pointer to the lock + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +moal_spin_unlock(IN t_void *pmoal_handle, IN t_void *plock) +{ + moal_lock *mlock = (moal_lock *)plock; + + if (mlock) { + spin_unlock_irqrestore(&mlock->lock, mlock->flags); + + return MLAN_STATUS_SUCCESS; + } else { + return MLAN_STATUS_FAILURE; + } +} + +/** + * @brief This function reads one block of firmware data from MOAL + * + * @param pmoal_handle Pointer to the MOAL context + * @param offset Offset from where the data will be copied + * @param len Length to be copied + * @param pbuf Buffer where the data will be copied + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +moal_get_fw_data(IN t_void *pmoal_handle, + IN t_u32 offset, IN t_u32 len, OUT t_u8 *pbuf) +{ + moal_handle *handle = (moal_handle *)pmoal_handle; + + if (!pbuf || !len) + return MLAN_STATUS_FAILURE; + + if (offset + len > handle->firmware->size) + return MLAN_STATUS_FAILURE; + + memcpy(pbuf, handle->firmware->data + offset, len); + + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function is called when MLAN completes the initialization firmware. + * + * @param pmoal_handle Pointer to the MOAL context + * @param status The status code for mlan_init_fw request + * @param phw pointer to mlan_hw_info + * @param ptbl pointer to mplan_bss_tbl + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +moal_get_hw_spec_complete(IN t_void *pmoal_handle, IN mlan_status status, + IN mlan_hw_info * phw, IN pmlan_bss_tbl ptbl) +{ + ENTER(); + if (status == MLAN_STATUS_SUCCESS) { + PRINTM(MCMND, "Get Hw Spec done, fw_cap=0x%x\n", phw->fw_cap); + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function is called when MLAN completes the initialization firmware. + * + * @param pmoal_handle Pointer to the MOAL context + * @param status The status code for mlan_init_fw request + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +moal_init_fw_complete(IN t_void *pmoal_handle, IN mlan_status status) +{ + moal_handle *handle = (moal_handle *)pmoal_handle; + ENTER(); + if (status == MLAN_STATUS_SUCCESS) + handle->hardware_status = HardwareStatusReady; + handle->init_wait_q_woken = MTRUE; + wake_up_interruptible(&handle->init_wait_q); + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function is called when MLAN shutdown firmware is completed. + * + * @param pmoal_handle Pointer to the MOAL context + * @param status The status code for mlan_shutdown request + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +moal_shutdown_fw_complete(IN t_void *pmoal_handle, IN mlan_status status) +{ + moal_handle *handle = (moal_handle *)pmoal_handle; + ENTER(); + handle->hardware_status = HardwareStatusNotReady; + handle->init_wait_q_woken = MTRUE; + wake_up_interruptible(&handle->init_wait_q); + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function is called when an MLAN IOCTL is completed. + * + * @param pmoal_handle Pointer to the MOAL context + * @param pioctl_req pointer to structure mlan_ioctl_req + * @param status The status code for mlan_ioctl request + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +moal_ioctl_complete(IN t_void *pmoal_handle, + IN pmlan_ioctl_req pioctl_req, IN mlan_status status) +{ + moal_handle *handle = (moal_handle *)pmoal_handle; + moal_private *priv = NULL; + wait_queue *wait; + unsigned long flags = 0; + ENTER(); + + if (!atomic_read(&handle->ioctl_pending)) + PRINTM(MERROR, "ERR: Unexpected IOCTL completed: %p\n", + pioctl_req); + else + atomic_dec(&handle->ioctl_pending); + priv = woal_bss_index_to_priv(handle, pioctl_req->bss_index); + if (!priv) { + PRINTM(MERROR, + "IOCTL %p complete with NULL priv, bss_index=%d\n", + pioctl_req, pioctl_req->bss_index); + LEAVE(); + return MLAN_STATUS_SUCCESS; + } + + if (status != MLAN_STATUS_SUCCESS && status != MLAN_STATUS_COMPLETE) + PRINTM(MERROR, + "IOCTL failed: %p id=0x%x, sub_id=0x%x action=%d, status_code=0x%x\n", + pioctl_req, pioctl_req->req_id, + (*(t_u32 *)pioctl_req->pbuf), (int)pioctl_req->action, + pioctl_req->status_code); + else + PRINTM(MIOCTL, + "IOCTL completed: %p id=0x%x sub_id=0x%x, action=%d, status=%d, status_code=0x%x\n", + pioctl_req, pioctl_req->req_id, + (*(t_u32 *)pioctl_req->pbuf), (int)pioctl_req->action, + status, pioctl_req->status_code); + + spin_lock_irqsave(&handle->driver_lock, flags); + wait = (wait_queue *)pioctl_req->reserved_1; + if (wait) { + wait->condition = MTRUE; + wait->status = status; + if (wait->wait_timeout) { + wake_up(&wait->wait); + } else { + if ((status != MLAN_STATUS_SUCCESS) && + (pioctl_req->status_code == + MLAN_ERROR_CMD_TIMEOUT)) { + PRINTM(MERROR, "IOCTL: command timeout\n"); + } else { + wake_up_interruptible(&wait->wait); + } + } + spin_unlock_irqrestore(&handle->driver_lock, flags); + } else { + spin_unlock_irqrestore(&handle->driver_lock, flags); + if ((status == MLAN_STATUS_SUCCESS) && + (pioctl_req->action == MLAN_ACT_GET)) + woal_process_ioctl_resp(priv, pioctl_req); + kfree(pioctl_req); + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function allocates mlan_buffer. + * + * @param pmoal_handle Pointer to the MOAL context + * @param size allocation size requested + * @param pmbuf pointer to pointer to the allocated buffer + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +moal_alloc_mlan_buffer(IN t_void *pmoal_handle, + IN t_u32 size, OUT pmlan_buffer *pmbuf) +{ + *pmbuf = woal_alloc_mlan_buffer((moal_handle *)pmoal_handle, size); + if (NULL == *pmbuf) + return MLAN_STATUS_FAILURE; + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function frees mlan_buffer. + * + * @param pmoal_handle Pointer to the MOAL context + * @param pmbuf pointer to buffer to be freed + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +moal_free_mlan_buffer(IN t_void *pmoal_handle, IN pmlan_buffer pmbuf) +{ + if (!pmbuf) + return MLAN_STATUS_FAILURE; + woal_free_mlan_buffer((moal_handle *)pmoal_handle, pmbuf); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function is called when MLAN complete send data packet. + * + * @param pmoal_handle Pointer to the MOAL context + * @param pmbuf Pointer to the mlan buffer structure + * @param status The status code for mlan_send_packet request + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +moal_send_packet_complete(IN t_void *pmoal_handle, + IN pmlan_buffer pmbuf, IN mlan_status status) +{ + moal_private *priv = NULL; + moal_handle *handle = (moal_handle *)pmoal_handle; + struct sk_buff *skb = NULL; +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) + t_u32 index = 0; +#endif + + ENTER(); + if (pmbuf && pmbuf->buf_type == MLAN_BUF_TYPE_RAW_DATA) { + woal_free_mlan_buffer(handle, pmbuf); + atomic_dec(&handle->tx_pending); + LEAVE(); + return MLAN_STATUS_SUCCESS; + } + if (pmbuf) { + priv = woal_bss_index_to_priv(pmoal_handle, pmbuf->bss_index); + skb = (struct sk_buff *)pmbuf->pdesc; + if (priv) { + woal_set_trans_start(priv->netdev); + if (skb) { + if (status == MLAN_STATUS_SUCCESS) { + priv->stats.tx_packets++; + priv->stats.tx_bytes += skb->len; + } else { + priv->stats.tx_errors++; + } +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) + index = skb_get_queue_mapping(skb); + atomic_dec(&handle->tx_pending); + if (atomic_dec_return + (&priv->wmm_tx_pending[index]) == + LOW_TX_PENDING) { + struct netdev_queue *txq = + netdev_get_tx_queue(priv-> + netdev, + index); + if (netif_tx_queue_stopped(txq)) { + netif_tx_wake_queue(txq); + PRINTM(MINFO, + "Wakeup Kernel Queue:%d\n", + index); + } + } +#else /*#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,29) */ + if (atomic_dec_return(&handle->tx_pending) < + LOW_TX_PENDING) { + int i; + for (i = 0; i < handle->priv_num; i++) { +#ifdef STA_SUPPORT + if ((GET_BSS_ROLE + (handle->priv[i]) == + MLAN_BSS_ROLE_STA) && + (handle->priv[i]-> + media_connected || + priv-> + is_adhoc_link_sensed)) { + woal_wake_queue(handle-> + priv + [i]-> + netdev); + } +#endif +#ifdef UAP_SUPPORT + if ((GET_BSS_ROLE + (handle->priv[i]) == + MLAN_BSS_ROLE_UAP) && + (handle->priv[i]-> + media_connected)) { + woal_wake_queue(handle-> + priv + [i]-> + netdev); + } +#endif + } + } +#endif /*#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,29) */ + } + } + if (skb) + dev_kfree_skb_any(skb); + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function write a command/data packet to card. + * This function blocks the call until it finishes + * + * @param pmoal_handle Pointer to the MOAL context + * @param pmbuf Pointer to the mlan buffer structure + * @param port Port number for sent + * @param timeout Timeout value in milliseconds (if 0 the wait is forever) + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +moal_write_data_sync(IN t_void *pmoal_handle, + IN pmlan_buffer pmbuf, IN t_u32 port, IN t_u32 timeout) +{ + return woal_write_data_sync((moal_handle *)pmoal_handle, pmbuf, port, + timeout); +} + +/** + * @brief This function read data packet/event/command from card. + * This function blocks the call until it finish + * + * @param pmoal_handle Pointer to the MOAL context + * @param pmbuf Pointer to the mlan buffer structure + * @param port Port number for read + * @param timeout Timeout value in milliseconds (if 0 the wait is forever) + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +moal_read_data_sync(IN t_void *pmoal_handle, + IN OUT pmlan_buffer pmbuf, IN t_u32 port, IN t_u32 timeout) +{ + return woal_read_data_sync((moal_handle *)pmoal_handle, pmbuf, port, + timeout); +} + +/** + * @brief This function writes data into card register. + * + * @param pmoal_handle Pointer to the MOAL context + * @param reg register offset + * @param data value + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +moal_write_reg(IN t_void *pmoal_handle, IN t_u32 reg, IN t_u32 data) +{ + return woal_write_reg((moal_handle *)pmoal_handle, reg, data); +} + +/** + * @brief This function reads data from card register. + * + * @param pmoal_handle Pointer to the MOAL context + * @param reg register offset + * @param data value + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +moal_read_reg(IN t_void *pmoal_handle, IN t_u32 reg, OUT t_u32 *data) +{ + return woal_read_reg((moal_handle *)pmoal_handle, reg, data); +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) +/** + * @brief This function uploads the packet to the network stack monitor interface + * + * @param handle Pointer to the MOAL context + * @param pmbuf Pointer to mlan_buffer + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING/MLAN_STATUS_FAILURE + */ +mlan_status +moal_recv_packet_to_mon_if(IN moal_handle *handle, IN pmlan_buffer pmbuf) +{ + mlan_status status = MLAN_STATUS_SUCCESS; + struct sk_buff *skb = NULL; + struct radiotap_header *rth = NULL; + radiotap_info rt_info; + t_u8 format = 0; + t_u8 bw = 0; + t_u8 gi = 0; + t_u8 ldpc = 0; + t_u8 chan_num; + t_u8 band = 0; + struct ieee80211_hdr *dot11_hdr = NULL; + t_u8 *payload = NULL; + ENTER(); + if (!pmbuf->pdesc) { + LEAVE(); + return status; + } + + skb = (struct sk_buff *)pmbuf->pdesc; + + if ((handle->mon_if) && netif_running(handle->mon_if->mon_ndev)) { + if (handle->mon_if->radiotap_enabled) { + if (skb_headroom(skb) < sizeof(*rth)) { + PRINTM(MERROR, + "%s No space to add Radio TAP header\n", + __func__); + status = MLAN_STATUS_FAILURE; + handle->mon_if->stats.rx_dropped++; + goto done; + } + dot11_hdr = + (struct ieee80211_hdr *)(pmbuf->pbuf + + pmbuf->data_offset); + memcpy(&rt_info, + pmbuf->pbuf + pmbuf->data_offset - + sizeof(rt_info), sizeof(rt_info)); + ldpc = (rt_info.rate_info.rate_info & 0x20) >> 5; + format = (rt_info.rate_info.rate_info & 0x18) >> 3; + bw = (rt_info.rate_info.rate_info & 0x06) >> 1; + gi = rt_info.rate_info.rate_info & 0x01; + skb_push(skb, sizeof(*rth)); + rth = (struct radiotap_header *)skb->data; + memset(skb->data, 0, sizeof(*rth)); + rth->hdr.it_version = PKTHDR_RADIOTAP_VERSION; + rth->hdr.it_pad = 0; + rth->hdr.it_len = cpu_to_le16(sizeof(*rth)); + rth->hdr.it_present = + cpu_to_le32((1 << IEEE80211_RADIOTAP_TSFT) | + (1 << IEEE80211_RADIOTAP_FLAGS) | + (1 << IEEE80211_RADIOTAP_CHANNEL) | + (1 << + IEEE80211_RADIOTAP_DBM_ANTSIGNAL) | + (1 << + IEEE80211_RADIOTAP_DBM_ANTNOISE) | + (1 << IEEE80211_RADIOTAP_ANTENNA)); + //Timstamp + rth->body.timestamp = cpu_to_le64(jiffies); + //Flags + rth->body.flags = (rt_info.extra_info.flags & + ~(RADIOTAP_FLAGS_USE_SGI_HT | + RADIOTAP_FLAGS_WITH_FRAGMENT | + RADIOTAP_FLAGS_WEP_ENCRYPTION | + RADIOTAP_FLAGS_FAILED_FCS_CHECK)); + //reverse fail fcs, 1 means pass FCS in FW, but means fail FCS in radiotap + rth->body.flags |= + (~rt_info.extra_info. + flags) & RADIOTAP_FLAGS_FAILED_FCS_CHECK; + if ((format == MLAN_RATE_FORMAT_HT) && (gi == 1)) + rth->body.flags |= RADIOTAP_FLAGS_USE_SGI_HT; + if (ieee80211_is_mgmt(dot11_hdr->frame_control) || + ieee80211_is_data(dot11_hdr->frame_control)) { + if ((ieee80211_has_morefrags + (dot11_hdr->frame_control)) || + (!ieee80211_is_first_frag + (dot11_hdr->seq_ctrl))) { + rth->body.flags |= + RADIOTAP_FLAGS_WITH_FRAGMENT; + } + } + if (ieee80211_is_data(dot11_hdr->frame_control) && + ieee80211_has_protected(dot11_hdr->frame_control)) { + payload = + (t_u8 *)dot11_hdr + + ieee80211_hdrlen(dot11_hdr-> + frame_control); + if (!(*(payload + 3) & 0x20)) //ExtIV bit shall be 0 for WEP frame + rth->body.flags |= + RADIOTAP_FLAGS_WEP_ENCRYPTION; + } + //Rate, t_u8 only apply for LG mode + if (format == MLAN_RATE_FORMAT_LG) { + rth->hdr.it_present |= + cpu_to_le32(1 << + IEEE80211_RADIOTAP_RATE); + rth->body.rate = rt_info.rate_info.bitrate; + } + //Channel + rth->body.channel.flags = 0; + if (rt_info.chan_num) + chan_num = rt_info.chan_num; + else + chan_num = + handle->mon_if->band_chan_cfg.channel; + band = (chan_num <= + 14) ? IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ; + rth->body.channel.frequency = + cpu_to_le16(ieee80211_channel_to_frequency + (chan_num, band)); + rth->body.channel.flags |= + cpu_to_le16((band == + IEEE80211_BAND_2GHZ) ? + CHANNEL_FLAGS_2GHZ : + CHANNEL_FLAGS_5GHZ); + if (rth->body.channel. + flags & cpu_to_le16(CHANNEL_FLAGS_2GHZ)) + rth->body.channel.flags |= + cpu_to_le16 + (CHANNEL_FLAGS_DYNAMIC_CCK_OFDM); + else + rth->body.channel.flags |= + cpu_to_le16(CHANNEL_FLAGS_OFDM); + if (handle->mon_if->chandef.chan && + (handle->mon_if->chandef.chan-> + flags & (IEEE80211_CHAN_PASSIVE_SCAN | + IEEE80211_CHAN_RADAR))) + rth->body.channel.flags |= + cpu_to_le16 + (CHANNEL_FLAGS_ONLY_PASSIVSCAN_ALLOW); + //Antenna + rth->body.antenna_signal = -(rt_info.nf - rt_info.snr); + rth->body.antenna_noise = -rt_info.nf; + rth->body.antenna = rt_info.antenna; + //MCS + if (format == MLAN_RATE_FORMAT_HT) { + rth->hdr.it_present |= + cpu_to_le32(1 << + IEEE80211_RADIOTAP_MCS); + rth->body.mcs.known = + rt_info.extra_info.mcs_known; + rth->body.mcs.flags = + rt_info.extra_info.mcs_flags; + //MCS mcs + rth->body.mcs.known |= + MCS_KNOWN_MCS_INDEX_KNOWN; + rth->body.mcs.mcs = rt_info.rate_info.mcs_index; + //MCS bw + rth->body.mcs.known |= MCS_KNOWN_BANDWIDTH; + rth->body.mcs.flags &= ~(0x03); //Clear, 20MHz as default + if (bw == 1) + rth->body.mcs.flags |= RX_BW_40; + //MCS gi + rth->body.mcs.known |= MCS_KNOWN_GUARD_INTERVAL; + rth->body.mcs.flags &= ~(1 << 2); + if (gi) + rth->body.mcs.flags |= gi << 2; + //MCS FEC + rth->body.mcs.known |= MCS_KNOWN_FEC_TYPE; + rth->body.mcs.flags &= ~(1 << 4); + if (ldpc) + rth->body.mcs.flags |= ldpc << 4; + } + } + skb_set_mac_header(skb, 0); + skb->ip_summed = CHECKSUM_UNNECESSARY; + skb->pkt_type = PACKET_OTHERHOST; + skb->protocol = htons(ETH_P_802_2); + memset(skb->cb, 0, sizeof(skb->cb)); + skb->dev = handle->mon_if->mon_ndev; + + handle->mon_if->stats.rx_bytes += skb->len; + handle->mon_if->stats.rx_packets++; + + if (in_interrupt()) + netif_rx(skb); + else + netif_rx_ni(skb); + + status = MLAN_STATUS_PENDING; + } + +done: + + LEAVE(); + return status; +} +#endif + +/** + * @brief This function uploads the packet to the network stack + * + * @param pmoal_handle Pointer to the MOAL context + * @param pmbuf Pointer to the mlan buffer structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +moal_recv_packet(IN t_void *pmoal_handle, IN pmlan_buffer pmbuf) +{ + mlan_status status = MLAN_STATUS_SUCCESS; + moal_private *priv = NULL; + struct sk_buff *skb = NULL; + moal_handle *handle = (moal_handle *)pmoal_handle; + dot11_rxcontrol rxcontrol; + t_u8 rx_info_flag = MFALSE; + int j; + ENTER(); + if (pmbuf) { + + priv = woal_bss_index_to_priv(pmoal_handle, pmbuf->bss_index); + skb = (struct sk_buff *)pmbuf->pdesc; + if (priv) { + if (skb) { + skb_reserve(skb, pmbuf->data_offset); + skb_put(skb, pmbuf->data_len); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + if (pmbuf->flags & MLAN_BUF_FLAG_NET_MONITOR) { + status = moal_recv_packet_to_mon_if + (pmoal_handle, pmbuf); + if (status == MLAN_STATUS_PENDING) + atomic_dec(&handle-> + mbufalloc_count); + goto done; + } +#endif + pmbuf->pdesc = NULL; + pmbuf->pbuf = NULL; + pmbuf->data_offset = pmbuf->data_len = 0; + /* pkt been submit to kernel, no need to free by mlan */ + status = MLAN_STATUS_PENDING; + atomic_dec(&handle->mbufalloc_count); + } else { + PRINTM(MERROR, "%s without skb attach!!!\n", + __func__); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + /** drop the packet without skb in monitor mode */ + if (pmbuf->flags & MLAN_BUF_FLAG_NET_MONITOR) { + PRINTM(MINFO, + "%s Drop packet without skb\n", + __func__); + status = MLAN_STATUS_FAILURE; + priv->stats.rx_dropped++; + goto done; + } +#endif + skb = dev_alloc_skb(pmbuf->data_len + + MLAN_NET_IP_ALIGN); + if (!skb) { + PRINTM(MERROR, "%s fail to alloc skb\n", + __func__); + status = MLAN_STATUS_FAILURE; + priv->stats.rx_dropped++; + goto done; + } + skb_reserve(skb, MLAN_NET_IP_ALIGN); + memcpy(skb->data, + (t_u8 *)(pmbuf->pbuf + + pmbuf->data_offset), + pmbuf->data_len); + skb_put(skb, pmbuf->data_len); + + } + skb->dev = priv->netdev; + skb->protocol = eth_type_trans(skb, priv->netdev); + skb->ip_summed = CHECKSUM_NONE; + + priv->stats.rx_bytes += skb->len; + priv->stats.rx_packets++; +#ifdef ANDROID_KERNEL + if (wakelock_timeout) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) + __pm_wakeup_event(&handle->ws, + wakelock_timeout); +#else + wake_lock_timeout(&handle->wake_lock, + wakelock_timeout); +#endif + } +#endif + if (priv->rx_protocols.protocol_num) { + for (j = 0; j < priv->rx_protocols.protocol_num; + j++) { + if (htons(skb->protocol) == + priv->rx_protocols.protocols[j]) + rx_info_flag = MTRUE; + } + } + if (rx_info_flag && + (skb_tailroom(skb) > sizeof(rxcontrol))) { + rxcontrol.datarate = pmbuf->u.rx_info.data_rate; + rxcontrol.channel = pmbuf->u.rx_info.channel; + rxcontrol.antenna = pmbuf->u.rx_info.antenna; + rxcontrol.rssi = pmbuf->u.rx_info.rssi; + skb_put(skb, sizeof(dot11_rxcontrol)); + memmove(skb->data + sizeof(dot11_rxcontrol), + skb->data, + skb->len - sizeof(dot11_rxcontrol)); + memcpy(skb->data, &rxcontrol, + sizeof(dot11_rxcontrol)); + } + if (in_interrupt()) + netif_rx(skb); + else { + if (atomic_read(&handle->rx_pending) > + MAX_RX_PENDING_THRHLD) + netif_rx(skb); + else + netif_rx_ni(skb); + } + } + } +done: + LEAVE(); + return status; +} + +/** + * @brief This function handles event receive + * + * @param pmoal_handle Pointer to the MOAL context + * @param pmevent Pointer to the mlan event structure + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +moal_recv_event(IN t_void *pmoal_handle, IN pmlan_event pmevent) +{ +#ifdef STA_SUPPORT + int custom_len = 0; +#ifdef STA_CFG80211 + unsigned long flags; +#endif +#endif + moal_private *priv = NULL; +#if defined(STA_SUPPORT) || defined(UAP_SUPPORT) + moal_private *pmpriv = NULL; +#endif +#if defined(STA_WEXT) || defined(UAP_WEXT) +#if defined(STA_SUPPORT) || defined(UAP_WEXT) +#if defined(UAP_SUPPORT) || defined(STA_WEXT) + union iwreq_data wrqu; +#endif +#endif +#endif +#if defined(SDIO_SUSPEND_RESUME) + mlan_ds_ps_info pm_info; +#endif + char event[512] = { 0 }; + t_u8 category = 0; + t_u8 action_code = 0; + char *buf; + t_u8 peer_addr[ETH_ALEN]; + moal_wnm_tm_msmt *tm_ind; + moal_wlan_802_11_header *header; + moal_timestamps *tsstamp; + t_u8 payload_len, i; + moal_ptp_context *ptp_context; + t_u8 *req_ie = NULL; + t_u16 ie_len = 0; + apinfo *pinfo = NULL, *req_tlv = NULL; + MrvlIEtypesHeader_t *tlv = NULL; + t_u16 tlv_type = 0, tlv_len = 0, tlv_buf_left = 0; +#if defined(FW_ROAMING) || (defined(ROAMING_OFFLOAD) && defined(STA_CFG80211)) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) + struct cfg80211_roam_info roam_info; +#endif +#endif + + ENTER(); + + if ((pmevent->event_id != MLAN_EVENT_ID_DRV_DEFER_RX_WORK) && + (pmevent->event_id != MLAN_EVENT_ID_DRV_DEFER_HANDLING) && + (pmevent->event_id != MLAN_EVENT_ID_DRV_MGMT_FRAME)) + PRINTM(MEVENT, "event id:0x%x\n", pmevent->event_id); + if (pmevent->event_id == MLAN_EVENT_ID_FW_DUMP_INFO) { + woal_store_firmware_dump(pmoal_handle, pmevent); + goto done; + } + priv = woal_bss_index_to_priv(pmoal_handle, pmevent->bss_index); + if (priv == NULL) { + PRINTM(MERROR, "%s: priv is null\n", __func__); + goto done; + } + if (priv->netdev == NULL) { + PRINTM(MERROR, "%s: netdev is null\n", __func__); + goto done; + } + switch (pmevent->event_id) { +#ifdef STA_SUPPORT + case MLAN_EVENT_ID_FW_ADHOC_LINK_SENSED: + priv->is_adhoc_link_sensed = MTRUE; + if (!netif_carrier_ok(priv->netdev)) + netif_carrier_on(priv->netdev); + woal_wake_queue(priv->netdev); +#ifdef STA_WEXT + if (IS_STA_WEXT(cfg80211_wext)) + woal_send_iwevcustom_event(priv, + CUS_EVT_ADHOC_LINK_SENSED); +#endif + woal_broadcast_event(priv, CUS_EVT_ADHOC_LINK_SENSED, + strlen(CUS_EVT_ADHOC_LINK_SENSED)); + break; + + case MLAN_EVENT_ID_FW_ADHOC_LINK_LOST: + woal_stop_queue(priv->netdev); + if (netif_carrier_ok(priv->netdev)) + netif_carrier_off(priv->netdev); + priv->is_adhoc_link_sensed = MFALSE; +#ifdef STA_WEXT + if (IS_STA_WEXT(cfg80211_wext)) + woal_send_iwevcustom_event(priv, + CUS_EVT_ADHOC_LINK_LOST); +#endif + woal_broadcast_event(priv, CUS_EVT_ADHOC_LINK_LOST, + strlen(CUS_EVT_ADHOC_LINK_LOST)); + break; + + case MLAN_EVENT_ID_DRV_CONNECTED: +#ifdef STA_WEXT + if (IS_STA_WEXT(cfg80211_wext) && + pmevent->event_len == ETH_ALEN) { + memset(wrqu.ap_addr.sa_data, 0x00, ETH_ALEN); + memcpy(wrqu.ap_addr.sa_data, pmevent->event_buf, + ETH_ALEN); + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + wireless_send_event(priv->netdev, SIOCGIWAP, &wrqu, + NULL); + } +#endif +#ifdef STA_CFG80211 + if (IS_STA_CFG80211(cfg80211_wext)) { + memcpy(priv->cfg_bssid, pmevent->event_buf, ETH_ALEN); + woal_set_scan_time(priv, ACTIVE_SCAN_CHAN_TIME, + PASSIVE_SCAN_CHAN_TIME, + MIN_SPECIFIC_SCAN_CHAN_TIME); + } +#endif + custom_len = strlen(CUS_EVT_AP_CONNECTED); + memmove(pmevent->event_buf + custom_len, pmevent->event_buf, + pmevent->event_len); + memcpy(pmevent->event_buf, CUS_EVT_AP_CONNECTED, custom_len); + pmevent->event_len += custom_len; + woal_broadcast_event(priv, pmevent->event_buf, + pmevent->event_len); + woal_update_dscp_mapping(priv); + priv->media_connected = MTRUE; + if (!netif_carrier_ok(priv->netdev)) + netif_carrier_on(priv->netdev); + woal_wake_queue(priv->netdev); + + break; + + case MLAN_EVENT_ID_DRV_SCAN_REPORT: + PRINTM(MINFO, "Scan report\n"); + + if (priv->report_scan_result) { + priv->report_scan_result = MFALSE; +#ifdef STA_CFG80211 + if (IS_STA_CFG80211(cfg80211_wext)) { + if (priv->phandle->scan_request) { + PRINTM(MINFO, + "Reporting scan results\n"); + woal_inform_bss_from_scan_result(priv, + NULL, + MOAL_NO_WAIT); + if (!priv->phandle->first_scan_done) { + priv->phandle->first_scan_done = + MTRUE; + woal_set_scan_time(priv, + ACTIVE_SCAN_CHAN_TIME, + PASSIVE_SCAN_CHAN_TIME, + SPECIFIC_SCAN_CHAN_TIME); + } + spin_lock_irqsave(&priv->phandle-> + scan_req_lock, flags); + if (priv->phandle->scan_request) { + woal_cfg80211_scan_done(priv-> + phandle-> + scan_request, + MFALSE); + priv->phandle->scan_request = + NULL; + } + spin_unlock_irqrestore(&priv->phandle-> + scan_req_lock, + flags); + } + } +#endif /* STA_CFG80211 */ + +#ifdef STA_WEXT + if (IS_STA_WEXT(cfg80211_wext)) { + memset(&wrqu, 0, sizeof(union iwreq_data)); + wireless_send_event(priv->netdev, SIOCGIWSCAN, + &wrqu, NULL); + } +#endif + woal_broadcast_event(priv, (t_u8 *)&pmevent->event_id, + sizeof(mlan_event_id)); + + } + if (priv->phandle->scan_pending_on_block == MTRUE) { + priv->phandle->scan_pending_on_block = MFALSE; + priv->phandle->scan_priv = NULL; + MOAL_REL_SEMAPHORE(&priv->phandle->async_sem); + } + break; + + case MLAN_EVENT_ID_DRV_OBSS_SCAN_PARAM: + memmove((pmevent->event_buf + strlen(CUS_EVT_OBSS_SCAN_PARAM) + + 1), pmevent->event_buf, pmevent->event_len); + memcpy(pmevent->event_buf, (t_u8 *)CUS_EVT_OBSS_SCAN_PARAM, + strlen(CUS_EVT_OBSS_SCAN_PARAM)); + pmevent->event_buf[strlen(CUS_EVT_OBSS_SCAN_PARAM)] = 0; + woal_broadcast_event(priv, pmevent->event_buf, + pmevent->event_len + + strlen(CUS_EVT_OBSS_SCAN_PARAM)); + +#ifdef STA_WEXT + if (IS_STA_WEXT(cfg80211_wext)) { + memset(&wrqu, 0, sizeof(union iwreq_data)); + wrqu.data.pointer = pmevent->event_buf; + wrqu.data.length = + pmevent->event_len + + strlen(CUS_EVT_OBSS_SCAN_PARAM) + 1; + wireless_send_event(priv->netdev, IWEVCUSTOM, &wrqu, + pmevent->event_buf); + } +#endif + break; + case MLAN_EVENT_ID_FW_BW_CHANGED: + memmove((pmevent->event_buf + strlen(CUS_EVT_BW_CHANGED) + 1), + pmevent->event_buf, pmevent->event_len); + memcpy(pmevent->event_buf, (t_u8 *)CUS_EVT_BW_CHANGED, + strlen(CUS_EVT_BW_CHANGED)); + pmevent->event_buf[strlen(CUS_EVT_BW_CHANGED)] = 0; + woal_broadcast_event(priv, pmevent->event_buf, + pmevent->event_len + + strlen(CUS_EVT_BW_CHANGED)); + +#ifdef STA_WEXT + if (IS_STA_WEXT(cfg80211_wext)) { + memset(&wrqu, 0, sizeof(union iwreq_data)); + wrqu.data.pointer = pmevent->event_buf; + wrqu.data.length = + pmevent->event_len + + strlen(CUS_EVT_BW_CHANGED) + 1; + wireless_send_event(priv->netdev, IWEVCUSTOM, &wrqu, + pmevent->event_buf); + } +#endif + break; + + case MLAN_EVENT_ID_FW_DISCONNECTED: + woal_send_disconnect_to_system(priv); +#ifdef STA_WEXT + /* Reset wireless stats signal info */ + if (IS_STA_WEXT(cfg80211_wext)) { + priv->w_stats.qual.level = 0; + priv->w_stats.qual.noise = 0; + } +#endif +#ifdef REASSOCIATION + if (priv->reassoc_on == MTRUE) { + PRINTM(MINFO, "Reassoc: trigger the timer\n"); + priv->reassoc_required = MTRUE; + priv->phandle->is_reassoc_timer_set = MTRUE; + woal_mod_timer(&priv->phandle->reassoc_timer, + REASSOC_TIMER_DEFAULT); + } else { + priv->rate_index = AUTO_RATE; + } +#endif /* REASSOCIATION */ + break; + + case MLAN_EVENT_ID_FW_MIC_ERR_UNI: +#ifdef STA_WEXT + if (IS_STA_WEXT(cfg80211_wext)) { +#if WIRELESS_EXT >= 18 + woal_send_mic_error_event(priv, + MLAN_EVENT_ID_FW_MIC_ERR_UNI); +#else + woal_send_iwevcustom_event(priv, + CUS_EVT_MLME_MIC_ERR_UNI); +#endif + } +#endif /* STA_WEXT */ +#ifdef STA_CFG80211 + if (IS_STA_CFG80211(cfg80211_wext)) { + cfg80211_michael_mic_failure(priv->netdev, + priv->cfg_bssid, + NL80211_KEYTYPE_PAIRWISE, + -1, NULL, GFP_KERNEL); + } +#endif + woal_broadcast_event(priv, CUS_EVT_MLME_MIC_ERR_UNI, + strlen(CUS_EVT_MLME_MIC_ERR_UNI)); + break; + case MLAN_EVENT_ID_FW_MIC_ERR_MUL: +#ifdef STA_WEXT + if (IS_STA_WEXT(cfg80211_wext)) { +#if WIRELESS_EXT >= 18 + woal_send_mic_error_event(priv, + MLAN_EVENT_ID_FW_MIC_ERR_MUL); +#else + woal_send_iwevcustom_event(priv, + CUS_EVT_MLME_MIC_ERR_MUL); +#endif + } +#endif /* STA_WEXT */ +#ifdef STA_CFG80211 + if (IS_STA_CFG80211(cfg80211_wext)) { + cfg80211_michael_mic_failure(priv->netdev, + priv->cfg_bssid, + NL80211_KEYTYPE_GROUP, -1, + NULL, GFP_KERNEL); + } +#endif + woal_broadcast_event(priv, CUS_EVT_MLME_MIC_ERR_MUL, + strlen(CUS_EVT_MLME_MIC_ERR_MUL)); + break; + case MLAN_EVENT_ID_FW_BCN_RSSI_LOW: +#ifdef STA_WEXT + if (IS_STA_WEXT(cfg80211_wext)) + woal_send_iwevcustom_event(priv, + CUS_EVT_BEACON_RSSI_LOW); +#endif +#ifdef STA_CFG80211 + if (IS_STA_CFG80211(cfg80211_wext)) { +#if CFG80211_VERSION_CODE > KERNEL_VERSION(2, 6, 35) + cfg80211_cqm_rssi_notify(priv->netdev, + NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) + *(t_s16 *)pmevent->event_buf, +#endif + GFP_KERNEL); + priv->last_event |= EVENT_BCN_RSSI_LOW; +#endif + if (!hw_test && priv->roaming_enabled) + woal_config_bgscan_and_rssi(priv, MTRUE); +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + woal_cfg80211_rssi_monitor_event(priv, + *(t_s16 *)pmevent-> + event_buf); +#endif + } +#endif + woal_broadcast_event(priv, CUS_EVT_BEACON_RSSI_LOW, + strlen(CUS_EVT_BEACON_RSSI_LOW)); + break; + case MLAN_EVENT_ID_FW_BCN_RSSI_HIGH: +#ifdef STA_WEXT + if (IS_STA_WEXT(cfg80211_wext)) + woal_send_iwevcustom_event(priv, + CUS_EVT_BEACON_RSSI_HIGH); +#endif +#ifdef STA_CFG80211 + if (IS_STA_CFG80211(cfg80211_wext)) { + if (!priv->mrvl_rssi_low) { +#if CFG80211_VERSION_CODE > KERNEL_VERSION(2, 6, 35) + cfg80211_cqm_rssi_notify(priv->netdev, + NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) + *(t_s16 *)pmevent-> + event_buf, +#endif + GFP_KERNEL); +#endif + woal_set_rssi_threshold(priv, + MLAN_EVENT_ID_FW_BCN_RSSI_HIGH, + MOAL_NO_WAIT); + } +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + woal_cfg80211_rssi_monitor_event(priv, + *(t_s16 *)pmevent-> + event_buf); +#endif + } +#endif + woal_broadcast_event(priv, CUS_EVT_BEACON_RSSI_HIGH, + strlen(CUS_EVT_BEACON_RSSI_HIGH)); + break; + case MLAN_EVENT_ID_FW_BCN_SNR_LOW: +#ifdef STA_WEXT + if (IS_STA_WEXT(cfg80211_wext)) + woal_send_iwevcustom_event(priv, + CUS_EVT_BEACON_SNR_LOW); +#endif + woal_broadcast_event(priv, CUS_EVT_BEACON_SNR_LOW, + strlen(CUS_EVT_BEACON_SNR_LOW)); + break; + case MLAN_EVENT_ID_FW_BCN_SNR_HIGH: +#ifdef STA_WEXT + if (IS_STA_WEXT(cfg80211_wext)) + woal_send_iwevcustom_event(priv, + CUS_EVT_BEACON_SNR_HIGH); +#endif + woal_broadcast_event(priv, CUS_EVT_BEACON_SNR_HIGH, + strlen(CUS_EVT_BEACON_SNR_HIGH)); + break; + case MLAN_EVENT_ID_FW_MAX_FAIL: +#ifdef STA_WEXT + if (IS_STA_WEXT(cfg80211_wext)) + woal_send_iwevcustom_event(priv, CUS_EVT_MAX_FAIL); +#endif + woal_broadcast_event(priv, CUS_EVT_MAX_FAIL, + strlen(CUS_EVT_MAX_FAIL)); + break; + case MLAN_EVENT_ID_FW_DATA_RSSI_LOW: +#ifdef STA_WEXT + if (IS_STA_WEXT(cfg80211_wext)) + woal_send_iwevcustom_event(priv, CUS_EVT_DATA_RSSI_LOW); +#endif + woal_broadcast_event(priv, CUS_EVT_DATA_RSSI_LOW, + strlen(CUS_EVT_DATA_RSSI_LOW)); + break; + case MLAN_EVENT_ID_FW_DATA_SNR_LOW: +#ifdef STA_WEXT + if (IS_STA_WEXT(cfg80211_wext)) + woal_send_iwevcustom_event(priv, CUS_EVT_DATA_SNR_LOW); +#endif + woal_broadcast_event(priv, CUS_EVT_DATA_SNR_LOW, + strlen(CUS_EVT_DATA_SNR_LOW)); + break; + case MLAN_EVENT_ID_FW_DATA_RSSI_HIGH: +#ifdef STA_WEXT + if (IS_STA_WEXT(cfg80211_wext)) + woal_send_iwevcustom_event(priv, + CUS_EVT_DATA_RSSI_HIGH); +#endif + woal_broadcast_event(priv, CUS_EVT_DATA_RSSI_HIGH, + strlen(CUS_EVT_DATA_RSSI_HIGH)); + break; + case MLAN_EVENT_ID_FW_DATA_SNR_HIGH: +#ifdef STA_WEXT + if (IS_STA_WEXT(cfg80211_wext)) + woal_send_iwevcustom_event(priv, CUS_EVT_DATA_SNR_HIGH); +#endif + woal_broadcast_event(priv, CUS_EVT_DATA_SNR_HIGH, + strlen(CUS_EVT_DATA_SNR_HIGH)); + break; + case MLAN_EVENT_ID_FW_LINK_QUALITY: +#ifdef STA_WEXT + if (IS_STA_WEXT(cfg80211_wext)) + woal_send_iwevcustom_event(priv, CUS_EVT_LINK_QUALITY); +#endif + woal_broadcast_event(priv, CUS_EVT_LINK_QUALITY, + strlen(CUS_EVT_LINK_QUALITY)); + break; + case MLAN_EVENT_ID_FW_PORT_RELEASE: +#ifdef STA_WEXT + if (IS_STA_WEXT(cfg80211_wext)) + woal_send_iwevcustom_event(priv, CUS_EVT_PORT_RELEASE); +#endif + woal_broadcast_event(priv, CUS_EVT_PORT_RELEASE, + strlen(CUS_EVT_PORT_RELEASE)); + break; + case MLAN_EVENT_ID_FW_PRE_BCN_LOST: +#ifdef STA_WEXT + if (IS_STA_WEXT(cfg80211_wext)) + woal_send_iwevcustom_event(priv, + CUS_EVT_PRE_BEACON_LOST); +#endif +#ifdef STA_CFG80211 +#if CFG80211_VERSION_CODE > KERNEL_VERSION(2, 6, 35) + if (IS_STA_CFG80211(cfg80211_wext)) { + struct cfg80211_bss *bss = NULL; + bss = cfg80211_get_bss(priv->wdev->wiphy, NULL, + priv->cfg_bssid, NULL, 0, + WLAN_CAPABILITY_ESS, + WLAN_CAPABILITY_ESS); + if (bss) + cfg80211_unlink_bss(priv->wdev->wiphy, bss); + if (!hw_test && priv->roaming_enabled) + woal_config_bgscan_and_rssi(priv, MFALSE); + priv->last_event |= EVENT_PRE_BCN_LOST; + } +#endif +#endif + woal_broadcast_event(priv, CUS_EVT_PRE_BEACON_LOST, + strlen(CUS_EVT_PRE_BEACON_LOST)); + break; + case MLAN_EVENT_ID_FW_DEBUG_INFO: +#ifdef STA_WEXT + if (IS_STA_WEXT(cfg80211_wext)) + woal_send_iwevcustom_event(priv, pmevent->event_buf); +#endif + woal_broadcast_event(priv, pmevent->event_buf, + pmevent->event_len); + break; + case MLAN_EVENT_ID_FW_WMM_CONFIG_CHANGE: +#ifdef STA_WEXT + if (IS_STA_WEXT(cfg80211_wext)) + woal_send_iwevcustom_event(priv, + WMM_CONFIG_CHANGE_INDICATION); +#endif + woal_broadcast_event(priv, WMM_CONFIG_CHANGE_INDICATION, + strlen(WMM_CONFIG_CHANGE_INDICATION)); + break; + + case MLAN_EVENT_ID_DRV_REPORT_STRING: + PRINTM(MINFO, "Report string %s\n", pmevent->event_buf); +#ifdef STA_WEXT + if (IS_STA_WEXT(cfg80211_wext)) + woal_send_iwevcustom_event(priv, pmevent->event_buf); +#endif + woal_broadcast_event(priv, pmevent->event_buf, + strlen(pmevent->event_buf)); + break; + case MLAN_EVENT_ID_FW_WEP_ICV_ERR: + DBG_HEXDUMP(MCMD_D, "WEP ICV error", pmevent->event_buf, + pmevent->event_len); +#ifdef STA_WEXT + if (IS_STA_WEXT(cfg80211_wext)) + woal_send_iwevcustom_event(priv, CUS_EVT_WEP_ICV_ERR); +#endif + woal_broadcast_event(priv, CUS_EVT_WEP_ICV_ERR, + strlen(CUS_EVT_WEP_ICV_ERR)); + break; + + case MLAN_EVENT_ID_DRV_DEFER_HANDLING: + queue_work(priv->phandle->workqueue, &priv->phandle->main_work); + break; + case MLAN_EVENT_ID_DRV_FLUSH_RX_WORK: + if (napi) { + napi_synchronize(&priv->phandle->napi_rx); + break; + } + flush_workqueue(priv->phandle->rx_workqueue); + break; + case MLAN_EVENT_ID_DRV_FLUSH_MAIN_WORK: + flush_workqueue(priv->phandle->workqueue); + break; + case MLAN_EVENT_ID_DRV_DEFER_RX_WORK: + if (napi) { + napi_schedule(&priv->phandle->napi_rx); + break; + } + queue_work(priv->phandle->rx_workqueue, + &priv->phandle->rx_work); + break; + case MLAN_EVENT_ID_DRV_DBG_DUMP: + priv->phandle->driver_state = MTRUE; + woal_moal_debug_info(priv, NULL, MFALSE); + woal_broadcast_event(priv, CUS_EVT_DRIVER_HANG, + strlen(CUS_EVT_DRIVER_HANG)); +#ifdef STA_CFG80211 +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + if (IS_STA_OR_UAP_CFG80211(cfg80211_wext)) + woal_cfg80211_vendor_event(priv, event_hang, + CUS_EVT_DRIVER_HANG, + strlen(CUS_EVT_DRIVER_HANG)); +#endif +#endif + woal_process_hang(priv->phandle); + break; + case MLAN_EVENT_ID_FW_BG_SCAN: + if (priv->media_connected == MTRUE) + priv->bg_scan_start = MFALSE; + priv->bg_scan_reported = MTRUE; +#ifdef STA_WEXT + if (IS_STA_WEXT(cfg80211_wext)) { + memset(&wrqu, 0, sizeof(union iwreq_data)); + wireless_send_event(priv->netdev, SIOCGIWSCAN, &wrqu, + NULL); + } +#endif +#ifdef STA_CFG80211 + if (IS_STA_CFG80211(cfg80211_wext)) { + priv->last_event |= EVENT_BG_SCAN_REPORT; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) + if (priv->sched_scanning && + !priv->phandle->cfg80211_suspend) { + mlan_scan_resp scan_resp; + woal_get_scan_table(priv, MOAL_NO_WAIT, + &scan_resp); + PRINTM(MIOCTL, + "Trigger mlan get bgscan result\n"); + } +#endif + if (!hw_test && priv->roaming_enabled +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) + && !priv->phandle->cfg80211_suspend +#endif + ) { + priv->roaming_required = MTRUE; +#ifdef ANDROID_KERNEL +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) + __pm_wakeup_event(&priv->phandle->ws, + ROAMING_WAKE_LOCK_TIMEOUT); +#else + wake_lock_timeout(&priv->phandle->wake_lock, + ROAMING_WAKE_LOCK_TIMEOUT); +#endif +#endif + wake_up_interruptible(&priv->phandle-> + reassoc_thread.wait_q); + } + } +#endif + break; + case MLAN_EVENT_ID_FW_BG_SCAN_STOPPED: +#ifdef STA_CFG80211 +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) + if (IS_STA_CFG80211(cfg80211_wext)) { + if (priv->sched_scanning) { + cfg80211_sched_scan_stopped(priv->wdev->wiphy +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) + , 0 +#endif + ); + PRINTM(MEVENT, "Sched_Scan stopped\n"); + priv->sched_scanning = MFALSE; + } + } +#endif +#endif + break; + case MLAN_EVENT_ID_DRV_BGSCAN_RESULT: +#ifdef STA_CFG80211 +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) + if (IS_STA_CFG80211(cfg80211_wext)) { + if (priv->sched_scanning && + !priv->phandle->cfg80211_suspend) { + woal_inform_bss_from_scan_result(priv, NULL, + MOAL_NO_WAIT); + cfg80211_sched_scan_results(priv->wdev->wiphy +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) + , 0 +#endif + ); + priv->last_event = 0; + PRINTM(MEVENT, + "Reporting Sched_Scan results\n"); + } + } +#endif +#endif + break; +#ifdef UAP_CFG80211 +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) + case MLAN_EVENT_ID_FW_CHANNEL_REPORT_RDY: + if (priv->phandle->is_cac_timer_set) { + t_u8 radar_detected = pmevent->event_buf[0]; + PRINTM(MEVENT, "%s radar found when CAC \n", + radar_detected ? "" : "No"); + moal_stop_timer(priv->phandle, + &priv->phandle->cac_timer); + priv->phandle->is_cac_timer_set = MFALSE; + if (radar_detected) { +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + cfg80211_cac_event(priv->netdev, + &priv->phandle->dfs_channel, + NL80211_RADAR_CAC_ABORTED, + GFP_KERNEL); +#else + cfg80211_cac_event(priv->netdev, + NL80211_RADAR_CAC_ABORTED, + GFP_KERNEL); +#endif + cfg80211_radar_event(priv->wdev->wiphy, + &priv->phandle-> + dfs_channel, GFP_KERNEL); + } else { +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + cfg80211_cac_event(priv->netdev, + &priv->phandle->dfs_channel, + NL80211_RADAR_CAC_FINISHED, + GFP_KERNEL); +#else + cfg80211_cac_event(priv->netdev, + NL80211_RADAR_CAC_FINISHED, + GFP_KERNEL); +#endif + } + memset(&priv->phandle->dfs_channel, 0, + sizeof(struct cfg80211_chan_def)); + priv->phandle->cac_bss_index = 0xff; + } + break; + case MLAN_EVENT_ID_FW_RADAR_DETECTED: + if (priv->phandle->is_cac_timer_set) { + if (priv->bss_index == priv->phandle->cac_bss_index) { + PRINTM(MEVENT, "radar detected during CAC \n"); + woal_cancel_timer(&priv->phandle->cac_timer); + priv->phandle->is_cac_timer_set = MFALSE; + /* downstream: cancel the unfinished CAC in Firmware */ + woal_11h_cancel_chan_report_ioctl(priv, + MOAL_NO_WAIT); + /* upstream: inform cfg80211 */ +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + cfg80211_cac_event(priv->netdev, + &priv->phandle->dfs_channel, + NL80211_RADAR_CAC_ABORTED, + GFP_KERNEL); +#else + cfg80211_cac_event(priv->netdev, + NL80211_RADAR_CAC_ABORTED, + GFP_KERNEL); +#endif + cfg80211_radar_event(priv->wdev->wiphy, + &priv->phandle-> + dfs_channel, GFP_KERNEL); + + memset(&priv->phandle->dfs_channel, 0, + sizeof(priv->phandle->dfs_channel)); + priv->phandle->cac_bss_index = 0xff; + } else { + PRINTM(MERROR, + " Radar event for incorrect inferface \n"); + } + } else { + PRINTM(MEVENT, "radar detected during BSS active \n"); +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + if (dfs_offload) + woal_cfg80211_dfs_vendor_event(priv, + event_dfs_radar_detected, + &priv->chan); + else +#endif + cfg80211_radar_event(priv->wdev->wiphy, + &priv->chan, GFP_KERNEL); + } + break; +#endif +#endif + case MLAN_EVENT_ID_FW_CHANNEL_SWITCH_ANN: +#ifdef STA_WEXT + if (IS_STA_WEXT(cfg80211_wext)) + woal_send_iwevcustom_event(priv, + CUS_EVT_CHANNEL_SWITCH_ANN); +#endif + woal_broadcast_event(priv, CUS_EVT_CHANNEL_SWITCH_ANN, + strlen(CUS_EVT_CHANNEL_SWITCH_ANN)); + break; +#endif /* STA_SUPPORT */ + case MLAN_EVENT_ID_FW_CHAN_SWITCH_COMPLETE: +#if defined(UAP_SUPPORT) + if (priv->bss_role == MLAN_BSS_ROLE_UAP) { +#ifdef UAP_CFG80211 + chan_band_info *pchan_info = + (chan_band_info *) pmevent->event_buf; + if (IS_UAP_CFG80211(cfg80211_wext)) { + PRINTM(MMSG, + "CSA/ECSA: Switch to new channel %d complete!\n", + pchan_info->channel); + priv->channel = pchan_info->channel; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3,12,0) + if (priv->csa_chan.chan && + (pchan_info->channel == + priv->csa_chan.chan->hw_value)) { + memcpy(&priv->chan, &priv->csa_chan, + sizeof(struct + cfg80211_chan_def)); + } +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3,8,0) + if (priv->uap_host_based) { + PRINTM(MEVENT, + "UAP: 11n=%d, chan=%d, center_chan=%d, band=%d, width=%d, 2Offset=%d\n", + pchan_info->is_11n_enabled, + pchan_info->channel, + pchan_info->center_chan, + pchan_info->bandcfg.chanBand, + pchan_info->bandcfg.chanWidth, + pchan_info->bandcfg.chan2Offset); + woal_cfg80211_notify_uap_channel(priv, + pchan_info); + } +#endif + } +#endif + } + if (priv->uap_tx_blocked) { + if (!netif_carrier_ok(priv->netdev)) + netif_carrier_on(priv->netdev); + woal_start_queue(priv->netdev); + priv->uap_tx_blocked = MFALSE; + } + priv->phandle->chsw_wait_q_woken = MTRUE; + wake_up_interruptible(&priv->phandle->chsw_wait_q); +#endif + break; + case MLAN_EVENT_ID_FW_STOP_TX: + woal_stop_queue(priv->netdev); + if (netif_carrier_ok(priv->netdev)) + netif_carrier_off(priv->netdev); + break; + case MLAN_EVENT_ID_FW_START_TX: + if (!netif_carrier_ok(priv->netdev)) + netif_carrier_on(priv->netdev); + woal_wake_queue(priv->netdev); + break; + case MLAN_EVENT_ID_FW_HS_WAKEUP: + /* simulate HSCFG_CANCEL command */ + woal_cancel_hs(priv, MOAL_NO_WAIT); +#ifdef STA_SUPPORT + pmpriv = woal_get_priv((moal_handle *)pmoal_handle, + MLAN_BSS_ROLE_STA); +#ifdef STA_WEXT + if (IS_STA_WEXT(cfg80211_wext) && pmpriv) + woal_send_iwevcustom_event(pmpriv, CUS_EVT_HS_WAKEUP); +#endif /* STA_WEXT */ + if (pmpriv) + woal_broadcast_event(pmpriv, CUS_EVT_HS_WAKEUP, + strlen(CUS_EVT_HS_WAKEUP)); +#endif /*STA_SUPPORT */ +#ifdef UAP_SUPPORT + pmpriv = woal_get_priv((moal_handle *)pmoal_handle, + MLAN_BSS_ROLE_UAP); + if (pmpriv) { + pmevent->event_id = UAP_EVENT_ID_HS_WAKEUP; + woal_broadcast_event(pmpriv, (t_u8 *)&pmevent->event_id, + sizeof(t_u32)); + } +#endif /* UAP_SUPPORT */ + break; + case MLAN_EVENT_ID_DRV_HS_ACTIVATED: +#ifdef STA_SUPPORT + pmpriv = woal_get_priv((moal_handle *)pmoal_handle, + MLAN_BSS_ROLE_STA); +#ifdef STA_WEXT + if (IS_STA_WEXT(cfg80211_wext) && pmpriv) + woal_send_iwevcustom_event(pmpriv, + CUS_EVT_HS_ACTIVATED); +#endif /* STA_WEXT */ + if (pmpriv) + woal_broadcast_event(pmpriv, CUS_EVT_HS_ACTIVATED, + strlen(CUS_EVT_HS_ACTIVATED)); +#endif /* STA_SUPPORT */ +#if defined(UAP_SUPPORT) + pmpriv = woal_get_priv((moal_handle *)pmoal_handle, + MLAN_BSS_ROLE_UAP); + if (pmpriv) { + pmevent->event_id = UAP_EVENT_ID_DRV_HS_ACTIVATED; + woal_broadcast_event(pmpriv, (t_u8 *)&pmevent->event_id, + sizeof(t_u32)); + } +#endif +#if defined(SDIO_SUSPEND_RESUME) + if (priv->phandle->suspend_fail == MFALSE) { + woal_get_pm_info(priv, &pm_info); + if (pm_info.is_suspend_allowed == MTRUE) { + priv->phandle->hs_activated = MTRUE; +#ifdef MMC_PM_FUNC_SUSPENDED + woal_wlan_is_suspended(priv->phandle); +#endif + } + priv->phandle->hs_activate_wait_q_woken = MTRUE; + wake_up_interruptible(&priv->phandle-> + hs_activate_wait_q); + } +#endif + break; + case MLAN_EVENT_ID_DRV_HS_DEACTIVATED: +#ifdef STA_SUPPORT + pmpriv = woal_get_priv((moal_handle *)pmoal_handle, + MLAN_BSS_ROLE_STA); +#ifdef STA_WEXT + if (IS_STA_WEXT(cfg80211_wext) && pmpriv) + woal_send_iwevcustom_event(pmpriv, + CUS_EVT_HS_DEACTIVATED); +#endif /* STA_WEXT */ + if (pmpriv) + woal_broadcast_event(pmpriv, CUS_EVT_HS_DEACTIVATED, + strlen(CUS_EVT_HS_DEACTIVATED)); +#endif /* STA_SUPPORT */ +#if defined(UAP_SUPPORT) + pmpriv = woal_get_priv((moal_handle *)pmoal_handle, + MLAN_BSS_ROLE_UAP); + if (pmpriv) { + pmevent->event_id = UAP_EVENT_ID_DRV_HS_DEACTIVATED; + woal_broadcast_event(pmpriv, (t_u8 *)&pmevent->event_id, + sizeof(t_u32)); + } +#endif +#if defined(SDIO_SUSPEND_RESUME) + priv->phandle->hs_activated = MFALSE; +#endif + break; +#ifdef UAP_SUPPORT + case MLAN_EVENT_ID_UAP_FW_BSS_START: + if (priv->hist_data) + woal_hist_data_reset(priv); + priv->bss_started = MTRUE; + priv->skip_cac = MFALSE; + if (!netif_carrier_ok(priv->netdev)) + netif_carrier_on(priv->netdev); + woal_start_queue(priv->netdev); + memcpy(priv->current_addr, pmevent->event_buf + 6, ETH_ALEN); + memcpy(priv->netdev->dev_addr, priv->current_addr, ETH_ALEN); + woal_broadcast_event(priv, pmevent->event_buf, + pmevent->event_len); +#ifdef STA_SUPPORT +#ifdef STA_CFG80211 + pmpriv = woal_get_priv((moal_handle *)pmoal_handle, + MLAN_BSS_ROLE_STA); + if (IS_STA_CFG80211(cfg80211_wext) && pmpriv) + woal_set_scan_time(pmpriv, ACTIVE_SCAN_CHAN_TIME, + PASSIVE_SCAN_CHAN_TIME, + MIN_SPECIFIC_SCAN_CHAN_TIME); +#endif +#endif +#ifdef UAP_CFG80211 +#if defined(DFS_TESTING_SUPPORT) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) + if (priv->chan_under_nop) { + PRINTM(MMSG, + "Channel Under Nop: notify cfg80211 new channel=%d\n", + priv->channel); + cfg80211_ch_switch_notify(priv->netdev, &priv->chan); + priv->chan_under_nop = MFALSE; + } +#endif +#endif +#endif + break; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 5, 0) + case MLAN_EVENT_ID_DRV_UAP_CHAN_INFO: +#ifdef UAP_CFG80211 + if (IS_UAP_CFG80211(cfg80211_wext)) { + chan_band_info *pchan_info = + (chan_band_info *) pmevent->event_buf; + PRINTM(MEVENT, + "UAP: 11n=%d, chan=%d, center_chan=%d, band=%d, width=%d, 2Offset=%d\n", + pchan_info->is_11n_enabled, pchan_info->channel, + pchan_info->center_chan, + pchan_info->bandcfg.chanBand, + pchan_info->bandcfg.chanWidth, + pchan_info->bandcfg.chan2Offset); + if (priv->uap_host_based && + (priv->channel != pchan_info->channel)) + woal_cfg80211_notify_uap_channel(priv, + pchan_info); + } +#endif + break; +#endif + case MLAN_EVENT_ID_UAP_FW_BSS_ACTIVE: + priv->media_connected = MTRUE; + if (!netif_carrier_ok(priv->netdev)) + netif_carrier_on(priv->netdev); + woal_wake_queue(priv->netdev); + woal_broadcast_event(priv, pmevent->event_buf, + pmevent->event_len); + break; + case MLAN_EVENT_ID_UAP_FW_BSS_IDLE: + priv->media_connected = MFALSE; + woal_broadcast_event(priv, pmevent->event_buf, + pmevent->event_len); + break; +#if defined(STA_CFG80211) || defined(UAP_CFG80211) + case MLAN_EVENT_ID_FW_REMAIN_ON_CHAN_EXPIRED: + if (IS_STA_OR_UAP_CFG80211(cfg80211_wext)) { + PRINTM(MEVENT, + "FW_REMAIN_ON_CHANNEL_EXPIRED cookie = %#llx\n", + priv->phandle->cookie); + priv->phandle->remain_on_channel = MFALSE; + if (priv->phandle->cookie && + !priv->phandle->is_remain_timer_set) { + cfg80211_remain_on_channel_expired( +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 6, 0) + priv-> + netdev, +#else + priv-> + wdev, +#endif + priv-> + phandle-> + cookie, + &priv-> + phandle-> + chan, +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 8, 0) + priv-> + phandle-> + channel_type, +#endif + GFP_ATOMIC); + priv->phandle->cookie = 0; + } + } + break; +#endif + case MLAN_EVENT_ID_UAP_FW_STA_CONNECT: +#if defined(STA_CFG80211) || defined(UAP_CFG80211) + if (IS_STA_OR_UAP_CFG80211(cfg80211_wext)) { + struct station_info sinfo; + t_u8 addr[ETH_ALEN]; + + sinfo.filled = 0; + sinfo.generation = 0; + /* copy the station mac address */ + memset(addr, 0xFF, ETH_ALEN); + memcpy(addr, pmevent->event_buf, ETH_ALEN); + /** these field add in kernel 3.2, but some + * kernel do have the pacth to support it, + * like T3T and pxa978T 3.0.31 JB, these + * patch are needed to support + * wpa_supplicant 2.x */ +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 0, 31) + if (pmevent->event_len > ETH_ALEN) { +#if CFG80211_VERSION_CODE < KERNEL_VERSION(4, 0, 0) + /* set station info filled flag */ + sinfo.filled |= STATION_INFO_ASSOC_REQ_IES; +#endif + /* get the assoc request ies and length */ + sinfo.assoc_req_ies = + (const t_u8 *)(pmevent->event_buf + + ETH_ALEN); + sinfo.assoc_req_ies_len = + pmevent->event_len - ETH_ALEN; + + } +#endif /* KERNEL_VERSION */ + if (priv->netdev && priv->wdev) + cfg80211_new_sta(priv->netdev, + (t_u8 *)addr, &sinfo, + GFP_KERNEL); + } +#endif /* UAP_CFG80211 */ + memmove((pmevent->event_buf + strlen(CUS_EVT_STA_CONNECTED) + + 1), pmevent->event_buf, pmevent->event_len); + memcpy(pmevent->event_buf, (t_u8 *)CUS_EVT_STA_CONNECTED, + strlen(CUS_EVT_STA_CONNECTED)); + pmevent->event_buf[strlen(CUS_EVT_STA_CONNECTED)] = 0; + woal_broadcast_event(priv, pmevent->event_buf, + pmevent->event_len + + strlen(CUS_EVT_STA_CONNECTED)); +#ifdef UAP_WEXT + if (IS_UAP_WEXT(cfg80211_wext)) { + memset(&wrqu, 0, sizeof(union iwreq_data)); + wrqu.data.pointer = pmevent->event_buf; + if ((pmevent->event_len + + strlen(CUS_EVT_STA_CONNECTED) + 1) > IW_CUSTOM_MAX) + wrqu.data.length = + ETH_ALEN + + strlen(CUS_EVT_STA_CONNECTED) + 1; + else + wrqu.data.length = + pmevent->event_len + + strlen(CUS_EVT_STA_CONNECTED) + 1; + wireless_send_event(priv->netdev, IWEVCUSTOM, &wrqu, + pmevent->event_buf); + } +#endif /* UAP_WEXT */ + break; + case MLAN_EVENT_ID_UAP_FW_STA_DISCONNECT: +#ifdef UAP_CFG80211 + if (IS_UAP_CFG80211(cfg80211_wext)) { +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) + /* skip 2 bytes extra header will get the mac address */ + if (priv->netdev && priv->wdev) + cfg80211_del_sta(priv->netdev, + pmevent->event_buf + 2, + GFP_KERNEL); +#endif /* KERNEL_VERSION */ + } +#endif /* UAP_CFG80211 */ + memmove((pmevent->event_buf + strlen(CUS_EVT_STA_DISCONNECTED) + + 1), pmevent->event_buf, pmevent->event_len); + memcpy(pmevent->event_buf, (t_u8 *)CUS_EVT_STA_DISCONNECTED, + strlen(CUS_EVT_STA_DISCONNECTED)); + pmevent->event_buf[strlen(CUS_EVT_STA_DISCONNECTED)] = 0; + woal_broadcast_event(priv, pmevent->event_buf, + pmevent->event_len + + strlen(CUS_EVT_STA_DISCONNECTED)); + +#ifdef UAP_WEXT + if (IS_UAP_WEXT(cfg80211_wext)) { + memset(&wrqu, 0, sizeof(union iwreq_data)); + wrqu.data.pointer = pmevent->event_buf; + wrqu.data.length = + pmevent->event_len + + strlen(CUS_EVT_STA_DISCONNECTED) + 1; + wireless_send_event(priv->netdev, IWEVCUSTOM, &wrqu, + pmevent->event_buf); + } +#endif /* UAP_WEXT */ + break; + case MLAN_EVENT_ID_DRV_MGMT_FRAME: + buf = (t_u8 *)(pmevent->event_buf + sizeof(pmevent->event_id)); + payload_len = + pmevent->event_len - sizeof(pmevent->event_id) - + sizeof(moal_timestamps); + category = *(buf + sizeof(moal_wlan_802_11_header)); + action_code = *(buf + sizeof(moal_wlan_802_11_header) + 1); + header = (moal_wlan_802_11_header *) (buf); + tm_ind = (moal_wnm_tm_msmt *) (buf + + sizeof(moal_wlan_802_11_header) + + 2); + tsstamp = (moal_timestamps *) ((t_u8 *)buf + payload_len); + memcpy(peer_addr, (t_u8 *)&header->addr2, ETH_ALEN); + if ((category == IEEE_MGMT_ACTION_CATEGORY_UNPROTECT_WNM) && + (action_code == 0x1)) { + sprintf(event, + "%s " FULL_MACSTR + " %u %u %u %u %u %u %u %u %u %u %lu %llu", + CUS_EVT_TM_FRAME_INDICATION, + FULL_MAC2STR(peer_addr), tm_ind->dialog_token, + tsstamp->t2, tsstamp->t2_err, tsstamp->t3, + tsstamp->t3_err, tm_ind->follow_up_dialog_token, + tm_ind->t1, tm_ind->t1_err, tm_ind->t4, + tm_ind->t4_err, + (unsigned long)tsstamp->t2 * 10L, + tsstamp->ingress_time); + + if (payload_len > + (sizeof(moal_wnm_tm_msmt) + + sizeof(moal_wlan_802_11_header) + 2)) { + ptp_context = + (moal_ptp_context *) (buf + + sizeof + (moal_wlan_802_11_header) + + 2 + + sizeof + (moal_wnm_tm_msmt)); + sprintf(event + strlen(event), " %02x %02x", + ptp_context->vendor_specific, + ptp_context->length); + for (i = 0; i < ptp_context->length; i++) + sprintf(event + strlen(event), " %02x", + ptp_context->data[i]); + } + woal_broadcast_event(priv, event, strlen(event)); + } +#ifdef UAP_WEXT + if (IS_UAP_WEXT(cfg80211_wext)) { + woal_broadcast_event(priv, pmevent->event_buf, + pmevent->event_len); + } +#endif /* UAP_WEXT */ +#if defined(STA_CFG80211) || defined(UAP_CFG80211) + if (IS_STA_OR_UAP_CFG80211(cfg80211_wext)) { +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + if (priv->netdev + && priv->netdev->ieee80211_ptr->wiphy->mgmt_stypes + && priv->mgmt_subtype_mask) { + /* frmctl + durationid + addr1 + addr2 + addr3 + seqctl */ +#define PACKET_ADDR4_POS (2 + 2 + 6 + 6 + 6 + 2) + t_u8 *pkt; + int freq = + priv->phandle-> + remain_on_channel ? priv->phandle->chan. + center_freq : + woal_get_active_intf_freq(priv); + if (!freq) { + if (!priv->phandle->chan.center_freq) { + PRINTM(MINFO, + "Skip to report mgmt packet to cfg80211\n"); + break; + } + freq = priv->phandle->chan.center_freq; + } + + pkt = ((t_u8 *)pmevent->event_buf + + sizeof(pmevent->event_id)); + + /* move addr4 */ + memmove(pkt + PACKET_ADDR4_POS, + pkt + PACKET_ADDR4_POS + ETH_ALEN, + pmevent->event_len - + sizeof(pmevent->event_id) + - PACKET_ADDR4_POS - ETH_ALEN); +#ifdef WIFI_DIRECT_SUPPORT + if (ieee80211_is_action + (((struct ieee80211_mgmt *)pkt)-> + frame_control)) + woal_cfg80211_display_p2p_actframe(pkt, + pmevent-> + event_len + - + sizeof + (pmevent-> + event_id) + - + MLAN_MAC_ADDR_LENGTH, + ieee80211_get_channel + (priv-> + wdev-> + wiphy, + freq), + MFALSE); +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) + cfg80211_rx_mgmt( +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) + priv->wdev, +#else + priv->netdev, +#endif + freq, 0, + ((const t_u8 *)pmevent-> + event_buf) + + sizeof(pmevent-> + event_id), + pmevent->event_len - + sizeof(pmevent-> + event_id) - + MLAN_MAC_ADDR_LENGTH +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) + , 0 +#endif +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 18, 0) + , GFP_ATOMIC +#endif + ); +#else + cfg80211_rx_mgmt(priv->netdev, freq, + ((const t_u8 *)pmevent-> + event_buf) + + sizeof(pmevent->event_id), + pmevent->event_len - + sizeof(pmevent->event_id) - + MLAN_MAC_ADDR_LENGTH, + GFP_ATOMIC); +#endif + } +#endif /* KERNEL_VERSION */ + } +#endif /* STA_CFG80211 || UAP_CFG80211 */ + break; +#endif /* UAP_SUPPORT */ + case MLAN_EVENT_ID_DRV_PASSTHRU: + woal_broadcast_event(priv, pmevent->event_buf, + pmevent->event_len); + break; + case MLAN_EVENT_ID_DRV_ASSOC_FAILURE_REPORT: + PRINTM(MINFO, "Assoc result\n"); + + if (priv->media_connected) { + PRINTM(MINFO, "Assoc_Rpt: Media Connected\n"); + if (!netif_carrier_ok(priv->netdev)) { + PRINTM(MINFO, "Assoc_Rpt: Carrier On\n"); + netif_carrier_on(priv->netdev); + } + PRINTM(MINFO, "Assoc_Rpt: Queue Start\n"); + woal_wake_queue(priv->netdev); + } + break; + case MLAN_EVENT_ID_DRV_MEAS_REPORT: + /* We have received measurement report, wakeup measurement wait queue */ + PRINTM(MINFO, "Measurement Report\n"); + /* Going out of CAC checking period */ + if (priv->phandle->cac_period == MTRUE) { + priv->phandle->cac_period = MFALSE; + if (priv->phandle->meas_wait_q_woken == MFALSE) { + priv->phandle->meas_wait_q_woken = MTRUE; + wake_up_interruptible(&priv->phandle-> + meas_wait_q); + } + + /* Execute delayed BSS START command */ + if (priv->phandle->delay_bss_start == MTRUE) { + mlan_ioctl_req *req = NULL; + mlan_ds_bss *bss = NULL; + + /* Clear flag */ + priv->phandle->delay_bss_start = MFALSE; + + PRINTM(MMSG, + "Now CAC measure period end. Execute delayed BSS Start command.\n"); + + req = woal_alloc_mlan_ioctl_req(sizeof + (mlan_ds_bss)); + if (!req) { + PRINTM(MERROR, + "Failed to allocate ioctl request buffer\n"); + goto done; + } + bss = (mlan_ds_bss *)req->pbuf; + req->req_id = MLAN_IOCTL_BSS; + req->action = MLAN_ACT_SET; + bss->sub_command = MLAN_OID_BSS_START; + memcpy(&bss->param.ssid_bssid, + &priv->phandle->delay_ssid_bssid, + sizeof(mlan_ssid_bssid)); + + if (woal_request_ioctl(priv, req, MOAL_NO_WAIT) + != MLAN_STATUS_PENDING) { + PRINTM(MERROR, + "Delayed BSS Start operation failed!\n"); + kfree(req); + } + + PRINTM(MMSG, "BSS START Complete!\n"); + } +#ifdef UAP_SUPPORT +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + if (priv->uap_host_based && dfs_offload) + woal_cfg80211_dfs_vendor_event(priv, + event_dfs_cac_finished, + &priv->chan); +#endif +#endif +#endif + + } + break; + case MLAN_EVENT_ID_DRV_TDLS_TEARDOWN_REQ: +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) +#ifdef STA_CFG80211 + if (IS_STA_CFG80211(cfg80211_wext)) { + tdls_tear_down_event *tdls_event = + (tdls_tear_down_event *)pmevent->event_buf; + cfg80211_tdls_oper_request(priv->netdev, + tdls_event->peer_mac_addr, + NL80211_TDLS_TEARDOWN, + tdls_event->reason_code, + GFP_KERNEL); + } +#endif +#endif + break; + case MLAN_EVENT_ID_FW_TX_STATUS: + { + unsigned long flag; + tx_status_event *tx_status = + (tx_status_event *)(pmevent->event_buf + 4); + struct tx_status_info *tx_info = NULL; + t_u8 catagory = 0, action = 0, dialog_token = 0; + t_u8 peer_addr[6]; + confirm_timestamps tsbuff = { 0 }; + + PRINTM(MINFO, + "Receive Tx status: tx_token=%d, pkt_type=0x%x, status=%d tx_seq_num=%d\n", + tx_status->tx_token_id, tx_status->packet_type, + tx_status->status, priv->tx_seq_num); + spin_lock_irqsave(&priv->tx_stat_lock, flag); + tx_info = + woal_get_tx_info(priv, tx_status->tx_token_id); + if (tx_info) { + bool ack; + struct sk_buff *skb = + (struct sk_buff *)tx_info->tx_skb; + list_del(&tx_info->link); + spin_unlock_irqrestore(&priv->tx_stat_lock, + flag); + if (!tx_status->status) + ack = true; + else + ack = false; + { + catagory = + ((struct ieee80211_mgmt *)skb-> + data)->u.action.category; + action = *(& + ((struct ieee80211_mgmt *) + skb->data)->u.action. + category + 1); + dialog_token = *(&((struct ieee80211_mgmt *)skb->data)->u.action.category + 2); //dt is 2 bytes after category + memcpy(peer_addr, + (t_u8 + *)((struct ieee80211_mgmt *) + skb->data)->da, 6); + PRINTM(MEVENT, + "Wlan: Tx status=%d, cat = %d, act = %d, dt = %d\n", + ack, catagory, action, + dialog_token); + /* this is a tx done for timining measurement action frame + * so we need to send the tx timestamp of txed frame back to supplicant + * for this send the timestamps along with the tx_status event buffer */ + if (catagory == + IEEE_MGMT_ACTION_CATEGORY_UNPROTECT_WNM + && action == 0x1) { + /* create a timestamp buffer to send to host supplicant */ + + PRINTM(MEVENT, + "Wlan: Tx t1=%lld, t4 = %lld, t1_error = %lld, t4_error=%lld\n", + tx_status->t1_tstamp, + tx_status->t4_tstamp, + tx_status->t1_error, + tx_status->t4_error); + + /* for timestamps only use lower 32-bits as spec defines 11v timestamps as 32-bits */ + tsbuff.t4 = + (u32) + woal_le32_to_cpu + (tx_status->t4_tstamp); + tsbuff.t1 = + (u32) + woal_le32_to_cpu + (tx_status->t1_tstamp); + tsbuff.t4_error = + (u8)tx_status->t4_error; + tsbuff.t1_error = + (u8)tx_status->t4_error; + tsbuff.egress_time = + (u64) + woal_le64_to_cpu + (tx_status-> + egress_time); + + if (skb_tailroom(skb) < + sizeof(confirm_timestamps)) + { + struct sk_buff *new_skb + = NULL; + PRINTM(MWARN, + "Tx Status: Insufficient skb tailroom %d\n", + skb_tailroom + (skb)); + /* Insufficient skb tailroom - allocate a new skb */ + new_skb = + skb_copy_expand + (skb, 0, + sizeof + (confirm_timestamps), + GFP_ATOMIC); + if (unlikely(!new_skb)) { + PRINTM(MERROR, + "Tx Status: Cannot allocate skb\n"); + dev_kfree_skb_any + (skb); + goto done; + } + skb = new_skb; + PRINTM(MINFO, + "new skb tailroom %d\n", + skb_tailroom + (skb)); + } + if (tx_info->tx_cookie) { + memcpy(skb_put + (skb, + sizeof + (confirm_timestamps)), + &tsbuff, + sizeof + (confirm_timestamps)); + } else { + sprintf(event, + "%s " + FULL_MACSTR + " %u %u %u %u %u %u %llu", + CUS_EVT_TIMING_MSMT_CONFIRM, + FULL_MAC2STR + (peer_addr), + dialog_token, + tsbuff.t1, + tsbuff.t1_error, + tsbuff.t4, + tsbuff.t4_error, + tsbuff.t1 * 10, + tsbuff. + egress_time); + woal_broadcast_event + (priv, event, + sizeof(event)); + } + } + } +#if defined(STA_CFG80211) || defined(UAP_CFG80211) + if (tx_info->tx_cookie) { +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 37) +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 6, 0) + cfg80211_mgmt_tx_status(priv->netdev, + tx_info-> + tx_cookie, + skb->data, + skb->len, ack, + GFP_ATOMIC); +#else + cfg80211_mgmt_tx_status(priv->wdev, + tx_info-> + tx_cookie, + skb->data, + skb->len, ack, + GFP_ATOMIC); +#endif +#endif + } +#endif + dev_kfree_skb_any(skb); + kfree(tx_info); + } else + spin_unlock_irqrestore(&priv->tx_stat_lock, + flag); + } + break; + + case MLAN_EVENT_ID_DRV_FT_RESPONSE: + if (priv->phandle->fw_roam_enable) + break; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3,10,0) +#ifdef STA_CFG80211 + if (IS_STA_CFG80211(cfg80211_wext)) { + struct cfg80211_ft_event_params ft_event; + if (priv->ft_pre_connect) + break; + memset(&ft_event, 0, + sizeof(struct cfg80211_ft_event_params)); + PRINTM(MMSG, + "wlan : FT response target AP " MACSTR "\n", + MAC2STR((t_u8 *)pmevent->event_buf)); + DBG_HEXDUMP(MDAT_D, "FT-event ", pmevent->event_buf, + pmevent->event_len); + memcpy(priv->target_ap_bssid, pmevent->event_buf, + ETH_ALEN); + ft_event.target_ap = priv->target_ap_bssid; + ft_event.ies = pmevent->event_buf + ETH_ALEN; + ft_event.ies_len = pmevent->event_len - ETH_ALEN; + /*TSPEC info is needed by RIC, However the TS operation is configured by mlanutl */ + /*So do not add RIC temporally */ + /*when add RIC, 1. query TS status, 2. copy tspec from addts command */ + ft_event.ric_ies = NULL; + ft_event.ric_ies_len = 0; + + cfg80211_ft_event(priv->netdev, &ft_event); + priv->ft_pre_connect = MTRUE; + + if (priv->ft_roaming_triggered_by_driver || + !(priv->ft_cap & MBIT(0))) { + priv->ft_wait_condition = MTRUE; + wake_up(&priv->ft_wait_q); + } + } +#endif +#endif + break; + case MLAN_EVENT_ID_FW_ROAM_OFFLOAD_RESULT: + memcpy(priv->cfg_bssid, pmevent->event_buf, ETH_ALEN); + tlv = (MrvlIEtypesHeader_t *)((t_u8 *)pmevent->event_buf + + MLAN_MAC_ADDR_LENGTH); + tlv_buf_left = pmevent->event_len - MLAN_MAC_ADDR_LENGTH; + while (tlv_buf_left >= sizeof(MrvlIEtypesHeader_t)) { + tlv_type = woal_le16_to_cpu(tlv->type); + tlv_len = woal_le16_to_cpu(tlv->len); + + if (tlv_buf_left < + (tlv_len + sizeof(MrvlIEtypesHeader_t))) { + PRINTM(MERROR, + "Error processing firmware roam success TLVs, bytes left < TLV length\n"); + break; + } + + switch (tlv_type) { + case TLV_TYPE_APINFO: + pinfo = (apinfo *) tlv; + break; + case TLV_TYPE_ASSOC_REQ_IE: + req_tlv = (apinfo *) tlv; + break; + default: + break; + } + tlv_buf_left -= tlv_len + sizeof(MrvlIEtypesHeader_t); + tlv = (MrvlIEtypesHeader_t *)((t_u8 *)tlv + tlv_len + + sizeof + (MrvlIEtypesHeader_t)); + } + if (!pinfo) { + PRINTM(MERROR, + "ERROR:AP info in roaming event buffer is NULL\n"); + goto done; + } + if (req_tlv) { + req_ie = req_tlv->rsp_ie; + ie_len = req_tlv->header.len; + } + + woal_inform_bss_from_scan_result(priv, NULL, MOAL_NO_WAIT); +#if defined(FW_ROAMING) || (defined(ROAMING_OFFLOAD) && defined(STA_CFG80211)) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) + memset(&roam_info, 0, sizeof(struct cfg80211_roam_info)); + roam_info.bssid = priv->cfg_bssid; + roam_info.req_ie = req_ie; + roam_info.req_ie_len = ie_len; + roam_info.resp_ie = pinfo->rsp_ie; + roam_info.resp_ie_len = pinfo->header.len; + cfg80211_roamed(priv->netdev, &roam_info, GFP_KERNEL); +#else +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) + cfg80211_roamed(priv->netdev, NULL, priv->cfg_bssid, req_ie, + ie_len, pinfo->rsp_ie, pinfo->header.len, + GFP_KERNEL); +#else + cfg80211_roamed(priv->netdev, priv->cfg_bssid, req_ie, ie_len, + pinfo->rsp_ie, pinfo->header.len, GFP_KERNEL); +#endif +#endif +#ifdef STA_CFG80211 +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + woal_roam_ap_info(priv, pmevent->event_buf, pmevent->event_len); +#endif +#endif +#endif + PRINTM(MMSG, "FW Roamed to bssid " MACSTR " successfully\n", + MAC2STR(priv->cfg_bssid)); + break; + default: + break; + } +done: + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prints the debug message in mlan + * + * @param pmoal_handle Pointer to the MOAL context + * @param level debug level + * @param pformat point to string format buf + * + * @return N/A + */ +t_void +moal_print(IN t_void *pmoal_handle, IN t_u32 level, IN char *pformat, IN ...) +{ +#ifdef DEBUG_LEVEL1 + va_list args; + + if (level & MHEX_DUMP) { + t_u8 *buf = NULL; + int len = 0; + + va_start(args, pformat); + buf = (t_u8 *)va_arg(args, t_u8 *); + len = (int)va_arg(args, int); + va_end(args); + +#ifdef DEBUG_LEVEL2 + if (level & MINFO) + HEXDUMP((char *)pformat, buf, len); + else +#endif /* DEBUG_LEVEL2 */ + { + if (level & MERROR) + DBG_HEXDUMP(MERROR, (char *)pformat, buf, len); + if (level & MCMD_D) + DBG_HEXDUMP(MCMD_D, (char *)pformat, buf, len); + if (level & MDAT_D) + DBG_HEXDUMP(MDAT_D, (char *)pformat, buf, len); + if (level & MIF_D) + DBG_HEXDUMP(MIF_D, (char *)pformat, buf, len); + if (level & MFW_D) + DBG_HEXDUMP(MFW_D, (char *)pformat, buf, len); + if (level & MEVT_D) + DBG_HEXDUMP(MEVT_D, (char *)pformat, buf, len); + } + } else { + if (drvdbg & level) { + va_start(args, pformat); + vprintk(pformat, args); + va_end(args); + } + } +#endif /* DEBUG_LEVEL1 */ +} + +/** + * @brief This function prints the network interface name + * + * @param pmoal_handle Pointer to the MOAL context + * @param bss_index BSS index + * @param level debug level + * + * @return N/A + */ +t_void +moal_print_netintf(IN t_void *pmoal_handle, IN t_u32 bss_index, IN t_u32 level) +{ +#ifdef DEBUG_LEVEL1 + moal_handle *phandle = (moal_handle *)pmoal_handle; + + if (phandle) { + if ((bss_index < MLAN_MAX_BSS_NUM) && phandle->priv[bss_index] + && phandle->priv[bss_index]->netdev) { + if (drvdbg & level) + printk("%s: ", + phandle->priv[bss_index]->netdev->name); + } + } +#endif /* DEBUG_LEVEL1 */ +} + +/** + * @brief This function asserts the existence of the passed argument + * + * @param pmoal_handle A pointer to moal_private structure + * @param cond Condition to check + * + * @return N/A + */ +t_void +moal_assert(IN t_void *pmoal_handle, IN t_u32 cond) +{ + if (!cond) { + panic("Assert failed: Panic!"); + } +} + +/** + * @brief This function save the histogram data + * + * @param pmoal_handle A pointer to moal_private structure + * @param bss_index BSS index + * @param rx_rate rx rate index + * @param snr snr + * @param nflr noise floor + * @param antenna antenna + * + * @return N/A + */ +t_void +moal_hist_data_add(IN t_void *pmoal_handle, IN t_u32 bss_index, IN t_u8 rx_rate, + IN t_s8 snr, IN t_s8 nflr, IN t_u8 antenna) +{ + moal_private *priv = NULL; + priv = woal_bss_index_to_priv(pmoal_handle, bss_index); + if (priv && antenna >= priv->phandle->histogram_table_num) + antenna = 0; + if (priv && priv->hist_data[antenna]) + woal_hist_data_add(priv, rx_rate, snr, nflr, antenna); +} + +/** + * @brief This function update the peer signal + * + * @param pmoal_handle A pointer to moal_private structure + * @param bss_index BSS index + * @param peer_addr peer address + * @param snr snr + * @param nflr noise floor + * + * @return N/A + */ +t_void +moal_updata_peer_signal(IN t_void *pmoal_handle, IN t_u32 bss_index, + IN t_u8 *peer_addr, IN t_s8 snr, IN t_s8 nflr) +{ + moal_private *priv = NULL; + struct tdls_peer *peer = NULL; + unsigned long flags; + priv = woal_bss_index_to_priv(pmoal_handle, bss_index); + if (priv && priv->enable_auto_tdls) { + spin_lock_irqsave(&priv->tdls_lock, flags); + list_for_each_entry(peer, &priv->tdls_list, link) { + if (!memcmp(peer->peer_addr, peer_addr, ETH_ALEN)) { + peer->rssi = nflr - snr; + peer->rssi_jiffies = jiffies; + break; + } + } + spin_unlock_irqrestore(&priv->tdls_lock, flags); + } +} + +/** +* @brief This function records host time in nano seconds +* +* @return 64 bit value of host time in nano seconds +*/ +s64 +get_host_time_ns(void) +{ + struct timespec ts; + getnstimeofday(&ts); + return timespec_to_ns(&ts); +} + +/** + * @brief Retrieves the current system time + * + * @param time Pointer for the seconds of system time + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status +moal_get_host_time_ns(OUT t_u64 *time) +{ + struct timespec ts; + t_u64 hclk_val; + + getnstimeofday(&ts); + hclk_val = (ts.tv_sec * 1000000000L) + ts.tv_nsec; + *time = hclk_val; + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Performs division of 64-bit num with base + * @brief do_div does two things + * @brief 1. modifies the 64-bit num in place with + * @brief the quotient, i.e., num becomes quotient + * @brief 2. returns the 32-bit reminder + * + * @param num dividend + * @param base divisor + * @return returns quotient + */ +t_u32 +moal_do_div(IN t_u64 num, IN t_u32 base) +{ + return do_div(num, base); +} diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_shim.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_shim.h new file mode 100644 index 000000000000..68deb09bb1fd --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_shim.h @@ -0,0 +1,111 @@ +/** @file moal_shim.h + * + * @brief This file contains declaration referring to + * functions defined in moal module + * + * Copyright (C) 2008-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ +/************************************************************* +Change Log: + 10/21/2008: initial version +************************************************************/ + +#ifndef _MOAL_H +#define _MOAL_H + +mlan_status moal_get_fw_data(IN t_void *pmoal_handle, + IN t_u32 offset, IN t_u32 len, OUT t_u8 *pbuf); +mlan_status moal_get_hw_spec_complete(IN t_void *pmoal_handle, + IN mlan_status status, + IN mlan_hw_info * phw, + IN pmlan_bss_tbl ptbl); +mlan_status moal_init_fw_complete(IN t_void *pmoal_handle, + IN mlan_status status); +mlan_status moal_shutdown_fw_complete(IN t_void *pmoal_handle, + IN mlan_status status); +mlan_status moal_ioctl_complete(IN t_void *pmoal_handle, + IN pmlan_ioctl_req pioctl_req, + IN mlan_status status); +mlan_status moal_alloc_mlan_buffer(IN t_void *pmoal_handle, IN t_u32 size, + OUT pmlan_buffer *pmbuf); +mlan_status moal_free_mlan_buffer(IN t_void *pmoal_handle, + IN pmlan_buffer pmbuf); +mlan_status moal_send_packet_complete(IN t_void *pmoal_handle, + IN pmlan_buffer pmbuf, + IN mlan_status status); + +/** moal_write_reg */ +mlan_status moal_write_reg(IN t_void *pmoal_handle, + IN t_u32 reg, IN t_u32 data); +/** moal_read_reg */ +mlan_status moal_read_reg(IN t_void *pmoal_handle, + IN t_u32 reg, OUT t_u32 *data); +mlan_status moal_write_data_sync(IN t_void *pmoal_handle, + IN pmlan_buffer pmbuf, + IN t_u32 port, IN t_u32 timeout); +mlan_status moal_read_data_sync(IN t_void *pmoal_handle, + IN OUT pmlan_buffer pmbuf, + IN t_u32 port, IN t_u32 timeout); +mlan_status moal_recv_packet(IN t_void *pmoal_handle, IN pmlan_buffer pmbuf); +mlan_status moal_recv_event(IN t_void *pmoal_handle, IN pmlan_event pmevent); +mlan_status moal_malloc(IN t_void *pmoal_handle, + IN t_u32 size, IN t_u32 flag, OUT t_u8 **ppbuf); +mlan_status moal_mfree(IN t_void *pmoal_handle, IN t_u8 *pbuf); +mlan_status moal_vmalloc(IN t_void *pmoal_handle, + IN t_u32 size, OUT t_u8 **ppbuf); +mlan_status moal_vfree(IN t_void *pmoal_handle, IN t_u8 *pbuf); +t_void *moal_memset(IN t_void *pmoal_handle, + IN t_void *pmem, IN t_u8 byte, IN t_u32 num); +t_void *moal_memcpy(IN t_void *pmoal_handle, + IN t_void *pdest, IN const t_void *psrc, IN t_u32 num); +t_void *moal_memmove(IN t_void *pmoal_handle, + IN t_void *pdest, IN const t_void *psrc, IN t_u32 num); +t_s32 moal_memcmp(IN t_void *pmoal_handle, + IN const t_void *pmem1, IN const t_void *pmem2, IN t_u32 num); +/** moal_udelay */ +t_void moal_udelay(IN t_void *pmoal_handle, IN t_u32 udelay); +mlan_status moal_get_system_time(IN t_void *pmoal_handle, OUT t_u32 *psec, + OUT t_u32 *pusec); +mlan_status moal_init_lock(IN t_void *pmoal_handle, OUT t_void **pplock); +mlan_status moal_free_lock(IN t_void *pmoal_handle, IN t_void *plock); +mlan_status moal_spin_lock(IN t_void *pmoal_handle, IN t_void *plock); +mlan_status moal_spin_unlock(IN t_void *pmoal_handle, IN t_void *plock); +t_void moal_print(IN t_void *pmoal_handle, IN t_u32 level, IN char *pformat, + IN ...); +t_void moal_print_netintf(IN t_void *pmoal_handle, IN t_u32 bss_index, + IN t_u32 level); +t_void moal_assert(IN t_void *pmoal_handle, IN t_u32 cond); +t_void moal_hist_data_add(IN t_void *pmoal_handle, IN t_u32 bss_index, + IN t_u8 rx_rate, IN t_s8 snr, IN t_s8 nflr, + IN t_u8 antenna); + +t_void moal_updata_peer_signal(IN t_void *pmoal_handle, IN t_u32 bss_index, + IN t_u8 *peer_addr, IN t_s8 snr, IN t_s8 nflr); +mlan_status moal_get_host_time_ns(OUT t_u64 *time); +t_u32 moal_do_div(IN t_u64 num, IN t_u32 base); + +mlan_status moal_init_timer(IN t_void *pmoal_handle, + OUT t_void **pptimer, + IN t_void (*callback) (t_void *pcontext), + IN t_void *pcontext); +mlan_status moal_free_timer(IN t_void *pmoal_handle, IN t_void *ptimer); +mlan_status moal_start_timer(IN t_void *pmoal_handle, + IN t_void *ptimer, + IN t_u8 periodic, IN t_u32 msec); +mlan_status moal_stop_timer(IN t_void *pmoal_handle, IN t_void *ptimer); + +#endif /*_MOAL_H */ diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_sta_cfg80211.c b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_sta_cfg80211.c new file mode 100644 index 000000000000..70de561dbfc3 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_sta_cfg80211.c @@ -0,0 +1,7373 @@ +/** @file moal_sta_cfg80211.c + * + * @brief This file contains the functions for STA CFG80211. + * + * Copyright (C) 2011-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +#include "moal_cfg80211.h" +#include "moal_cfgvendor.h" +#include "moal_sta_cfg80211.h" +#include "moal_eth_ioctl.h" +#ifdef UAP_SUPPORT +#include "moal_uap.h" +#endif + +extern int cfg80211_wext; + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) +extern int fw_region; +#endif + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) +extern int dfs_offload; +#endif + +extern int cntry_txpwr; + +/* Supported crypto cipher suits to be advertised to cfg80211 */ +const u32 cfg80211_cipher_suites[] = { + WLAN_CIPHER_SUITE_WEP40, + WLAN_CIPHER_SUITE_WEP104, + WLAN_CIPHER_SUITE_TKIP, + WLAN_CIPHER_SUITE_CCMP, + WLAN_CIPHER_SUITE_SMS4, + WLAN_CIPHER_SUITE_AES_CMAC, +}; + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) +static int woal_cfg80211_set_monitor_channel(struct wiphy *wiphy, + struct cfg80211_chan_def *chandef); +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 9, 0) +static void +#else +static int +#endif + +woal_cfg80211_reg_notifier(struct wiphy *wiphy, + struct regulatory_request *request); + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) +static int woal_cfg80211_scan(struct wiphy *wiphy, + struct cfg80211_scan_request *request); +#else +static int woal_cfg80211_scan(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_scan_request *request); +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4,5,0) +static void woal_cfg80211_abort_scan(struct wiphy *wiphy, + struct wireless_dev *wdev); +#endif +static int woal_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_connect_params *sme); + +static int woal_cfg80211_disconnect(struct wiphy *wiphy, + struct net_device *dev, t_u16 reason_code); + +static int woal_cfg80211_get_station(struct wiphy *wiphy, + struct net_device *dev, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 16, 0) + const t_u8 *mac, +#else + t_u8 *mac, +#endif + struct station_info *sinfo); + +static int woal_cfg80211_dump_station(struct wiphy *wiphy, + struct net_device *dev, int idx, + t_u8 *mac, struct station_info *sinfo); + +static int woal_cfg80211_dump_survey(struct wiphy *wiphy, + struct net_device *dev, int idx, + struct survey_info *survey); +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) +static int woal_cfg80211_get_channel(struct wiphy *wiphy, + struct wireless_dev *wdev, + struct cfg80211_chan_def *chandef); +#endif +static int woal_cfg80211_set_power_mgmt(struct wiphy *wiphy, + struct net_device *dev, bool enabled, + int timeout); +#if CFG80211_VERSION_CODE > KERNEL_VERSION(2, 6, 35) +static int woal_cfg80211_set_cqm_rssi_config(struct wiphy *wiphy, + struct net_device *dev, + s32 rssi_thold, u32 rssi_hyst); +#endif + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 31) +static int woal_cfg80211_get_tx_power(struct wiphy *wiphy, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + struct wireless_dev *wdev, +#endif + int *dbm); + +static int woal_cfg80211_set_tx_power(struct wiphy *wiphy, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + struct wireless_dev *wdev, +#endif +#if CFG80211_VERSION_CODE < KERNEL_VERSION(2, 6, 36) + enum tx_power_setting type, +#else + enum nl80211_tx_power_setting type, +#endif + int dbm); +#endif + +static int woal_cfg80211_join_ibss(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_ibss_params *params); + +static int woal_cfg80211_leave_ibss(struct wiphy *wiphy, + struct net_device *dev); + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) +static int woal_cfg80211_mgmt_tx_cancel_wait(struct wiphy *wiphy, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) + struct wireless_dev *wdev, +#else + struct net_device *dev, +#endif + u64 cookie); + +static int woal_cfg80211_remain_on_channel(struct wiphy *wiphy, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) + struct wireless_dev *wdev, +#else + struct net_device *dev, +#endif + struct ieee80211_channel *chan, +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 8, 0) + enum nl80211_channel_type + channel_type, +#endif + unsigned int duration, u64 * cookie); + +static int woal_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) + struct wireless_dev *wdev, +#else + struct net_device *dev, +#endif + u64 cookie); +#endif /* KERNEL_VERSION */ + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) +int woal_cfg80211_sched_scan_start(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_sched_scan_request *request); +int woal_cfg80211_sched_scan_stop(struct wiphy *wiphy, struct net_device *dev +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) + , u64 reqid +#endif + ); +#endif + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) +int woal_cfg80211_resume(struct wiphy *wiphy); +int woal_cfg80211_suspend(struct wiphy *wiphy, struct cfg80211_wowlan *wow); +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 5, 0) +static void woal_cfg80211_set_wakeup(struct wiphy *wiphy, bool enabled); +#endif + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3,2,0) +void woal_check_auto_tdls(struct wiphy *wiphy, struct net_device *dev); +int woal_cfg80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 16, 0) + const u8 *peer, +#else + u8 *peer, +#endif + enum nl80211_tdls_operation oper); +int woal_cfg80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 16, 0) + const u8 *peer, +#else + u8 *peer, +#endif + u8 action_code, u8 dialog_token, u16 status_code, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 15, 0) + u32 peer_capability, +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 17, 0) + bool initiator, +#endif + const u8 *extra_ies, size_t extra_ies_len); +static int + woal_cfg80211_add_station(struct wiphy *wiphy, struct net_device *dev, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 16, 0) + const u8 *mac, +#else + u8 *mac, +#endif + struct station_parameters *params); +static int + woal_cfg80211_change_station(struct wiphy *wiphy, struct net_device *dev, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 16, 0) + const u8 *mac, +#else + u8 *mac, +#endif + struct station_parameters *params); +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) +static int + +woal_cfg80211_tdls_channel_switch(struct wiphy *wiphy, + struct net_device *dev, + const u8 *addr, u8 oper_class, + struct cfg80211_chan_def *chandef); + +void woal_cfg80211_tdls_cancel_channel_switch(struct wiphy *wiphy, + struct net_device *dev, + const u8 *addr); +#endif +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3,10,0) +int woal_cfg80211_update_ft_ies(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_update_ft_ies_params *ftie); +#endif +/** cfg80211 operations */ +static struct cfg80211_ops woal_cfg80211_ops = { + .change_virtual_intf = woal_cfg80211_change_virtual_intf, + .scan = woal_cfg80211_scan, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4,5,0) + .abort_scan = woal_cfg80211_abort_scan, +#endif + .connect = woal_cfg80211_connect, + .disconnect = woal_cfg80211_disconnect, + .get_station = woal_cfg80211_get_station, + .dump_station = woal_cfg80211_dump_station, + .dump_survey = woal_cfg80211_dump_survey, + .set_wiphy_params = woal_cfg80211_set_wiphy_params, +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 5, 0) + .set_channel = woal_cfg80211_set_channel, +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + .get_channel = woal_cfg80211_get_channel, +#endif + .join_ibss = woal_cfg80211_join_ibss, + .leave_ibss = woal_cfg80211_leave_ibss, + .add_key = woal_cfg80211_add_key, + .del_key = woal_cfg80211_del_key, + .set_default_key = woal_cfg80211_set_default_key, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) + .set_default_mgmt_key = woal_cfg80211_set_default_mgmt_key, +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 1, 0) + .set_rekey_data = woal_cfg80211_set_rekey_data, +#endif + .set_pmksa = woal_cfg80211_set_pmksa, + .del_pmksa = woal_cfg80211_del_pmksa, + .flush_pmksa = woal_cfg80211_flush_pmksa, + .set_power_mgmt = woal_cfg80211_set_power_mgmt, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 31) + .set_tx_power = woal_cfg80211_set_tx_power, + .get_tx_power = woal_cfg80211_get_tx_power, +#endif + .set_bitrate_mask = woal_cfg80211_set_bitrate_mask, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) + .sched_scan_start = woal_cfg80211_sched_scan_start, + .sched_scan_stop = woal_cfg80211_sched_scan_stop, +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) + .suspend = woal_cfg80211_suspend, + .resume = woal_cfg80211_resume, +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 5, 0) + .set_wakeup = woal_cfg80211_set_wakeup, +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 38) + .set_antenna = woal_cfg80211_set_antenna, + .get_antenna = woal_cfg80211_get_antenna, +#endif +#if CFG80211_VERSION_CODE > KERNEL_VERSION(2, 6, 35) + .set_cqm_rssi_config = woal_cfg80211_set_cqm_rssi_config, +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3,2,0) + .tdls_oper = woal_cfg80211_tdls_oper, + .tdls_mgmt = woal_cfg80211_tdls_mgmt, + .add_station = woal_cfg80211_add_station, + .change_station = woal_cfg80211_change_station, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) + .tdls_channel_switch = + woal_cfg80211_tdls_channel_switch,.tdls_cancel_channel_switch = + woal_cfg80211_tdls_cancel_channel_switch, +#endif +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3,10,0) + .update_ft_ies = woal_cfg80211_update_ft_ies, +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3,14,0) + .set_qos_map = woal_cfg80211_set_qos_map, +#endif +#ifdef UAP_CFG80211 +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) + .set_coalesce = woal_cfg80211_set_coalesce, +#endif + .add_virtual_intf = + woal_cfg80211_add_virtual_intf,.del_virtual_intf = + woal_cfg80211_del_virtual_intf, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) + .start_ap = woal_cfg80211_add_beacon,.change_beacon = + woal_cfg80211_set_beacon,.stop_ap = woal_cfg80211_del_beacon, +#else + .add_beacon = woal_cfg80211_add_beacon,.set_beacon = + woal_cfg80211_set_beacon,.del_beacon = woal_cfg80211_del_beacon, +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + .change_bss = woal_cfg80211_change_bss, +#endif + .del_station = woal_cfg80211_del_station, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 1, 0) + .set_txq_params = woal_cfg80211_set_txq_params, +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 9, 0) + .set_mac_acl = woal_cfg80211_set_mac_acl, +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) + .start_radar_detection = + woal_cfg80211_start_radar_detection,.channel_switch = + woal_cfg80211_channel_switch, +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 37) + .mgmt_frame_register = + woal_cfg80211_mgmt_frame_register,.mgmt_tx = + woal_cfg80211_mgmt_tx, +#endif +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + .mgmt_tx_cancel_wait = + woal_cfg80211_mgmt_tx_cancel_wait,.remain_on_channel = + woal_cfg80211_remain_on_channel,.cancel_remain_on_channel = + woal_cfg80211_cancel_remain_on_channel, +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + .set_monitor_channel = woal_cfg80211_set_monitor_channel, +#endif +}; + +/** Region code mapping */ +typedef struct _region_code_t { + /** Region */ + t_u8 region[COUNTRY_CODE_LEN]; +} region_code_t; + +static const struct ieee80211_regdomain mrvl_regdom = { + .n_reg_rules = 4, + .alpha2 = "99", + .reg_rules = { + /* IEEE 802.11b/g, channels 1..11 */ + REG_RULE(2412 - 10, 2472 + 10, 40, 6, 20, 0), + /* If any */ + /* IEEE 802.11 channel 14 - Only JP enables + * this and for 802.11b only + */ + REG_RULE(2484 - 10, 2484 + 10, 20, 6, 20, 0), + /* IEEE 802.11a, channel 36..64 */ + REG_RULE(5150 - 10, 5350 + 10, 80, 6, 20, 0), + /* IEEE 802.11a, channel 100..165 */ + REG_RULE(5470 - 10, 5850 + 10, 80, 6, 20, 0),} +}; + +/******************************************************** + Local Variables +********************************************************/ +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 37) +static const struct ieee80211_txrx_stypes + ieee80211_mgmt_stypes[NUM_NL80211_IFTYPES] = { + [NL80211_IFTYPE_ADHOC] = { + .tx = BIT(IEEE80211_STYPE_ACTION >> 4), + .rx = BIT(IEEE80211_STYPE_ACTION >> 4), + }, + [NL80211_IFTYPE_STATION] = { + .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_RESP >> 4), + .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 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] = { + .tx = 0x0000, + .rx = 0x0000, + }, +#if defined(WIFI_DIRECT_SUPPORT) +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION + [NL80211_IFTYPE_P2P_CLIENT] = { + .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_RESP >> 4), + .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4), + }, + [NL80211_IFTYPE_P2P_GO] = { + .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_RESP >> 4), + .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), + }, +#endif +#endif + [NL80211_IFTYPE_MESH_POINT] = { + .tx = 0x0000, + .rx = 0x0000, + }, + +}; +#endif + +#if CFG80211_VERSION_CODE > KERNEL_VERSION(3, 0, 0) +/** + * NOTE: types in all the sets must be equals to the + * initial value of wiphy->interface_modes + */ +static const struct ieee80211_iface_limit cfg80211_ap_sta_limits[] = { + { + .max = 4, + .types = MBIT(NL80211_IFTYPE_STATION) | +#ifdef UAP_CFG80211 + MBIT(NL80211_IFTYPE_AP) | MBIT(NL80211_IFTYPE_MONITOR) | +#endif +#if defined(WIFI_DIRECT_SUPPORT) +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION + MBIT(NL80211_IFTYPE_P2P_GO) | MBIT(NL80211_IFTYPE_P2P_CLIENT) | +#endif +#endif + MBIT(NL80211_IFTYPE_ADHOC) + } +}; + +static struct ieee80211_iface_combination cfg80211_iface_comb_ap_sta = { + .limits = cfg80211_ap_sta_limits, + .num_different_channels = 1, + .n_limits = ARRAY_SIZE(cfg80211_ap_sta_limits), + .max_interfaces = 4, + .beacon_int_infra_match = MTRUE, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) + .radar_detect_widths = MBIT(NL80211_CHAN_WIDTH_20_NOHT) + | MBIT(NL80211_CHAN_WIDTH_20), +#endif +}; +#endif + +extern moal_handle *m_handle[]; +extern int hw_test; +extern int ps_mode; +/** Region alpha2 string */ +char *reg_alpha2; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) +int country_ie_ignore = 0; +int beacon_hints = 0; +#endif + +#if defined(WIFI_DIRECT_SUPPORT) +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION +extern int p2p_enh; +#endif +#endif + +int cfg80211_drcs = 0; + +#ifdef CONFIG_PM +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) +static const struct wiphy_wowlan_support wowlan_support = { + .flags = WIPHY_WOWLAN_ANY | WIPHY_WOWLAN_MAGIC_PKT, + .n_patterns = MAX_NUM_FILTERS, + .pattern_min_len = 1, + .pattern_max_len = WOWLAN_MAX_PATTERN_LEN, + .max_pkt_offset = WOWLAN_MAX_OFFSET_LEN, +}; + +static const struct wiphy_wowlan_support wowlan_support_with_gtk = { + .flags = WIPHY_WOWLAN_ANY | WIPHY_WOWLAN_MAGIC_PKT + | WIPHY_WOWLAN_SUPPORTS_GTK_REKEY | + WIPHY_WOWLAN_GTK_REKEY_FAILURE, + .n_patterns = MAX_NUM_FILTERS, + .pattern_min_len = 1, + .pattern_max_len = WOWLAN_MAX_PATTERN_LEN, + .max_pkt_offset = WOWLAN_MAX_OFFSET_LEN, +}; +#endif +#endif + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) +static const struct wiphy_coalesce_support coalesce_support = { + .n_rules = COALESCE_MAX_RULES, + .max_delay = MAX_COALESCING_DELAY, + .n_patterns = COALESCE_MAX_FILTERS, + .pattern_min_len = 1, + .pattern_max_len = MAX_PATTERN_LEN, + .max_pkt_offset = MAX_OFFSET_LEN, +}; +#endif + +/******************************************************** + Global Variables +********************************************************/ + +/******************************************************** + Local Functions +********************************************************/ +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) +static int +woal_cfg80211_set_monitor_channel(struct wiphy *wiphy, + struct cfg80211_chan_def *chandef) +{ + moal_handle *handle = (moal_handle *)woal_get_wiphy_priv(wiphy); + moal_private *priv = + (moal_private *)woal_get_priv(handle, MLAN_BSS_ROLE_STA); + netmon_band_chan_cfg band_chan_cfg; + t_u32 bandwidth = 0; + int ret = -EFAULT; + + ENTER(); + + if (handle->mon_if) { + if (cfg80211_chandef_identical + (&handle->mon_if->chandef, chandef)) { + ret = 0; + goto done; + } + + memset(&band_chan_cfg, 0x00, sizeof(band_chan_cfg)); + /* Set channel */ + band_chan_cfg.channel = + ieee80211_frequency_to_channel(chandef->chan-> + center_freq); + /* Set band */ + if (chandef->chan->band == IEEE80211_BAND_2GHZ) + band_chan_cfg.band |= (BAND_B | BAND_G); + if (chandef->chan->band == IEEE80211_BAND_5GHZ) + band_chan_cfg.band |= BAND_A; + if (chandef->chan->band == IEEE80211_BAND_2GHZ) + band_chan_cfg.band |= BAND_GN; + if (chandef->chan->band == IEEE80211_BAND_5GHZ) + band_chan_cfg.band |= BAND_AN; + /* Set bandwidth */ + if (chandef->width == NL80211_CHAN_WIDTH_20) + bandwidth = CHANNEL_BW_20MHZ; + else if (chandef->width == NL80211_CHAN_WIDTH_40) + bandwidth = + chandef->center_freq1 > + chandef->chan-> + center_freq ? CHANNEL_BW_40MHZ_ABOVE : + CHANNEL_BW_40MHZ_BELOW; + band_chan_cfg.chan_bandwidth = bandwidth; + + if (MLAN_STATUS_SUCCESS != + woal_set_net_monitor(priv, MOAL_IOCTL_WAIT, + CHANNEL_SPEC_SNIFFER_MODE, 0x7, + &band_chan_cfg)) { + PRINTM(MERROR, "%s: woal_set_net_monitor fail\n", + __func__); + ret = -EFAULT; + goto done; + } + + memcpy(&handle->mon_if->band_chan_cfg, &band_chan_cfg, + sizeof(handle->mon_if->band_chan_cfg)); + handle->mon_if->chandef = *chandef; + + if (handle->mon_if->chandef.chan) + PRINTM(MINFO, + "set_monitor_channel+++ chan[band=%d center_freq=%d hw_value=%d] width=%d center_freq1=%d center_freq2=%d\n", + handle->mon_if->chandef.chan->band, + handle->mon_if->chandef.chan->center_freq, + handle->mon_if->chandef.chan->hw_value, + handle->mon_if->chandef.width, + handle->mon_if->chandef.center_freq1, + handle->mon_if->chandef.center_freq2); + PRINTM(MINFO, + "set_monitor_channel+++ band=%x channel=%d bandwidth=%d\n", + handle->mon_if->band_chan_cfg.band, + handle->mon_if->band_chan_cfg.channel, + handle->mon_if->band_chan_cfg.chan_bandwidth); + ret = 0; + } + +done: + LEAVE(); + return ret; +} +#endif + +/** + * @brief This function check cfg80211 special region code. + * + * @param region_string Region string + * + * @return MTRUE/MFALSE + */ +t_u8 +is_cfg80211_special_region_code(char *region_string) +{ + t_u8 i; + region_code_t cfg80211_special_region_code[] = { + {"00 "}, {"99 "}, {"98 "}, {"97 "} + }; + + for (i = 0; i < COUNTRY_CODE_LEN && region_string[i]; i++) + region_string[i] = toupper(region_string[i]); + + for (i = 0; i < ARRAY_SIZE(cfg80211_special_region_code); i++) { + if (!memcmp(region_string, + cfg80211_special_region_code[i].region, + COUNTRY_CODE_LEN)) { + PRINTM(MIOCTL, "special region code=%s\n", + region_string); + return MTRUE; + } + } + return MFALSE; +} + +/** + * @brief Get the encryption mode from cipher + * + * @param cipher Cipher cuite + * @param wpa_enabled WPA enable or disable + * + * @return MLAN_ENCRYPTION_MODE_* + */ +static int +woal_cfg80211_get_encryption_mode(t_u32 cipher, int *wpa_enabled) +{ + int encrypt_mode; + + ENTER(); + + *wpa_enabled = 0; + switch (cipher) { + case MW_AUTH_CIPHER_NONE: + encrypt_mode = MLAN_ENCRYPTION_MODE_NONE; + break; + case WLAN_CIPHER_SUITE_WEP40: + encrypt_mode = MLAN_ENCRYPTION_MODE_WEP40; + break; + case WLAN_CIPHER_SUITE_WEP104: + encrypt_mode = MLAN_ENCRYPTION_MODE_WEP104; + break; + case WLAN_CIPHER_SUITE_TKIP: + encrypt_mode = MLAN_ENCRYPTION_MODE_TKIP; + *wpa_enabled = 1; + break; + case WLAN_CIPHER_SUITE_CCMP: + encrypt_mode = MLAN_ENCRYPTION_MODE_CCMP; + *wpa_enabled = 1; + break; + default: + encrypt_mode = -1; + } + + LEAVE(); + return encrypt_mode; +} + +/** + * @brief get associate failure status code + * + * @param priv Pointer to the moal_private driver data struct + * + * @return IEEE status code + */ +static int +woal_get_assoc_status(moal_private *priv) +{ + int ret = WLAN_STATUS_UNSPECIFIED_FAILURE; + t_u16 status = (t_u16)(priv->assoc_status & 0xffff); + t_u16 cap = (t_u16)(priv->assoc_status >> 16); + + switch (cap) { + case 0xfffd: + case 0xfffe: + ret = status; + break; + case 0xfffc: + ret = WLAN_STATUS_AUTH_TIMEOUT; + break; + default: + break; + } + PRINTM(MCMND, "Assoc fail: status=%d, cap=0x%x, IEEE status=%d\n", + status, cap, ret); + return ret; +} + +/** + * @brief Check the pairwise or group cipher for + * WEP enabled or not + * + * @param cipher MLAN Cipher cuite + * + * @return 1 -- enable or 0 -- disable + */ +static int +woal_cfg80211_is_alg_wep(t_u32 cipher) +{ + int alg = 0; + ENTER(); + + if (cipher == MLAN_ENCRYPTION_MODE_WEP40 || + cipher == MLAN_ENCRYPTION_MODE_WEP104) + alg = 1; + + LEAVE(); + return alg; +} + +/** + * @brief Convert NL802.11 channel type into driver channel type + * + * The mapping is as follows - + * NL80211_CHAN_NO_HT -> CHANNEL_BW_20MHZ + * NL80211_CHAN_HT20 -> CHANNEL_BW_20MHZ + * NL80211_CHAN_HT40PLUS -> CHANNEL_BW_40MHZ_ABOVE + * NL80211_CHAN_HT40MINUS -> CHANNEL_BW_40MHZ_BELOW + * Others -> CHANNEL_BW_20MHZ + * + * @param channel_type Channel type + * + * @return Driver channel type + */ +static int +woal_cfg80211_channel_type_to_channel(enum nl80211_channel_type channel_type) +{ + int channel; + + ENTER(); + + switch (channel_type) { + case NL80211_CHAN_NO_HT: + case NL80211_CHAN_HT20: + channel = CHANNEL_BW_20MHZ; + break; + case NL80211_CHAN_HT40PLUS: + channel = CHANNEL_BW_40MHZ_ABOVE; + break; + case NL80211_CHAN_HT40MINUS: + channel = CHANNEL_BW_40MHZ_BELOW; + break; + default: + channel = CHANNEL_BW_20MHZ; + } + LEAVE(); + return channel; +} + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) +/** + * @brief get nl80211_channel_type from cfg80211_chan_def + * + * @param chan_def struct cfg80211_chan_def + * + * @return nl80211_channel_type type + */ +static enum nl80211_channel_type +woal_channel_to_nl80211_channel_type(struct cfg80211_chan_def *chan_def) +{ + enum nl80211_channel_type channel_type = 0; + + ENTER(); + + switch (chan_def->width) { + case NL80211_CHAN_WIDTH_20: + /** Channel width 20MHz**/ + channel_type = NL80211_CHAN_HT20; + break; + case NL80211_CHAN_WIDTH_40: + /** Channel width 40MHz**/ + if (chan_def->center_freq1 < chan_def->chan->center_freq) + channel_type = NL80211_CHAN_HT40MINUS; + else if (chan_def->center_freq1 > chan_def->chan->center_freq) + channel_type = NL80211_CHAN_HT40PLUS; + break; + default: + channel_type = NL80211_CHAN_HT20; + break; + } + LEAVE(); + return channel_type; +} +#endif + +/** + * @brief Convert NL80211 interface type to MLAN_BSS_MODE_* + * + * @param iftype Interface type of NL80211 + * + * @return Driver bss mode + */ +static t_u32 +woal_nl80211_iftype_to_mode(enum nl80211_iftype iftype) +{ + switch (iftype) { + case NL80211_IFTYPE_ADHOC: + return MLAN_BSS_MODE_IBSS; + case NL80211_IFTYPE_STATION: + return MLAN_BSS_MODE_INFRA; + case NL80211_IFTYPE_UNSPECIFIED: + default: + return MLAN_BSS_MODE_AUTO; + } +} + +/** + * @brief Control WPS Session Enable/Disable + * + * @param priv Pointer to the moal_private driver data struct + * @param enable enable/disable flag + * + * @return 0 --success, otherwise fail + */ +static int +woal_wps_cfg(moal_private *priv, int enable) +{ + int ret = 0; + mlan_ds_wps_cfg *pwps = NULL; + mlan_ioctl_req *req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + PRINTM(MINFO, "WOAL_WPS_SESSION\n"); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_wps_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + pwps = (mlan_ds_wps_cfg *)req->pbuf; + req->req_id = MLAN_IOCTL_WPS_CFG; + req->action = MLAN_ACT_SET; + pwps->sub_command = MLAN_OID_WPS_CFG_SESSION; + if (enable) + pwps->param.wps_session = MLAN_WPS_CFG_SESSION_START; + else + pwps->param.wps_session = MLAN_WPS_CFG_SESSION_END; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief configure ASSOC IE + * + * @param priv A pointer to moal private structure + * @param ie A pointer to ie data + * @param ie_len The length of ie data + * @param wait_option wait option + * + * @return 0 -- success, otherwise fail + */ +static int +woal_cfg80211_assoc_ies_cfg(moal_private *priv, t_u8 *ie, int ie_len, + t_u8 wait_option) +{ + int bytes_left = ie_len; + t_u8 *pcurrent_ptr = ie; + int total_ie_len; + t_u8 element_len; + int ret = MLAN_STATUS_SUCCESS; + IEEEtypes_ElementId_e element_id; + IEEEtypes_VendorSpecific_t *pvendor_ie; + t_u8 wps_oui[] = { 0x00, 0x50, 0xf2, 0x04 }; + + while (bytes_left >= 2) { + element_id = (IEEEtypes_ElementId_e)(*((t_u8 *)pcurrent_ptr)); + element_len = *((t_u8 *)pcurrent_ptr + 1); + total_ie_len = element_len + sizeof(IEEEtypes_Header_t); + if (bytes_left < total_ie_len) { + PRINTM(MERROR, + "InterpretIE: Error in processing IE, bytes left < IE length\n"); + bytes_left = 0; + continue; + } + switch (element_id) { + case RSN_IE: + if (MLAN_STATUS_SUCCESS != + woal_set_get_gen_ie(priv, MLAN_ACT_SET, + pcurrent_ptr, &total_ie_len, + wait_option)) { + PRINTM(MERROR, "Fail to set RSN IE\n"); + ret = -EFAULT; + goto done; + } + PRINTM(MIOCTL, "Set RSN IE\n"); + break; + case VENDOR_SPECIFIC_221: + pvendor_ie = (IEEEtypes_VendorSpecific_t *)pcurrent_ptr; + if (!memcmp + (pvendor_ie->vend_hdr.oui, wps_oui, + sizeof(pvendor_ie->vend_hdr.oui)) && + (pvendor_ie->vend_hdr.oui_type == wps_oui[3])) { + PRINTM(MIOCTL, "Enable WPS session\n"); + woal_wps_cfg(priv, MTRUE); + } + if (MLAN_STATUS_SUCCESS != + woal_set_get_gen_ie(priv, MLAN_ACT_SET, + pcurrent_ptr, &total_ie_len, + wait_option)) { + PRINTM(MERROR, + "Fail to Set VENDOR SPECIFIC IE\n"); + ret = -EFAULT; + goto done; + } + PRINTM(MIOCTL, + "Set VENDOR SPECIFIC IE, OUI: %02x:%02x:%02x:%02x\n", + pvendor_ie->vend_hdr.oui[0], + pvendor_ie->vend_hdr.oui[1], + pvendor_ie->vend_hdr.oui[2], + pvendor_ie->vend_hdr.oui_type); + break; + case MOBILITY_DOMAIN: + break; + case FAST_BSS_TRANSITION: + if (MLAN_STATUS_SUCCESS != + woal_set_get_gen_ie(priv, MLAN_ACT_SET, + pcurrent_ptr, &total_ie_len, + wait_option)) { + PRINTM(MERROR, + "Fail to set" + "FAST_BSS_TRANSITION IE\n"); + ret = -EFAULT; + goto done; + } + PRINTM(MIOCTL, "Set FAST_BSS_TRANSITION IE\n"); + break; + case RIC: + if (MLAN_STATUS_SUCCESS != + woal_set_get_gen_ie(priv, MLAN_ACT_SET, + pcurrent_ptr, &total_ie_len, + wait_option)) { + PRINTM(MERROR, + "Fail to set" + "RESOURCE INFORMATION CONTAINER IE\n"); + ret = -EFAULT; + goto done; + } + PRINTM(MIOCTL, + "Set RESOURCE INFORMATION CONTAINER IE\n"); + break; + case EXT_CAPABILITY: + if (MLAN_STATUS_SUCCESS != + woal_set_get_gen_ie(priv, MLAN_ACT_SET, + pcurrent_ptr, &total_ie_len, + wait_option)) { + PRINTM(MERROR, + "Fail to set Extended Capabilites IE\n"); + ret = -EFAULT; + goto done; + } + PRINTM(MIOCTL, "Set Extended Capabilities IE\n"); + break; + default: + if (MLAN_STATUS_SUCCESS != + woal_set_get_gen_ie(priv, MLAN_ACT_SET, + pcurrent_ptr, &total_ie_len, + wait_option)) { + PRINTM(MERROR, "Fail to set GEN IE\n"); + ret = -EFAULT; + goto done; + } + PRINTM(MIOCTL, "Set GEN IE\n"); + break; + } + pcurrent_ptr += element_len + 2; + /* Need to account for IE ID and IE Len */ + bytes_left -= (element_len + 2); + } +done: + return ret; +} + +/** + * @brief Send domain info command to FW + * + * @param priv A pointer to moal_private structure + * @param wait_option wait option + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status +woal_send_domain_info_cmd_fw(moal_private *priv, t_u8 wait_option) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + enum ieee80211_band band; + struct ieee80211_supported_band *sband = NULL; + struct ieee80211_channel *channel = NULL; + t_u8 no_of_sub_band = 0; + t_u8 no_of_parsed_chan = 0; + t_u8 first_chan = 0, next_chan = 0, max_pwr = 0; + t_u8 i, flag = 0; + mlan_ds_11d_cfg *cfg_11d = NULL; + mlan_ioctl_req *req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (!priv->wdev || !priv->wdev->wiphy) { + PRINTM(MERROR, "No wdev or wiphy in priv\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + band = priv->phandle->band; + if (!priv->wdev->wiphy->bands[band]) { + PRINTM(MERROR, "11D: setting domain info in FW failed band=%d", + band); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + if (MTRUE == + is_cfg80211_special_region_code(priv->phandle->country_code)) { + PRINTM(MIOCTL, + "skip region code config, cfg80211 special region code: %s\n", + priv->phandle->country_code); + goto done; + } + PRINTM(MIOCTL, "Send domain info: country=%c%c band=%d\n", + priv->phandle->country_code[0], priv->phandle->country_code[1], + band); + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11d_cfg)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + cfg_11d = (mlan_ds_11d_cfg *)req->pbuf; + cfg_11d->sub_command = MLAN_OID_11D_DOMAIN_INFO; + req->req_id = MLAN_IOCTL_11D_CFG; + req->action = MLAN_ACT_SET; + + /* Set country code */ + cfg_11d->param.domain_info.country_code[0] = + priv->phandle->country_code[0]; + cfg_11d->param.domain_info.country_code[1] = + priv->phandle->country_code[1]; + cfg_11d->param.domain_info.country_code[2] = ' '; + cfg_11d->param.domain_info.band = band; + + sband = priv->wdev->wiphy->bands[band]; + for (i = 0; (i < sband->n_channels) && + (no_of_sub_band < MRVDRV_MAX_SUBBAND_802_11D); i++) { + channel = &sband->channels[i]; + if (channel->flags & IEEE80211_CHAN_DISABLED) + continue; + + if (!flag) { + flag = 1; + next_chan = first_chan = (t_u32)channel->hw_value; + max_pwr = channel->max_power; + no_of_parsed_chan = 1; + continue; + } + + if (channel->hw_value == next_chan + 1 && + channel->max_power == max_pwr) { + next_chan++; + no_of_parsed_chan++; + } else { + cfg_11d->param.domain_info.sub_band[no_of_sub_band] + .first_chan = first_chan; + cfg_11d->param.domain_info.sub_band[no_of_sub_band] + .no_of_chan = no_of_parsed_chan; + cfg_11d->param.domain_info.sub_band[no_of_sub_band] + .max_tx_pwr = max_pwr; + no_of_sub_band++; + next_chan = first_chan = (t_u32)channel->hw_value; + max_pwr = channel->max_power; + no_of_parsed_chan = 1; + } + } + + if (flag && (no_of_sub_band < MRVDRV_MAX_SUBBAND_802_11D)) { + cfg_11d->param.domain_info.sub_band[no_of_sub_band] + .first_chan = first_chan; + cfg_11d->param.domain_info.sub_band[no_of_sub_band] + .no_of_chan = no_of_parsed_chan; + cfg_11d->param.domain_info.sub_band[no_of_sub_band] + .max_tx_pwr = max_pwr; + no_of_sub_band++; + } + cfg_11d->param.domain_info.no_of_sub_band = no_of_sub_band; + + /* Send domain info command to FW */ + status = woal_request_ioctl(priv, req, wait_option); + if (status != MLAN_STATUS_SUCCESS) { + ret = MLAN_STATUS_FAILURE; + PRINTM(MERROR, "11D: Error setting domain info in FW\n"); + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Request the driver to change the channel and + * change domain info according to that channel + * + * @param priv A pointer to moal_private structure + * @param chan A pointer to ieee80211_channel structure + * @param channel_type Channel type of nl80211_channel_type + * @param wait_option wait option + * + * @return 0 -- success, otherwise fail + */ +int +woal_set_rf_channel(moal_private *priv, + struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type, t_u8 wait_option) +{ + int ret = 0; + t_u32 mode, config_bands = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_radio_cfg *radio_cfg = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + int chan_width = 0; + + ENTER(); + + if (!chan) { + LEAVE(); + return -EINVAL; + } + mode = woal_nl80211_iftype_to_mode(priv->wdev->iftype); + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_radio_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + radio_cfg = (mlan_ds_radio_cfg *)req->pbuf; + radio_cfg->sub_command = MLAN_OID_BAND_CFG; + req->req_id = MLAN_IOCTL_RADIO_CFG; + /* Get config_bands, adhoc_start_band and adhoc_channel values from MLAN */ + req->action = MLAN_ACT_GET; + status = woal_request_ioctl(priv, req, wait_option); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + req->action = MLAN_ACT_SET; + priv->phandle->band = chan->band; + chan_width = woal_cfg80211_channel_type_to_channel(channel_type); + /* Set appropriate bands */ + if (chan->band == IEEE80211_BAND_2GHZ) + config_bands = BAND_B | BAND_G | BAND_GN; + else { + config_bands = BAND_AN | BAND_A; + } + if (mode == MLAN_BSS_MODE_IBSS) { + radio_cfg->param.band_cfg.adhoc_start_band = config_bands; + radio_cfg->param.band_cfg.adhoc_channel = + ieee80211_frequency_to_channel(chan->center_freq); + } + /* Set channel offset */ + radio_cfg->param.band_cfg.adhoc_chan_bandwidth = chan_width; + + status = woal_request_ioctl(priv, req, wait_option); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + woal_send_domain_info_cmd_fw(priv, wait_option); + + PRINTM(MINFO, + "Setting band %d, channel bandwidth %d and mode = %d channel=%d\n", + config_bands, radio_cfg->param.band_cfg.adhoc_chan_bandwidth, + mode, ieee80211_frequency_to_channel(chan->center_freq)); + + if (MLAN_STATUS_SUCCESS != + woal_change_adhoc_chan(priv, + ieee80211_frequency_to_channel(chan-> + center_freq), + wait_option)) { + ret = -EFAULT; + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set ewpa mode + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * @param ssid_bssid A pointer to mlan_ssid_bssid structure + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail + */ +mlan_status +woal_set_ewpa_mode(moal_private *priv, t_u8 wait_option, + mlan_ssid_bssid *ssid_bssid) +{ + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_sec_cfg *sec = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto error; + } + /* Fill request buffer */ + sec = (mlan_ds_sec_cfg *)req->pbuf; + sec->sub_command = MLAN_OID_SEC_CFG_PASSPHRASE; + req->req_id = MLAN_IOCTL_SEC_CFG; + req->action = MLAN_ACT_GET; + + /* Try Get All */ + memset(&sec->param.passphrase, 0, sizeof(mlan_ds_passphrase)); + memcpy(&sec->param.passphrase.ssid, &ssid_bssid->ssid, + sizeof(sec->param.passphrase.ssid)); + memcpy(&sec->param.passphrase.bssid, &ssid_bssid->bssid, + MLAN_MAC_ADDR_LENGTH); + sec->param.passphrase.psk_type = MLAN_PSK_QUERY; + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, wait_option); + if (status != MLAN_STATUS_SUCCESS) + goto error; + sec->param.ewpa_enabled = MFALSE; + if (sec->param.passphrase.psk_type == MLAN_PSK_PASSPHRASE) { + if (sec->param.passphrase.psk.passphrase.passphrase_len > 0) + sec->param.ewpa_enabled = MTRUE; + } else if (sec->param.passphrase.psk_type == MLAN_PSK_PMK) + sec->param.ewpa_enabled = MTRUE; + + sec->sub_command = MLAN_OID_SEC_CFG_EWPA_ENABLED; + req->action = MLAN_ACT_SET; + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, wait_option); + +error: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return status; +} + +/** + * @brief Set encryption mode and enable WPA + * + * @param priv A pointer to moal_private structure + * @param encrypt_mode Encryption mode + * @param wpa_enabled WPA enable or not + * @param wait_option wait option + * + * @return 0 -- success, otherwise fail + */ +static int +woal_cfg80211_set_auth(moal_private *priv, int encrypt_mode, + int wpa_enabled, t_u8 wait_option) +{ + int ret = 0; + + ENTER(); + + if (MLAN_STATUS_SUCCESS != + woal_set_encrypt_mode(priv, wait_option, encrypt_mode)) + ret = -EFAULT; + + if (wpa_enabled) { + if (MLAN_STATUS_SUCCESS != + woal_set_wpa_enable(priv, wait_option, 1)) + ret = -EFAULT; + } + + LEAVE(); + return ret; +} + +/** + * @brief Informs the CFG802.11 subsystem of a new BSS connection. + * + * The following information are sent to the CFG802.11 subsystem + * to register the new BSS connection. If we do not register the new BSS, + * a kernel panic will result. + * - MAC address + * - Capabilities + * - Beacon period + * - RSSI value + * - Channel + * - Supported rates IE + * - Extended capabilities IE + * - DS parameter set IE + * - HT Capability IE + * - Vendor Specific IE (221) + * - WPA IE + * - RSN IE + * + * @param priv A pointer to moal_private structure + * @param ssid_bssid A pointer to A pointer to mlan_ssid_bssid structure + * @param wait_option wait_option + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +woal_inform_bss_from_scan_result(moal_private *priv, + mlan_ssid_bssid *ssid_bssid, t_u8 wait_option) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + struct ieee80211_channel *chan; + mlan_scan_resp scan_resp; + BSSDescriptor_t *scan_table; + t_u64 ts = 0; + u16 cap_info = 0; + int i = 0; + struct cfg80211_bss *pub = NULL; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + struct timespec tstamp; +#endif + ENTER(); + if (!priv->wdev || !priv->wdev->wiphy) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + memset(&scan_resp, 0, sizeof(scan_resp)); + if (MLAN_STATUS_SUCCESS != woal_get_scan_table(priv, + wait_option, + &scan_resp)) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + if (scan_resp.num_in_scan_table) { + scan_table = (BSSDescriptor_t *)scan_resp.pscan_table; + for (i = 0; i < scan_resp.num_in_scan_table; i++) { + if (ssid_bssid) { + /* Inform specific BSS only */ + if (memcmp + (ssid_bssid->ssid.ssid, + scan_table[i].ssid.ssid, + ssid_bssid->ssid.ssid_len) || + memcmp(ssid_bssid->bssid, + scan_table[i].mac_address, ETH_ALEN)) + continue; + } + if (!scan_table[i].freq) { + scan_table[i].freq = + ieee80211_channel_to_frequency((int) + scan_table + [i]. + channel +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + , + woal_band_cfg_to_ieee_band + (scan_table + [i]. + bss_band) +#endif + ); + } + chan = ieee80211_get_channel(priv->wdev->wiphy, + scan_table[i].freq); + if (!chan) { + PRINTM(MCMND, + "Fail to get chan with freq: channel=%d freq=%d\n", + (int)scan_table[i].channel, + (int)scan_table[i].freq); + continue; + } +#if defined(WIFI_DIRECT_SUPPORT) +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION + if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT && + !ssid_bssid) { + if (!strncmp + (scan_table[i].ssid.ssid, "DIRECT-", + strlen("DIRECT-"))) { + PRINTM(MCMND, + "wlan: P2P device " MACSTR + " found, channel=%d\n", + MAC2STR(scan_table[i]. + mac_address), + (int)chan->hw_value); + } + } +#endif +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + /** Andorid's Location service is expecting timestamp to be + * local time (in microsecond) since boot; + * and not the TSF found in the beacon. */ + get_monotonic_boottime(&tstamp); + ts = (t_u64)tstamp.tv_sec * 1000000 + + tstamp.tv_nsec / 1000; +#else + memcpy(&ts, scan_table[i].time_stamp, sizeof(ts)); +#endif + memcpy(&cap_info, &scan_table[i].cap_info, + sizeof(cap_info)); + pub = cfg80211_inform_bss(priv->wdev->wiphy, chan, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 18, 0) + CFG80211_BSS_FTYPE_UNKNOWN, +#endif + scan_table[i].mac_address, + ts, cap_info, + scan_table[i].beacon_period, + scan_table[i].pbeacon_buf + + WLAN_802_11_FIXED_IE_SIZE, + scan_table[i]. + beacon_buf_size - + WLAN_802_11_FIXED_IE_SIZE, + -RSSI_DBM_TO_MDM(scan_table + [i].rssi), + GFP_KERNEL); + if (pub) { +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 8, 0) + pub->len_information_elements = + pub->len_beacon_ies; +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 9, 0) + cfg80211_put_bss(priv->wdev->wiphy, pub); +#else + cfg80211_put_bss(pub); +#endif + } + } + } +done: + LEAVE(); + return ret; +} + +/** + * @brief Informs the CFG802.11 subsystem of a new IBSS connection. + * + * The following information are sent to the CFG802.11 subsystem + * to register the new IBSS connection. If we do not register the + * new IBSS, a kernel panic will result. + * - MAC address + * - Capabilities + * - Beacon period + * - RSSI value + * - Channel + * - Supported rates IE + * - Extended capabilities IE + * - DS parameter set IE + * - HT Capability IE + * - Vendor Specific IE (221) + * - WPA IE + * - RSN IE + * + * @param priv A pointer to moal_private structure + * @param cahn A pointer to ieee80211_channel structure + * @param beacon_interval Beacon interval + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status +woal_cfg80211_inform_ibss_bss(moal_private *priv, + struct ieee80211_channel *chan, + t_u16 beacon_interval) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_bss_info bss_info; + mlan_ds_get_signal signal; + t_u8 ie_buf[MLAN_MAX_SSID_LENGTH + sizeof(IEEEtypes_Header_t)]; + int ie_len = 0; + struct cfg80211_bss *bss = NULL; + + ENTER(); + + ret = woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info); + if (ret) + goto done; + + memset(ie_buf, 0, sizeof(ie_buf)); + ie_buf[0] = WLAN_EID_SSID; + ie_buf[1] = bss_info.ssid.ssid_len; + + memcpy(&ie_buf[sizeof(IEEEtypes_Header_t)], + &bss_info.ssid.ssid, bss_info.ssid.ssid_len); + ie_len = ie_buf[1] + sizeof(IEEEtypes_Header_t); + + /* Get signal information from the firmware */ + memset(&signal, 0, sizeof(mlan_ds_get_signal)); + if (MLAN_STATUS_SUCCESS != + woal_get_signal_info(priv, MOAL_IOCTL_WAIT, &signal)) { + PRINTM(MERROR, "Error getting signal information\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + bss = cfg80211_inform_bss(priv->wdev->wiphy, chan, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 18, 0) + CFG80211_BSS_FTYPE_UNKNOWN, +#endif + bss_info.bssid, 0, WLAN_CAPABILITY_IBSS, + beacon_interval, ie_buf, ie_len, + signal.bcn_rssi_avg, GFP_KERNEL); + if (bss) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 9, 0) + cfg80211_put_bss(priv->wdev->wiphy, bss); +#else + cfg80211_put_bss(bss); +#endif +done: + LEAVE(); + return ret; +} + +/** + * @brief Process country IE before assoicate + * + * @param priv A pointer to moal_private structure + * @param bss A pointer to cfg80211_bss structure + * + * @return 0 -- success, otherwise fail + */ +static int +woal_process_country_ie(moal_private *priv, struct cfg80211_bss *bss) +{ + u8 *country_ie, country_ie_len; + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_11d_cfg *cfg_11d = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + country_ie = (u8 *)ieee80211_bss_get_ie(bss, WLAN_EID_COUNTRY); + if (!country_ie) { + PRINTM(MIOCTL, "No country IE found!\n"); + woal_send_domain_info_cmd_fw(priv, MOAL_IOCTL_WAIT); + LEAVE(); + return 0; + } + + country_ie_len = country_ie[1]; + if (country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN) { + PRINTM(MIOCTL, "Wrong Country IE length!\n"); + woal_send_domain_info_cmd_fw(priv, MOAL_IOCTL_WAIT); + LEAVE(); + return 0; + } + PRINTM(MIOCTL, "Find bss country IE: %c%c band=%d\n", country_ie[2], + country_ie[3], priv->phandle->band); + priv->phandle->country_code[0] = country_ie[2]; + priv->phandle->country_code[1] = country_ie[3]; + priv->phandle->country_code[2] = ' '; + if (MLAN_STATUS_SUCCESS != + woal_set_region_code(priv, priv->phandle->country_code)) + PRINTM(MERROR, "Set country code failed!\n"); + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11d_cfg)); + if (req == NULL) { + PRINTM(MERROR, "Fail to allocate mlan_ds_11d_cfg buffer\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + cfg_11d = (mlan_ds_11d_cfg *)req->pbuf; + cfg_11d->sub_command = MLAN_OID_11D_DOMAIN_INFO; + req->req_id = MLAN_IOCTL_11D_CFG; + req->action = MLAN_ACT_SET; + + /* Set country code */ + cfg_11d->param.domain_info.country_code[0] = + priv->phandle->country_code[0]; + cfg_11d->param.domain_info.country_code[1] = + priv->phandle->country_code[1]; + cfg_11d->param.domain_info.country_code[2] = ' '; + + /** IEEE80211_BAND_2GHZ or IEEE80211_BAND_5GHZ */ + cfg_11d->param.domain_info.band = priv->phandle->band; + + country_ie_len -= COUNTRY_CODE_LEN; + cfg_11d->param.domain_info.no_of_sub_band = + MIN(MRVDRV_MAX_SUBBAND_802_11D, + (country_ie_len / + sizeof(struct ieee80211_country_ie_triplet))); + memcpy((u8 *)cfg_11d->param.domain_info.sub_band, + &country_ie[2] + COUNTRY_CODE_LEN, + cfg_11d->param.domain_info.no_of_sub_band * + sizeof(mlan_ds_subband_set_t)); + + /* Send domain info command to FW */ + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = MLAN_STATUS_FAILURE; + PRINTM(MERROR, "11D: Error setting domain info in FW\n"); + goto done; + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Request scan based on connect parameter + * + * @param priv A pointer to moal_private structure + * @param conn_param A pointer to connect parameters + * @param wait_option wait option + * + * @return 0 -- success, otherwise fail + */ +int +woal_cfg80211_connect_scan(moal_private *priv, + struct cfg80211_connect_params *conn_param, + t_u8 wait_option) +{ + moal_handle *handle = priv->phandle; + int ret = 0; + wlan_user_scan_cfg scan_req; + enum ieee80211_band band; + struct ieee80211_supported_band *sband; + struct ieee80211_channel *ch; + int chan_idx = 0, i; + ENTER(); + if (handle->scan_pending_on_block == MTRUE) { + PRINTM(MINFO, "scan already in processing...\n"); + LEAVE(); + return ret; + } +#ifdef REASSOCIATION + if (MOAL_ACQ_SEMAPHORE_BLOCK(&handle->reassoc_sem)) { + PRINTM(MERROR, "Acquire semaphore error, woal_do_combo_scan\n"); + LEAVE(); + return -EBUSY; + } +#endif /* REASSOCIATION */ + priv->report_scan_result = MTRUE; + memset(&scan_req, 0x00, sizeof(scan_req)); + memcpy(scan_req.ssid_list[0].ssid, conn_param->ssid, + conn_param->ssid_len); + scan_req.ssid_list[0].max_len = 0; + if (conn_param->channel) { + scan_req.chan_list[0].chan_number = + conn_param->channel->hw_value; + scan_req.chan_list[0].radio_type = conn_param->channel->band; + if (conn_param->channel-> + flags & (IEEE80211_CHAN_PASSIVE_SCAN | + IEEE80211_CHAN_RADAR)) + scan_req.chan_list[0].scan_type = + MLAN_SCAN_TYPE_PASSIVE; + else + scan_req.chan_list[0].scan_type = MLAN_SCAN_TYPE_ACTIVE; + scan_req.chan_list[0].scan_time = 0; + } else { + for (band = 0; (band < IEEE80211_NUM_BANDS); band++) { + if (!priv->wdev->wiphy->bands[band]) + continue; + sband = priv->wdev->wiphy->bands[band]; + for (i = 0; (i < sband->n_channels); i++) { + ch = &sband->channels[i]; + if (ch->flags & IEEE80211_CHAN_DISABLED) + continue; + scan_req.chan_list[chan_idx].radio_type = band; + if (ch-> + flags & (IEEE80211_CHAN_PASSIVE_SCAN | + IEEE80211_CHAN_RADAR)) + scan_req.chan_list[chan_idx].scan_type = + MLAN_SCAN_TYPE_PASSIVE; + else + scan_req.chan_list[chan_idx].scan_type = + MLAN_SCAN_TYPE_ACTIVE; + scan_req.chan_list[chan_idx].chan_number = + (u32)ch->hw_value; + chan_idx++; + } + } + } + ret = woal_request_userscan(priv, wait_option, &scan_req); +#ifdef REASSOCIATION + MOAL_REL_SEMAPHORE(&handle->reassoc_sem); +#endif + LEAVE(); + return ret; + +} + +/** + * @brief Request the driver for (re)association + * + * @param priv A pointer to moal_private structure + * @param sme A pointer to connect parameters + * @param wait_option wait option + * + * @return 0 -- success, otherwise fail + */ +int +woal_cfg80211_assoc(moal_private *priv, void *sme, t_u8 wait_option) +{ + struct cfg80211_ibss_params *ibss_param = NULL; + struct cfg80211_connect_params *conn_param = NULL; + mlan_802_11_ssid req_ssid; + mlan_ssid_bssid ssid_bssid; + mlan_ioctl_req *req = NULL; + int ret = 0; + t_u32 auth_type = 0, mode; + int wpa_enabled = 0; + int group_enc_mode = 0, pairwise_enc_mode = 0; + int alg_is_wep = 0; + + t_u8 *ssid, ssid_len = 0, *bssid; + t_u8 *ie = NULL; + int ie_len = 0; + enum nl80211_channel_type chan_type = 0; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + struct cfg80211_chan_def *chan_def = NULL; +#endif + struct ieee80211_channel *channel = NULL; + t_u16 beacon_interval = 0; + bool privacy; + struct cfg80211_bss *bss = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (!sme) { + LEAVE(); + return -EFAULT; + } + + mode = woal_nl80211_iftype_to_mode(priv->wdev->iftype); + + if (mode == MLAN_BSS_MODE_IBSS) { + ibss_param = (struct cfg80211_ibss_params *)sme; + ssid = (t_u8 *)ibss_param->ssid; + ssid_len = ibss_param->ssid_len; + bssid = (t_u8 *)ibss_param->bssid; +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 8, 0) + channel = ibss_param->channel; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 3, 0) + chan_type = ibss_param->channel_type; +#else + chan_type = 0; +#endif +#else + chan_def = &ibss_param->chandef; + channel = ibss_param->chandef.chan; +#endif + if (channel) + priv->phandle->band = channel->band; + if (ibss_param->ie_len) + ie = (t_u8 *)ibss_param->ie; + ie_len = ibss_param->ie_len; + beacon_interval = ibss_param->beacon_interval; + privacy = ibss_param->privacy; + + } else { + conn_param = (struct cfg80211_connect_params *)sme; + ssid = (t_u8 *)conn_param->ssid; + ssid_len = conn_param->ssid_len; + bssid = (t_u8 *)conn_param->bssid; + channel = conn_param->channel; + if (channel) + priv->phandle->band = channel->band; + if (conn_param->ie_len) + ie = (t_u8 *)conn_param->ie; + ie_len = conn_param->ie_len; + privacy = conn_param->privacy; + bss = cfg80211_get_bss(priv->wdev->wiphy, channel, bssid, ssid, + ssid_len, WLAN_CAPABILITY_ESS, + WLAN_CAPABILITY_ESS); + if (bss) { + if (!reg_alpha2 || + strncmp(reg_alpha2, "99", strlen("99"))) + woal_process_country_ie(priv, bss); +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 9, 0) + cfg80211_put_bss(priv->wdev->wiphy, bss); +#else + cfg80211_put_bss(bss); +#endif + } else + woal_send_domain_info_cmd_fw(priv, wait_option); +#ifdef STA_WEXT + if (IS_STA_WEXT(cfg80211_wext)) { + switch (conn_param->crypto.wpa_versions) { + case NL80211_WPA_VERSION_2: + priv->wpa_version = IW_AUTH_WPA_VERSION_WPA2; + break; + case NL80211_WPA_VERSION_1: + priv->wpa_version = IW_AUTH_WPA_VERSION_WPA; + break; + default: + priv->wpa_version = 0; + break; + } + if (conn_param->crypto.n_akm_suites) { + switch (conn_param->crypto.akm_suites[0]) { + case WLAN_AKM_SUITE_PSK: + priv->key_mgmt = IW_AUTH_KEY_MGMT_PSK; + break; + case WLAN_AKM_SUITE_8021X: + priv->key_mgmt = + IW_AUTH_KEY_MGMT_802_1X; + break; + default: + priv->key_mgmt = 0; + break; + } + } + } +#endif + } + + memset(&req_ssid, 0, sizeof(mlan_802_11_ssid)); + memset(&ssid_bssid, 0, sizeof(mlan_ssid_bssid)); + + req_ssid.ssid_len = ssid_len; + if (ssid_len > MW_ESSID_MAX_SIZE) { + PRINTM(MERROR, "Invalid SSID - aborting\n"); + ret = -EINVAL; + goto done; + } + + memcpy(req_ssid.ssid, ssid, ssid_len); + if (!req_ssid.ssid_len || req_ssid.ssid[0] < 0x20) { + PRINTM(MERROR, "Invalid SSID - aborting\n"); + ret = -EINVAL; + goto done; + } + + if ((mode == MLAN_BSS_MODE_IBSS) && channel) { +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + chan_type = woal_channel_to_nl80211_channel_type(chan_def); +#endif + if (MLAN_STATUS_SUCCESS != woal_set_rf_channel(priv, + channel, + chan_type, + wait_option)) { + ret = -EFAULT; + goto done; + } + } + + if (MLAN_STATUS_SUCCESS != + woal_set_ewpa_mode(priv, wait_option, &ssid_bssid)) { + ret = -EFAULT; + goto done; + } + + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_set_key(priv, 0, 0, NULL, 0, NULL, 0, + KEY_INDEX_CLEAR_ALL, NULL, 1, wait_option)) { + /* Disable keys and clear all previous security settings */ + ret = -EFAULT; + goto done; + } +#ifdef STA_CFG80211 + if (IS_STA_CFG80211(cfg80211_wext)) { + /** Check if current roaming support OKC offload roaming */ + if (conn_param && conn_param->crypto.n_akm_suites && + conn_param->crypto.akm_suites[0] == WLAN_AKM_SUITE_8021X) { + if (priv->okc_roaming_ie && priv->okc_ie_len) { + ie = priv->okc_roaming_ie; + ie_len = priv->okc_ie_len; + } + } + } +#endif + + if ((priv->ft_pre_connect || + (conn_param && conn_param->auth_type == NL80211_AUTHTYPE_FT)) + && priv->ft_ie_len) { + ie = priv->ft_ie; + ie_len = priv->ft_ie_len; + priv->ft_ie_len = 0; + } + if (ie && ie_len) { /* Set the IE */ + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_assoc_ies_cfg(priv, ie, ie_len, + wait_option)) { + ret = -EFAULT; + goto done; + } + } + + if (conn_param && mode != MLAN_BSS_MODE_IBSS) { + /* These parameters are only for managed mode */ + if (conn_param->auth_type == NL80211_AUTHTYPE_OPEN_SYSTEM) + auth_type = MLAN_AUTH_MODE_OPEN; + else if (conn_param->auth_type == NL80211_AUTHTYPE_SHARED_KEY) + auth_type = MLAN_AUTH_MODE_SHARED; + else if (conn_param->auth_type == NL80211_AUTHTYPE_NETWORK_EAP) + auth_type = MLAN_AUTH_MODE_NETWORKEAP; + else if (conn_param->auth_type == NL80211_AUTHTYPE_FT) + auth_type = MLAN_AUTH_MODE_FT; + else + auth_type = MLAN_AUTH_MODE_AUTO; + if (priv->ft_pre_connect) + auth_type = MLAN_AUTH_MODE_FT; + if (MLAN_STATUS_SUCCESS != + woal_set_auth_mode(priv, wait_option, auth_type)) { + ret = -EFAULT; + goto done; + } + + if (conn_param->crypto.n_ciphers_pairwise) { + pairwise_enc_mode = + woal_cfg80211_get_encryption_mode(conn_param-> + crypto.ciphers_pairwise + [0], + &wpa_enabled); + ret = woal_cfg80211_set_auth(priv, pairwise_enc_mode, + wpa_enabled, wait_option); + if (ret) + goto done; + } + + if (conn_param->crypto.cipher_group) { + group_enc_mode = + woal_cfg80211_get_encryption_mode(conn_param-> + crypto.cipher_group, + &wpa_enabled); + ret = woal_cfg80211_set_auth(priv, group_enc_mode, + wpa_enabled, wait_option); + if (ret) + goto done; + } + + if (conn_param->key) { + alg_is_wep = + woal_cfg80211_is_alg_wep(pairwise_enc_mode) | + woal_cfg80211_is_alg_wep(group_enc_mode); + if (alg_is_wep) { + PRINTM(MINFO, + "Setting wep encryption with key len %d\n", + conn_param->key_len); + /* Set the WEP key */ + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_set_wep_keys(priv, + conn_param->key, + conn_param-> + key_len, + conn_param-> + key_idx, + wait_option)) { + ret = -EFAULT; + goto done; + } + /* Enable the WEP key by key index */ + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_set_wep_keys(priv, NULL, 0, + conn_param-> + key_idx, + wait_option)) { + ret = -EFAULT; + goto done; + } + } + } + } + + if (mode == MLAN_BSS_MODE_IBSS) { + mlan_ds_bss *bss = NULL; + /* Change beacon interval */ + if ((beacon_interval < MLAN_MIN_BEACON_INTERVAL) || + (beacon_interval > MLAN_MAX_BEACON_INTERVAL)) { + ret = -EINVAL; + goto done; + } + kfree(req); + req = NULL; + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + bss = (mlan_ds_bss *)req->pbuf; + req->req_id = MLAN_IOCTL_BSS; + req->action = MLAN_ACT_SET; + bss->sub_command = MLAN_OID_IBSS_BCN_INTERVAL; + bss->param.bcn_interval = beacon_interval; + status = woal_request_ioctl(priv, req, wait_option); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + /* "privacy" is set only for ad-hoc mode */ + if (privacy) { + /* + * Keep MLAN_ENCRYPTION_MODE_WEP40 for now so that + * the firmware can find a matching network from the + * scan. cfg80211 does not give us the encryption + * mode at this stage so just setting it to wep here + */ + if (MLAN_STATUS_SUCCESS != + woal_set_auth_mode(priv, wait_option, + MLAN_AUTH_MODE_OPEN)) { + ret = -EFAULT; + goto done; + } + + wpa_enabled = 0; + ret = woal_cfg80211_set_auth(priv, + MLAN_ENCRYPTION_MODE_WEP104, + wpa_enabled, wait_option); + if (ret) + goto done; + } + } + memcpy(&ssid_bssid.ssid, &req_ssid, sizeof(mlan_802_11_ssid)); + if (bssid) + memcpy(&ssid_bssid.bssid, bssid, ETH_ALEN); + if (MLAN_STATUS_SUCCESS != + woal_find_essid(priv, &ssid_bssid, wait_option)) { + /* Do specific SSID scanning */ + if (mode != MLAN_BSS_MODE_IBSS) + ret = woal_cfg80211_connect_scan(priv, conn_param, + wait_option); + else + ret = woal_request_scan(priv, wait_option, &req_ssid); + if (ret) { + ret = -EFAULT; + goto done; + } + } + + /* Disconnect before try to associate */ + if (mode == MLAN_BSS_MODE_IBSS) + woal_disconnect(priv, wait_option, NULL, + DEF_DEAUTH_REASON_CODE); + + if (mode != MLAN_BSS_MODE_IBSS) { + if (MLAN_STATUS_SUCCESS != + woal_find_best_network(priv, wait_option, &ssid_bssid)) { + ret = -EFAULT; + goto done; + } + /* Inform the BSS information to kernel, otherwise + * kernel will give a panic after successful assoc */ + if (MLAN_STATUS_SUCCESS != + woal_inform_bss_from_scan_result(priv, &ssid_bssid, + wait_option)) { + ret = -EFAULT; + goto done; + } + } else if (MLAN_STATUS_SUCCESS != + woal_find_best_network(priv, wait_option, &ssid_bssid)) + /* Adhoc start, Check the channel command */ + woal_11h_channel_check_ioctl(priv, wait_option); + + PRINTM(MINFO, "Trying to associate to %s and bssid " MACSTR "\n", + (char *)req_ssid.ssid, MAC2STR(ssid_bssid.bssid)); + + /* Zero SSID implies use BSSID to connect */ + if (bssid) + memset(&ssid_bssid.ssid, 0, sizeof(mlan_802_11_ssid)); + else /* Connect to BSS by ESSID */ + memset(&ssid_bssid.bssid, 0, MLAN_MAC_ADDR_LENGTH); + if (channel) { + ssid_bssid.channel_flags = channel->flags; + PRINTM(MCMND, "channel flags=0x%x\n", channel->flags); + } + if (MLAN_STATUS_SUCCESS != + woal_bss_start(priv, MOAL_IOCTL_WAIT_TIMEOUT, &ssid_bssid)) { + ret = -EFAULT; + goto done; + } + + /* Inform the IBSS information to kernel, otherwise + * kernel will give a panic after successful assoc */ + if (mode == MLAN_BSS_MODE_IBSS) { + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_inform_ibss_bss(priv, channel, + beacon_interval)) { + ret = -EFAULT; + goto done; + } + } + +done: + if (ret) { + /* clear the encryption mode */ + woal_cfg80211_set_auth(priv, MLAN_ENCRYPTION_MODE_NONE, MFALSE, + wait_option); + /* clear IE */ + ie_len = 0; + if (MLAN_STATUS_SUCCESS != + woal_set_get_gen_ie(priv, MLAN_ACT_SET, NULL, &ie_len, + wait_option)) { + PRINTM(MERROR, "Could not clear RSN IE\n"); + ret = -EFAULT; + } + } + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Request the driver to dump the station information + * + * @param priv A pointer to moal_private structure + * @param sinfo A pointer to station_info structure + * + * @return 0 -- success, otherwise fail + */ +static mlan_status +woal_cfg80211_dump_station_info(moal_private *priv, struct station_info *sinfo) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_get_signal signal; + mlan_ds_get_stats stats; + mlan_ioctl_req *req = NULL; + mlan_ds_rate *rate = NULL; + t_u16 Rates[12] = { + 0x02, 0x04, 0x0B, 0x16, + 0x0C, 0x12, 0x18, 0x24, + 0x30, 0x48, 0x60, 0x6c + }; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) + mlan_bss_info bss_info; + t_u8 dtim_period = 0; +#endif + + ENTER(); +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) + sinfo->filled = + BIT(NL80211_STA_INFO_RX_BYTES) | BIT(NL80211_STA_INFO_TX_BYTES) + | BIT(NL80211_STA_INFO_RX_PACKETS) | + BIT(NL80211_STA_INFO_TX_PACKETS) | BIT(NL80211_STA_INFO_SIGNAL) + | BIT(NL80211_STA_INFO_TX_BITRATE); +#else + sinfo->filled = STATION_INFO_RX_BYTES | STATION_INFO_TX_BYTES | + STATION_INFO_RX_PACKETS | STATION_INFO_TX_PACKETS | + STATION_INFO_SIGNAL | STATION_INFO_TX_BITRATE; +#endif + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 37) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) + sinfo->filled |= BIT(NL80211_STA_INFO_TX_FAILED); +#else + sinfo->filled |= STATION_INFO_TX_FAILED; +#endif +#endif + + /* Get signal information from the firmware */ + memset(&signal, 0, sizeof(mlan_ds_get_signal)); + if (MLAN_STATUS_SUCCESS != + woal_get_signal_info(priv, MOAL_IOCTL_WAIT, &signal)) { + PRINTM(MERROR, "Error getting signal information\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* Get stats information from the firmware */ + memset(&stats, 0, sizeof(mlan_ds_get_stats)); + if (MLAN_STATUS_SUCCESS != + woal_get_stats_info(priv, MOAL_IOCTL_WAIT, &stats)) { + PRINTM(MERROR, "Error getting stats information\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_rate)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + rate = (mlan_ds_rate *)req->pbuf; + rate->sub_command = MLAN_OID_GET_DATA_RATE; + req->req_id = MLAN_IOCTL_RATE; + req->action = MLAN_ACT_GET; + ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (ret != MLAN_STATUS_SUCCESS) + goto done; + if (rate->param.data_rate.tx_rate_format != MLAN_RATE_FORMAT_LG) { + if (rate->param.data_rate.tx_rate_format == MLAN_RATE_FORMAT_HT) { + sinfo->txrate.flags = RATE_INFO_FLAGS_MCS; + if (rate->param.data_rate.tx_ht_bw == MLAN_HT_BW40) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) + sinfo->txrate.bw = RATE_INFO_BW_40; +#else + sinfo->txrate.flags |= + RATE_INFO_FLAGS_40_MHZ_WIDTH; +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) + else + sinfo->txrate.bw = RATE_INFO_BW_20; +#endif + } + if (rate->param.data_rate.tx_ht_gi == MLAN_HT_SGI) + sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI; + sinfo->txrate.mcs = rate->param.data_rate.tx_mcs_index; + } else { + /* Bit rate is in 500 kb/s units. Convert it to 100kb/s units */ + sinfo->txrate.legacy = + Rates[rate->param.data_rate.tx_data_rate] * 5; + } + sinfo->rx_bytes = priv->stats.rx_bytes; + sinfo->tx_bytes = priv->stats.tx_bytes; + sinfo->rx_packets = priv->stats.rx_packets; + sinfo->tx_packets = priv->stats.tx_packets; + sinfo->signal = signal.bcn_rssi_avg; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 37) + sinfo->tx_failed = stats.failed; +#endif + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) + /* Update BSS information */ +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) + sinfo->filled |= BIT(NL80211_STA_INFO_BSS_PARAM); +#else + sinfo->filled |= STATION_INFO_BSS_PARAM; +#endif + sinfo->bss_param.flags = 0; + ret = woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info); + if (ret) + goto done; + if (bss_info.capability_info & WLAN_CAPABILITY_SHORT_PREAMBLE) + sinfo->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_PREAMBLE; + if (bss_info.capability_info & WLAN_CAPABILITY_SHORT_SLOT_TIME) + sinfo->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_SLOT_TIME; + sinfo->bss_param.beacon_interval = bss_info.beacon_interval; + /* Get DTIM period */ + ret = woal_set_get_dtim_period(priv, MLAN_ACT_GET, + MOAL_IOCTL_WAIT, &dtim_period); + if (ret) { + PRINTM(MERROR, "Get DTIM period failed\n"); + goto done; + } + sinfo->bss_param.dtim_period = dtim_period; +#endif + +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + + LEAVE(); + return ret; +} + +/******************************************************** + Global Functions +********************************************************/ +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) +/** + * @brief Set all radar channel's dfs_state + * + * @param wiphy A pointer to wiphy structure + * + * @return N/A + */ +void +woal_update_radar_chans_dfs_state(struct wiphy *wiphy) +{ + enum ieee80211_band band; + struct ieee80211_supported_band *sband; + int i; + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + sband = wiphy->bands[band]; + if (!sband) + continue; + for (i = 0; i < sband->n_channels; i++) { + if (sband->channels[i].flags & IEEE80211_CHAN_RADAR) { + if (dfs_offload) + sband->channels[i].dfs_state = + NL80211_DFS_AVAILABLE; + else + sband->channels[i].dfs_state = + NL80211_DFS_USABLE; + } + } + } + PRINTM(MCMND, "Set radar dfs_state: dfs_offload=%d\n", dfs_offload); +} +#endif + +/** + * @brief Request the driver to change regulatory domain + * + * @param wiphy A pointer to wiphy structure + * @param request A pointer to regulatory_request structure + * + * @return 0 + */ +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 9, 0) +static void +#else +static int +#endif +woal_cfg80211_reg_notifier(struct wiphy *wiphy, + struct regulatory_request *request) +{ + moal_private *priv = NULL; + moal_handle *handle = (moal_handle *)woal_get_wiphy_priv(wiphy); + t_u8 region[COUNTRY_CODE_LEN]; +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 9, 0) + int ret = 0; +#endif + + ENTER(); + + priv = woal_get_priv(handle, MLAN_BSS_ROLE_ANY); + if (!priv) { + PRINTM(MFATAL, "Unable to get priv in %s()\n", __func__); + LEAVE(); +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 9, 0) + return -EINVAL; +#else + return; +#endif + } + + PRINTM(MIOCTL, "cfg80211 regulatory domain callback " + "%c%c\n", request->alpha2[0], request->alpha2[1]); +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + if (dfs_offload) + woal_update_radar_chans_dfs_state(wiphy); +#endif + memset(region, 0, sizeof(region)); + memcpy(region, request->alpha2, sizeof(request->alpha2)); + region[2] = ' '; + if (MTRUE == is_cfg80211_special_region_code(region)) { + PRINTM(MIOCTL, "Skip configure special region code\n"); + LEAVE(); +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 9, 0) + return ret; +#else + return; +#endif + } + if ((handle->country_code[0] != request->alpha2[0]) || + (handle->country_code[1] != request->alpha2[1])) { + t_u8 country_code[COUNTRY_CODE_LEN]; + memset(country_code, 0, sizeof(country_code)); + country_code[0] = request->alpha2[0]; + country_code[1] = request->alpha2[1]; + if (cntry_txpwr) { + if (MLAN_STATUS_SUCCESS != + woal_request_country_power_table(priv, + country_code)) { +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 9, 0) + return -EFAULT; +#else + return; +#endif + } + } + } + handle->country_code[0] = request->alpha2[0]; + handle->country_code[1] = request->alpha2[1]; + handle->country_code[2] = ' '; + if (MLAN_STATUS_SUCCESS != + woal_set_region_code(priv, handle->country_code)) + PRINTM(MERROR, "Set country code failed!\n"); + switch (request->initiator) { + case NL80211_REGDOM_SET_BY_DRIVER: + PRINTM(MIOCTL, "Regulatory domain BY_DRIVER\n"); + break; + case NL80211_REGDOM_SET_BY_CORE: + PRINTM(MIOCTL, "Regulatory domain BY_CORE\n"); + break; + case NL80211_REGDOM_SET_BY_USER: + PRINTM(MIOCTL, "Regulatory domain BY_USER\n"); + break; + /* TODO: apply driver specific changes in channel flags based + on the request initiator if necessory. * */ + case NL80211_REGDOM_SET_BY_COUNTRY_IE: + PRINTM(MIOCTL, "Regulatory domain BY_COUNTRY_IE\n"); + break; + } + if (priv->wdev && priv->wdev->wiphy && + (request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE)) + woal_send_domain_info_cmd_fw(priv, MOAL_IOCTL_WAIT); + + LEAVE(); +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 9, 0) + return ret; +#endif +} + +#ifdef UAP_CFG80211 +/** + * @brief Swithces BSS role of interface + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option (MOAL_IOCTL_WAIT or MOAL_NO_WAIT) + * @param bss_role bss role + * + * @return 0 --success, otherwise fail + */ +mlan_status +woal_role_switch(moal_private *priv, t_u8 wait_option, t_u8 bss_role) +{ + int ret = 0; + mlan_ds_bss *bss = NULL; + mlan_ioctl_req *req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + bss = (mlan_ds_bss *)req->pbuf; + bss->sub_command = MLAN_OID_BSS_ROLE; + req->req_id = MLAN_IOCTL_BSS; + req->action = MLAN_ACT_SET; + bss->param.bss_role = bss_role; + + status = woal_request_ioctl(priv, req, wait_option); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief request scan + * + * @param priv A pointer to moal_private structure + * @param scan_cfg A pointer to wlan_user_scan_cfg structure + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status +woal_uap_scan(moal_private *priv, wlan_user_scan_cfg *scan_cfg) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + moal_handle *handle = priv->phandle; + moal_private *tmp_priv; + u8 role; + + ENTER(); + if (priv->bss_index > 0) + tmp_priv = woal_get_priv(handle, MLAN_BSS_ROLE_ANY); + else + tmp_priv = priv; + if (!tmp_priv) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + role = GET_BSS_ROLE(tmp_priv); + if (role == MLAN_BSS_ROLE_UAP) + woal_role_switch(tmp_priv, MOAL_IOCTL_WAIT, MLAN_BSS_ROLE_STA); +#ifdef REASSOCIATION + if (MOAL_ACQ_SEMAPHORE_BLOCK(&handle->reassoc_sem)) { + PRINTM(MERROR, "Acquire semaphore error, woal_do_combo_scan\n"); + goto done; + } +#endif /* REASSOCIATION */ + tmp_priv->report_scan_result = MTRUE; + ret = woal_request_userscan(tmp_priv, MOAL_IOCTL_WAIT, scan_cfg); + woal_sched_timeout(5); +#ifdef REASSOCIATION + MOAL_REL_SEMAPHORE(&handle->reassoc_sem); +#endif +done: + if (role == MLAN_BSS_ROLE_UAP) + woal_role_switch(tmp_priv, MOAL_IOCTL_WAIT, MLAN_BSS_ROLE_UAP); + LEAVE(); + return ret; +} +#endif + +static int +woal_find_wps_ie_in_probereq(const t_u8 *ie, int len) +{ + int left_len = len; + const t_u8 *pos = ie; + t_u8 ie_id, ie_len; + IEEEtypes_VendorSpecific_t *pvendor_ie = NULL; + const u8 wps_oui[4] = { 0x00, 0x50, 0xf2, 0x04 }; + + while (left_len >= 2) { + ie_id = *pos; + ie_len = *(pos + 1); + if ((ie_len + 2) > left_len) + break; + if (ie_id == VENDOR_SPECIFIC_221) { + pvendor_ie = (IEEEtypes_VendorSpecific_t *)pos; + if (!memcmp + (pvendor_ie->vend_hdr.oui, wps_oui, + sizeof(pvendor_ie->vend_hdr.oui)) && + pvendor_ie->vend_hdr.oui_type == wps_oui[3]) + return MTRUE; + } + + pos += (ie_len + 2); + left_len -= (ie_len + 2); + } + + return MFALSE; +} + +/** + * @brief check if the scan result expired + * + * @param priv A pointer to moal_private + * + * + * @return MTRUE/MFALSE; + */ +t_u8 +woal_is_scan_result_expired(moal_private *priv) +{ + mlan_scan_resp scan_resp; + struct timeval t; + ENTER(); + if (!woal_is_any_interface_active(priv->phandle)) { + LEAVE(); + return MTRUE; + } + + if (MLAN_STATUS_SUCCESS != + woal_get_scan_table(priv, MOAL_IOCTL_WAIT, &scan_resp)) { + LEAVE(); + return MTRUE; + } + do_gettimeofday(&t); +/** scan result expired value */ +#define SCAN_RESULT_EXPIRTED 1 + if (t.tv_sec > (scan_resp.age_in_secs + SCAN_RESULT_EXPIRTED)) { + LEAVE(); + return MTRUE; + } + LEAVE(); + return MFALSE; +} + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) +/** + * @brief Request the driver to do a scan. Always returning + * zero meaning that the scan request is given to driver, + * and will be valid until passed to cfg80211_scan_done(). + * To inform scan results, call cfg80211_inform_bss(). + * + * @param wiphy A pointer to wiphy structure + * @param request A pointer to cfg80211_scan_request structure + * + * @return 0 -- success, otherwise fail + */ +static int +woal_cfg80211_scan(struct wiphy *wiphy, struct cfg80211_scan_request *request) +#else +/** + * @brief Request the driver to do a scan. Always returning + * zero meaning that the scan request is given to driver, + * and will be valid until passed to cfg80211_scan_done(). + * To inform scan results, call cfg80211_inform_bss(). + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param request A pointer to cfg80211_scan_request structure + * + * @return 0 -- success, otherwise fail + */ +static int +woal_cfg80211_scan(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_scan_request *request) +#endif +{ +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) + struct net_device *dev = request->wdev->netdev; +#endif + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + wlan_user_scan_cfg scan_req; + mlan_bss_info bss_info; + struct ieee80211_channel *chan; + int ret = 0, i; + unsigned long flags; + + ENTER(); + + PRINTM(MINFO, "Received scan request on %s\n", dev->name); + if (priv->phandle->scan_pending_on_block == MTRUE) { + PRINTM(MCMND, "scan already in processing...\n"); + LEAVE(); + return -EAGAIN; + } +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) + if (priv->last_event & EVENT_BG_SCAN_REPORT) { + PRINTM(MCMND, "block scan while pending BGSCAN result\n"); + priv->last_event = 0; + LEAVE(); + return -EAGAIN; + } +#endif +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#ifdef WIFI_DIRECT_SUPPORT +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION + if (priv->phandle->is_go_timer_set && + priv->wdev->iftype != NL80211_IFTYPE_P2P_GO) { + PRINTM(MCMND, "block scan in go timer....\n"); + LEAVE(); + return -EAGAIN; + } +#endif +#endif +#endif + if (priv->fake_scan_complete || !woal_is_scan_result_expired(priv)) { + PRINTM(MEVENT, "Reporting fake scan results\n"); + woal_inform_bss_from_scan_result(priv, NULL, MOAL_IOCTL_WAIT); + woal_cfg80211_scan_done(request, MFALSE); + return ret; + } + memset(&bss_info, 0, sizeof(bss_info)); + if (MLAN_STATUS_SUCCESS == + woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info)) { + if (bss_info.scan_block) { + PRINTM(MEVENT, "Block scan in mlan module\n"); + woal_inform_bss_from_scan_result(priv, NULL, + MOAL_IOCTL_WAIT); + woal_cfg80211_scan_done(request, MFALSE); + return ret; + } + } + if (priv->phandle->scan_request && + priv->phandle->scan_request != request) { + PRINTM(MCMND, + "different scan_request is coming before previous one is finished on %s...\n", + dev->name); + LEAVE(); + return -EBUSY; + } + spin_lock_irqsave(&priv->phandle->scan_req_lock, flags); + priv->phandle->scan_request = request; + spin_unlock_irqrestore(&priv->phandle->scan_req_lock, flags); + memset(&scan_req, 0x00, sizeof(scan_req)); +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 7, 0) + if (!is_broadcast_ether_addr(request->bssid)) { + memcpy(scan_req.specific_bssid, request->bssid, ETH_ALEN); + PRINTM(MIOCTL, "scan: bssid=" MACSTR "\n", + MAC2STR(scan_req.specific_bssid)); + } +#endif + + if (priv->phandle->scan_request->n_channels <= 38) + scan_req.ext_scan_type = EXT_SCAN_ENHANCE; + +#ifdef WIFI_DIRECT_SUPPORT + if (priv->phandle->miracast_mode || + woal_is_any_interface_active(priv->phandle)) + scan_req.scan_chan_gap = priv->phandle->scan_chan_gap; + else + scan_req.scan_chan_gap = 0; +#endif + for (i = 0; i < priv->phandle->scan_request->n_ssids; i++) { + memcpy(scan_req.ssid_list[i].ssid, + priv->phandle->scan_request->ssids[i].ssid, + priv->phandle->scan_request->ssids[i].ssid_len); + if (priv->phandle->scan_request->ssids[i].ssid_len) + scan_req.ssid_list[i].max_len = 0; + else + scan_req.ssid_list[i].max_len = 0xff; + PRINTM(MIOCTL, "scan: ssid=%s\n", scan_req.ssid_list[i].ssid); + } +#if defined(WIFI_DIRECT_SUPPORT) +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION + if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT && + priv->phandle->scan_request->n_ssids) { + if (!memcmp(scan_req.ssid_list[0].ssid, "DIRECT-", 7)) + scan_req.ssid_list[0].max_len = 0xfe; + } +#endif +#endif + for (i = 0; + i < MIN(WLAN_USER_SCAN_CHAN_MAX, + priv->phandle->scan_request->n_channels); i++) { + chan = priv->phandle->scan_request->channels[i]; + scan_req.chan_list[i].chan_number = chan->hw_value; + scan_req.chan_list[i].radio_type = chan->band; + if ((chan-> + flags & (IEEE80211_CHAN_PASSIVE_SCAN | + IEEE80211_CHAN_RADAR)) + || !priv->phandle->scan_request->n_ssids) + scan_req.chan_list[i].scan_type = + MLAN_SCAN_TYPE_PASSIVE; + else + scan_req.chan_list[i].scan_type = MLAN_SCAN_TYPE_ACTIVE; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 8, 0) + scan_req.chan_list[i].scan_time = + priv->phandle->scan_request->duration; +#else + scan_req.chan_list[i].scan_time = 0; +#endif +#if defined(WIFI_DIRECT_SUPPORT) +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION + if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT && + priv->phandle->scan_request->n_ssids) { + if (!memcmp(scan_req.ssid_list[0].ssid, "DIRECT-", 7)) + scan_req.chan_list[i].scan_time = + MIN_SPECIFIC_SCAN_CHAN_TIME; + } +#endif +#endif +#ifdef WIFI_DIRECT_SUPPORT + if (priv->phandle->miracast_mode) + scan_req.chan_list[i].scan_time = + priv->phandle->miracast_scan_time; + else if (woal_is_any_interface_active(priv->phandle)) + scan_req.chan_list[i].scan_time = + MIN_SPECIFIC_SCAN_CHAN_TIME; +#endif +#ifdef UAP_CFG80211 + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) + scan_req.chan_list[i].scan_time = + MIN_SPECIFIC_SCAN_CHAN_TIME; +#endif + } + if (priv->phandle->scan_request->ie && + priv->phandle->scan_request->ie_len) { + if (woal_find_wps_ie_in_probereq + ((t_u8 *)priv->phandle->scan_request->ie, + priv->phandle->scan_request->ie_len)) { + PRINTM(MIOCTL, + "Notify firmware only keep probe response\n"); + scan_req.proberesp_only = MTRUE; + } + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_mgmt_frame_ie(priv, NULL, 0, + NULL, 0, NULL, 0, + (t_u8 *)priv->phandle-> + scan_request->ie, + priv->phandle->scan_request-> + ie_len, MGMT_MASK_PROBE_REQ, + MOAL_IOCTL_WAIT)) { + PRINTM(MERROR, "Fail to set scan request IE\n"); + ret = -EFAULT; + goto done; + } + } else { + /** Clear SCAN IE in Firmware */ + if (priv->probereq_index != MLAN_CUSTOM_IE_AUTO_IDX_MASK) + woal_cfg80211_mgmt_frame_ie(priv, NULL, 0, NULL, 0, + NULL, 0, NULL, 0, + MGMT_MASK_PROBE_REQ, + MOAL_IOCTL_WAIT); + } +#ifdef UAP_CFG80211 + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) { + /** use sync scan for uap */ + ret = woal_uap_scan(priv, &scan_req); + if (!ret) { + LEAVE(); + return ret; + } else { + PRINTM(MERROR, "Uap SCAN failure\n"); + goto done; + } + } +#endif + if (MLAN_STATUS_SUCCESS != woal_do_scan(priv, &scan_req)) { + PRINTM(MERROR, "woal_do_scan fails!\n"); + ret = -EAGAIN; + goto done; + } +done: + if (ret) { + spin_lock_irqsave(&priv->phandle->scan_req_lock, flags); + woal_cfg80211_scan_done(request, MTRUE); + priv->phandle->scan_request = NULL; + priv->phandle->scan_priv = NULL; + spin_unlock_irqrestore(&priv->phandle->scan_req_lock, flags); + } else + PRINTM(MMSG, "wlan: %s START SCAN\n", dev->name); + LEAVE(); + return ret; +} + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) +static void +woal_cfg80211_abort_scan(struct wiphy *wiphy, struct wireless_dev *wdev) +{ + moal_private *priv = (moal_private *)woal_get_netdev_priv(wdev->netdev); + ENTER(); + PRINTM(MMSG, "wlan: ABORT SCAN start\n"); + woal_cancel_scan(priv, MOAL_IOCTL_WAIT); + LEAVE(); + return; +} +#endif +/** + * @brief construct and send ft action request + * +* @param priv A pointer to moal_private structure + * @param ie A pointer to ft ie + * @param le Value of ie len + * @param bssid A pointer to target ap bssid + * @ + * @return 0 -- success, otherwise fail + */ +static int +woal_send_ft_action_requst(moal_private *priv, t_u8 *ie, t_u8 len, t_u8 *bssid, + t_u8 *target_ap) +{ + IEEE80211_MGMT *mgmt = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + pmlan_buffer pmbuf = NULL; + t_u32 pkt_type; + t_u32 tx_control; + t_u16 packet_len = 0; + t_u8 addr[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; + int ret = 0; + + ENTER(); + + /* pkt_type + tx_control */ +#define HEADER_SIZE 8 + /* frmctl + durationid + addr1 + addr2 + addr3 + seqctl + addr4 */ +#define MGMT_HEADER_LEN (2 + 2 + 6 + 6 + 6 + 2 +6) + /* 14 = category + action + sta addr + target ap */ +#define FT_REQUEST_LEN 14 + packet_len = (t_u16)len + MGMT_HEADER_LEN + FT_REQUEST_LEN; + pmbuf = woal_alloc_mlan_buffer(priv->phandle, + MLAN_MIN_DATA_HEADER_LEN + HEADER_SIZE + + packet_len + sizeof(packet_len)); + if (!pmbuf) { + PRINTM(MERROR, "Fail to allocate mlan_buffer\n"); + ret = -ENOMEM; + goto done; + } + + pmbuf->data_offset = MLAN_MIN_DATA_HEADER_LEN; + pkt_type = MRVL_PKT_TYPE_MGMT_FRAME; + tx_control = 0; + /* Add pkt_type and tx_control */ + memcpy(pmbuf->pbuf + pmbuf->data_offset, &pkt_type, sizeof(pkt_type)); + memcpy(pmbuf->pbuf + pmbuf->data_offset + sizeof(pkt_type), &tx_control, + sizeof(tx_control)); + /*Add packet len */ + memcpy(pmbuf->pbuf + pmbuf->data_offset + HEADER_SIZE, &packet_len, + sizeof(packet_len)); + + mgmt = (IEEE80211_MGMT *)(pmbuf->pbuf + pmbuf->data_offset + + HEADER_SIZE + sizeof(packet_len)); + memset(mgmt, 0, MGMT_HEADER_LEN); + mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | + IEEE80211_STYPE_ACTION); + memcpy(mgmt->da, bssid, ETH_ALEN); + memcpy(mgmt->sa, priv->current_addr, ETH_ALEN); + memcpy(mgmt->bssid, bssid, ETH_ALEN); + memcpy(mgmt->addr4, addr, ETH_ALEN); + + mgmt->u.ft_req.category = 0x06; /**ft action code 0x6*/ + mgmt->u.ft_req.action = 0x1; /**ft action request*/ + memcpy(mgmt->u.ft_req.sta_addr, priv->current_addr, ETH_ALEN); + memcpy(mgmt->u.ft_req.target_ap_addr, target_ap, ETH_ALEN); + + if (ie && len) + memcpy((t_u8 *)(&mgmt->u.ft_req.variable), ie, len); + + pmbuf->data_len = HEADER_SIZE + packet_len + sizeof(packet_len); + pmbuf->buf_type = MLAN_BUF_TYPE_RAW_DATA; + pmbuf->bss_index = priv->bss_index; + pmbuf->priority = 7; + + status = mlan_send_packet(priv->phandle->pmlan_adapter, pmbuf); + + switch (status) { + case MLAN_STATUS_PENDING: + atomic_inc(&priv->phandle->tx_pending); + queue_work(priv->phandle->workqueue, &priv->phandle->main_work); + break; + case MLAN_STATUS_SUCCESS: + woal_free_mlan_buffer(priv->phandle, pmbuf); + break; + case MLAN_STATUS_FAILURE: + default: + woal_free_mlan_buffer(priv->phandle, pmbuf); + ret = -EFAULT; + break; + } + +done: + LEAVE(); + return ret; +} + +/** + * @brief construct and send ft auth request + * +* @param priv A pointer to moal_private structure + * @param ie A pointer to ft ie + * @param le Value of ie len + * @param bssid A pointer to target ap bssid + * @ + * @return 0 -- success, otherwise fail + */ +static int +woal_send_ft_auth_requst(moal_private *priv, t_u8 *ie, t_u8 len, t_u8 *bssid) +{ + IEEE80211_MGMT *mgmt = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + pmlan_buffer pmbuf = NULL; + t_u32 pkt_type; + t_u32 tx_control; + t_u16 packet_len = 0; + t_u8 addr[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; + int ret = 0; + + ENTER(); + /* pkt_type + tx_control */ +#define HEADER_SIZE 8 + /* frmctl + durationid + addr1 + addr2 + addr3 + seqctl + addr4 */ +#define MGMT_HEADER_LEN (2 + 2 + 6 + 6 + 6 + 2 +6) + /* 6 = auth_alg + auth_transaction +auth_status */ +#define AUTH_BODY_LEN 6 + packet_len = (t_u16)len + MGMT_HEADER_LEN + AUTH_BODY_LEN; + pmbuf = woal_alloc_mlan_buffer(priv->phandle, + MLAN_MIN_DATA_HEADER_LEN + HEADER_SIZE + + packet_len + sizeof(packet_len)); + if (!pmbuf) { + PRINTM(MERROR, "Fail to allocate mlan_buffer\n"); + ret = -ENOMEM; + goto done; + } + + pmbuf->data_offset = MLAN_MIN_DATA_HEADER_LEN; + pkt_type = MRVL_PKT_TYPE_MGMT_FRAME; + tx_control = 0; + /* Add pkt_type and tx_control */ + memcpy(pmbuf->pbuf + pmbuf->data_offset, &pkt_type, sizeof(pkt_type)); + memcpy(pmbuf->pbuf + pmbuf->data_offset + sizeof(pkt_type), &tx_control, + sizeof(tx_control)); + /*Add packet len */ + memcpy(pmbuf->pbuf + pmbuf->data_offset + HEADER_SIZE, &packet_len, + sizeof(packet_len)); + + mgmt = (IEEE80211_MGMT *)(pmbuf->pbuf + pmbuf->data_offset + + HEADER_SIZE + sizeof(packet_len)); + memset(mgmt, 0, MGMT_HEADER_LEN); + mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | + IEEE80211_STYPE_AUTH); + memcpy(mgmt->da, bssid, ETH_ALEN); + memcpy(mgmt->sa, priv->current_addr, ETH_ALEN); + memcpy(mgmt->bssid, bssid, ETH_ALEN); + memcpy(mgmt->addr4, addr, ETH_ALEN); + + mgmt->u.auth.auth_alg = cpu_to_le16(WLAN_AUTH_FT); + mgmt->u.auth.auth_transaction = cpu_to_le16(1); + mgmt->u.auth.status_code = cpu_to_le16(0); + if (ie && len) + memcpy((t_u8 *)(&mgmt->u.auth.variable), ie, len); + + pmbuf->data_len = HEADER_SIZE + packet_len + sizeof(packet_len); + pmbuf->buf_type = MLAN_BUF_TYPE_RAW_DATA; + pmbuf->bss_index = priv->bss_index; + pmbuf->priority = 7; + + status = mlan_send_packet(priv->phandle->pmlan_adapter, pmbuf); + + switch (status) { + case MLAN_STATUS_PENDING: + atomic_inc(&priv->phandle->tx_pending); + queue_work(priv->phandle->workqueue, &priv->phandle->main_work); + break; + case MLAN_STATUS_SUCCESS: + woal_free_mlan_buffer(priv->phandle, pmbuf); + break; + case MLAN_STATUS_FAILURE: + default: + woal_free_mlan_buffer(priv->phandle, pmbuf); + ret = -EFAULT; + break; + } + +done: + LEAVE(); + return ret; +} + +/** + * @brief connect the AP through ft over air. + * + * @param priv A pointer to moal_private structure + * @param bssid A pointer to bssid + * @param chan struct ieee80211_channel + * + * @return 0 -- success, otherwise fail + */ +static int +woal_connect_ft_over_air(moal_private *priv, t_u8 *bssid, + struct ieee80211_channel *chan) +{ + struct wiphy *wiphy = priv->wdev->wiphy; + mlan_bss_info bss_info; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + t_u8 status = 0; +#endif + t_u8 wait_option = MOAL_IOCTL_WAIT; + int ret = 0; + long timeout = 0; + + ENTER(); + + if (!bssid) { + PRINTM(MERROR, + "Invalid bssid, unable to connect AP to through FT\n"); + LEAVE(); + return -EFAULT; + } + + if (!priv->ft_roaming_triggered_by_driver) { + wait_option = MOAL_IOCTL_WAIT; + memset(&bss_info, 0, sizeof(bss_info)); + woal_get_bss_info(priv, wait_option, &bss_info); + } + + if (priv->ft_roaming_triggered_by_driver || (priv->media_connected && + bss_info.mdid == + priv->ft_md && + bss_info.ft_cap == + priv->ft_cap)) { + ret = MTRUE; + + /*enable auth register frame */ +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 6, 0) + woal_cfg80211_mgmt_frame_register(wiphy, priv->netdev, + IEEE80211_STYPE_AUTH, MTRUE); +#else + woal_cfg80211_mgmt_frame_register(wiphy, priv->wdev, + IEEE80211_STYPE_AUTH, MTRUE); +#endif + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) +#define AUTH_TX_DEFAULT_WAIT_TIME 1200 + woal_cfg80211_remain_on_channel_cfg(priv, wait_option, MFALSE, + &status, chan, 0, + AUTH_TX_DEFAULT_WAIT_TIME); +#endif + /*construct auth request and send out */ + woal_send_ft_auth_requst(priv, priv->ft_ie, priv->ft_ie_len, + bssid); + PRINTM(MMSG, "wlan: send out FT auth,wait for auth response\n"); + /*wait until received auth response */ + priv->ft_wait_condition = MFALSE; + timeout = + wait_event_timeout(priv->ft_wait_q, + priv->ft_wait_condition, 1 * HZ); + if (!timeout) { + /*connet fail */ + if (!priv->ft_roaming_triggered_by_driver) { + woal_inform_bss_from_scan_result(priv, NULL, + wait_option); + cfg80211_connect_result(priv->netdev, + priv->cfg_bssid, NULL, + 0, NULL, 0, + WLAN_STATUS_SUCCESS, + GFP_KERNEL); + } + priv->ft_roaming_triggered_by_driver = MFALSE; + PRINTM(MMSG, + "wlan: keep connected to bssid " MACSTR "\n", + MAC2STR(priv->cfg_bssid)); + } else { + PRINTM(MMSG, "wlan: FT auth received \n"); + memcpy(priv->target_ap_bssid, bssid, ETH_ALEN); + } +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + woal_cfg80211_remain_on_channel_cfg(priv, wait_option, MTRUE, + &status, NULL, 0, 0); +#endif +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 6, 0) + woal_cfg80211_mgmt_frame_register(wiphy, priv->netdev, + IEEE80211_STYPE_AUTH, MFALSE); +#else + woal_cfg80211_mgmt_frame_register(wiphy, priv->wdev, + IEEE80211_STYPE_AUTH, MFALSE); +#endif + } + + LEAVE(); + return ret; +} + +/** + * @brief connect the AP through ft over DS. + * + * @param priv A pointer to moal_private structure + * @param bssid A pointer to bssid + * @param chan struct ieee80211_channel + * + * @return 0 -- success, otherwise fail + */ +static int +woal_connect_ft_over_ds(moal_private *priv, t_u8 *bssid, + struct ieee80211_channel *pchan) +{ +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + t_u8 status = 0; +#endif + t_u8 wait_option = MOAL_IOCTL_WAIT; + struct ieee80211_channel chan; + mlan_bss_info bss_info; + int ret = 0; + long timeout = 0; + + ENTER(); + + if (!priv->ft_roaming_triggered_by_driver) + wait_option = MOAL_IOCTL_WAIT; + + memset(&bss_info, 0, sizeof(bss_info)); + woal_get_bss_info(priv, wait_option, &bss_info); + chan.band = (bss_info.bss_chan < 36) ? + IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ; + chan.center_freq = ieee80211_channel_to_frequency(bss_info.bss_chan +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + , chan.band +#endif + ); + + if (priv->media_connected) { +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + woal_cfg80211_remain_on_channel_cfg(priv, wait_option, MFALSE, + &status, &chan, 0, 1200); +#endif + /*construct ft action request and send out */ + woal_send_ft_action_requst(priv, priv->ft_ie, priv->ft_ie_len, + (t_u8 *)priv->cfg_bssid, bssid); + PRINTM(MMSG, + "wlan: send out FT request,wait for FT response\n"); + /*wait until received auth response */ + priv->ft_wait_condition = MFALSE; + timeout = + wait_event_timeout(priv->ft_wait_q, + priv->ft_wait_condition, 1 * HZ); + if (!timeout) { + /*go over air, as current AP may be unreachable */ + PRINTM(MMSG, "wlan: go over air\n"); +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + woal_cfg80211_remain_on_channel_cfg(priv, wait_option, + MTRUE, &status, + NULL, 0, 0); +#endif + woal_connect_ft_over_air(priv, bssid, pchan); + LEAVE(); + return ret; + } else { + PRINTM(MMSG, "wlan: received FT response\n"); + memcpy(priv->target_ap_bssid, bssid, ETH_ALEN); + } +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + woal_cfg80211_remain_on_channel_cfg(priv, wait_option, MTRUE, + &status, NULL, 0, 0); +#endif + + } + + LEAVE(); + return ret; +} + +/** + * @brief start FT Roaming. + * + * @param priv A pointer to moal_private structure + * @param ssid_bssid A pointer to mlan_ssid_bssid structure + * + * + * @return 0 -- success, otherwise fail + */ +static int +woal_start_ft_roaming(moal_private *priv, mlan_ssid_bssid *ssid_bssid) +{ + struct ieee80211_channel chan; + int ret = 0; + + ENTER(); + PRINTM(MEVENT, "Try to start FT roaming......\n"); + chan.band = (ssid_bssid->channel < 36) ? + IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ; + chan.center_freq = ieee80211_channel_to_frequency(ssid_bssid->channel +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + , chan.band +#endif + ); + + priv->ft_roaming_triggered_by_driver = MTRUE; + if (!(priv->last_event & EVENT_PRE_BCN_LOST) && + (ssid_bssid->ft_cap & MBIT(0))) { + woal_connect_ft_over_ds(priv, (t_u8 *)&ssid_bssid->bssid, + &chan); + } else { + /*if pre beacon lost, it need to send auth request instead ft action request when ft over ds */ + + woal_connect_ft_over_air(priv, (t_u8 *)&ssid_bssid->bssid, + &chan); + } + + LEAVE(); + return ret; +} + +/** + * @brief Request the driver to connect to the ESS with + * the specified parameters from kernel + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param sme A pointer to cfg80211_connect_params structure + * + * @return 0 -- success, otherwise fail + */ +static int +woal_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_connect_params *sme) +{ + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + int ret = 0; + mlan_bss_info bss_info; + unsigned long flags; + mlan_ds_misc_assoc_rsp assoc_rsp; + IEEEtypes_AssocRsp_t *passoc_rsp = NULL; + mlan_ssid_bssid ssid_bssid; + moal_handle *handle = priv->phandle; + int i; + + ENTER(); + + PRINTM(MINFO, "Received association request on %s\n", dev->name); +#ifdef UAP_CFG80211 + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) { + LEAVE(); + return 0; + } +#endif + if (priv->wdev->iftype != NL80211_IFTYPE_STATION +#if defined(WIFI_DIRECT_SUPPORT) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 37) + && priv->wdev->iftype != NL80211_IFTYPE_P2P_CLIENT +#endif /* KERNEL_VERSION */ +#endif /* WIFI_DIRECT_SUPPORT && V14_FEATURE */ + ) { + PRINTM(MERROR, + "Received infra assoc request when station not in infra mode\n"); + LEAVE(); + return -EINVAL; + } + + memset(&ssid_bssid, 0, sizeof(ssid_bssid)); + memcpy(&ssid_bssid.ssid.ssid, sme->ssid, sme->ssid_len); + ssid_bssid.ssid.ssid_len = sme->ssid_len; + if (sme->bssid) + memcpy(&ssid_bssid.bssid, sme->bssid, ETH_ALEN); + /* Not allowed to connect to the same AP which is already connected + with other interface */ + for (i = 0; i < handle->priv_num; i++) { + if (handle->priv[i] != priv && + MTRUE == woal_is_connected(handle->priv[i], &ssid_bssid)) { + PRINTM(MMSG, + "wlan: already connected with other interface, bssid " + MACSTR "\n", + MAC2STR(handle->priv[i]->cfg_bssid)); + LEAVE(); + return -EINVAL; + } + } + + /** cancel pending scan */ + woal_cancel_scan(priv, MOAL_IOCTL_WAIT); + +#if defined(WIFI_DIRECT_SUPPORT) +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION + if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT + && (priv->wdev->iftype == NL80211_IFTYPE_STATION + || priv->wdev->iftype == NL80211_IFTYPE_P2P_CLIENT)) { + /* if bsstype == wifi direct, and iftype == station or p2p client, + * that means wpa_supplicant wants to enable wifi direct + * functionality, so we should init p2p client. + * + * Note that due to kernel iftype check, ICS wpa_supplicant + * could not updaet iftype to init p2p client, so we have to + * done it here. + * */ + if (MLAN_STATUS_SUCCESS != woal_cfg80211_init_p2p_client(priv)) { + PRINTM(MERROR, + "Init p2p client for wpa_supplicant failed.\n"); + ret = -EFAULT; + + LEAVE(); + return ret; + } + } + if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT) { + /* WAR for P2P connection with Samsung TV */ + woal_sched_timeout(200); + } +#endif +#endif + /*Fast BSS Transition use ft-over-air */ + if (priv->media_connected && priv->ft_ie_len && + !(priv->ft_cap & MBIT(0))) { + ret = woal_connect_ft_over_air(priv, (t_u8 *)sme->bssid, + sme->channel); + if (ret == MTRUE) { + LEAVE(); + return 0; + } + } + priv->cfg_connect = MTRUE; + if (priv->scan_type == MLAN_SCAN_TYPE_PASSIVE) + woal_set_scan_type(priv, MLAN_SCAN_TYPE_ACTIVE); + priv->assoc_status = 0; + ret = woal_cfg80211_assoc(priv, (void *)sme, MOAL_IOCTL_WAIT); + + if (priv->scan_type == MLAN_SCAN_TYPE_PASSIVE) + woal_set_scan_type(priv, MLAN_SCAN_TYPE_PASSIVE); + if (!ret) { + memset(&assoc_rsp, 0, sizeof(mlan_ds_misc_assoc_rsp)); + woal_get_assoc_rsp(priv, &assoc_rsp, MOAL_IOCTL_WAIT); + passoc_rsp = (IEEEtypes_AssocRsp_t *)assoc_rsp.assoc_resp_buf; + priv->rssi_low = DEFAULT_RSSI_LOW_THRESHOLD; + if (priv->bss_type == MLAN_BSS_TYPE_STA) + woal_save_conn_params(priv, sme); + memset(&bss_info, 0, sizeof(bss_info)); + woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info); + priv->channel = bss_info.bss_chan; + } + spin_lock_irqsave(&priv->connect_lock, flags); + priv->cfg_connect = MFALSE; + if (!ret && priv->media_connected) { + PRINTM(MMSG, + "wlan: Connected to bssid " MACSTR " successfully\n", + MAC2STR(priv->cfg_bssid)); + spin_unlock_irqrestore(&priv->connect_lock, flags); + cfg80211_connect_result(priv->netdev, priv->cfg_bssid, NULL, 0, + passoc_rsp->ie_buffer, + assoc_rsp.assoc_resp_len - + ASSOC_RESP_FIXED_SIZE, + WLAN_STATUS_SUCCESS, GFP_KERNEL); + } else { + PRINTM(MINFO, "wlan: Failed to connect to bssid " MACSTR "\n", + MAC2STR(priv->cfg_bssid)); + memset(priv->cfg_bssid, 0, ETH_ALEN); + priv->ft_ie_len = 0; + priv->ft_pre_connect = MFALSE; + spin_unlock_irqrestore(&priv->connect_lock, flags); + cfg80211_connect_result(priv->netdev, priv->cfg_bssid, NULL, 0, + NULL, 0, woal_get_assoc_status(priv), + GFP_KERNEL); + + } + LEAVE(); + return 0; +} + +/** + * @brief Request the driver to disconnect + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param reason_code Reason code + * + * @return 0 -- success, otherwise fail + */ +static int +woal_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev, + t_u16 reason_code) +{ + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + long start_time; + + ENTER(); + PRINTM(MMSG, + "wlan: Received disassociation request on %s, reason: %u\n", + dev->name, reason_code); +#ifdef UAP_CFG80211 + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) { + LEAVE(); + return 0; + } +#endif + priv->phandle->driver_state = woal_check_driver_status(priv->phandle); + if (priv->phandle->driver_state) { + PRINTM(MERROR, + "Block woal_cfg80211_disconnect in abnormal driver state\n"); + LEAVE(); + return -EFAULT; + } + + if (priv->cfg_disconnect) { + PRINTM(MERROR, "Disassociation already in progress\n"); + LEAVE(); + return -EBUSY; + } + + if (priv->media_connected == MFALSE) { + PRINTM(MMSG, " Already disconnected\n"); + LEAVE(); + return 0; + } + + priv->cfg_disconnect = MTRUE; + start_time = jiffies; + if (woal_disconnect + (priv, MOAL_IOCTL_WAIT_TIMEOUT, priv->cfg_bssid, + reason_code) != MLAN_STATUS_SUCCESS) { + priv->cfg_disconnect = MFALSE; + LEAVE(); + return -EFAULT; + } + /**Add delay to avoid auth failure after wps success */ + if ((jiffies - start_time) < 1 * HZ) + woal_sched_timeout(1500); + + priv->cfg_disconnect = MFALSE; + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 11, 0) + if (priv->wdev->iftype == NL80211_IFTYPE_STATION) + cfg80211_disconnected(priv->netdev, 0, NULL, 0, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 2, 0) + true, +#endif + GFP_KERNEL); +#endif + + memset(priv->cfg_bssid, 0, ETH_ALEN); + if (priv->bss_type == MLAN_BSS_TYPE_STA) + woal_clear_conn_params(priv); + priv->channel = 0; + + LEAVE(); + return 0; +} + +/** + * @brief Request the driver to get the station information + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param mac MAC address of the station + * @param sinfo A pointer to station_info structure + * + * @return 0 -- success, otherwise fail + */ +static int +woal_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 16, 0) + const t_u8 *mac, +#else + t_u8 *mac, +#endif + struct station_info *sinfo) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + + ENTER(); + +#ifdef UAP_CFG80211 + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) { + LEAVE(); + return woal_uap_cfg80211_get_station(wiphy, dev, mac, sinfo); + } +#endif + if (priv->media_connected == MFALSE) { + PRINTM(MINFO, "cfg80211: Media not connected!\n"); + LEAVE(); + return -ENOENT; + } + + if (MLAN_STATUS_SUCCESS != woal_cfg80211_dump_station_info(priv, sinfo)) { + PRINTM(MERROR, "cfg80211: Failed to get station info\n"); + ret = -EFAULT; + } +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3,2,0) + woal_check_auto_tdls(wiphy, dev); +#endif + LEAVE(); + return ret; +} + +/** + * @brief Request the driver to dump the station information + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param idx Station index + * @param mac MAC address of the station + * @param sinfo A pointer to station_info structure + * + * @return 0 -- success, otherwise fail + */ +static int +woal_cfg80211_dump_station(struct wiphy *wiphy, + struct net_device *dev, int idx, + t_u8 *mac, struct station_info *sinfo) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + + ENTER(); + +#ifdef UAP_CFG80211 + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) { + LEAVE(); + return woal_uap_cfg80211_dump_station(wiphy, dev, idx, mac, + sinfo); + } +#endif + + if (!priv->media_connected || idx != 0) { + PRINTM(MINFO, + "cfg80211: Media not connected or not for this station!\n"); + LEAVE(); + return -ENOENT; + } + + memcpy(mac, priv->cfg_bssid, ETH_ALEN); + + if (MLAN_STATUS_SUCCESS != woal_cfg80211_dump_station_info(priv, sinfo)) { + PRINTM(MERROR, "cfg80211: Failed to get station info\n"); + ret = -EFAULT; + } + + LEAVE(); + return ret; +} + +/** + * @brief Convert driver band configuration to IEEE band type + * + * @param bandcfg Driver band configuration + * + * @return IEEE band type + */ +t_u8 +woal_bandcfg_to_ieee_band(Band_Config_t bandcfg) +{ + t_u8 ret_radio_type = 0; + + ENTER(); + + switch (bandcfg.chanBand) { + case BAND_5GHZ: + ret_radio_type = IEEE80211_BAND_5GHZ; + break; + case BAND_2GHZ: + default: + ret_radio_type = IEEE80211_BAND_2GHZ; + break; + } + LEAVE(); + return ret_radio_type; +} + +/** + * @brief Request the driver to dump survey info + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param idx Station index + * @param survey A pointer to survey_info structure + * + * @return 0 -- success, otherwise fail + */ +static int +woal_cfg80211_dump_survey(struct wiphy *wiphy, struct net_device *dev, + int idx, struct survey_info *survey) +{ + int ret = -ENOENT; + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + enum ieee80211_band band; + ChanStatistics_t *pchan_stats = NULL; + mlan_scan_resp scan_resp; + + ENTER(); + PRINTM(MIOCTL, "dump_survey idx=%d\n", idx); + + memset(&scan_resp, 0, sizeof(scan_resp)); + if (MLAN_STATUS_SUCCESS != woal_get_scan_table(priv, + MOAL_IOCTL_WAIT, + &scan_resp)) { + ret = -EFAULT; + goto done; + } + pchan_stats = (ChanStatistics_t *)scan_resp.pchan_stats; + if (idx > scan_resp.num_in_chan_stats || idx < 0) { + ret = -EFAULT; + goto done; + } + if (idx == scan_resp.num_in_chan_stats || + !pchan_stats[idx].cca_scan_duration) + goto done; + ret = 0; + memset(survey, 0, sizeof(*survey)); + band = woal_bandcfg_to_ieee_band(pchan_stats[idx].bandcfg); + survey->channel = + ieee80211_get_channel(wiphy, + ieee80211_channel_to_frequency(pchan_stats + [idx]. + chan_num +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + , band +#endif + )); + survey->filled = SURVEY_INFO_NOISE_DBM; + survey->noise = pchan_stats[idx].noise; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 37) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) + survey->filled |= SURVEY_INFO_TIME | SURVEY_INFO_TIME_BUSY; + survey->time = pchan_stats[idx].cca_scan_duration; + survey->time_busy = pchan_stats[idx].cca_busy_duration; +#else + survey->filled |= + SURVEY_INFO_CHANNEL_TIME | SURVEY_INFO_CHANNEL_TIME_BUSY; + survey->channel_time = pchan_stats[idx].cca_scan_duration; + survey->channel_time_busy = pchan_stats[idx].cca_busy_duration; +#endif +#endif +done: + LEAVE(); + return ret; +} + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) +/** + * @brief Function gets channel info from cfg80211 + * + * @param wiphy A pointer to wiphy structure + * @param wdev A pointer to wireless_dev structure + * @param chandef A pointer to cfg80211_chan_def + * + * @return 0 -- success, otherwise fail + */ +static int +woal_cfg80211_get_channel(struct wiphy *wiphy, + struct wireless_dev *wdev, + struct cfg80211_chan_def *chandef) +{ + moal_private *priv = (moal_private *)woal_get_netdev_priv(wdev->netdev); + chan_band_info channel; + moal_handle *handle = (moal_handle *)woal_get_wiphy_priv(wiphy); + + memset(&channel, 0x00, sizeof(channel)); + + if (wdev->iftype == NL80211_IFTYPE_MONITOR) { + if ((handle->mon_if) && + (handle->mon_if->mon_ndev == wdev->netdev)) { + *chandef = handle->mon_if->chandef; + return 0; + } + return -EFAULT; + } +#ifdef UAP_SUPPORT + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) { + if (priv->bss_started == MTRUE) { + if (MLAN_STATUS_SUCCESS != + woal_set_get_ap_channel(priv, MLAN_ACT_GET, + MOAL_IOCTL_WAIT, + &channel)) { + PRINTM(MERROR, "Fail to get ap channel \n"); + return -EFAULT; + } + } else { + PRINTM(MERROR, "get_channel when AP is not started\n"); + return -EFAULT; + } + } else +#endif + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) { + if (priv->media_connected == MTRUE) { + if (MLAN_STATUS_SUCCESS != + woal_get_sta_channel(priv, MOAL_IOCTL_WAIT, + &channel)) { + PRINTM(MERROR, "Fail to get sta channel \n"); + return -EFAULT; + } + } else { + PRINTM(MERROR, + "get_channel when STA is not connected\n"); + return -EFAULT; + } + } else { + PRINTM(MERROR, "BssRole not support %d.\n", GET_BSS_ROLE(priv)); + return -EFAULT; + } + + if (MLAN_STATUS_FAILURE == woal_chandef_create(priv, chandef, &channel)) + return -EFAULT; + else + return 0; +} +#endif + +/** + * @brief Request the driver to Join the specified + * IBSS (or create if necessary) + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param params A pointer to cfg80211_ibss_params structure + * + * @return 0 -- success, otherwise fail + */ +static int +woal_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_ibss_params *params) +{ + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + int ret = 0; + + ENTER(); + + if (priv->wdev->iftype != NL80211_IFTYPE_ADHOC) { + PRINTM(MERROR, + "Request IBSS join received when station not in ibss mode\n"); + LEAVE(); + return -EINVAL; + } + + ret = woal_cfg80211_assoc(priv, (void *)params, MOAL_IOCTL_WAIT); + + if (!ret) { +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 15, 0) + cfg80211_ibss_joined(priv->netdev, priv->cfg_bssid, + params->chandef.chan, GFP_KERNEL); +#else + cfg80211_ibss_joined(priv->netdev, priv->cfg_bssid, GFP_KERNEL); +#endif + PRINTM(MINFO, "Joined/created adhoc network with bssid" + MACSTR " successfully\n", MAC2STR(priv->cfg_bssid)); + } else { + PRINTM(MINFO, "Failed creating/joining adhoc network\n"); + memset(priv->cfg_bssid, 0, ETH_ALEN); + } + + LEAVE(); + return ret; +} + +/** + * @brief Request the driver to leave the IBSS + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * + * @return 0 -- success, otherwise fail + */ +static int +woal_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev) +{ + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + + ENTER(); + + if (priv->cfg_disconnect) { + PRINTM(MERROR, "IBSS leave already in progress\n"); + LEAVE(); + return -EBUSY; + } + + if (priv->media_connected == MFALSE) { + LEAVE(); + return -EINVAL; + } + + priv->cfg_disconnect = 1; + + PRINTM(MINFO, "Leaving from IBSS " MACSTR "\n", + MAC2STR(priv->cfg_bssid)); + if (woal_disconnect + (priv, MOAL_IOCTL_WAIT, priv->cfg_bssid, + DEF_DEAUTH_REASON_CODE) != MLAN_STATUS_SUCCESS) { + priv->cfg_disconnect = 0; + LEAVE(); + return -EFAULT; + } + priv->cfg_disconnect = 0; + memset(priv->cfg_bssid, 0, ETH_ALEN); + + LEAVE(); + return 0; +} + +/** + * @brief Request the driver to change the IEEE power save + * mdoe + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param enabled Enable or disable + * @param timeout Timeout value + * + * @return 0 -- success, otherwise fail + */ +static int +woal_cfg80211_set_power_mgmt(struct wiphy *wiphy, + struct net_device *dev, bool enabled, int timeout) +{ + int ret = 0, disabled; + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + + ENTER(); + if (hw_test || (ps_mode == MLAN_INIT_PARA_DISABLED)) { + PRINTM(MIOCTL, "block set power hw_test=%d ps_mode=%d\n", + hw_test, ps_mode); + LEAVE(); + return -EFAULT; + } + priv->phandle->driver_state = woal_check_driver_status(priv->phandle); + if (priv->phandle->driver_state) { + PRINTM(MERROR, + "Block woal_cfg80211_set_power_mgmt in abnormal driver state\n"); + LEAVE(); + return -EFAULT; + } +#if defined(WIFI_DIRECT_SUPPORT) +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION + if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT) { + PRINTM(MIOCTL, "skip set power for p2p interface\n"); + LEAVE(); + return ret; + } +#endif +#endif + if (enabled) + disabled = 0; + else + disabled = 1; + + if (MLAN_STATUS_SUCCESS != + woal_set_get_power_mgmt(priv, MLAN_ACT_SET, &disabled, timeout, + MOAL_IOCTL_WAIT)) { + ret = -EOPNOTSUPP; + } + + LEAVE(); + return ret; +} + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 31) +/** + * @brief Request the driver to get the transmit power info + * + * @param wiphy A pointer to wiphy structure + * @param type TX power adjustment type + * @param dbm TX power in dbm + * + * @return 0 -- success, otherwise fail + */ +static int +woal_cfg80211_get_tx_power(struct wiphy *wiphy, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + struct wireless_dev *wdev, +#endif + int *dbm) +{ + int ret = 0; + moal_private *priv = NULL; + mlan_power_cfg_t power_cfg; + moal_handle *handle = (moal_handle *)woal_get_wiphy_priv(wiphy); + + ENTER(); + + if (!handle) { + PRINTM(MFATAL, "Unable to get handle\n"); + LEAVE(); + return -EFAULT; + } + + priv = woal_get_priv(handle, MLAN_BSS_ROLE_ANY); + + if (!priv) { + PRINTM(MFATAL, "Unable to get priv in %s()\n", __func__); + LEAVE(); + return -EFAULT; + } + + if (MLAN_STATUS_SUCCESS != + woal_set_get_tx_power(priv, MLAN_ACT_GET, &power_cfg)) { + LEAVE(); + return -EFAULT; + } + + *dbm = power_cfg.power_level; + + LEAVE(); + return ret; +} + +/** + * @brief Request the driver to change the transmit power + * + * @param wiphy A pointer to wiphy structure + * @param type TX power adjustment type + * @param dbm TX power in dbm + * + * @return 0 -- success, otherwise fail + */ +static int +woal_cfg80211_set_tx_power(struct wiphy *wiphy, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + struct wireless_dev *wdev, +#endif +#if CFG80211_VERSION_CODE < KERNEL_VERSION(2, 6, 36) + enum tx_power_setting type, +#else + enum nl80211_tx_power_setting type, +#endif + int dbm) +{ + int ret = 0; + moal_private *priv = NULL; + moal_handle *handle = (moal_handle *)woal_get_wiphy_priv(wiphy); + mlan_power_cfg_t power_cfg; + + ENTER(); + + priv = woal_get_priv(handle, MLAN_BSS_ROLE_ANY); + if (!priv) { + PRINTM(MFATAL, "Unable to get priv in %s()\n", __func__); + LEAVE(); + return -EFAULT; + } + + if (type) { + power_cfg.is_power_auto = 0; + power_cfg.power_level = dbm; + } else + power_cfg.is_power_auto = 1; + + if (MLAN_STATUS_SUCCESS != + woal_set_get_tx_power(priv, MLAN_ACT_SET, &power_cfg)) + ret = -EFAULT; + + LEAVE(); + return ret; +} +#endif + +#if CFG80211_VERSION_CODE > KERNEL_VERSION(2, 6, 35) +/** + * CFG802.11 operation handler for connection quality monitoring. + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param rssi_thold rssi threshold + * @param rssi_hyst rssi hysteresis + */ +static int +woal_cfg80211_set_cqm_rssi_config(struct wiphy *wiphy, + struct net_device *dev, + s32 rssi_thold, u32 rssi_hyst) +{ + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + ENTER(); + priv->cqm_rssi_thold = rssi_thold; + priv->cqm_rssi_high_thold = rssi_thold; + priv->cqm_rssi_hyst = rssi_hyst; + + PRINTM(MIOCTL, "rssi_thold=%d rssi_hyst=%d\n", + (int)rssi_thold, (int)rssi_hyst); + woal_set_rssi_threshold(priv, 0, MOAL_IOCTL_WAIT); + LEAVE(); + return 0; +} +#endif + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) +/** + * @brief remain on channel config + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * @param cancel cancel remain on channel flag + * @param status A pointer to status, success, in process or reject + * @param chan A pointer to ieee80211_channel structure + * @param channel_type channel_type, + * @param duration Duration wait to receive frame + * + * @return 0 -- success, otherwise fail + */ +int +woal_cfg80211_remain_on_channel_cfg(moal_private *priv, + t_u8 wait_option, t_u8 remove, t_u8 *status, + struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type, + t_u32 duration) +{ + mlan_ds_remain_chan chan_cfg; + int ret = 0; + + ENTER(); + + if (!status || (!chan && !remove)) { + LEAVE(); + return -EFAULT; + } + memset(&chan_cfg, 0, sizeof(mlan_ds_remain_chan)); + if (remove) { + chan_cfg.remove = MTRUE; + } else { +#ifdef WIFI_DIRECT_SUPPORT + if (priv->phandle->is_go_timer_set) { + PRINTM(MINFO, + "block remain on channel while go timer is on\n"); + LEAVE(); + return -EBUSY; + } +#endif + if (chan->band == IEEE80211_BAND_2GHZ) + chan_cfg.bandcfg.chanBand = BAND_2GHZ; + else if (chan->band == IEEE80211_BAND_5GHZ) + chan_cfg.bandcfg.chanBand = BAND_5GHZ; + switch (channel_type) { + case NL80211_CHAN_HT40MINUS: + chan_cfg.bandcfg.chan2Offset = SEC_CHAN_BELOW; + break; + case NL80211_CHAN_HT40PLUS: + chan_cfg.bandcfg.chan2Offset = SEC_CHAN_ABOVE; + break; + + case NL80211_CHAN_NO_HT: + case NL80211_CHAN_HT20: + default: + break; + } + chan_cfg.channel = + ieee80211_frequency_to_channel(chan->center_freq); + chan_cfg.remain_period = duration; + } + if (MLAN_STATUS_SUCCESS == + woal_set_remain_channel_ioctl(priv, wait_option, &chan_cfg)) + *status = chan_cfg.status; + else + ret = -EFAULT; + LEAVE(); + return ret; +} + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) +/** + * @brief tx mgmt frame + * + * @param wiphy A pointer to wiphy structure + * @param wdev A pointer to wireless_dev structure + * @param cookie A pointer to frame cookie + * + * @return 0 -- success, otherwise fail + */ +static int +woal_cfg80211_mgmt_tx_cancel_wait(struct wiphy *wiphy, + struct wireless_dev *wdev, u64 cookie) +#else +/** + * @brief tx mgmt frame + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param cookie A pointer to frame cookie + * + * @return 0 -- success, otherwise fail + */ +static int +woal_cfg80211_mgmt_tx_cancel_wait(struct wiphy *wiphy, + struct net_device *dev, u64 cookie) +#endif +{ +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) + struct net_device *dev = wdev->netdev; +#endif + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + int ret = 0; + t_u8 status = 1; + moal_private *remain_priv = NULL; + + ENTER(); + + if (priv->phandle->remain_on_channel) { + remain_priv = + priv->phandle->priv[priv->phandle->remain_bss_index]; + if (!remain_priv) { + PRINTM(MERROR, + "mgmt_tx_cancel_wait: Wrong remain_bss_index=%d\n", + priv->phandle->remain_bss_index); + ret = -EFAULT; + goto done; + } + if (woal_cfg80211_remain_on_channel_cfg + (remain_priv, MOAL_IOCTL_WAIT, MTRUE, &status, NULL, 0, + 0)) { + PRINTM(MERROR, + "mgmt_tx_cancel_wait: Fail to cancel remain on channel\n"); + ret = -EFAULT; + goto done; + } + if (priv->phandle->cookie) { + cfg80211_remain_on_channel_expired( +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 6, 0) + remain_priv-> + netdev, +#else + remain_priv-> + wdev, +#endif + priv-> + phandle-> + cookie, + &priv-> + phandle->chan, +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 8, 0) + priv-> + phandle-> + channel_type, +#endif + GFP_ATOMIC); + priv->phandle->cookie = 0; + } + priv->phandle->remain_on_channel = MFALSE; + } + +done: + LEAVE(); + return ret; +} + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) +/** + * @brief Make chip remain on channel + * + * @param wiphy A pointer to wiphy structure + * @param wdev A pointer to wireless_dev structure + * @param chan A pointer to ieee80211_channel structure + * @param channel_type Channel type + * @param duration Duration for timer + * @param cookie A pointer to timer cookie + * + * @return 0 -- success, otherwise fail + */ +static int +woal_cfg80211_remain_on_channel(struct wiphy *wiphy, + struct wireless_dev *wdev, + struct ieee80211_channel *chan, +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 8, 0) + enum nl80211_channel_type channel_type, +#endif + unsigned int duration, u64 * cookie) +#else +/** + * @brief Make chip remain on channel + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param chan A pointer to ieee80211_channel structure + * @param channel_type Channel type + * @param duration Duration for timer + * @param cookie A pointer to timer cookie + * + * @return 0 -- success, otherwise fail + */ +static int +woal_cfg80211_remain_on_channel(struct wiphy *wiphy, + struct net_device *dev, + struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type, + unsigned int duration, u64 * cookie) +#endif +{ +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) + struct net_device *dev = wdev->netdev; +#endif + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + int ret = 0; + t_u8 status = 1; + moal_private *remain_priv = NULL; + + ENTER(); + + if (!chan || !cookie) { + PRINTM(MERROR, "Invalid parameter for remain on channel\n"); + ret = -EFAULT; + goto done; + } + /** cancel previous remain on channel */ + if (priv->phandle->remain_on_channel && + ((priv->phandle->chan.center_freq != chan->center_freq) + )) { + remain_priv = + priv->phandle->priv[priv->phandle->remain_bss_index]; + if (!remain_priv) { + PRINTM(MERROR, + "remain_on_channel: Wrong remain_bss_index=%d\n", + priv->phandle->remain_bss_index); + ret = -EFAULT; + goto done; + } + if (woal_cfg80211_remain_on_channel_cfg + (remain_priv, MOAL_IOCTL_WAIT, MTRUE, &status, NULL, 0, + 0)) { + PRINTM(MERROR, + "remain_on_channel: Fail to cancel remain on channel\n"); + ret = -EFAULT; + goto done; + } + priv->phandle->cookie = 0; + priv->phandle->remain_on_channel = MFALSE; + } + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_remain_on_channel_cfg(priv, MOAL_IOCTL_WAIT, + MFALSE, &status, chan, +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 8, 0) + channel_type, +#else + 0, +#endif + (t_u32)duration)) { + ret = -EFAULT; + goto done; + } + + if (status) { + PRINTM(MMSG, + "%s: Set remain on Channel: channel=%d with status=%d\n", + dev->name, + ieee80211_frequency_to_channel(chan->center_freq), + status); + if (!priv->phandle->remain_on_channel) { + priv->phandle->is_remain_timer_set = MTRUE; + woal_mod_timer(&priv->phandle->remain_timer, duration); + } + } + + /* remain on channel operation success */ + /* we need update the value cookie */ +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 8, 0) + *cookie = (u64) random32() | 1; +#else + *cookie = (u64) prandom_u32() | 1; +#endif + priv->phandle->remain_on_channel = MTRUE; + priv->phandle->remain_bss_index = priv->bss_index; + priv->phandle->cookie = *cookie; +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 8, 0) + priv->phandle->channel_type = channel_type; +#endif + memcpy(&priv->phandle->chan, chan, sizeof(struct ieee80211_channel)); + + if (status == 0) + PRINTM(MIOCTL, + "%s: Set remain on Channel: channel=%d cookie = %#llx\n", + dev->name, + ieee80211_frequency_to_channel(chan->center_freq), + priv->phandle->cookie); + + cfg80211_ready_on_channel( +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 6, 0) + dev, +#else + priv->wdev, +#endif + *cookie, chan, +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 8, 0) + channel_type, +#endif + duration, GFP_KERNEL); + +done: + LEAVE(); + return ret; +} + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) +/** + * @brief Cancel remain on channel + * + * @param wiphy A pointer to wiphy structure + * @param wdev A pointer to wireless_dev structure + * @param cookie A pointer to timer cookie + * + * @return 0 -- success, otherwise fail + */ +static int +woal_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy, + struct wireless_dev *wdev, u64 cookie) +#else +/** + * @brief Cancel remain on channel + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param cookie A pointer to timer cookie + * + * @return 0 -- success, otherwise fail + */ +static int +woal_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy, + struct net_device *dev, u64 cookie) +#endif +{ +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) + struct net_device *dev = wdev->netdev; +#endif + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + moal_private *remain_priv = NULL; + int ret = 0; + t_u8 status = 1; + + ENTER(); + PRINTM(MIOCTL, "Cancel remain on Channel: cookie = %#llx\n", cookie); + remain_priv = priv->phandle->priv[priv->phandle->remain_bss_index]; + if (!remain_priv) { + PRINTM(MERROR, + "cancel_remain_on_channel: Wrong remain_bss_index=%d\n", + priv->phandle->remain_bss_index); + ret = -EFAULT; + goto done; + } + if (woal_cfg80211_remain_on_channel_cfg + (remain_priv, MOAL_IOCTL_WAIT, MTRUE, &status, NULL, 0, 0)) { + PRINTM(MERROR, + "cancel_remain_on_channel: Fail to cancel remain on channel\n"); + ret = -EFAULT; + goto done; + } + + priv->phandle->remain_on_channel = MFALSE; + if (priv->phandle->cookie) + priv->phandle->cookie = 0; +done: + LEAVE(); + return ret; +} +#endif /* KERNEL_VERSION */ + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) +/** + * @brief start sched scan + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param request A pointer to struct cfg80211_sched_scan_request + * + * @return 0 -- success, otherwise fail + */ +int +woal_cfg80211_sched_scan_start(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_sched_scan_request *request) +{ + struct ieee80211_channel *chan = NULL; + int i = 0; + int ret = 0; + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + struct cfg80211_ssid *ssid = NULL; + ENTER(); +#ifdef UAP_CFG80211 + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) { + LEAVE(); + return -EFAULT; + } +#endif + + memset(&priv->scan_cfg, 0, sizeof(priv->scan_cfg)); + if (!request) { + PRINTM(MERROR, "Invalid sched_scan req parameter\n"); + LEAVE(); + return -EINVAL; + } +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 4, 0) + PRINTM(MIOCTL, + "%s sched scan: n_ssids=%d n_match_sets=%d n_channels=%d interval=%d ie_len=%d\n", + priv->netdev->name, request->n_ssids, request->n_match_sets, + request->n_channels, request->scan_plans[0].interval, + (int)request->ie_len); +#else + PRINTM(MIOCTL, + "%s sched scan: n_ssids=%d n_match_sets=%d n_channels=%d interval=%d ie_len=%d\n", + priv->netdev->name, request->n_ssids, request->n_match_sets, + request->n_channels, request->interval, (int)request->ie_len); +#endif + /** We have pending scan, start bgscan later */ + if (priv->phandle->scan_pending_on_block) + priv->scan_cfg.start_later = MTRUE; + for (i = 0; i < request->n_match_sets; i++) { + ssid = &request->match_sets[i].ssid; + strncpy(priv->scan_cfg.ssid_list[i].ssid, ssid->ssid, + ssid->ssid_len); + priv->scan_cfg.ssid_list[i].max_len = 0; + PRINTM(MIOCTL, "sched scan: ssid=%s\n", ssid->ssid); + } + /** Add broadcast scan, when n_match_sets = 0 */ + if (!request->n_match_sets) + priv->scan_cfg.ssid_list[0].max_len = 0xff; + for (i = 0; i < MIN(WLAN_BG_SCAN_CHAN_MAX, request->n_channels); i++) { + chan = request->channels[i]; + priv->scan_cfg.chan_list[i].chan_number = chan->hw_value; + priv->scan_cfg.chan_list[i].radio_type = chan->band; + if (chan-> + flags & (IEEE80211_CHAN_PASSIVE_SCAN | + IEEE80211_CHAN_RADAR)) + priv->scan_cfg.chan_list[i].scan_type = + MLAN_SCAN_TYPE_PASSIVE; + else + priv->scan_cfg.chan_list[i].scan_type = + MLAN_SCAN_TYPE_ACTIVE; + priv->scan_cfg.chan_list[i].scan_time = 0; +#ifdef WIFI_DIRECT_SUPPORT + if (priv->phandle->miracast_mode) + priv->scan_cfg.chan_list[i].scan_time = + priv->phandle->miracast_scan_time; +#endif + } + priv->scan_cfg.chan_per_scan = + MIN(WLAN_BG_SCAN_CHAN_MAX, request->n_channels); + + /** set scan request IES */ + if (request->ie && request->ie_len) { + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_mgmt_frame_ie(priv, NULL, 0, + NULL, 0, NULL, 0, + (t_u8 *)request->ie, + request->ie_len, + MGMT_MASK_PROBE_REQ, + MOAL_IOCTL_WAIT)) { + PRINTM(MERROR, "Fail to set sched scan IE\n"); + ret = -EFAULT; + goto done; + } + } else { + /** Clear SCAN IE in Firmware */ + if (priv->probereq_index != MLAN_CUSTOM_IE_AUTO_IDX_MASK) + woal_cfg80211_mgmt_frame_ie(priv, NULL, 0, NULL, 0, + NULL, 0, NULL, 0, + MGMT_MASK_PROBE_REQ, + MOAL_IOCTL_WAIT); + } + + /* Interval between scan cycles in milliseconds,supplicant set to 10 second */ + /* We want to use 30 second for per scan cycle */ + priv->scan_cfg.scan_interval = MIN_BGSCAN_INTERVAL; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 4, 0) + /* only support 1 scan plan now */ + if (request->scan_plans[0].interval > MIN_BGSCAN_INTERVAL) + priv->scan_cfg.scan_interval = request->scan_plans[0].interval; +#else + if (request->interval > MIN_BGSCAN_INTERVAL) + priv->scan_cfg.scan_interval = request->interval; +#endif + priv->scan_cfg.repeat_count = DEF_REPEAT_COUNT; + priv->scan_cfg.report_condition = + BG_SCAN_SSID_MATCH | BG_SCAN_WAIT_ALL_CHAN_DONE; + priv->scan_cfg.bss_type = MLAN_BSS_MODE_INFRA; + priv->scan_cfg.action = BG_SCAN_ACT_SET; + priv->scan_cfg.enable = MTRUE; +#ifdef WIFI_DIRECT_SUPPORT + if (priv->phandle->miracast_mode) + priv->scan_cfg.scan_chan_gap = priv->phandle->scan_chan_gap; + else + priv->scan_cfg.scan_chan_gap = 0; +#endif + + if (MLAN_STATUS_SUCCESS == + woal_request_bgscan(priv, MOAL_IOCTL_WAIT, &priv->scan_cfg)) { + priv->sched_scanning = MTRUE; + priv->bg_scan_start = MTRUE; + priv->bg_scan_reported = MFALSE; + } else + ret = -EFAULT; +done: + LEAVE(); + return ret; +} + +/** + * @brief stop sched scan + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * + * @return 0 -- success, otherwise fail + */ +int +woal_cfg80211_sched_scan_stop(struct wiphy *wiphy, struct net_device *dev +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) + , u64 reqid +#endif + ) +{ + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + ENTER(); + PRINTM(MIOCTL, "sched scan stop\n"); + priv->sched_scanning = MFALSE; + woal_stop_bg_scan(priv, MOAL_NO_WAIT); + priv->bg_scan_start = MFALSE; + priv->bg_scan_reported = MFALSE; + LEAVE(); + return 0; +} +#endif + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) +/** + * @brief cfg80211_resume handler + * + * @param wiphy A pointer to wiphy structure + * + * @return 0 -- success, otherwise fail + */ +int +woal_cfg80211_resume(struct wiphy *wiphy) +{ + moal_handle *handle = (moal_handle *)woal_get_wiphy_priv(wiphy); + moal_private *priv = woal_get_priv(handle, MLAN_BSS_ROLE_STA); +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 11, 0) && defined(CONFIG_PM) + struct cfg80211_wowlan_wakeup wakeup_report; +#endif + mlan_ds_hs_wakeup_reason wakeup_reason; + int i; + + PRINTM(MCMND, "<--- Enter woal_cfg80211_resume --->\n"); + for (i = 0; i < MIN(handle->priv_num, MLAN_MAX_BSS_NUM); i++) { + if (handle->priv[i] && + (GET_BSS_ROLE(handle->priv[i]) == MLAN_BSS_ROLE_STA)) { + if (handle->priv[i]->last_event & EVENT_BG_SCAN_REPORT) { + if (handle->priv[i]->sched_scanning) { + woal_inform_bss_from_scan_result + (handle->priv[i], NULL, + MOAL_IOCTL_WAIT); + cfg80211_sched_scan_results(handle-> + priv[i]-> + wdev->wiphy +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) + , 0 +#endif + ); + handle->priv[i]->last_event = 0; + PRINTM(MIOCTL, + "Report sched scan result in cfg80211 resume\n"); + } + if (!hw_test && + handle->priv[i]->roaming_enabled) { + handle->priv[i]->roaming_required = + MTRUE; +#ifdef ANDROID_KERNEL +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) + __pm_wakeup_event(&handle->ws, + ROAMING_WAKE_LOCK_TIMEOUT); +#else + wake_lock_timeout(&handle->wake_lock, + ROAMING_WAKE_LOCK_TIMEOUT); +#endif +#endif + wake_up_interruptible(&handle-> + reassoc_thread. + wait_q); + } + } + } + } + + woal_get_wakeup_reason(priv, &wakeup_reason); + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 11, 0) && defined(CONFIG_PM) + memset(&wakeup_report, 0, sizeof(struct cfg80211_wowlan_wakeup)); + wakeup_report.pattern_idx = -1; + + switch (wakeup_reason.hs_wakeup_reason) { + case NO_HSWAKEUP_REASON: + break; + case BCAST_DATA_MATCHED: + break; + case MCAST_DATA_MATCHED: + break; + case UCAST_DATA_MATCHED: + break; + case MASKTABLE_EVENT_MATCHED: + break; + case NON_MASKABLE_EVENT_MATCHED: + break; + case NON_MASKABLE_CONDITION_MATCHED: + if (wiphy->wowlan_config->disconnect) + wakeup_report.disconnect = true; + break; + case MAGIC_PATTERN_MATCHED: + if (wiphy->wowlan_config->magic_pkt) + wakeup_report.magic_pkt = true; + if (wiphy->wowlan_config->n_patterns) + wakeup_report.pattern_idx = 1; + break; + case CONTROL_FRAME_MATCHED: + break; + case MANAGEMENT_FRAME_MATCHED: + break; + case GTK_REKEY_FAILURE: + if (wiphy->wowlan_config->gtk_rekey_failure) + wakeup_report.gtk_rekey_failure = true; + break; + default: + break; + } + + if ((wakeup_reason.hs_wakeup_reason > 0) && + (wakeup_reason.hs_wakeup_reason <= 10)) { + cfg80211_report_wowlan_wakeup(priv->wdev, &wakeup_report, + GFP_KERNEL); + } +#endif + + handle->cfg80211_suspend = MFALSE; + PRINTM(MCMND, "<--- Leave woal_cfg80211_resume --->\n"); + return 0; +} + +/** + * @brief cfg80211_suspend handler + * + * @param wiphy A pointer to wiphy structure + * @param wow A pointer to cfg80211_wowlan + * + * @return 0 -- success, otherwise fail + */ +int +woal_cfg80211_suspend(struct wiphy *wiphy, struct cfg80211_wowlan *wow) +{ + moal_handle *handle = (moal_handle *)woal_get_wiphy_priv(wiphy); + int i; + int ret = 0; + + PRINTM(MCMND, "<--- Enter woal_cfg80211_suspend --->\n"); + for (i = 0; i < MIN(handle->priv_num, MLAN_MAX_BSS_NUM); i++) { + if (handle->priv[i] && + (GET_BSS_ROLE(handle->priv[i]) == MLAN_BSS_ROLE_STA)) { + if (handle->scan_request) { + PRINTM(MIOCTL, + "Cancel pending scan in woal_cfg80211_suspend\n"); + woal_cancel_scan(handle->priv[i], + MOAL_IOCTL_WAIT); + } + handle->priv[i]->last_event = 0; + } + } + + handle->cfg80211_suspend = MTRUE; + + PRINTM(MCMND, "<--- Leave woal_cfg80211_suspend --->\n"); + return ret; +} +#endif + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 5, 0) +static void +woal_cfg80211_set_wakeup(struct wiphy *wiphy, bool enabled) +{ + moal_handle *handle = (moal_handle *)woal_get_wiphy_priv(wiphy); + + device_set_wakeup_enable(handle->hotplug_device, enabled); +} +#endif + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3,2,0) +/** + * @brief TDLS operation ioctl handler + * + * @param priv A pointer to moal_private structure + * @param peer A pointer to peer mac + * @apram action action for TDLS + * @return 0 --success, otherwise fail + */ +static int +woal_tdls_oper(moal_private *priv, u8 *peer, t_u8 action) +{ + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_misc_cfg *misc = NULL; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (ioctl_req == NULL) { + ret = -ENOMEM; + goto done; + } + misc = (mlan_ds_misc_cfg *)ioctl_req->pbuf; + misc->sub_command = MLAN_OID_MISC_TDLS_OPER; + ioctl_req->req_id = MLAN_IOCTL_MISC_CFG; + ioctl_req->action = MLAN_ACT_SET; + misc->param.tdls_oper.tdls_action = action; + memcpy(misc->param.tdls_oper.peer_mac, peer, ETH_ALEN); + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + LEAVE(); + return ret; +} + +/** + * @brief TDLS operation ioctl handler + * + * @param priv A pointer to moal_private structure + * @param peer A pointer to peer mac + * @param tdls_ies A pointer to mlan_ds_misc_tdls_ies structure + * @param flags TDLS ie flags + * + * @return 0 --success, otherwise fail + */ +static int +woal_tdls_get_ies(moal_private *priv, u8 *peer, mlan_ds_misc_tdls_ies *tdls_ies, + t_u16 flags) +{ + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_misc_cfg *misc = NULL; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (ioctl_req == NULL) { + ret = -ENOMEM; + goto done; + } + misc = (mlan_ds_misc_cfg *)ioctl_req->pbuf; + misc->sub_command = MLAN_OID_MISC_GET_TDLS_IES; + ioctl_req->req_id = MLAN_IOCTL_MISC_CFG; + ioctl_req->action = MLAN_ACT_GET; + misc->param.tdls_ies.flags = flags; + memcpy(misc->param.tdls_ies.peer_mac, peer, ETH_ALEN); + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + if (tdls_ies) + memcpy(tdls_ies, &misc->param.tdls_ies, + sizeof(mlan_ds_misc_tdls_ies)); +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + LEAVE(); + return ret; +} + +/** + * @brief append tdls ext_capability + * + * @param skb A pointer to sk_buff structure + * + * @return N/A + */ +static void +woal_tdls_add_ext_capab(struct sk_buff *skb, mlan_ds_misc_tdls_ies *tdls_ies) +{ + u8 *pos = NULL; + if (tdls_ies->ext_cap[0] == WLAN_EID_EXT_CAPABILITY) { + pos = (void *)skb_put(skb, sizeof(IEEEtypes_ExtCap_t)); + memcpy(pos, tdls_ies->ext_cap, sizeof(IEEEtypes_ExtCap_t)); + } else { + PRINTM(MERROR, "Fail to append tdls ext_capability\n"); + } +} + +/** + * @brief append supported rates + * + * @param priv A pointer to moal_private structure + * @param skb A pointer to sk_buff structure + * @param band AP's band + * + * @return N/A + */ +static void +woal_add_supported_rates_ie(moal_private *priv, struct sk_buff *skb, + enum ieee80211_band band) +{ + t_u8 basic_rates[] = { + 0x82, 0x84, 0x8b, 0x96, + 0x0c, 0x12, 0x18, 0x24 + }; + t_u8 basic_rates_5G[] = { + 0x0c, 0x12, 0x18, 0x24, + 0x30, 0x48, 0x60, 0x6c + }; + t_u8 *pos; + t_u8 rate_num = 0; + if (band == IEEE80211_BAND_2GHZ) + rate_num = sizeof(basic_rates); + else + rate_num = sizeof(basic_rates_5G); + + if (skb_tailroom(skb) < rate_num + 2) + return; + + pos = skb_put(skb, rate_num + 2); + *pos++ = WLAN_EID_SUPP_RATES; + *pos++ = rate_num; + if (band == IEEE80211_BAND_2GHZ) + memcpy(pos, basic_rates, rate_num); + else + memcpy(pos, basic_rates_5G, rate_num); + return; +} + +/** + * @brief append ext_supported rates + * + * @param priv A pointer to moal_private structure + * @param skb A pointer to sk_buff structure + * @param band AP's band + * + * @return N/A + */ +static void +woal_add_ext_supported_rates_ie(moal_private *priv, struct sk_buff *skb, + enum ieee80211_band band) +{ + t_u8 ext_rates[] = { 0x0c, 0x12, 0x18, 0x60 }; + t_u8 *pos; + t_u8 rate_num = sizeof(ext_rates); + + if (band != IEEE80211_BAND_2GHZ) + return; + + if (skb_tailroom(skb) < rate_num + 2) + return; + + pos = skb_put(skb, rate_num + 2); + *pos++ = WLAN_EID_EXT_SUPP_RATES; + *pos++ = rate_num; + memcpy(pos, ext_rates, rate_num); + return; +} + +/** + * @brief append wmm ie + * + * @param priv A pointer to moal_private structure + * @param skb A pointer to sk_buff structure + * @param wmm_type WMM_TYPE_INFO/WMM_TYPE_PARAMETER + * @param pQosInfo A pointer to qos info + * + * @return N/A + */ +static void +woal_add_wmm_ie(moal_private *priv, struct sk_buff *skb, t_u8 wmm_type, + t_u8 *pQosInfo) +{ + t_u8 wmmInfoElement[] = { 0x00, 0x50, 0xf2, + 0x02, 0x00, 0x01 + }; + t_u8 wmmParamElement[] = { 0x00, 0x50, 0xf2, + 0x02, 0x01, 0x01 + }; + t_u8 ac_vi[] = { 0x42, 0x43, 0x5e, 0x00 }; + t_u8 ac_vo[] = { 0x62, 0x32, 0x2f, 0x00 }; + t_u8 ac_be[] = { 0x03, 0xa4, 0x00, 0x00 }; + t_u8 ac_bk[] = { 0x27, 0xa4, 0x00, 0x00 }; + t_u8 qosInfo = 0x0; + t_u8 reserved = 0; + t_u8 wmm_id = 221; + t_u8 wmmParamIe_len = 24; + t_u8 wmmInfoIe_len = 7; + t_u8 len = 0; + t_u8 *pos; + + if (skb_tailroom(skb) < wmmParamIe_len + 2) + return; + + qosInfo = (pQosInfo == NULL) ? 0xf : (*pQosInfo); + /*wmm parameter */ + if (wmm_type == WMM_TYPE_PARAMETER) { + pos = skb_put(skb, wmmParamIe_len + 2); + len = wmmParamIe_len; + } else { + pos = skb_put(skb, wmmInfoIe_len + 2); + len = wmmInfoIe_len; + } + + *pos++ = wmm_id; + *pos++ = len; + /*wmm parameter */ + if (wmm_type == WMM_TYPE_PARAMETER) { + memcpy(pos, wmmParamElement, sizeof(wmmParamElement)); + pos += sizeof(wmmParamElement); + } else { + memcpy(pos, wmmInfoElement, sizeof(wmmInfoElement)); + pos += sizeof(wmmInfoElement); + } + *pos++ = qosInfo; + /*wmm parameter */ + if (wmm_type == WMM_TYPE_PARAMETER) { + *pos++ = reserved; + memcpy(pos, ac_be, sizeof(ac_be)); + pos += sizeof(ac_be); + memcpy(pos, ac_bk, sizeof(ac_bk)); + pos += sizeof(ac_bk); + memcpy(pos, ac_vi, sizeof(ac_vi)); + pos += sizeof(ac_vi); + memcpy(pos, ac_vo, sizeof(ac_vo)); + } + return; +} + +/** + * @brief update tdls peer status + * + * @param priv A pointer to moal_private structure + * @param peer_addr A point to peer mac address + * @param link_status link status + * + * @return N/A +*/ +t_void +woal_updata_peer_status(moal_private *priv, t_u8 *peer_addr, + tdlsStatus_e link_status) +{ + struct tdls_peer *peer = NULL; + unsigned long flags; + if (priv && priv->enable_auto_tdls) { + spin_lock_irqsave(&priv->tdls_lock, flags); + list_for_each_entry(peer, &priv->tdls_list, link) { + if (!memcmp(peer->peer_addr, peer_addr, ETH_ALEN)) { + if ((link_status == TDLS_NOT_SETUP) && + (peer->link_status == + TDLS_SETUP_INPROGRESS)) + peer->num_failure++; + else if (link_status == TDLS_SETUP_COMPLETE) + peer->num_failure = 0; + peer->link_status = link_status; + break; + } + } + spin_unlock_irqrestore(&priv->tdls_lock, flags); + } +} + +/** + * @brief add tdls peer + * + * @param priv A pointer to moal_private structure + * @param peer A point to peer address + * + * @return N/A +*/ +t_void +woal_add_tdls_peer(moal_private *priv, t_u8 *peer) +{ + struct tdls_peer *tdls_peer = NULL; + unsigned long flags; + t_u8 find_peer = MFALSE; + if (priv && priv->enable_auto_tdls) { + spin_lock_irqsave(&priv->tdls_lock, flags); + list_for_each_entry(tdls_peer, &priv->tdls_list, link) { + if (!memcmp(tdls_peer->peer_addr, peer, ETH_ALEN)) { + tdls_peer->link_status = TDLS_SETUP_INPROGRESS; + tdls_peer->rssi_jiffies = jiffies; + find_peer = MTRUE; + break; + } + } + if (!find_peer) { + /* create new TDLS peer */ + tdls_peer = + kzalloc(sizeof(struct tdls_peer), GFP_ATOMIC); + if (tdls_peer) { + memcpy(tdls_peer->peer_addr, peer, ETH_ALEN); + tdls_peer->link_status = TDLS_SETUP_INPROGRESS; + tdls_peer->rssi_jiffies = jiffies; + INIT_LIST_HEAD(&tdls_peer->link); + list_add_tail(&tdls_peer->link, + &priv->tdls_list); + PRINTM(MCMND, + "Add to TDLS list: peer=" MACSTR "\n", + MAC2STR(peer)); + } + } + spin_unlock_irqrestore(&priv->tdls_lock, flags); + } +} + +/** + * @brief check auto tdls + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * + * @return N/A +*/ +void +woal_check_auto_tdls(struct wiphy *wiphy, struct net_device *dev) +{ + t_u8 bcast_addr[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + struct tdls_peer *tdls_peer = NULL; + unsigned long flags; + t_u8 tdls_discovery = MFALSE; + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + + ENTER(); + if (priv && priv->enable_auto_tdls) { + priv->tdls_check_tx = MFALSE; + spin_lock_irqsave(&priv->tdls_lock, flags); + list_for_each_entry(tdls_peer, &priv->tdls_list, link) { + if ((jiffies - tdls_peer->rssi_jiffies) > + TDLS_IDLE_TIME) { + tdls_peer->rssi = 0; + if (tdls_peer->num_failure < + TDLS_MAX_FAILURE_COUNT) + tdls_discovery = MTRUE; + } +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + if (tdls_peer->rssi && + (tdls_peer->rssi >= TDLS_RSSI_LOW_THRESHOLD)) { + if (tdls_peer->link_status == + TDLS_SETUP_COMPLETE) { + tdls_peer->link_status = TDLS_TEAR_DOWN; + PRINTM(MMSG, + "Wlan: Tear down TDLS link, peer=" + MACSTR " rssi=%d\n", + MAC2STR(tdls_peer->peer_addr), + -tdls_peer->rssi); + cfg80211_tdls_oper_request(dev, + tdls_peer-> + peer_addr, + NL80211_TDLS_TEARDOWN, + TDLS_TEARN_DOWN_REASON_UNSPECIFIC, + GFP_ATOMIC); + } + } else if (tdls_peer->rssi && + (tdls_peer->rssi <= + TDLS_RSSI_HIGH_THRESHOLD)) { + if ((tdls_peer->link_status == TDLS_NOT_SETUP) + && (tdls_peer->num_failure < + TDLS_MAX_FAILURE_COUNT)) { + priv->tdls_check_tx = MTRUE; + PRINTM(MCMND, + "Wlan: Find TDLS peer=" MACSTR + " rssi=%d\n", + MAC2STR(tdls_peer->peer_addr), + -tdls_peer->rssi); + + } + } +#endif + } + spin_unlock_irqrestore(&priv->tdls_lock, flags); + } + if (tdls_discovery) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 15, 0) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 17, 0) + woal_cfg80211_tdls_mgmt(wiphy, dev, bcast_addr, + TDLS_DISCOVERY_REQUEST, 1, 0, 0, 0, + NULL, 0); +#else + woal_cfg80211_tdls_mgmt(wiphy, dev, bcast_addr, + TDLS_DISCOVERY_REQUEST, 1, 0, 0, NULL, + 0); +#endif +#else + woal_cfg80211_tdls_mgmt(wiphy, dev, bcast_addr, + TDLS_DISCOVERY_REQUEST, 1, 0, NULL, 0); +#endif + LEAVE(); +} + +/** + * @brief woal construct tdls data frame + * + * @param priv A pointer to moal_private structure + * @param peer A pointer to peer mac + * @param action_code tdls action code + * @param dialog_token dialog_token + * @param status_code status_code + * @param skb skb buffer + * + * @return 0 -- success, otherwise fail + */ +static int +woal_construct_tdls_data_frame(moal_private *priv, + t_u8 *peer, t_u8 action_code, t_u8 dialog_token, + t_u16 status_code, struct sk_buff *skb) +{ + + struct ieee80211_tdls_data *tf; + t_u16 capability; + IEEEtypes_HTCap_t *HTcap; + IEEEtypes_HTInfo_t *HTInfo; + IEEEtypes_2040BSSCo_t *BSSCo; + IEEEtypes_Generic_t *pSupp_chan = NULL, *pRegulatory_class = NULL; + mlan_ds_misc_tdls_ies *tdls_ies = NULL; + int ret = 0; + mlan_bss_info bss_info; + enum ieee80211_band band; + mlan_fw_info fw_info; + + ENTER(); + + memset(&bss_info, 0, sizeof(bss_info)); + if (MLAN_STATUS_SUCCESS != + woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info)) { + PRINTM(MERROR, "Fail to get bss info\n"); + LEAVE(); + return -EFAULT; + } + band = woal_band_cfg_to_ieee_band(bss_info.bss_band); + tdls_ies = kzalloc(sizeof(mlan_ds_misc_tdls_ies), GFP_KERNEL); + if (!tdls_ies) { + PRINTM(MERROR, "Fail to alloc memory for tdls_ies\n"); + LEAVE(); + return -ENOMEM; + } + + capability = 0x2421; + + tf = (void *)skb_put(skb, offsetof(struct ieee80211_tdls_data, u)); + memcpy(tf->da, peer, ETH_ALEN); + memcpy(tf->sa, priv->current_addr, ETH_ALEN); + tf->ether_type = cpu_to_be16(MLAN_ETHER_PKT_TYPE_TDLS_ACTION); + tf->payload_type = WLAN_TDLS_SNAP_RFTYPE; + woal_request_get_fw_info(priv, MOAL_IOCTL_WAIT, &fw_info); + + switch (action_code) { + case WLAN_TDLS_SETUP_REQUEST: + woal_tdls_get_ies(priv, peer, tdls_ies, + TDLS_IE_FLAGS_SETUP | TDLS_IE_FLAGS_EXTCAP | + TDLS_IE_FLAGS_HTCAP | + TDLS_IE_FLAGS_SUPP_CS_IE); + + tf->category = WLAN_CATEGORY_TDLS; + tf->action_code = 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(capability); + woal_add_supported_rates_ie(priv, skb, band); + woal_add_ext_supported_rates_ie(priv, skb, band); + break; + case WLAN_TDLS_SETUP_RESPONSE: + woal_tdls_get_ies(priv, peer, tdls_ies, + TDLS_IE_FLAGS_EXTCAP | TDLS_IE_FLAGS_HTCAP | + TDLS_IE_FLAGS_SUPP_CS_IE); + + tf->category = WLAN_CATEGORY_TDLS; + tf->action_code = 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(capability); + + woal_add_supported_rates_ie(priv, skb, band); + woal_add_ext_supported_rates_ie(priv, skb, band); + break; + case WLAN_TDLS_SETUP_CONFIRM: + woal_tdls_get_ies(priv, peer, tdls_ies, + TDLS_IE_FLAGS_EXTCAP | TDLS_IE_FLAGS_HTINFO | + TDLS_IE_FLAGS_QOS_INFO); + + tf->category = WLAN_CATEGORY_TDLS; + tf->action_code = 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: + tf->category = WLAN_CATEGORY_TDLS; + tf->action_code = 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: + tf->category = WLAN_CATEGORY_TDLS; + tf->action_code = WLAN_TDLS_DISCOVERY_REQUEST; + + skb_put(skb, sizeof(tf->u.discover_req)); + tf->u.discover_req.dialog_token = dialog_token; + break; + default: + ret = -EINVAL; + goto done; + } + + if (action_code == WLAN_TDLS_SETUP_REQUEST || + action_code == WLAN_TDLS_SETUP_RESPONSE) { + /* supported chanel ie */ + if (tdls_ies->supp_chan[0] == SUPPORTED_CHANNELS) { + pSupp_chan = + (void *)skb_put(skb, + sizeof(IEEEtypes_Header_t) + + tdls_ies->supp_chan[1]); + memset(pSupp_chan, 0, + sizeof(IEEEtypes_Header_t) + + tdls_ies->supp_chan[1]); + memcpy(pSupp_chan, tdls_ies->supp_chan, + sizeof(IEEEtypes_Header_t) + + tdls_ies->supp_chan[1]); + } + /* supported regulatory class ie */ + if (tdls_ies->regulatory_class[0] == REGULATORY_CLASS) { + pRegulatory_class = + (void *)skb_put(skb, + sizeof(IEEEtypes_Header_t) + + tdls_ies->regulatory_class[1]); + memset(pRegulatory_class, 0, + sizeof(IEEEtypes_Header_t) + + tdls_ies->regulatory_class[1]); + memcpy(pRegulatory_class, tdls_ies->regulatory_class, + sizeof(IEEEtypes_Header_t) + + tdls_ies->regulatory_class[1]); + } + woal_tdls_add_ext_capab(skb, tdls_ies); + } + + /* TODO we should fill in ht_cap and htinfo with correct value */ + switch (action_code) { + case WLAN_TDLS_SETUP_REQUEST: + case WLAN_TDLS_SETUP_RESPONSE: + /*HT capability */ + if (tdls_ies->ht_cap[0] == HT_CAPABILITY) { + HTcap = (void *)skb_put(skb, sizeof(IEEEtypes_HTCap_t)); + memset(HTcap, 0, sizeof(IEEEtypes_HTCap_t)); + memcpy(HTcap, tdls_ies->ht_cap, + sizeof(IEEEtypes_HTCap_t)); + } else { + PRINTM(MIOCTL, "No TDLS HT capability\n"); + } + + /*20_40_bss_coexist */ + BSSCo = (void *)skb_put(skb, sizeof(IEEEtypes_2040BSSCo_t)); + memset(BSSCo, 0, sizeof(IEEEtypes_2040BSSCo_t)); + BSSCo->ieee_hdr.element_id = BSSCO_2040; + BSSCo->ieee_hdr.len = + sizeof(IEEEtypes_2040BSSCo_t) - + sizeof(IEEEtypes_Header_t); + BSSCo->bss_co_2040.bss_co_2040_value = 0x01; + + break; + case WLAN_TDLS_SETUP_CONFIRM: + /*HT information */ + if (tdls_ies->ht_info[0] == HT_OPERATION) { + HTInfo = (void *)skb_put(skb, + sizeof(IEEEtypes_HTInfo_t)); + memset(HTInfo, 0, sizeof(IEEEtypes_HTInfo_t)); + memcpy(HTInfo, tdls_ies->ht_info, + sizeof(IEEEtypes_HTInfo_t)); + } else + PRINTM(MIOCTL, "No TDLS HT information\n"); + break; + default: + break; + } + + if (action_code == WLAN_TDLS_SETUP_REQUEST || + action_code == WLAN_TDLS_SETUP_RESPONSE) { + /*wmm info */ + woal_add_wmm_ie(priv, skb, WMM_TYPE_INFO, NULL); + } else if (action_code == WLAN_TDLS_SETUP_CONFIRM) { + /*wmm parameter */ + woal_add_wmm_ie(priv, skb, WMM_TYPE_PARAMETER, + &tdls_ies->QosInfo); + } + +done: + kfree(tdls_ies); + return ret; +} + +/** + * @brief woal construct tdls action frame + * + * @param priv A pointer to moal_private structure + * @param peer A pointer to peer mac + * @param action_code tdls action code + * @param dialog_token dialog_token + * @param status_code status_code + * @param skb skb buffer + * + * @return 0 -- success, otherwise fail + */ +static int +woal_construct_tdls_action_frame(moal_private *priv, + t_u8 *peer, t_u8 action_code, + t_u8 dialog_token, t_u16 status_code, + struct sk_buff *skb) +{ + struct ieee80211_mgmt *mgmt; + t_u8 addr[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + t_u16 capability; + t_u8 *pos = NULL; + mlan_ds_misc_tdls_ies *tdls_ies = NULL; + mlan_bss_info bss_info; + enum ieee80211_band band; + IEEEtypes_Generic_t *pSupp_chan = NULL, *pRegulatory_class = NULL; + + int ret = 0; + + ENTER(); + + memset(&bss_info, 0, sizeof(bss_info)); + if (MLAN_STATUS_SUCCESS != + woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info)) { + PRINTM(MERROR, "Fail to get bss info\n"); + LEAVE(); + return -EFAULT; + } + band = woal_band_cfg_to_ieee_band(bss_info.bss_band); + + tdls_ies = kzalloc(sizeof(mlan_ds_misc_tdls_ies), GFP_KERNEL); + if (!tdls_ies) { + PRINTM(MERROR, "Fail to alloc memory for tdls_ies\n"); + LEAVE(); + return -ENOMEM; + } + + mgmt = (void *)skb_put(skb, 24); + memset(mgmt, 0, 24); + memcpy(mgmt->da, peer, ETH_ALEN); + memcpy(mgmt->sa, priv->current_addr, ETH_ALEN); + memcpy(mgmt->bssid, priv->cfg_bssid, ETH_ALEN); + + mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | + IEEE80211_STYPE_ACTION); + /* add address 4 */ + pos = skb_put(skb, ETH_ALEN); + + capability = 0x2421; + + switch (action_code) { + case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: + woal_tdls_get_ies(priv, peer, tdls_ies, + TDLS_IE_FLAGS_EXTCAP | + TDLS_IE_FLAGS_SUPP_CS_IE); + 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(capability); + /* move back for addr4 */ + memmove(pos + ETH_ALEN, &mgmt->u.action.category, + 1 + sizeof(mgmt->u.action.u.tdls_discover_resp)); + /** init address 4 */ + memcpy(pos, addr, ETH_ALEN); + + woal_add_supported_rates_ie(priv, skb, band); + woal_add_ext_supported_rates_ie(priv, skb, band); + woal_tdls_add_ext_capab(skb, tdls_ies); + /* supported chanel ie */ + if (tdls_ies->supp_chan[0] == SUPPORTED_CHANNELS) { + pSupp_chan = + (void *)skb_put(skb, + sizeof(IEEEtypes_Header_t) + + tdls_ies->supp_chan[1]); + memset(pSupp_chan, 0, + sizeof(IEEEtypes_Header_t) + + tdls_ies->supp_chan[1]); + memcpy(pSupp_chan, tdls_ies->supp_chan, + sizeof(IEEEtypes_Header_t) + + tdls_ies->supp_chan[1]); + } + /* supported regulatory class ie */ + if (tdls_ies->regulatory_class[0] == REGULATORY_CLASS) { + pRegulatory_class = + (void *)skb_put(skb, + sizeof(IEEEtypes_Header_t) + + tdls_ies->regulatory_class[1]); + memset(pRegulatory_class, 0, + sizeof(IEEEtypes_Header_t) + + tdls_ies->regulatory_class[1]); + memcpy(pRegulatory_class, tdls_ies->regulatory_class, + sizeof(IEEEtypes_Header_t) + + tdls_ies->regulatory_class[1]); + } + + break; + default: + ret = -EINVAL; + break; + } + if (tdls_ies) + kfree(tdls_ies); + return ret; +} + +/** + * @brief woal add tdls link identifier ie + * + * @param skb skb buffer + * @param src_addr source address + * @param peer peer address + * @param bssid AP's bssid + * + * @return NA + */ +static void +woal_tdls_add_link_ie(struct sk_buff *skb, u8 *src_addr, u8 *peer, u8 *bssid) +{ + struct ieee80211_tdls_lnkie *lnkid; + + 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, bssid, ETH_ALEN); + memcpy(lnkid->init_sta, src_addr, ETH_ALEN); + memcpy(lnkid->resp_sta, peer, ETH_ALEN); +} + +/** + * @brief woal send tdls action frame + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param peer A pointer to peer mac + * @param action_code tdls action code + * @param dialog_token dialog_token + * @param status_code status_code + * @param extra_ies A pointer to extra ie buffer + * @param extra_ies_len etra ie len + * @param skb skb buffer + * + * @return 0 -- success, otherwise fail + */ +static int +woal_send_tdls_action_frame(struct wiphy *wiphy, struct net_device *dev, + t_u8 *peer, u8 action_code, t_u8 dialog_token, + t_u16 status_code, const t_u8 *extra_ies, + size_t extra_ies_len) +{ + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + pmlan_buffer pmbuf = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + struct sk_buff *skb = NULL; + t_u8 *pos; + t_u32 pkt_type; + t_u32 tx_control; + t_u16 pkt_len; + int ret = 0; + + ENTER(); + +#define HEADER_SIZE 8 /* pkt_type + tx_control */ + + pmbuf = woal_alloc_mlan_buffer(priv->phandle, MLAN_MIN_DATA_HEADER_LEN + HEADER_SIZE + sizeof(pkt_len) + max(sizeof(struct ieee80211_mgmt), sizeof(struct ieee80211_tdls_data)) + 50 + /* supported rates */ + sizeof(IEEEtypes_ExtCap_t) + /* ext capab */ + extra_ies_len + + sizeof(IEEEtypes_tdls_linkie)); + if (!pmbuf) { + PRINTM(MERROR, "Fail to allocate mlan_buffer\n"); + ret = -ENOMEM; + goto done; + } + + skb = (struct sk_buff *)pmbuf->pdesc; + + skb_put(skb, MLAN_MIN_DATA_HEADER_LEN); + + pos = skb_put(skb, HEADER_SIZE + sizeof(pkt_len)); + pkt_type = MRVL_PKT_TYPE_MGMT_FRAME; + tx_control = 0; + memset(pos, 0, HEADER_SIZE + sizeof(pkt_len)); + memcpy(pos, &pkt_type, sizeof(pkt_type)); + memcpy(pos + sizeof(pkt_type), &tx_control, sizeof(tx_control)); + + woal_construct_tdls_action_frame(priv, peer, action_code, + dialog_token, status_code, skb); + + if (extra_ies_len) + memcpy(skb_put(skb, extra_ies_len), extra_ies, extra_ies_len); + + /* the TDLS link IE is always added last */ + /* we are the responder */ + woal_tdls_add_link_ie(skb, peer, priv->current_addr, priv->cfg_bssid); + + /* + * According to 802.11z: Setup req/resp are sent in AC_BK, otherwise + * we should default to AC_VI. + */ + skb_set_queue_mapping(skb, WMM_AC_VI); + skb->priority = 5; + + pmbuf->data_offset = MLAN_MIN_DATA_HEADER_LEN; + pmbuf->data_len = skb->len - pmbuf->data_offset; + pmbuf->priority = skb->priority; + pmbuf->buf_type = MLAN_BUF_TYPE_RAW_DATA; + pmbuf->bss_index = priv->bss_index; + + pkt_len = pmbuf->data_len - HEADER_SIZE - sizeof(pkt_len); + memcpy(pmbuf->pbuf + pmbuf->data_offset + HEADER_SIZE, &pkt_len, + sizeof(pkt_len)); + + DBG_HEXDUMP(MDAT_D, "TDLS action:", pmbuf->pbuf + pmbuf->data_offset, + pmbuf->data_len); + + status = mlan_send_packet(priv->phandle->pmlan_adapter, pmbuf); + + switch (status) { + case MLAN_STATUS_PENDING: + atomic_inc(&priv->phandle->tx_pending); + queue_work(priv->phandle->workqueue, &priv->phandle->main_work); + break; + case MLAN_STATUS_SUCCESS: + woal_free_mlan_buffer(priv->phandle, pmbuf); + break; + case MLAN_STATUS_FAILURE: + default: + woal_free_mlan_buffer(priv->phandle, pmbuf); + ret = -EFAULT; + break; + } + +done: + LEAVE(); + return ret; +} + +/** + * @brief woal send tdls data frame + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param peer A pointer to peer mac + * @param action_code tdls action code + * @param dialog_token dialog_token + * @param status_code status_code + * @param extra_ies A pointer to extra ie buffer + * @param extra_ies_len etra ie len + * @param skb skb buffer + * + * @return 0 -- success, otherwise fail + */ +static int +woal_send_tdls_data_frame(struct wiphy *wiphy, struct net_device *dev, + t_u8 *peer, u8 action_code, t_u8 dialog_token, + t_u16 status_code, const t_u8 *extra_ies, + size_t extra_ies_len) +{ + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + pmlan_buffer pmbuf = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + struct sk_buff *skb = NULL; + int ret = 0; +#if CFG80211_VERSION_CODE > KERNEL_VERSION(2, 6, 29) + t_u32 index = 0; +#endif + + ENTER(); + + skb = dev_alloc_skb(priv->extra_tx_head_len + MLAN_MIN_DATA_HEADER_LEN + sizeof(mlan_buffer) + max(sizeof(struct ieee80211_mgmt), sizeof(struct ieee80211_tdls_data)) + 50 + /* supported rates */ + sizeof(IEEEtypes_ExtCap_t) + /* ext capab */ + 3 + /* Qos Info */ + sizeof(IEEEtypes_WmmParameter_t) + /*wmm ie */ + sizeof(IEEEtypes_HTCap_t) + + sizeof(IEEEtypes_2040BSSCo_t) + + sizeof(IEEEtypes_HTInfo_t) + extra_ies_len + + sizeof(IEEEtypes_tdls_linkie)); + if (!skb) + return -ENOMEM; + + skb_reserve(skb, + MLAN_MIN_DATA_HEADER_LEN + sizeof(mlan_buffer) + + priv->extra_tx_head_len); + + woal_construct_tdls_data_frame(priv, peer, + action_code, dialog_token, + status_code, skb); + + if (extra_ies_len) + memcpy(skb_put(skb, extra_ies_len), extra_ies, extra_ies_len); + + /* the TDLS link IE is always added last */ + switch (action_code) { + case WLAN_TDLS_SETUP_REQUEST: + case WLAN_TDLS_SETUP_CONFIRM: + case WLAN_TDLS_TEARDOWN: + case WLAN_TDLS_DISCOVERY_REQUEST: + /* we are the initiator */ + woal_tdls_add_link_ie(skb, priv->current_addr, peer, + priv->cfg_bssid); + break; + case WLAN_TDLS_SETUP_RESPONSE: + /* we are the responder */ + woal_tdls_add_link_ie(skb, peer, priv->current_addr, + priv->cfg_bssid); + break; + default: + ret = -ENOTSUPP; + goto fail; + } + + /* + * According to 802.11z: Setup req/resp are sent in AC_BK, otherwise + * we should default to AC_VI. + */ + switch (action_code) { + case WLAN_TDLS_SETUP_REQUEST: + case WLAN_TDLS_SETUP_RESPONSE: + skb_set_queue_mapping(skb, WMM_AC_BK); + skb->priority = 2; + break; + default: + skb_set_queue_mapping(skb, WMM_AC_VI); + skb->priority = 5; + break; + } + + pmbuf = (mlan_buffer *)skb->head; + memset((t_u8 *)pmbuf, 0, sizeof(mlan_buffer)); + pmbuf->bss_index = priv->bss_index; + pmbuf->pdesc = skb; + pmbuf->pbuf = skb->head + sizeof(mlan_buffer); + + pmbuf->data_offset = skb->data - (skb->head + sizeof(mlan_buffer)); + pmbuf->data_len = skb->len; + pmbuf->priority = skb->priority; + pmbuf->buf_type = MLAN_BUF_TYPE_DATA; + + DBG_HEXDUMP(MDAT_D, "TDLS data:", pmbuf->pbuf + pmbuf->data_offset, + pmbuf->data_len); + + status = mlan_send_packet(priv->phandle->pmlan_adapter, pmbuf); + + switch (status) { + case MLAN_STATUS_PENDING: + atomic_inc(&priv->phandle->tx_pending); +#if CFG80211_VERSION_CODE > KERNEL_VERSION(2, 6, 29) + index = skb_get_queue_mapping(skb); + atomic_inc(&priv->wmm_tx_pending[index]); +#endif + queue_work(priv->phandle->workqueue, &priv->phandle->main_work); + /*delay 10 ms to guarantee the teardown/confirm frame can be sent out before disalbe/enable tdls link + * if we don't delay and return immediately, wpa_supplicant will call disalbe/enable tdls link + * this may cause tdls link disabled/enabled before teardown/confirm frame sent out */ + if (action_code == WLAN_TDLS_TEARDOWN || + action_code == WLAN_TDLS_SETUP_CONFIRM) + woal_sched_timeout(10); + break; + case MLAN_STATUS_SUCCESS: + dev_kfree_skb(skb); + break; + case MLAN_STATUS_FAILURE: + default: + dev_kfree_skb(skb); + ret = -ENOTSUPP; + break; + } + + LEAVE(); + return ret; +fail: + dev_kfree_skb(skb); + return ret; +} + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 17, 0) +/** + * @brief Tx TDLS packet + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param peer A pointer to peer mac + * @param action_code tdls action code + * @param dialog_token dialog_token + * @param status_code status_code + * @param peer_capability peer capability + * @param initiator initiator + * @param extra_ies A pointer to extra ie buffer + * @param extra_ies_len etra ie len + * + * @return 0 -- success, otherwise fail + */ +int +woal_cfg80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, + const t_u8 *peer, + u8 action_code, t_u8 dialog_token, + t_u16 status_code, t_u32 peer_capability, + bool initiator, + const t_u8 *extra_ies, size_t extra_ies_len) +#else +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 15, 0) +/** + * @brief Tx TDLS packet + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param peer A pointer to peer mac + * @param action_code tdls action code + * @param dialog_token dialog_token + * @param status_code status_code + * @param peer_capability peer capability + * @param extra_ies A pointer to extra ie buffer + * @param extra_ies_len etra ie len + * + * @return 0 -- success, otherwise fail + */ +int +woal_cfg80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 16, 0) + const t_u8 *peer, +#else + t_u8 *peer, +#endif + u8 action_code, t_u8 dialog_token, + t_u16 status_code, t_u32 peer_capability, + const t_u8 *extra_ies, size_t extra_ies_len) +#else +/** + * @brief Tx TDLS packet + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param peer A pointer to peer mac + * @param action_code tdls action code + * @param dialog_token dialog_token + * @param status_code status_code + * @param extra_ies A pointer to extra ie buffer + * @param extra_ies_len etra ie len + * + * @return 0 -- success, otherwise fail + */ +int +woal_cfg80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, + t_u8 *peer, u8 action_code, t_u8 dialog_token, + t_u16 status_code, const t_u8 *extra_ies, + size_t extra_ies_len) +#endif +#endif +{ + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + int ret = 0; + mlan_bss_info bss_info; + + ENTER(); + + if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS)) { + LEAVE(); + return -ENOTSUPP; + } + /* make sure we are not in uAP mode and Go mode */ + if (priv->bss_type != MLAN_BSS_TYPE_STA) { + LEAVE(); + return -ENOTSUPP; + } + + /* check if AP prohited TDLS */ + memset(&bss_info, 0, sizeof(bss_info)); + woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info); + if (IS_EXTCAP_TDLS_PROHIBITED(bss_info.ext_cap)) { + PRINTM(MMSG, "TDLS is prohibited by AP\n"); + LEAVE(); + return -ENOTSUPP; + } + + switch (action_code) { + case TDLS_SETUP_REQUEST: + woal_add_tdls_peer(priv, (t_u8 *)peer); + PRINTM(MMSG, + "wlan: Send TDLS Setup Request to " MACSTR + " status_code=%d\n", MAC2STR(peer), status_code); + ret = woal_send_tdls_data_frame(wiphy, dev, (t_u8 *)peer, + action_code, dialog_token, + status_code, extra_ies, + extra_ies_len); + break; + case TDLS_SETUP_RESPONSE: + PRINTM(MMSG, + "wlan: Send TDLS Setup Response to " MACSTR + " status_code=%d\n", MAC2STR(peer), status_code); + ret = woal_send_tdls_data_frame(wiphy, dev, (t_u8 *)peer, + action_code, dialog_token, + status_code, extra_ies, + extra_ies_len); + break; + case TDLS_SETUP_CONFIRM: + PRINTM(MMSG, + "wlan: Send TDLS Confirm to " MACSTR " status_code=%d\n", + MAC2STR(peer), status_code); + ret = woal_send_tdls_data_frame(wiphy, dev, (t_u8 *)peer, + action_code, dialog_token, + status_code, extra_ies, + extra_ies_len); + break; + case TDLS_TEARDOWN: + PRINTM(MMSG, "wlan: Send TDLS Tear down to " MACSTR "\n", + MAC2STR(peer)); + ret = woal_send_tdls_data_frame(wiphy, dev, (t_u8 *)peer, + action_code, dialog_token, + status_code, extra_ies, + extra_ies_len); + break; + case TDLS_DISCOVERY_REQUEST: + PRINTM(MMSG, + "wlan: Send TDLS Discovery Request to " MACSTR "\n", + MAC2STR(peer)); + ret = woal_send_tdls_data_frame(wiphy, dev, (t_u8 *)peer, + action_code, dialog_token, + status_code, extra_ies, + extra_ies_len); + break; + case TDLS_DISCOVERY_RESPONSE: + PRINTM(MMSG, + "wlan: Send TDLS Discovery Response to " MACSTR "\n", + MAC2STR(peer)); + ret = woal_send_tdls_action_frame(wiphy, dev, (t_u8 *)peer, + action_code, dialog_token, + status_code, extra_ies, + extra_ies_len); + break; + default: + break; + } + + LEAVE(); + return ret; + +} + +/** + * @brief cfg80211_tdls_oper handler + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param peer tdls peer mac + * @param oper tdls operation code + * + * @return 0 -- success, otherwise fail + */ +int +woal_cfg80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 16, 0) + const u8 *peer, +#else + u8 *peer, +#endif + enum nl80211_tdls_operation oper) +{ + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + t_u8 action; + int ret = 0; + t_u8 event_buf[32]; + int custom_len = 0; + + ENTER(); + + if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS)) + return -ENOTSUPP; + + if (!(wiphy->flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP)) + return -ENOTSUPP; + /* make sure we are in managed mode, and associated */ + if (priv->bss_type != MLAN_BSS_TYPE_STA) + return -ENOTSUPP; + + PRINTM(MIOCTL, "wlan: TDLS peer=" MACSTR ", oper=%d\n", MAC2STR(peer), + oper); + switch (oper) { + case NL80211_TDLS_ENABLE_LINK: + /*Configure TDLS link first */ + woal_tdls_oper(priv, (u8 *)peer, WLAN_TDLS_CONFIG_LINK); + woal_updata_peer_status(priv, (t_u8 *)peer, + TDLS_SETUP_COMPLETE); + PRINTM(MMSG, "wlan: TDLS_ENABLE_LINK: peer=" MACSTR "\n", + MAC2STR(peer)); + action = WLAN_TDLS_ENABLE_LINK; + memset(event_buf, 0, sizeof(event_buf)); + custom_len = strlen(CUS_EVT_TDLS_CONNECTED); + memcpy(event_buf, CUS_EVT_TDLS_CONNECTED, custom_len); + memcpy(event_buf + custom_len, peer, ETH_ALEN); + woal_broadcast_event(priv, event_buf, custom_len + ETH_ALEN); + break; + case NL80211_TDLS_DISABLE_LINK: + woal_updata_peer_status(priv, (t_u8 *)peer, TDLS_NOT_SETUP); + PRINTM(MMSG, "wlan: TDLS_DISABLE_LINK: peer=" MACSTR "\n", + MAC2STR(peer)); + action = WLAN_TDLS_DISABLE_LINK; + memset(event_buf, 0, sizeof(event_buf)); + custom_len = strlen(CUS_EVT_TDLS_TEARDOWN); + memcpy(event_buf, CUS_EVT_TDLS_TEARDOWN, custom_len); + memcpy(event_buf + custom_len, peer, ETH_ALEN); + woal_broadcast_event(priv, event_buf, custom_len + ETH_ALEN); + break; + case NL80211_TDLS_TEARDOWN: + case NL80211_TDLS_SETUP: + case NL80211_TDLS_DISCOVERY_REQ: + return 0; + + default: + return -ENOTSUPP; + } + ret = woal_tdls_oper(priv, (u8 *)peer, action); + + LEAVE(); + return ret; +} + +/** + * @brief add station + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param mac A pointer to peer mac + * @param params station parameters + * + * @return 0 -- success, otherwise fail + */ +static int +woal_cfg80211_add_station(struct wiphy *wiphy, struct net_device *dev, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 16, 0) + const u8 *mac, +#else + u8 *mac, +#endif + struct station_parameters *params) +{ + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + int ret = 0; + ENTER(); + if (!(params->sta_flags_set & MBIT(NL80211_STA_FLAG_TDLS_PEER))) + goto done; + /* make sure we are in connected mode */ + if ((priv->bss_type != MLAN_BSS_TYPE_STA) || + (priv->media_connected == MFALSE)) { + ret = -ENOTSUPP; + goto done; + } + PRINTM(MMSG, "wlan: TDLS add peer station, address =" MACSTR "\n", + MAC2STR(mac)); + ret = woal_tdls_oper(priv, (u8 *)mac, WLAN_TDLS_CREATE_LINK); +done: + return ret; +} + +/** + * @brief change station info + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param mac A pointer to peer mac + * @param params station parameters + * + * @return 0 -- success, otherwise fail + */ +static int +woal_cfg80211_change_station(struct wiphy *wiphy, struct net_device *dev, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 16, 0) + const u8 *mac, +#else + u8 *mac, +#endif + struct station_parameters *params) +{ + int ret = 0; + + ENTER(); + + /**do nothing*/ + + LEAVE(); + return ret; +} + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) +/** + * @brief tdls channel switch + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param addr A pointer to peer addr + * @param oper_class The operating class + * @param chandef A pointer to cfg80211_chan_def structure + * + * @return 0 -- success, otherwise fail + */ +static int +woal_cfg80211_tdls_channel_switch(struct wiphy *wiphy, + struct net_device *dev, + const u8 *addr, u8 oper_class, + struct cfg80211_chan_def *chandef) +{ + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_misc_cfg *misc = NULL; + mlan_ds_misc_tdls_config *tdls_data = NULL; + tdls_all_config *tdls_all_cfg = NULL; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + mlan_bss_info bss_info; + + ENTER(); + + /* check if AP prohited TDLS channel switch */ + memset(&bss_info, 0, sizeof(bss_info)); + woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info); + if (IS_EXTCAP_TDLS_CHLSWITCHPROHIB(bss_info.ext_cap)) { + PRINTM(MMSG, "TDLS Channel Switching is prohibited by AP\n"); + LEAVE(); + return -ENOTSUPP; + } + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (ioctl_req == NULL) { + ret = -ENOMEM; + goto done; + } + + misc = (mlan_ds_misc_cfg *)ioctl_req->pbuf; + misc->sub_command = MLAN_OID_MISC_TDLS_OPER; + ioctl_req->req_id = MLAN_IOCTL_MISC_CFG; + ioctl_req->action = MLAN_ACT_SET; + + tdls_data = &misc->param.tdls_config; + tdls_data->tdls_action = WLAN_TDLS_INIT_CHAN_SWITCH; + + tdls_all_cfg = (tdls_all_config *)tdls_data->tdls_data; + memcpy(tdls_all_cfg->u.tdls_chan_switch.peer_mac_addr, addr, ETH_ALEN); + tdls_all_cfg->u.tdls_chan_switch.primary_channel = + chandef->chan->hw_value; + tdls_all_cfg->u.tdls_chan_switch.band = chandef->chan->band; + tdls_all_cfg->u.tdls_chan_switch.regulatory_class = oper_class; + + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "TDLS channel switch request failed.\n"); + ret = -EFAULT; + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + LEAVE(); + return ret; +} + +/** + * @brief tdls cancel channel switch + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param addr A pointer to peer addr + * + */ +void +woal_cfg80211_tdls_cancel_channel_switch(struct wiphy *wiphy, + struct net_device *dev, const u8 *addr) +{ + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_misc_cfg *misc = NULL; + mlan_ds_misc_tdls_config *tdls_data = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + int ret = 0; + + ENTER(); + + if (!priv || !priv->phandle) { + PRINTM(MERROR, "priv or handle is null\n"); + ret = -EFAULT; + goto done; + } + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (ioctl_req == NULL) { + ret = -ENOMEM; + goto done; + } + + misc = (mlan_ds_misc_cfg *)ioctl_req->pbuf; + misc->sub_command = MLAN_OID_MISC_TDLS_CONFIG; + ioctl_req->req_id = MLAN_IOCTL_MISC_CFG; + ioctl_req->action = MLAN_ACT_SET; + + tdls_data = &misc->param.tdls_config; + tdls_data->tdls_action = WLAN_TDLS_STOP_CHAN_SWITCH; + memcpy(tdls_data->tdls_data, addr, ETH_ALEN); + + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + PRINTM(MIOCTL, "Tdls channel switch stop!\n"); +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + + LEAVE(); +} +#endif +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3,10,0) +/** + * @brief Update ft ie for Fast BSS Transition + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param ftie A pointer to cfg80211_update_ft_ies_params structure + * + * @return 0 success , other failure + */ +int +woal_cfg80211_update_ft_ies(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_update_ft_ies_params *ftie) +{ + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + IEEEtypes_MobilityDomain_t *md_ie = NULL; + int ret = 0; + mlan_ds_misc_assoc_rsp assoc_rsp; + IEEEtypes_AssocRsp_t *passoc_rsp = NULL; + mlan_bss_info bss_info; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) + struct cfg80211_roam_info roam_info; +#endif + + ENTER(); + + if (!ftie) { + LEAVE(); + return ret; + } +#ifdef MLAN_64BIT + PRINTM(MINFO, "==>woal_cfg80211_update_ft_ies %lx \n", ftie->ie_len); +#else + PRINTM(MINFO, "==>woal_cfg80211_update_ft_ies %x \n", ftie->ie_len); +#endif + md_ie = (IEEEtypes_MobilityDomain_t *)woal_parse_ie_tlv(ftie->ie, + ftie->ie_len, + MOBILITY_DOMAIN); + if (!md_ie) { + PRINTM(MERROR, "No Mobility domain IE\n"); + LEAVE(); + return ret; + } + priv->ft_cap = md_ie->ft_cap; + memset(priv->ft_ie, 0, MAX_IE_SIZE); + memcpy(priv->ft_ie, ftie->ie, MIN(ftie->ie_len, MAX_IE_SIZE)); + priv->ft_ie_len = ftie->ie_len; + priv->ft_md = ftie->md; + + if (!priv->ft_pre_connect) { + LEAVE(); + return ret; + } + /* check if is different AP */ + if (!memcmp + (&priv->target_ap_bssid, priv->cfg_bssid, MLAN_MAC_ADDR_LENGTH)) { + PRINTM(MMSG, "This is the same AP, no Fast bss transition\n"); + priv->ft_pre_connect = MFALSE; + priv->ft_ie_len = 0; + LEAVE(); + return 0; + } + + /* start fast BSS transition to target AP */ + priv->assoc_status = 0; + priv->sme_current.bssid = priv->conn_bssid; + memcpy((void *)priv->sme_current.bssid, &priv->target_ap_bssid, + MLAN_MAC_ADDR_LENGTH); + ret = woal_cfg80211_assoc(priv, (void *)&priv->sme_current, + MOAL_IOCTL_WAIT); + + if ((priv->ft_cap & MBIT(0)) || priv->ft_roaming_triggered_by_driver) { + if (!ret) { + woal_inform_bss_from_scan_result(priv, NULL, + MOAL_IOCTL_WAIT); + memset(&assoc_rsp, 0, sizeof(mlan_ds_misc_assoc_rsp)); + woal_get_assoc_rsp(priv, &assoc_rsp, MOAL_IOCTL_WAIT); + passoc_rsp = + (IEEEtypes_AssocRsp_t *)assoc_rsp. + assoc_resp_buf; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) + memset(&roam_info, 0, + sizeof(struct cfg80211_roam_info)); + roam_info.bssid = priv->cfg_bssid; + roam_info.req_ie = priv->sme_current.ie; + roam_info.req_ie_len = priv->sme_current.ie_len; + roam_info.resp_ie = passoc_rsp->ie_buffer; + roam_info.resp_ie_len = + assoc_rsp.assoc_resp_len - + ASSOC_RESP_FIXED_SIZE; + cfg80211_roamed(priv->netdev, &roam_info, GFP_KERNEL); +#else +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) + cfg80211_roamed(priv->netdev, NULL, priv->cfg_bssid, + priv->sme_current.ie, + priv->sme_current.ie_len, + passoc_rsp->ie_buffer, + assoc_rsp.assoc_resp_len - + ASSOC_RESP_FIXED_SIZE, GFP_KERNEL); +#else + cfg80211_roamed(priv->netdev, priv->cfg_bssid, + priv->sme_current.ie, + priv->sme_current.ie_len, + passoc_rsp->ie_buffer, + assoc_rsp.assoc_resp_len - + ASSOC_RESP_FIXED_SIZE, GFP_KERNEL); +#endif +#endif + PRINTM(MMSG, + "Fast BSS transition to bssid " MACSTR + " successfully\n", MAC2STR(priv->cfg_bssid)); + } else { + PRINTM(MMSG, + "Fast BSS transition failed, keep connect to " + MACSTR " \n", MAC2STR(priv->cfg_bssid)); + priv->ft_ie_len = 0; + } + priv->ft_roaming_triggered_by_driver = MFALSE; + + } else { + PRINTM(MMSG, "Fast BSS Transition use ft-over-air\n"); + if (!ret) { + memset(&assoc_rsp, 0, sizeof(mlan_ds_misc_assoc_rsp)); + woal_get_assoc_rsp(priv, &assoc_rsp, MOAL_IOCTL_WAIT); + passoc_rsp = + (IEEEtypes_AssocRsp_t *)assoc_rsp. + assoc_resp_buf; + cfg80211_connect_result(priv->netdev, priv->cfg_bssid, + NULL, 0, passoc_rsp->ie_buffer, + assoc_rsp.assoc_resp_len - + ASSOC_RESP_FIXED_SIZE, + WLAN_STATUS_SUCCESS, + GFP_KERNEL); + PRINTM(MMSG, + "wlan: Fast Bss transition to bssid " MACSTR + " successfully\n", MAC2STR(priv->cfg_bssid)); + + memset(&bss_info, 0, sizeof(bss_info)); + woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info); + priv->channel = bss_info.bss_chan; + } else { + PRINTM(MMSG, + "wlan: Failed to connect to bssid " MACSTR "\n", + MAC2STR(priv->cfg_bssid)); + cfg80211_connect_result(priv->netdev, priv->cfg_bssid, + NULL, 0, NULL, 0, + woal_get_assoc_status(priv), + GFP_KERNEL); + memset(priv->cfg_bssid, 0, ETH_ALEN); + priv->ft_ie_len = 0; + } + } + + priv->ft_pre_connect = MFALSE; + LEAVE(); + return 0; +} +#endif + +/** + * @brief Save connect parameters for roaming + * + * @param priv A pointer to moal_private + * @param sme A pointer to cfg80211_connect_params structure + */ +void +woal_save_conn_params(moal_private *priv, struct cfg80211_connect_params *sme) +{ + ENTER(); + woal_clear_conn_params(priv); + memcpy(&priv->sme_current, sme, sizeof(struct cfg80211_connect_params)); + if (sme->channel) { + priv->sme_current.channel = &priv->conn_chan; + memcpy(priv->sme_current.channel, sme->channel, + sizeof(struct ieee80211_channel)); + } + if (sme->bssid) { + priv->sme_current.bssid = priv->conn_bssid; + memcpy((void *)priv->sme_current.bssid, sme->bssid, + MLAN_MAC_ADDR_LENGTH); + } + if (sme->ssid && sme->ssid_len) { + priv->sme_current.ssid = priv->conn_ssid; + memset(priv->conn_ssid, 0, MLAN_MAX_SSID_LENGTH); + memcpy((void *)priv->sme_current.ssid, sme->ssid, + sme->ssid_len); + } + if (sme->ie && sme->ie_len) { + priv->sme_current.ie = kzalloc(sme->ie_len, GFP_KERNEL); + memcpy((void *)priv->sme_current.ie, sme->ie, sme->ie_len); + } + if (sme->key && sme->key_len && (sme->key_len <= MAX_WEP_KEY_SIZE)) { + priv->sme_current.key = priv->conn_wep_key; + memcpy((t_u8 *)priv->sme_current.key, sme->key, sme->key_len); + } +} + +/** + * @brief clear connect parameters for ing + * + * @param priv A pointer to moal_private + */ +void +woal_clear_conn_params(moal_private *priv) +{ + ENTER(); + if (priv->sme_current.ie_len) + kfree(priv->sme_current.ie); + memset(&priv->sme_current, 0, sizeof(struct cfg80211_connect_params)); + priv->roaming_required = MFALSE; + LEAVE(); +} + +/** + * @brief Build new roaming connect ie for okc + * + * @param priv A pointer to moal_private + * @param entry A pointer to pmksa_entry + **/ +int +woal_update_okc_roaming_ie(moal_private *priv, struct pmksa_entry *entry) +{ + struct cfg80211_connect_params *sme = &priv->sme_current; + int ret = MLAN_STATUS_SUCCESS; + const t_u8 *sme_pos, *sme_ptr; + t_u8 *okc_ie_pos; + t_u8 id, ie_len; + int left_len; + + ENTER(); + + if (!sme->ie || !sme->ie_len) { + PRINTM(MERROR, "No connect ie saved in driver\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + if (!entry) { + PRINTM(MERROR, "No roaming ap pmkid\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + if (!priv->okc_roaming_ie) { + int okc_ie_len = sme->ie_len + sizeof(t_u16) + PMKID_LEN; + + /** Alloc new buffer for okc roaming ie */ + priv->okc_roaming_ie = kzalloc(okc_ie_len, GFP_KERNEL); + if (!priv->okc_roaming_ie) { + PRINTM(MERROR, "Fail to allocate assoc req ie\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + } + + /* Build OKC RSN IE with PMKID list + * Format of RSN IE: length(bytes) and container + * | 1| 1 | 2 | 4 | 2 | + * |id|len|version|group data cipher suite|pairwise cipher suite count| + * | 4 * m | 2 | 4 * n | 2 | + * |pairwise cipher suite list|AKM suite count|AKM suite list|RSN Cap | + * | 2 | 16 * s | 4 | + * |PMKIDCount|PMKID List|Group Management Cipher Suite| + */ +#define PAIRWISE_CIPHER_COUNT_OFFSET 8 +#define AKM_SUITE_COUNT_OFFSET(n) (10 + (n) * 4) +#define PMKID_COUNT_OFFSET(n) (14 + (n) * 4) + + sme_pos = sme->ie; + left_len = sme->ie_len; + okc_ie_pos = priv->okc_roaming_ie; + priv->okc_ie_len = 0; + + while (left_len >= 2) { + id = *sme_pos; + ie_len = *(sme_pos + 1); + if ((ie_len + 2) > left_len) { + PRINTM(MERROR, "Invalid ie len %d\n", ie_len); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + if (id == RSN_IE) { + t_u16 pairwise_count, akm_count; + t_u8 *rsn_ie_len; + int rsn_offset; + + pairwise_count = + *(t_u16 *)(sme_pos + + PAIRWISE_CIPHER_COUNT_OFFSET); + akm_count = + *(t_u16 *)(sme_pos + + AKM_SUITE_COUNT_OFFSET + (pairwise_count)); + rsn_offset = + PMKID_COUNT_OFFSET(pairwise_count + akm_count); + sme_ptr = (t_u8 *)(sme_pos + rsn_offset); + + memcpy(okc_ie_pos, sme_pos, rsn_offset); + rsn_ie_len = okc_ie_pos + 1; + okc_ie_pos += rsn_offset; + *(t_u16 *)okc_ie_pos = 1; + okc_ie_pos += sizeof(t_u16); + memcpy(okc_ie_pos, entry->pmkid, PMKID_LEN); + okc_ie_pos += PMKID_LEN; + priv->okc_ie_len += + rsn_offset + sizeof(t_u16) + PMKID_LEN; + *rsn_ie_len = + rsn_offset - 2 + sizeof(t_u16) + PMKID_LEN; + + if ((ie_len + 2) > rsn_offset) { + /** Previous conn ie include pmkid list */ + u16 pmkid_count = *(t_u16 *)sme_ptr; + rsn_offset += + (sizeof(t_u16) + + PMKID_LEN * pmkid_count); + if ((ie_len + 2) > rsn_offset) { + sme_ptr += + (sizeof(t_u16) + + PMKID_LEN * pmkid_count); + memcpy(okc_ie_pos, sme_ptr, + (ie_len + 2 - rsn_offset)); + okc_ie_pos += (ie_len + 2 - rsn_offset); + priv->okc_ie_len += + (ie_len + 2 - rsn_offset); + *rsn_ie_len += + (ie_len + 2 - rsn_offset); + } + } + } else { + memcpy(okc_ie_pos, sme_pos, ie_len + 2); + okc_ie_pos += ie_len + 2; + priv->okc_ie_len += ie_len + 2; + } + + sme_pos += (ie_len + 2); + left_len -= (ie_len + 2); + } + +done: + if (ret != MLAN_STATUS_SUCCESS) { + if (priv->okc_roaming_ie) { + kfree(priv->okc_roaming_ie); + priv->okc_roaming_ie = NULL; + priv->okc_ie_len = 0; + } + } + + LEAVE(); + return ret; +} + +/** + * @brief Start roaming: driver handle roaming + * + * @param priv A pointer to moal_private structure + * + * @return N/A + */ +void +woal_start_roaming(moal_private *priv) +{ + mlan_ds_get_signal signal; + mlan_ssid_bssid ssid_bssid; + char rssi_low[10]; + int ret = 0; + mlan_ds_misc_assoc_rsp assoc_rsp; + IEEEtypes_AssocRsp_t *passoc_rsp = NULL; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) + struct cfg80211_roam_info roam_info; +#endif + + ENTER(); + if (priv->ft_roaming_triggered_by_driver) { + PRINTM(MIOCTL, "FT roaming is in processing ...... \n"); + LEAVE(); + return; + } + + if (priv->last_event & EVENT_BG_SCAN_REPORT) { + woal_inform_bss_from_scan_result(priv, NULL, MOAL_IOCTL_WAIT); + PRINTM(MIOCTL, "Report bgscan result\n"); + } + if (priv->media_connected == MFALSE || !priv->sme_current.ssid_len) { + PRINTM(MIOCTL, "Not connected, ignore roaming\n"); + LEAVE(); + return; + } + + /* Get signal information from the firmware */ + memset(&signal, 0, sizeof(mlan_ds_get_signal)); + if (MLAN_STATUS_SUCCESS != + woal_get_signal_info(priv, MOAL_IOCTL_WAIT, &signal)) { + PRINTM(MERROR, "Error getting signal information\n"); + ret = -EFAULT; + goto done; + } + memset(&ssid_bssid, 0, sizeof(mlan_ssid_bssid)); + ssid_bssid.ssid.ssid_len = priv->sme_current.ssid_len; + memcpy(ssid_bssid.ssid.ssid, priv->sme_current.ssid, + priv->sme_current.ssid_len); + if (MLAN_STATUS_SUCCESS != + woal_find_best_network(priv, MOAL_IOCTL_WAIT, &ssid_bssid)) { + PRINTM(MIOCTL, "Can not find better network\n"); + ret = -EFAULT; + goto done; + } + /* check if we found different AP */ + if (!memcmp(&ssid_bssid.bssid, priv->cfg_bssid, MLAN_MAC_ADDR_LENGTH)) { + PRINTM(MIOCTL, "This is the same AP, no roaming\n"); + ret = -EFAULT; + goto done; + } + PRINTM(MIOCTL, "Find AP: bssid=" MACSTR ", signal=%d\n", + MAC2STR(ssid_bssid.bssid), ssid_bssid.rssi); + /* check signal */ + if (!(priv->last_event & EVENT_PRE_BCN_LOST)) { + if ((abs(signal.bcn_rssi_avg) - abs(ssid_bssid.rssi)) < + DELTA_RSSI) { + PRINTM(MERROR, "New AP's signal is not good too.\n"); + ret = -EFAULT; + goto done; + } + } +/**check if need start FT Roaming*/ + if (priv->ft_ie_len && (priv->ft_md == ssid_bssid.ft_md) && + (priv->ft_cap == ssid_bssid.ft_cap)) { + woal_start_ft_roaming(priv, &ssid_bssid); + goto done; + } + /* start roaming to new AP */ + priv->sme_current.bssid = priv->conn_bssid; + memcpy((void *)priv->sme_current.bssid, &ssid_bssid.bssid, + MLAN_MAC_ADDR_LENGTH); + +#ifdef STA_CFG80211 +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) + if (IS_STA_CFG80211(cfg80211_wext)) { + /** Check if current roaming support OKC offload roaming */ + if (priv->sme_current.crypto.n_akm_suites && + priv->sme_current.crypto.akm_suites[0] == + WLAN_AKM_SUITE_8021X) { + struct pmksa_entry *entry = NULL; + + /** Get OKC PMK Cache entry + * Firstly try to get pmksa from cfg80211 + */ + priv->wait_target_ap_pmkid = MTRUE; + cfg80211_pmksa_candidate_notify(priv->netdev, 0, + priv->sme_current.bssid, + MTRUE, GFP_ATOMIC); + if (wait_event_interruptible_timeout(priv->okc_wait_q, + !priv-> + wait_target_ap_pmkid, + OKC_WAIT_TARGET_PMKSA_TIMEOUT)) + { + PRINTM(MIOCTL, "OKC Roaming is ready\n"); + entry = priv->target_ap_pmksa; + } else { + /** Try to get pmksa from pmksa list */ + priv->wait_target_ap_pmkid = MFALSE; + entry = woal_get_pmksa_entry(priv, + priv->sme_current. + bssid); + } + /** Build okc roaming ie */ + woal_update_okc_roaming_ie(priv, entry); + priv->target_ap_pmksa = NULL; + } + } +#endif +#endif + + ret = woal_cfg80211_assoc(priv, (void *)&priv->sme_current, + MOAL_IOCTL_WAIT); + if (!ret) { + const t_u8 *ie; + int ie_len; + + woal_inform_bss_from_scan_result(priv, NULL, MOAL_IOCTL_WAIT); + memset(&assoc_rsp, 0, sizeof(mlan_ds_misc_assoc_rsp)); + woal_get_assoc_rsp(priv, &assoc_rsp, MOAL_IOCTL_WAIT); + passoc_rsp = (IEEEtypes_AssocRsp_t *)assoc_rsp.assoc_resp_buf; + + /** Update connect ie in roam event */ + ie = priv->sme_current.ie; + ie_len = priv->sme_current.ie_len; +#ifdef STA_CFG80211 + if (IS_STA_CFG80211(cfg80211_wext)) { + /** Check if current roaming support OKC offload roaming */ + if (priv->sme_current.crypto.n_akm_suites && + priv->sme_current.crypto.akm_suites[0] == + WLAN_AKM_SUITE_8021X) { + if (priv->okc_roaming_ie && priv->okc_ie_len) { + ie = priv->okc_roaming_ie; + ie_len = priv->okc_ie_len; + } + } + } +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) + memset(&roam_info, 0, sizeof(struct cfg80211_roam_info)); + roam_info.bssid = priv->cfg_bssid; + roam_info.req_ie = ie; + roam_info.req_ie_len = ie_len; + roam_info.resp_ie = passoc_rsp->ie_buffer; + roam_info.resp_ie_len = + assoc_rsp.assoc_resp_len - ASSOC_RESP_FIXED_SIZE; + cfg80211_roamed(priv->netdev, &roam_info, GFP_KERNEL); +#else +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) + cfg80211_roamed(priv->netdev, NULL, priv->cfg_bssid, ie, ie_len, + passoc_rsp->ie_buffer, + assoc_rsp.assoc_resp_len - + ASSOC_RESP_FIXED_SIZE, GFP_KERNEL); +#else + cfg80211_roamed(priv->netdev, priv->cfg_bssid, ie, ie_len, + passoc_rsp->ie_buffer, + assoc_rsp.assoc_resp_len - + ASSOC_RESP_FIXED_SIZE, GFP_KERNEL); +#endif +#endif + PRINTM(MMSG, "Roamed to bssid " MACSTR " successfully\n", + MAC2STR(priv->cfg_bssid)); + } else { + PRINTM(MIOCTL, "Roaming to bssid " MACSTR " failed\n", + MAC2STR(ssid_bssid.bssid)); + } +done: + /* config rssi low threshold again */ + priv->last_event = 0; + priv->rssi_low = DEFAULT_RSSI_LOW_THRESHOLD; + sprintf(rssi_low, "%d", priv->rssi_low); + woal_set_rssi_low_threshold(priv, rssi_low, MOAL_IOCTL_WAIT); + LEAVE(); + return; +} + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) +/** + * @brief This function update channel region config + * + * @param buf Buffer containing channel region config + * @param num_chan Length of buffer + * @param regd ieee80211_regdomain to be updated + * + * @return N/A + */ +static struct ieee80211_regdomain * +create_custom_regdomain(t_u8 *buf, t_u16 num_chan) +{ + struct ieee80211_reg_rule *rule; + bool new_rule; + int idx, freq, prev_freq = 0; + t_u32 bw, prev_bw = 0; + t_u8 chflags, prev_chflags = 0, valid_rules = 0; + struct ieee80211_regdomain *regd = NULL; + int regd_size; + + regd_size = sizeof(struct ieee80211_regdomain) + + num_chan * sizeof(struct ieee80211_reg_rule); + + regd = kzalloc(regd_size, GFP_KERNEL); + if (!regd) { + return NULL; + } + + for (idx = 0; idx < num_chan; idx++) { + t_u8 chan; + enum ieee80211_band band; + + chan = *buf++; + if (!chan) { + return NULL; + } + chflags = *buf++; + band = (chan <= 14) ? IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ; + freq = ieee80211_channel_to_frequency(chan, band); + new_rule = false; + + if (chflags & MARVELL_CHANNEL_DISABLED) + continue; + + if (band == IEEE80211_BAND_5GHZ) { + if (!(chflags & MARVELL_CHANNEL_NOHT80)) + bw = MHZ_TO_KHZ(80); + else if (!(chflags & MARVELL_CHANNEL_NOHT40)) + bw = MHZ_TO_KHZ(40); + else + bw = MHZ_TO_KHZ(20); + } else { + if (!(chflags & MARVELL_CHANNEL_NOHT40)) + bw = MHZ_TO_KHZ(40); + else + bw = MHZ_TO_KHZ(20); + } + + if (idx == 0 || prev_chflags != chflags || prev_bw != bw || + freq - prev_freq > 20) { + valid_rules++; + new_rule = true; + } + + rule = ®d->reg_rules[valid_rules - 1]; + + rule->freq_range.end_freq_khz = MHZ_TO_KHZ(freq + 10); + + prev_chflags = chflags; + prev_freq = freq; + prev_bw = bw; + + if (!new_rule) + continue; + + rule->freq_range.start_freq_khz = MHZ_TO_KHZ(freq - 10); + rule->power_rule.max_eirp = DBM_TO_MBM(19); +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + if (chflags & MARVELL_CHANNEL_PASSIVE) + rule->flags = NL80211_RRF_NO_IR; +#endif + if (chflags & MARVELL_CHANNEL_DFS) + rule->flags = NL80211_RRF_DFS; + + rule->freq_range.max_bandwidth_khz = bw; + } + + regd->n_reg_rules = valid_rules; + + /* set alpha2 from FW. */ + regd->alpha2[0] = '9'; + regd->alpha2[1] = '9'; + + return regd; +} + +/** + * @brief create custom channel regulatory config + * + * @param priv A pointer to moal_private structure + * + * @return if success pointer to ieee80211_regdomain, else NULL + */ +static struct ieee80211_regdomain * +woal_create_custom_regdomain(moal_private *priv) +{ + mlan_ds_misc_cfg *misc = NULL; + mlan_ioctl_req *req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + struct ieee80211_regdomain *regd = NULL; + t_u16 num_chan; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg) + + REGULATORY_CFG_LEN); + if (req == NULL) { + goto done; + } + misc = (mlan_ds_misc_cfg *)req->pbuf; + misc->sub_command = MLAN_OID_MISC_GET_CHAN_REGION_CFG; + req->req_id = MLAN_IOCTL_MISC_CFG; + req->action = MLAN_ACT_GET; + memset(&misc->param.custom_reg_domain, 0, + sizeof(misc->param.custom_reg_domain)); + /* Passing maximum buffer length to mlan */ + misc->param.custom_reg_domain.cfg_len = REGULATORY_CFG_LEN; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + goto done; + } + + if (misc->param.custom_reg_domain.cfg_len) { + num_chan = misc->param.custom_reg_domain.cfg_len / 2; + if (num_chan > NL80211_MAX_SUPP_REG_RULES) { + goto done; + } + + regd = create_custom_regdomain(misc->param.custom_reg_domain. + cfg_buf, num_chan); + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return regd; +} +#endif + +/** + * @brief Register the device with cfg80211 + * + * @param dev A pointer to net_device structure + * @param bss_type BSS type + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +woal_register_sta_cfg80211(struct net_device *dev, t_u8 bss_type) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + moal_private *priv = (moal_private *)netdev_priv(dev); + struct wireless_dev *wdev = NULL; + int psmode = 0; + + ENTER(); + + wdev = (struct wireless_dev *)&priv->w_dev; + memset(wdev, 0, sizeof(struct wireless_dev)); + wdev->wiphy = priv->phandle->wiphy; + if (!wdev->wiphy) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + if (bss_type == MLAN_BSS_TYPE_STA) { + wdev->iftype = NL80211_IFTYPE_STATION; + priv->roaming_enabled = MFALSE; + priv->roaming_required = MFALSE; + } +#if defined(WIFI_DIRECT_SUPPORT) +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION + if (bss_type == MLAN_BSS_TYPE_WIFIDIRECT) + wdev->iftype = NL80211_IFTYPE_STATION; +#endif +#endif + if (bss_type == MLAN_BSS_TYPE_NAN) + wdev->iftype = NL80211_IFTYPE_STATION; + dev_net_set(dev, wiphy_net(wdev->wiphy)); + dev->ieee80211_ptr = wdev; + SET_NETDEV_DEV(dev, wiphy_dev(wdev->wiphy)); + priv->wdev = wdev; + /* Get IEEE power save mode */ + if (MLAN_STATUS_SUCCESS == + woal_set_get_power_mgmt(priv, MLAN_ACT_GET, &psmode, 0, + MOAL_IOCTL_WAIT)) { + /* Save the IEEE power save mode to wiphy, because after + * warmreset wiphy power save should be updated instead + * of using the last saved configuration */ + if (psmode) + priv->wdev->ps = MTRUE; + else + priv->wdev->ps = MFALSE; + } + woal_send_domain_info_cmd_fw(priv, MOAL_IOCTL_WAIT); + LEAVE(); + return ret; +} + +/** + * @brief Initialize the wiphy + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +woal_cfg80211_init_wiphy(moal_private *priv, t_u8 wait_option) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + int retry_count, rts_thr, frag_thr; + struct wiphy *wiphy = NULL; + mlan_ioctl_req *req = NULL; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 38) + mlan_ds_radio_cfg *radio = NULL; +#endif + mlan_ds_11n_cfg *cfg_11n = NULL; + t_u32 hw_dev_cap; +#ifdef UAP_SUPPORT + mlan_uap_bss_param *sys_cfg = NULL; +#endif +#if CFG80211_VERSION_CODE > KERNEL_VERSION(3, 0, 0) + t_u16 enable = 0; +#endif + + ENTER(); + + wiphy = priv->phandle->wiphy; + /* Get 11n tx parameters from MLAN */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + cfg_11n = (mlan_ds_11n_cfg *)req->pbuf; + cfg_11n->sub_command = MLAN_OID_11N_HTCAP_CFG; + req->req_id = MLAN_IOCTL_11N_CFG; + req->action = MLAN_ACT_GET; + cfg_11n->param.htcap_cfg.hw_cap_req = MTRUE; + + ret = woal_request_ioctl(priv, req, wait_option); + if (ret != MLAN_STATUS_SUCCESS) + goto done; + hw_dev_cap = cfg_11n->param.htcap_cfg.htcap; + + /* Get supported MCS sets */ + memset(req->pbuf, 0, sizeof(mlan_ds_11n_cfg)); + cfg_11n->sub_command = MLAN_OID_11N_CFG_SUPPORTED_MCS_SET; + req->req_id = MLAN_IOCTL_11N_CFG; + req->action = MLAN_ACT_GET; + + ret = woal_request_ioctl(priv, req, wait_option); + if (ret != MLAN_STATUS_SUCCESS) + goto done; + + /* Initialize parameters for 2GHz and 5GHz bands */ + if (wiphy->bands[IEEE80211_BAND_2GHZ]) + woal_cfg80211_setup_ht_cap(&wiphy->bands[IEEE80211_BAND_2GHZ]-> + ht_cap, hw_dev_cap, + cfg_11n->param.supported_mcs_set); + /* For 2.4G band only card, this shouldn't be set */ + if (wiphy->bands[IEEE80211_BAND_5GHZ]) { + woal_cfg80211_setup_ht_cap(&wiphy->bands[IEEE80211_BAND_5GHZ]-> + ht_cap, hw_dev_cap, + cfg_11n->param.supported_mcs_set); + } + kfree(req); + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 38) + /* Get antenna modes */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_radio_cfg)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + radio = (mlan_ds_radio_cfg *)req->pbuf; + radio->sub_command = MLAN_OID_ANT_CFG; + req->req_id = MLAN_IOCTL_RADIO_CFG; + req->action = MLAN_ACT_GET; + + ret = woal_request_ioctl(priv, req, wait_option); + if (ret != MLAN_STATUS_SUCCESS) + goto done; + + /* Set available antennas to wiphy */ + wiphy->available_antennas_tx = radio->param.ant_cfg_1x1.antenna; + wiphy->available_antennas_rx = radio->param.ant_cfg_1x1.antenna; +#endif /* CFG80211_VERSION_CODE */ + + /* Set retry limit count to wiphy */ + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) { + if (MLAN_STATUS_SUCCESS != + woal_set_get_retry(priv, MLAN_ACT_GET, wait_option, + &retry_count)) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + } +#ifdef UAP_SUPPORT + else { + sys_cfg = kzalloc(sizeof(mlan_uap_bss_param), GFP_ATOMIC); + if (!sys_cfg) { + PRINTM(MERROR, + "Fail to alloc memory for mlan_uap_bss_param\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + if (MLAN_STATUS_SUCCESS != + woal_set_get_sys_config(priv, MLAN_ACT_GET, wait_option, + sys_cfg)) { + ret = MLAN_STATUS_FAILURE; + kfree(sys_cfg); + goto done; + } + retry_count = sys_cfg->retry_limit; + kfree(sys_cfg); + } +#endif + wiphy->retry_long = (t_u8)retry_count; + wiphy->retry_short = (t_u8)retry_count; + wiphy->max_scan_ie_len = MAX_IE_SIZE; + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 37) + wiphy->mgmt_stypes = ieee80211_mgmt_stypes; +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + wiphy->max_remain_on_channel_duration = MAX_REMAIN_ON_CHANNEL_DURATION; +#endif /* KERNEL_VERSION */ + + /* Set RTS threshold to wiphy */ + if (MLAN_STATUS_SUCCESS != + woal_set_get_rts(priv, MLAN_ACT_GET, wait_option, &rts_thr)) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + if (rts_thr < MLAN_RTS_MIN_VALUE || rts_thr > MLAN_RTS_MAX_VALUE) + rts_thr = MLAN_FRAG_RTS_DISABLED; + wiphy->rts_threshold = (t_u32)rts_thr; + + /* Set fragment threshold to wiphy */ + if (MLAN_STATUS_SUCCESS != + woal_set_get_frag(priv, MLAN_ACT_GET, wait_option, &frag_thr)) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + if (frag_thr < MLAN_RTS_MIN_VALUE || frag_thr > MLAN_RTS_MAX_VALUE) + frag_thr = MLAN_FRAG_RTS_DISABLED; + wiphy->frag_threshold = (t_u32)frag_thr; +#if CFG80211_VERSION_CODE > KERNEL_VERSION(3, 0, 0) + /* Enable multi-channel by default if multi-channel is supported */ + if (cfg80211_iface_comb_ap_sta.num_different_channels > 1) + enable = 1; + ret = woal_mc_policy_cfg(priv, &enable, wait_option, MLAN_ACT_SET); +#endif + +done: + LEAVE(); + if (ret != MLAN_STATUS_PENDING) + kfree(req); + return ret; +} + +/* + * This function registers the device with CFG802.11 subsystem. + * + * @param priv A pointer to moal_private + * + */ +mlan_status +woal_register_cfg80211(moal_private *priv) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + struct wiphy *wiphy; + void *wdev_priv = NULL; + mlan_fw_info fw_info; + char *country = NULL; + int index = 0; + + ENTER(); + + woal_request_get_fw_info(priv, MOAL_IOCTL_WAIT, &fw_info); + + wiphy = wiphy_new(&woal_cfg80211_ops, sizeof(moal_handle *)); + if (!wiphy) { + PRINTM(MERROR, "Could not allocate wiphy device\n"); + ret = MLAN_STATUS_FAILURE; + goto err_wiphy; + } +#ifdef CONFIG_PM +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 11, 0) + if (fw_info.fw_supplicant_support) + wiphy->wowlan = &wowlan_support_with_gtk; + else + wiphy->wowlan = &wowlan_support; +#else + wiphy->wowlan.flags = WIPHY_WOWLAN_ANY | WIPHY_WOWLAN_MAGIC_PKT; + if (fw_info.fw_supplicant_support) { + wiphy->wowlan.flags |= + WIPHY_WOWLAN_SUPPORTS_GTK_REKEY | + WIPHY_WOWLAN_GTK_REKEY_FAILURE; + } + wiphy->wowlan.n_patterns = MAX_NUM_FILTERS; + wiphy->wowlan.pattern_min_len = 1; + wiphy->wowlan.pattern_max_len = WOWLAN_MAX_PATTERN_LEN; + wiphy->wowlan.max_pkt_offset = WOWLAN_MAX_OFFSET_LEN; +#endif +#endif +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) + wiphy->coalesce = &coalesce_support; +#endif + wiphy->max_scan_ssids = MRVDRV_MAX_SSID_LIST_LENGTH; + wiphy->max_scan_ie_len = MAX_IE_SIZE; + wiphy->interface_modes = 0; + wiphy->interface_modes = + MBIT(NL80211_IFTYPE_STATION) | MBIT(NL80211_IFTYPE_ADHOC) | + MBIT(NL80211_IFTYPE_AP); + wiphy->interface_modes |= MBIT(NL80211_IFTYPE_MONITOR); + +#if defined(WIFI_DIRECT_SUPPORT) +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION + wiphy->interface_modes |= MBIT(NL80211_IFTYPE_P2P_GO) | + MBIT(NL80211_IFTYPE_P2P_CLIENT); +#endif +#endif + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + woal_register_cfg80211_vendor_command(wiphy); +#endif + /* Make this wiphy known to this driver only */ + wiphy->privid = mrvl_wiphy_privid; + + if (!fw_info.fw_bands) + fw_info.fw_bands = BAND_B | BAND_G; + if (fw_info.fw_bands & BAND_A) { + wiphy->bands[IEEE80211_BAND_5GHZ] = &cfg80211_band_5ghz; + priv->phandle->band = IEEE80211_BAND_5GHZ; + } + /* Supported bands */ + if (fw_info.fw_bands & (BAND_B | BAND_G | BAND_GN)) { + wiphy->bands[IEEE80211_BAND_2GHZ] = &cfg80211_band_2ghz; + /* If 2.4G enable, it will overwrite default to 2.4G */ + priv->phandle->band = IEEE80211_BAND_2GHZ; + } + + if (fw_info.fw_bands & BAND_A) { + /** reduce scan time from 110ms to 80ms */ + woal_set_scan_time(priv, INIT_ACTIVE_SCAN_CHAN_TIME, + INIT_PASSIVE_SCAN_CHAN_TIME, + INIT_SPECIFIC_SCAN_CHAN_TIME); +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) + cfg80211_iface_comb_ap_sta.radar_detect_widths |= + MBIT(NL80211_CHAN_WIDTH_40); +#endif + } else + woal_set_scan_time(priv, ACTIVE_SCAN_CHAN_TIME, + PASSIVE_SCAN_CHAN_TIME, + SPECIFIC_SCAN_CHAN_TIME); + + /* Initialize cipher suits */ + wiphy->cipher_suites = cfg80211_cipher_suites; + wiphy->n_cipher_suites = ARRAY_SIZE(cfg80211_cipher_suites); +#ifdef UAP_CFG80211 +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 9, 0) + wiphy->max_acl_mac_addrs = MAX_MAC_FILTER_NUM; +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 15, 0) + if (fw_info.max_ap_assoc_sta) + wiphy->max_ap_assoc_sta = fw_info.max_ap_assoc_sta; +#endif +#endif + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) + if (cfg80211_drcs) { + cfg80211_iface_comb_ap_sta.num_different_channels = 2; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) + cfg80211_iface_comb_ap_sta.radar_detect_widths = 0; +#endif + } + /* Initialize interface combinations */ + wiphy->iface_combinations = &cfg80211_iface_comb_ap_sta; + wiphy->n_iface_combinations = 1; +#endif + + memcpy(wiphy->perm_addr, priv->current_addr, ETH_ALEN); + wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; + + wiphy->flags = 0; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 33) + wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT; +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 3, 0) + wiphy->flags |= + WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | WIPHY_FLAG_OFFCHAN_TX; + wiphy->flags |= + WIPHY_FLAG_HAVE_AP_SME | WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD; + wiphy->flags |= WIPHY_FLAG_AP_UAPSD; +#endif +#ifdef ANDROID_KERNEL + wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME; +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) +#if CFG80211_VERSION_CODE < KERNEL_VERSION(4, 12, 0) + wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN; +#else + wiphy->max_sched_scan_reqs = 1; +#endif + wiphy->max_sched_scan_ssids = MRVDRV_MAX_SSID_LIST_LENGTH; + wiphy->max_sched_scan_ie_len = MAX_IE_SIZE; + wiphy->max_match_sets = MRVDRV_MAX_SSID_LIST_LENGTH; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 4, 0) + wiphy->max_sched_scan_plans = 1; +#endif +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3,2,0) + wiphy->flags |= + WIPHY_FLAG_SUPPORTS_TDLS | WIPHY_FLAG_TDLS_EXTERNAL_SETUP; +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3,19,0) + wiphy->features |= NL80211_FEATURE_TDLS_CHANNEL_SWITCH; +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3,4,0) + wiphy->features |= NL80211_FEATURE_HT_IBSS; +#endif + wiphy->reg_notifier = woal_cfg80211_reg_notifier; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) + wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH; +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 16, 0) + /* Indicate to cfg80211 that the driver can support + * CSA and ESCA,i.e., both types of channel switch + * Applications like hostapd 2.6 will append CSA IE + * and ECSA IE and expect the driver to advertise 2 + * in max_num_csa_counters to successfully issue a + * channel switch + */ + wiphy->max_num_csa_counters = MAX_CSA_COUNTERS_NUM; +#endif + wiphy->flags |= WIPHY_FLAG_CONTROL_PORT_PROTOCOL; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 8, 0) + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_SET_SCAN_DWELL); +#endif + /* Set struct moal_handle pointer in wiphy_priv */ + wdev_priv = wiphy_priv(wiphy); + *(unsigned long *)wdev_priv = (unsigned long)priv->phandle; + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + set_wiphy_dev(wiphy, (struct device *)priv->phandle->hotplug_device); +#endif + /* Set phy name */ + for (index = 0; index < MAX_MLAN_ADAPTER; index++) { + if (m_handle[index] == priv->phandle) { + dev_set_name(&wiphy->dev, mwiphy_name, index); + break; + } + } + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + if (beacon_hints) { + /* REGULATORY_DISABLE_BEACON_HINTS: NO-IR flag won't be removed on chn where an AP is visible! */ + wiphy->regulatory_flags |= REGULATORY_DISABLE_BEACON_HINTS; + } + if (country_ie_ignore) { + PRINTM(MIOCTL, "Don't follow countryIE provided by AP.\n"); + wiphy->regulatory_flags |= REGULATORY_COUNTRY_IE_IGNORE; + } else { + PRINTM(MIOCTL, "Follow countryIE provided by AP.\n"); + } +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + if (fw_region) { + priv->phandle->regd = woal_create_custom_regdomain(priv); + if (priv->phandle->regd) { + wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG | + REGULATORY_DISABLE_BEACON_HINTS | + REGULATORY_COUNTRY_IE_IGNORE; + wiphy_apply_custom_regulatory(wiphy, + priv->phandle->regd); + } else { + PRINTM(MERROR, + "creating custom regulatory domain failed\n"); + } + } +#endif + if (reg_alpha2 && !strncmp(reg_alpha2, "99", strlen("99"))) { +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG | + REGULATORY_DISABLE_BEACON_HINTS | + REGULATORY_COUNTRY_IE_IGNORE; +#else + wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY; +#endif + wiphy_apply_custom_regulatory(wiphy, &mrvl_regdom); + } +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 9, 0) + if (woal_request_extcap(priv, + (t_u8 *)&priv->extended_capabilities, + sizeof(priv->extended_capabilities)) < 0) + PRINTM(MERROR, + "Failed to get driver extended capability, use default\n"); + DBG_HEXDUMP(MCMD_D, "wiphy ext cap", + (t_u8 *)&priv->extended_capabilities, + sizeof(priv->extended_capabilities)); + wiphy->extended_capabilities = (t_u8 *)&priv->extended_capabilities; + wiphy->extended_capabilities_mask = + (t_u8 *)&priv->extended_capabilities; + wiphy->extended_capabilities_len = sizeof(priv->extended_capabilities); +#endif + if (wiphy_register(wiphy) < 0) { + PRINTM(MERROR, "Wiphy device registration failed!\n"); + ret = MLAN_STATUS_FAILURE; + goto err_wiphy; + } +#if defined(WIFI_DIRECT_SUPPORT) +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION + if (!p2p_enh) + wiphy->interface_modes &= ~(MBIT(NL80211_IFTYPE_P2P_GO) | + MBIT(NL80211_IFTYPE_P2P_CLIENT)); +#endif +#endif + wiphy->interface_modes &= ~(MBIT(NL80211_IFTYPE_MONITOR)); + + if ((!reg_alpha2 || strncmp(reg_alpha2, "99", strlen("99"))) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + && !priv->phandle->regd +#endif + ) { + /** we will try driver parameter first */ + if (reg_alpha2 && woal_is_valid_alpha2(reg_alpha2)) { + PRINTM(MIOCTL, "Notify reg_alpha2 %c%c\n", + reg_alpha2[0], reg_alpha2[1]); + regulatory_hint(wiphy, reg_alpha2); + } else { + country = region_code_2_string(fw_info.region_code); + if (country) { + if (fw_info.region_code != 0) { + PRINTM(MIOCTL, + "Notify hw region code=%d %c%c\n", + fw_info.region_code, country[0], + country[1]); + regulatory_hint(wiphy, country); + } + } else + PRINTM(MERROR, + "hw region code=%d not supported\n", + fw_info.region_code); + } + } + priv->phandle->wiphy = wiphy; + woal_cfg80211_init_wiphy(priv, MOAL_IOCTL_WAIT); + + return ret; +err_wiphy: + if (wiphy) + wiphy_free(wiphy); + LEAVE(); + return ret; +} + +module_param(cfg80211_drcs, int, 0); +MODULE_PARM_DESC(cfg80211_drcs, + "1: Enable DRCS support; 0: Disable DRCS support"); +module_param(reg_alpha2, charp, 0660); +MODULE_PARM_DESC(reg_alpha2, "Regulatory alpha2"); +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) +module_param(country_ie_ignore, int, 0); +MODULE_PARM_DESC(country_ie_ignore, + "0: Follow countryIE from AP and beacon hint enable; 1: Ignore countryIE from AP and beacon hint disable"); +module_param(beacon_hints, int, 0); +MODULE_PARM_DESC(beacon_hints, + "0: enable beacon hints(default); 1: disable beacon hints"); +#endif diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_sta_cfg80211.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_sta_cfg80211.h new file mode 100644 index 000000000000..b1048b8c30be --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_sta_cfg80211.h @@ -0,0 +1,42 @@ +/** @file moal_sta_cfg80211.h + * + * @brief This file contains the STA CFG80211 specific defines. + * + * Copyright (C) 2014-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +#ifndef _MOAL_STA_CFG80211_H_ +#define _MOAL_STA_CFG80211_H_ + +/** Convert RSSI signal strength from dBm to mBm (100*dBm) */ +#define RSSI_DBM_TO_MDM(x) ((x) * 100) + +mlan_status woal_register_sta_cfg80211(struct net_device *dev, t_u8 bss_type); + +mlan_status + +woal_cfg80211_set_key(moal_private *priv, t_u8 is_enable_wep, + t_u32 cipher, const t_u8 *key, int key_len, + const t_u8 *seq, int seq_len, t_u8 key_index, + const t_u8 *addr, int disable, t_u8 wait_option); + +mlan_status + +woal_cfg80211_set_wep_keys(moal_private *priv, const t_u8 *key, int key_len, + t_u8 index, t_u8 wait_option); + +#endif /* _MOAL_STA_CFG80211_H_ */ diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_uap.c b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_uap.c new file mode 100644 index 000000000000..84db3c833aee --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_uap.c @@ -0,0 +1,3540 @@ +/** @file moal_uap.c + * + * @brief This file contains the major functions in UAP + * driver. + * + * Copyright (C) 2008-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +/******************************************************** +Change log: + 10/21/2008: initial version +********************************************************/ + +#include "moal_main.h" +#include "moal_uap.h" +#include "moal_sdio.h" +#include "moal_eth_ioctl.h" +#if defined(STA_CFG80211) && defined(UAP_CFG80211) +#include "moal_cfg80211.h" +#endif + +/******************************************************** + Local Variables +********************************************************/ + +/******************************************************** + Global Variables +********************************************************/ +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) +extern int dfs_offload; +#endif +/******************************************************** + Local Functions +********************************************************/ +/** + * @brief uap addba parameter handler + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int +woal_uap_addba_param(struct net_device *dev, struct ifreq *req) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_11n_cfg *cfg_11n = NULL; + addba_param param; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + memset(¶m, 0, sizeof(param)); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "uap_addba_param() corrupt data\n"); + ret = -EFAULT; + goto done; + } + if (copy_from_user(¶m, req->ifr_data, sizeof(param))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + PRINTM(MIOCTL, + "addba param: action=%d, timeout=%d, txwinsize=%d, rxwinsize=%d txamsdu=%d rxamsdu=%d\n", + (int)param.action, (int)param.timeout, (int)param.txwinsize, + (int)param.rxwinsize, (int)param.txamsdu, (int)param.rxamsdu); + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); + if (ioctl_req == NULL) { + LEAVE(); + return -ENOMEM; + } + cfg_11n = (mlan_ds_11n_cfg *)ioctl_req->pbuf; + cfg_11n->sub_command = MLAN_OID_11N_CFG_ADDBA_PARAM; + ioctl_req->req_id = MLAN_IOCTL_11N_CFG; + + if (!param.action) + /* Get addba param from MLAN */ + ioctl_req->action = MLAN_ACT_GET; + else { + /* Set addba param in MLAN */ + ioctl_req->action = MLAN_ACT_SET; + cfg_11n->param.addba_param.timeout = param.timeout; + cfg_11n->param.addba_param.txwinsize = param.txwinsize; + cfg_11n->param.addba_param.rxwinsize = param.rxwinsize; + cfg_11n->param.addba_param.txamsdu = param.txamsdu; + cfg_11n->param.addba_param.rxamsdu = param.rxamsdu; + } + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + param.timeout = cfg_11n->param.addba_param.timeout; + param.txwinsize = cfg_11n->param.addba_param.txwinsize; + param.rxwinsize = cfg_11n->param.addba_param.rxwinsize; + param.txamsdu = cfg_11n->param.addba_param.txamsdu; + param.rxamsdu = cfg_11n->param.addba_param.rxamsdu; + + /* Copy to user */ + if (copy_to_user(req->ifr_data, ¶m, sizeof(param))) { + PRINTM(MERROR, "Copy to user failed!\n"); + ret = -EFAULT; + goto done; + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + LEAVE(); + return ret; +} + +/** + * @brief uap aggr priority tbl + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int +woal_uap_aggr_priotbl(struct net_device *dev, struct ifreq *req) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_11n_cfg *cfg_11n = NULL; + aggr_prio_tbl param; + int ret = 0; + int i = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + memset(¶m, 0, sizeof(param)); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "woal_uap_aggr_priotbl() corrupt data\n"); + ret = -EFAULT; + goto done; + } + if (copy_from_user(¶m, req->ifr_data, sizeof(param))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + DBG_HEXDUMP(MCMD_D, "aggr_prio_tbl", (t_u8 *)¶m, sizeof(param)); + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); + if (ioctl_req == NULL) { + LEAVE(); + return -ENOMEM; + } + cfg_11n = (mlan_ds_11n_cfg *)ioctl_req->pbuf; + cfg_11n->sub_command = MLAN_OID_11N_CFG_AGGR_PRIO_TBL; + ioctl_req->req_id = MLAN_IOCTL_11N_CFG; + + if (!param.action) { + /* Get aggr_prio_tbl from MLAN */ + ioctl_req->action = MLAN_ACT_GET; + } else { + /* Set aggr_prio_tbl in MLAN */ + ioctl_req->action = MLAN_ACT_SET; + for (i = 0; i < MAX_NUM_TID; i++) { + cfg_11n->param.aggr_prio_tbl.ampdu[i] = param.ampdu[i]; + cfg_11n->param.aggr_prio_tbl.amsdu[i] = param.amsdu[i]; + } + } + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + for (i = 0; i < MAX_NUM_TID; i++) { + param.ampdu[i] = cfg_11n->param.aggr_prio_tbl.ampdu[i]; + param.amsdu[i] = cfg_11n->param.aggr_prio_tbl.amsdu[i]; + } + /* Copy to user */ + if (copy_to_user(req->ifr_data, ¶m, sizeof(param))) { + PRINTM(MERROR, "Copy to user failed!\n"); + ret = -EFAULT; + goto done; + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + LEAVE(); + return ret; +} + +/** + * @brief uap addba reject tbl + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int +woal_uap_addba_reject(struct net_device *dev, struct ifreq *req) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_11n_cfg *cfg_11n = NULL; + addba_reject_para param; + int ret = 0; + int i = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + memset(¶m, 0, sizeof(param)); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "woal_uap_addba_reject() corrupt data\n"); + ret = -EFAULT; + goto done; + } + if (copy_from_user(¶m, req->ifr_data, sizeof(param))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + DBG_HEXDUMP(MCMD_D, "addba_reject tbl", (t_u8 *)¶m, sizeof(param)); + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); + if (ioctl_req == NULL) { + LEAVE(); + return -ENOMEM; + } + cfg_11n = (mlan_ds_11n_cfg *)ioctl_req->pbuf; + cfg_11n->sub_command = MLAN_OID_11N_CFG_ADDBA_REJECT; + ioctl_req->req_id = MLAN_IOCTL_11N_CFG; + + if (!param.action) { + /* Get addba_reject tbl from MLAN */ + ioctl_req->action = MLAN_ACT_GET; + } else { + /* Set addba_reject tbl in MLAN */ + ioctl_req->action = MLAN_ACT_SET; + for (i = 0; i < MAX_NUM_TID; i++) + cfg_11n->param.addba_reject[i] = param.addba_reject[i]; + } + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + for (i = 0; i < MAX_NUM_TID; i++) + param.addba_reject[i] = cfg_11n->param.addba_reject[i]; + /* Copy to user */ + if (copy_to_user(req->ifr_data, ¶m, sizeof(param))) { + PRINTM(MERROR, "Copy to user failed!\n"); + ret = -EFAULT; + goto done; + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + LEAVE(); + return ret; +} + +/** + * @brief uap get_fw_info handler + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int +woal_uap_get_fw_info(struct net_device *dev, struct ifreq *req) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + fw_info fw; + mlan_fw_info fw_info; + int ret = 0; + + ENTER(); + memset(&fw, 0, sizeof(fw)); + memset(&fw_info, 0, sizeof(fw_info)); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "woal_uap_get_fw_info() corrupt data\n"); + ret = -EFAULT; + goto done; + } + if (copy_from_user(&fw, req->ifr_data, sizeof(fw))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + if (MLAN_STATUS_SUCCESS != + woal_request_get_fw_info(priv, MOAL_IOCTL_WAIT, &fw_info)) { + ret = -EFAULT; + goto done; + } + fw.fw_release_number = fw_info.fw_ver; + fw.hw_dev_mcs_support = fw_info.hw_dev_mcs_support; + fw.fw_bands = fw_info.fw_bands; + fw.region_code = fw_info.region_code; + fw.hw_dot_11n_dev_cap = fw_info.hw_dot_11n_dev_cap; + /* Copy to user */ + if (copy_to_user(req->ifr_data, &fw, sizeof(fw))) { + PRINTM(MERROR, "Copy to user failed!\n"); + ret = -EFAULT; + goto done; + } +done: + LEAVE(); + return ret; +} + +/** + * @brief configure deep sleep + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int +woal_uap_deep_sleep(struct net_device *dev, struct ifreq *req) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_pm_cfg *pm = NULL; + deep_sleep_para param; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + memset(¶m, 0, sizeof(param)); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "woal_uap_deep_sleep() corrupt data\n"); + ret = -EFAULT; + goto done; + } + if (copy_from_user(¶m, req->ifr_data, sizeof(param))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + DBG_HEXDUMP(MCMD_D, "deep_sleep_para", (t_u8 *)¶m, sizeof(param)); + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg)); + if (ioctl_req == NULL) { + LEAVE(); + return -ENOMEM; + } + pm = (mlan_ds_pm_cfg *)ioctl_req->pbuf; + pm->sub_command = MLAN_OID_PM_CFG_DEEP_SLEEP; + ioctl_req->req_id = MLAN_IOCTL_PM_CFG; + + if (!param.action) { + /* Get deep_sleep status from MLAN */ + ioctl_req->action = MLAN_ACT_GET; + } else { + /* Set deep_sleep in MLAN */ + ioctl_req->action = MLAN_ACT_SET; + if (param.deep_sleep == MTRUE) { + pm->param.auto_deep_sleep.auto_ds = DEEP_SLEEP_ON; + pm->param.auto_deep_sleep.idletime = param.idle_time; + } else { + pm->param.auto_deep_sleep.auto_ds = DEEP_SLEEP_OFF; + } + } + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + if (pm->param.auto_deep_sleep.auto_ds == DEEP_SLEEP_ON) + param.deep_sleep = MTRUE; + else + param.deep_sleep = MFALSE; + param.idle_time = pm->param.auto_deep_sleep.idletime; + /* Copy to user */ + if (copy_to_user(req->ifr_data, ¶m, sizeof(param))) { + PRINTM(MERROR, "Copy to user failed!\n"); + ret = -EFAULT; + goto done; + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + LEAVE(); + return ret; +} + +/** + * @brief configure tx_pause settings + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int +woal_uap_txdatapause(struct net_device *dev, struct ifreq *req) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_misc_cfg *misc = NULL; + tx_data_pause_para param; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + memset(¶m, 0, sizeof(param)); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "woal_uap_txdatapause corrupt data\n"); + ret = -EFAULT; + goto done; + } + if (copy_from_user(¶m, req->ifr_data, sizeof(param))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + DBG_HEXDUMP(MCMD_D, "tx_data_pause_para", (t_u8 *)¶m, + sizeof(param)); + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (ioctl_req == NULL) { + LEAVE(); + return -ENOMEM; + } + misc = (mlan_ds_misc_cfg *)ioctl_req->pbuf; + misc->sub_command = MLAN_OID_MISC_TX_DATAPAUSE; + ioctl_req->req_id = MLAN_IOCTL_MISC_CFG; + + if (!param.action) { + /* Get Tx data pause status from MLAN */ + ioctl_req->action = MLAN_ACT_GET; + } else { + /* Set Tx data pause in MLAN */ + ioctl_req->action = MLAN_ACT_SET; + misc->param.tx_datapause.tx_pause = param.txpause; + misc->param.tx_datapause.tx_buf_cnt = param.txbufcnt; + } + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + param.txpause = misc->param.tx_datapause.tx_pause; + param.txbufcnt = misc->param.tx_datapause.tx_buf_cnt; + + /* Copy to user */ + if (copy_to_user(req->ifr_data, ¶m, sizeof(param))) { + PRINTM(MERROR, "Copy to user failed!\n"); + ret = -EFAULT; + goto done; + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + LEAVE(); + return ret; +} + +/** + * @brief uap sdcmd52rw ioctl handler + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int +woal_uap_sdcmd52_rw(struct net_device *dev, struct ifreq *req) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + sdcmd52_para param; + t_u8 func, data = 0; + int ret = 0, reg; + + ENTER(); + memset(¶m, 0, sizeof(param)); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "woal_uap_sdcmd52_rw() corrupt data\n"); + ret = -EFAULT; + goto done; + } + if (copy_from_user(¶m, req->ifr_data, sizeof(param))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + + func = (t_u8)param.cmd52_params[0]; + reg = (t_u32)param.cmd52_params[1]; + + if (!param.action) { + PRINTM(MINFO, "Cmd52 read, func=%d, reg=0x%08X\n", func, reg); + sdio_claim_host(((struct sdio_mmc_card *)priv->phandle->card)-> + func); + if (func) + data = sdio_readb(((struct sdio_mmc_card *)priv-> + phandle->card)->func, reg, &ret); + else + data = sdio_f0_readb(((struct sdio_mmc_card *)priv-> + phandle->card)->func, reg, &ret); + sdio_release_host(((struct sdio_mmc_card *)priv->phandle-> + card)->func); + if (ret) { + PRINTM(MERROR, + "sdio_readb: reading register 0x%X failed\n", + reg); + goto done; + } + param.cmd52_params[2] = data; + } else { + data = (t_u8)param.cmd52_params[2]; + PRINTM(MINFO, "Cmd52 write, func=%d, reg=0x%08X, data=0x%02X\n", + func, reg, data); + sdio_claim_host(((struct sdio_mmc_card *)priv->phandle->card)-> + func); + if (func) + sdio_writeb(((struct sdio_mmc_card *)priv->phandle-> + card)->func, data, reg, &ret); + else + sdio_f0_writeb(((struct sdio_mmc_card *)priv->phandle-> + card)->func, data, reg, &ret); + sdio_release_host(((struct sdio_mmc_card *)priv->phandle-> + card)->func); + if (ret) { + PRINTM(MERROR, + "sdio_writeb: writing register 0x%X failed\n", + reg); + goto done; + } + } + /* Copy to user */ + if (copy_to_user(req->ifr_data, ¶m, sizeof(param))) { + PRINTM(MERROR, "Copy to user failed!\n"); + ret = -EFAULT; + } +done: + LEAVE(); + return ret; +} + +/** + * @brief configure snmp mib + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int +woal_uap_snmp_mib(struct net_device *dev, struct ifreq *req) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_snmp_mib *snmp = NULL; + snmp_mib_para param; + t_u8 value[MAX_SNMP_VALUE_SIZE]; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + memset(¶m, 0, sizeof(param)); + memset(value, 0, MAX_SNMP_VALUE_SIZE); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "woal_uap_snmp_mib() corrupt data\n"); + ret = -EFAULT; + goto done; + } + + /* Copy from user */ + if (copy_from_user(¶m, req->ifr_data, sizeof(param))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + DBG_HEXDUMP(MCMD_D, "snmp_mib_para", (t_u8 *)¶m, sizeof(param)); + if (param.action) { + if (copy_from_user(value, req->ifr_data + sizeof(param), + MIN(param.oid_val_len, + MAX_SNMP_VALUE_SIZE))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + DBG_HEXDUMP(MCMD_D, "snmp_mib_para value", value, + MIN(param.oid_val_len, sizeof(t_u32))); + } + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_snmp_mib)); + if (ioctl_req == NULL) { + LEAVE(); + return -ENOMEM; + } + snmp = (mlan_ds_snmp_mib *)ioctl_req->pbuf; + ioctl_req->req_id = MLAN_IOCTL_SNMP_MIB; + switch (param.oid) { + case OID_80211D_ENABLE: + snmp->sub_command = MLAN_OID_SNMP_MIB_DOT11D; + break; + case OID_80211H_ENABLE: + snmp->sub_command = MLAN_OID_SNMP_MIB_DOT11H; + break; + default: + PRINTM(MERROR, "%s: Unsupported SNMP_MIB OID (%d).\n", __func__, + param.oid); + goto done; + } + + if (!param.action) { + /* Get mib value from MLAN */ + ioctl_req->action = MLAN_ACT_GET; + } else { + /* Set mib value to MLAN */ + ioctl_req->action = MLAN_ACT_SET; + snmp->param.oid_value = *(t_u32 *)value; + } + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + /* Copy to user */ + if (copy_to_user(req->ifr_data, ¶m, sizeof(param))) { + PRINTM(MERROR, "Copy to user failed!\n"); + ret = -EFAULT; + goto done; + } + if (!param.action) { /* GET */ + if (copy_to_user + (req->ifr_data + sizeof(param), &snmp->param.oid_value, + MIN(param.oid_val_len, sizeof(t_u32)))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + LEAVE(); + return ret; +} + +/** + * @brief configure domain info + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int +woal_uap_domain_info(struct net_device *dev, struct ifreq *req) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_11d_cfg *cfg11d = NULL; + domain_info_para param; + t_u8 tlv[MAX_DOMAIN_TLV_LEN]; + t_u16 tlv_data_len = 0; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + memset(¶m, 0, sizeof(param)); + memset(tlv, 0, MAX_DOMAIN_TLV_LEN); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "woal_uap_domain_info() corrupt data\n"); + ret = -EFAULT; + goto done; + } + + /* Copy from user */ + if (copy_from_user(¶m, req->ifr_data, sizeof(param))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + DBG_HEXDUMP(MCMD_D, "domain_info_para", (t_u8 *)¶m, sizeof(param)); + if (param.action) { + /* get tlv header */ + if (copy_from_user + (tlv, req->ifr_data + sizeof(param), TLV_HEADER_LEN)) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + tlv_data_len = ((t_u16 *)(tlv))[1]; + if ((TLV_HEADER_LEN + tlv_data_len) > sizeof(tlv)) { + PRINTM(MERROR, "TLV buffer is overflowed"); + ret = -EINVAL; + goto done; + } + /* get full tlv */ + if (copy_from_user(tlv, req->ifr_data + sizeof(param), + TLV_HEADER_LEN + tlv_data_len)) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + DBG_HEXDUMP(MCMD_D, "domain_info_para tlv", tlv, + TLV_HEADER_LEN + tlv_data_len); + } + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11d_cfg)); + if (ioctl_req == NULL) { + LEAVE(); + return -ENOMEM; + } + cfg11d = (mlan_ds_11d_cfg *)ioctl_req->pbuf; + ioctl_req->req_id = MLAN_IOCTL_11D_CFG; + cfg11d->sub_command = MLAN_OID_11D_DOMAIN_INFO; + + if (!param.action) { + /* Get mib value from MLAN */ + ioctl_req->action = MLAN_ACT_GET; + } else { + /* Set mib value to MLAN */ + ioctl_req->action = MLAN_ACT_SET; + memcpy(cfg11d->param.domain_tlv, tlv, + MIN(MAX_IE_SIZE, (TLV_HEADER_LEN + tlv_data_len))); + } + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + /* Copy to user */ + if (copy_to_user(req->ifr_data, ¶m, sizeof(param))) { + PRINTM(MERROR, "Copy to user failed!\n"); + ret = -EFAULT; + goto done; + } + if (!param.action) { /* GET */ + tlv_data_len = ((t_u16 *)(cfg11d->param.domain_tlv))[1]; + if (copy_to_user + (req->ifr_data + sizeof(param), &cfg11d->param.domain_tlv, + TLV_HEADER_LEN + tlv_data_len)) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + LEAVE(); + return ret; +} + +#if defined(DFS_TESTING_SUPPORT) +/** + * @brief configure dfs testing settings + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int +woal_uap_dfs_testing(struct net_device *dev, struct ifreq *req) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_11h_cfg *cfg11h = NULL; + dfs_testing_para param; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + memset(¶m, 0, sizeof(param)); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "woal_uap_dfs_testing() corrupt data\n"); + ret = -EFAULT; + goto done; + } + + /* Copy from user */ + if (copy_from_user(¶m, req->ifr_data, sizeof(param))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + DBG_HEXDUMP(MCMD_D, "dfs_testing_para", (t_u8 *)¶m, sizeof(param)); + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11h_cfg)); + if (ioctl_req == NULL) { + LEAVE(); + return -ENOMEM; + } + cfg11h = (mlan_ds_11h_cfg *)ioctl_req->pbuf; + ioctl_req->req_id = MLAN_IOCTL_11H_CFG; + cfg11h->sub_command = MLAN_OID_11H_DFS_TESTING; + + if (!param.action) { + /* Get mib value from MLAN */ + ioctl_req->action = MLAN_ACT_GET; + } else { + /* Set mib value to MLAN */ + ioctl_req->action = MLAN_ACT_SET; + cfg11h->param.dfs_testing.usr_cac_period_msec = + param.usr_cac_period; + cfg11h->param.dfs_testing.usr_nop_period_sec = + param.usr_nop_period; + cfg11h->param.dfs_testing.usr_no_chan_change = + param.no_chan_change; + cfg11h->param.dfs_testing.usr_fixed_new_chan = + param.fixed_new_chan; + priv->phandle->cac_period_jiffies = + param.usr_cac_period * HZ / 1000; + priv->user_cac_period_msec = + cfg11h->param.dfs_testing.usr_cac_period_msec; + } + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (!param.action) { /* GET */ + param.usr_cac_period = + cfg11h->param.dfs_testing.usr_cac_period_msec; + param.usr_nop_period = + cfg11h->param.dfs_testing.usr_nop_period_sec; + param.no_chan_change = + cfg11h->param.dfs_testing.usr_no_chan_change; + param.fixed_new_chan = + cfg11h->param.dfs_testing.usr_fixed_new_chan; + } + /* Copy to user */ + if (copy_to_user(req->ifr_data, ¶m, sizeof(param))) { + PRINTM(MERROR, "Copy to user failed!\n"); + ret = -EFAULT; + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + LEAVE(); + return ret; +} + +#ifdef UAP_CFG80211 +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) +/** + * @brief uap channel NOP status check ioctl handler + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * @param data BSS control type + * @return 0 --success, otherwise fail + */ +int +woal_uap_get_channel_nop_info(moal_private *priv, t_u8 wait_option, + mlan_ds_11h_chan_nop_info * ch_info) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_11h_cfg *ds_11hcfg = NULL; + + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (!ch_info) { + PRINTM(MERROR, "Invalid chan_info\n"); + LEAVE(); + return -EFAULT; + } + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11h_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + req->req_id = MLAN_IOCTL_11H_CFG; + req->action = MLAN_ACT_GET; + + ds_11hcfg = (mlan_ds_11h_cfg *)req->pbuf; + ds_11hcfg->sub_command = MLAN_OID_11H_CHAN_NOP_INFO; + memcpy(&ds_11hcfg->param.ch_nop_info, ch_info, + sizeof(mlan_ds_11h_chan_nop_info)); + status = woal_request_ioctl(priv, req, wait_option); + if (status == MLAN_STATUS_FAILURE) { + ret = -EFAULT; + goto done; + } + memcpy(ch_info, &ds_11hcfg->param.ch_nop_info, + sizeof(mlan_ds_11h_chan_nop_info)); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} +#endif +#endif +#endif + +/** + * @brief configure channel switch count + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int +woal_uap_chan_switch_count_cfg(struct net_device *dev, struct ifreq *req) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_11h_cfg *cfg11h = NULL; + cscount_cfg_t param; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + memset(¶m, 0, sizeof(param)); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "%s corrupt data\n", __func__); + ret = -EFAULT; + goto done; + } + + /* Copy from user */ + if (copy_from_user(¶m, req->ifr_data, sizeof(param))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + DBG_HEXDUMP(MCMD_D, "cscount_cfg_t", (t_u8 *)¶m, sizeof(param)); + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11h_cfg)); + if (ioctl_req == NULL) { + LEAVE(); + return -ENOMEM; + } + cfg11h = (mlan_ds_11h_cfg *)ioctl_req->pbuf; + ioctl_req->req_id = MLAN_IOCTL_11H_CFG; + cfg11h->sub_command = MLAN_OID_11H_CHAN_SWITCH_COUNT; + + if (!param.action) { + /* Get mib value from MLAN */ + ioctl_req->action = MLAN_ACT_GET; + } else { + /* Set mib value to MLAN */ + ioctl_req->action = MLAN_ACT_SET; + cfg11h->param.cs_count = param.cs_count; + } + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (!param.action) { /* GET */ + param.cs_count = cfg11h->param.cs_count; + } + /* Copy to user */ + if (copy_to_user(req->ifr_data, ¶m, sizeof(param))) { + PRINTM(MERROR, "Copy to user failed!\n"); + ret = -EFAULT; + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + LEAVE(); + return ret; +} + +/** + * @brief Configure TX beamforming support + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int +woal_uap_tx_bf_cfg(struct net_device *dev, struct ifreq *req) +{ + int ret = 0; + moal_private *priv = (moal_private *)netdev_priv(dev); + mlan_ds_11n_tx_bf_cfg bf_cfg; + tx_bf_cfg_para_hdr param; + t_u16 action = 0; + + ENTER(); + + memset(¶m, 0, sizeof(param)); + memset(&bf_cfg, 0, sizeof(bf_cfg)); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "woal_uap_tx_bf_cfg corrupt data\n"); + ret = -EFAULT; + goto done; + } + if (copy_from_user(¶m, req->ifr_data, sizeof(param))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + if (!param.action) + /* Get BF configurations */ + action = MLAN_ACT_GET; + else + /* Set BF configurations */ + action = MLAN_ACT_SET; + if (copy_from_user(&bf_cfg, req->ifr_data + sizeof(tx_bf_cfg_para_hdr), + sizeof(bf_cfg))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + DBG_HEXDUMP(MCMD_D, "bf_cfg", (t_u8 *)&bf_cfg, sizeof(bf_cfg)); + + if (MLAN_STATUS_SUCCESS != + woal_set_get_tx_bf_cfg(priv, action, &bf_cfg)) { + ret = -EFAULT; + goto done; + } + + /* Copy to user */ + if (copy_to_user(req->ifr_data + sizeof(tx_bf_cfg_para_hdr), + &bf_cfg, sizeof(bf_cfg))) { + PRINTM(MERROR, "Copy to user failed!\n"); + ret = -EFAULT; + goto done; + } + +done: + LEAVE(); + return ret; +} + +/** + * @brief Set/Get 11n configurations + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int +woal_uap_ht_tx_cfg(struct net_device *dev, struct ifreq *req) +{ + int ret = 0; + moal_private *priv = (moal_private *)netdev_priv(dev); + mlan_ds_11n_cfg *cfg_11n = NULL; + mlan_ds_11n_tx_cfg httx_cfg; + mlan_ioctl_req *ioctl_req = NULL; + ht_tx_cfg_para_hdr param; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + memset(¶m, 0, sizeof(ht_tx_cfg_para_hdr)); + memset(&httx_cfg, 0, sizeof(mlan_ds_11n_tx_cfg)); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "woal_uap_ht_tx_cfg corrupt data\n"); + ret = -EFAULT; + goto done; + } + if (copy_from_user(¶m, req->ifr_data, sizeof(ht_tx_cfg_para_hdr))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); + if (ioctl_req == NULL) { + ret = -ENOMEM; + goto done; + } + cfg_11n = (mlan_ds_11n_cfg *)ioctl_req->pbuf; + cfg_11n->sub_command = MLAN_OID_11N_CFG_TX; + ioctl_req->req_id = MLAN_IOCTL_11N_CFG; + if (copy_from_user + (&httx_cfg, req->ifr_data + sizeof(ht_tx_cfg_para_hdr), + sizeof(mlan_ds_11n_tx_cfg))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + if (!param.action) { + /* Get 11n tx parameters from MLAN */ + ioctl_req->action = MLAN_ACT_GET; + cfg_11n->param.tx_cfg.misc_cfg = BAND_SELECT_BG; + } else { + /* Set HT Tx configurations */ + cfg_11n->param.tx_cfg.httxcap = httx_cfg.httxcap; + PRINTM(MINFO, "SET: httxcap:0x%x\n", httx_cfg.httxcap); + cfg_11n->param.tx_cfg.misc_cfg = httx_cfg.misc_cfg; + PRINTM(MINFO, "SET: httxcap band:0x%x\n", httx_cfg.misc_cfg); + /* Update 11n tx parameters in MLAN */ + ioctl_req->action = MLAN_ACT_SET; + } + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + if (ioctl_req->action == MLAN_ACT_GET) { + httx_cfg.httxcap = cfg_11n->param.tx_cfg.httxcap; + PRINTM(MINFO, "GET: httxcap:0x%x\n", httx_cfg.httxcap); + cfg_11n->param.tx_cfg.httxcap = 0; + cfg_11n->param.tx_cfg.misc_cfg = BAND_SELECT_A; + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + httx_cfg.misc_cfg = cfg_11n->param.tx_cfg.httxcap; + PRINTM(MINFO, "GET: httxcap for 5GHz:0x%x\n", + httx_cfg.misc_cfg); + } + /* Copy to user */ + if (copy_to_user(req->ifr_data + sizeof(ht_tx_cfg_para_hdr), + &httx_cfg, sizeof(mlan_ds_11n_tx_cfg))) { + PRINTM(MERROR, "Copy to user failed!\n"); + ret = -EFAULT; + goto done; + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + LEAVE(); + return ret; +} + +/** + * @brief uap hs_cfg ioctl handler + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @invoke_hostcmd Argument + * + * @return 0 --success, otherwise fail + */ +static int +woal_uap_hs_cfg(struct net_device *dev, struct ifreq *req, + BOOLEAN invoke_hostcmd) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + mlan_ds_hs_cfg hscfg; + ds_hs_cfg hs_cfg; + mlan_bss_info bss_info; + t_u16 action; + int ret = 0; + + ENTER(); + + memset(&hscfg, 0, sizeof(mlan_ds_hs_cfg)); + memset(&hs_cfg, 0, sizeof(ds_hs_cfg)); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "uap_hs_cfg() corrupt data\n"); + ret = -EFAULT; + goto done; + } + if (copy_from_user(&hs_cfg, req->ifr_data, sizeof(ds_hs_cfg))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + + PRINTM(MIOCTL, + "ioctl hscfg: flags=0x%x condition=0x%x gpio=%d gap=0x%x\n", + hs_cfg.flags, hs_cfg.conditions, (int)hs_cfg.gpio, hs_cfg.gap); + + /* HS config is blocked if HS is already activated */ + if ((hs_cfg.flags & HS_CFG_FLAG_CONDITION) && + (hs_cfg.conditions != HOST_SLEEP_CFG_CANCEL || + invoke_hostcmd == MFALSE)) { + memset(&bss_info, 0, sizeof(bss_info)); + woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info); + if (bss_info.is_hs_configured) { + PRINTM(MERROR, "HS already configured\n"); + ret = -EFAULT; + goto done; + } + } + + if (hs_cfg.flags & HS_CFG_FLAG_SET) { + action = MLAN_ACT_SET; + if (hs_cfg.flags != HS_CFG_FLAG_ALL) { + woal_set_get_hs_params(priv, MLAN_ACT_GET, + MOAL_IOCTL_WAIT, &hscfg); + } + if (hs_cfg.flags & HS_CFG_FLAG_CONDITION) + hscfg.conditions = hs_cfg.conditions; + if (hs_cfg.flags & HS_CFG_FLAG_GPIO) + hscfg.gpio = hs_cfg.gpio; + if (hs_cfg.flags & HS_CFG_FLAG_GAP) + hscfg.gap = hs_cfg.gap; + + if (invoke_hostcmd == MTRUE) { + /* Issue IOCTL to set up parameters */ + hscfg.is_invoke_hostcmd = MFALSE; + if (MLAN_STATUS_SUCCESS != + woal_set_get_hs_params(priv, action, + MOAL_IOCTL_WAIT, &hscfg)) { + ret = -EFAULT; + goto done; + } + } + } else { + action = MLAN_ACT_GET; + } + + /* Issue IOCTL to invoke hostcmd */ + hscfg.is_invoke_hostcmd = invoke_hostcmd; + if (MLAN_STATUS_SUCCESS != + woal_set_get_hs_params(priv, action, MOAL_IOCTL_WAIT, &hscfg)) { + ret = -EFAULT; + goto done; + } + if (!(hs_cfg.flags & HS_CFG_FLAG_SET)) { + hs_cfg.flags = + HS_CFG_FLAG_CONDITION | HS_CFG_FLAG_GPIO | + HS_CFG_FLAG_GAP; + hs_cfg.conditions = hscfg.conditions; + hs_cfg.gpio = hscfg.gpio; + hs_cfg.gap = hscfg.gap; + /* Copy to user */ + if (copy_to_user(req->ifr_data, &hs_cfg, sizeof(ds_hs_cfg))) { + PRINTM(MERROR, "Copy to user failed!\n"); + ret = -EFAULT; + goto done; + } + } + +done: + LEAVE(); + return ret; +} + +/** + * @brief Set Host Sleep parameters + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * + * @return 0 --success, otherwise fail + */ +static int +woal_uap_hs_set_para(struct net_device *dev, struct ifreq *req) +{ + int ret = 0; + + ENTER(); + + if (req->ifr_data != NULL) { + ret = woal_uap_hs_cfg(dev, req, MFALSE); + goto done; + } else { + PRINTM(MERROR, "Invalid data\n"); + ret = -EINVAL; + goto done; + } +done: + LEAVE(); + return ret; +} + +/** + * @brief uap mgmt_frame_control ioctl handler + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int +woal_uap_mgmt_frame_control(struct net_device *dev, struct ifreq *req) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + int ret = 0; + t_u16 action = 0; + mgmt_frame_ctrl param; + mlan_uap_bss_param *sys_config = NULL; + + ENTER(); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "uap_mgmt_frame_ctrl() corrupt data\n"); + ret = -EFAULT; + goto done; + } + + /* Get user data */ + if (copy_from_user(¶m, req->ifr_data, sizeof(param))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + sys_config = kzalloc(sizeof(mlan_uap_bss_param), GFP_ATOMIC); + if (!sys_config) { + PRINTM(MERROR, "Fail to alloc memory for mlan_uap_bss_param\n"); + ret = -EFAULT; + goto done; + } + + if (param.action) + action = MLAN_ACT_SET; + else + action = MLAN_ACT_GET; + if (action == MLAN_ACT_SET) { + /* Initialize the invalid values so that the correct + values below are downloaded to firmware */ + woal_set_sys_config_invalid_data(sys_config); + sys_config->mgmt_ie_passthru_mask = param.mask; + } + + if (MLAN_STATUS_SUCCESS != + woal_set_get_sys_config(priv, action, MOAL_IOCTL_WAIT, + sys_config)) { + ret = -EFAULT; + goto done; + } + + if (action == MLAN_ACT_GET) { + param.mask = sys_config->mgmt_ie_passthru_mask; + if (copy_to_user(req->ifr_data, ¶m, sizeof(param))) { + PRINTM(MERROR, "Copy to user failed\n"); + ret = -EFAULT; + } + } +done: + kfree(sys_config); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get tx rate + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * + * @return 0 --success, otherwise fail + */ +static int +woal_uap_tx_rate_cfg(struct net_device *dev, struct ifreq *req) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + int ret = 0, i = 0; + mlan_ds_rate *rate = NULL; + mlan_ioctl_req *mreq = NULL; + tx_rate_cfg_t tx_rate_config; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "uap_tx_rate_cfg() corrupt data\n"); + ret = -EFAULT; + goto done; + } + + memset(&tx_rate_config, 0, sizeof(tx_rate_cfg_t)); + /* Get user data */ + if (copy_from_user + (&tx_rate_config, req->ifr_data, sizeof(tx_rate_cfg_t))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + + mreq = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_rate)); + if (mreq == NULL) { + ret = -ENOMEM; + goto done; + } + rate = (mlan_ds_rate *)mreq->pbuf; + rate->param.rate_cfg.rate_type = MLAN_RATE_INDEX; + rate->sub_command = MLAN_OID_RATE_CFG; + mreq->req_id = MLAN_IOCTL_RATE; + if (!(tx_rate_config.action)) + mreq->action = MLAN_ACT_GET; + else { + if ((tx_rate_config.user_data_cnt <= 0) || + (tx_rate_config.user_data_cnt > 3)) { + PRINTM(MERROR, "Invalid user_data_cnt\n"); + ret = -EINVAL; + goto done; + } + + mreq->action = MLAN_ACT_SET; + if (tx_rate_config.rate_format == AUTO_RATE) + rate->param.rate_cfg.is_rate_auto = 1; + else { + if ((tx_rate_config.rate_format < 0) || + (tx_rate_config.rate < 0)) { + PRINTM(MERROR, + "Invalid format or rate selection\n"); + ret = -EINVAL; + goto done; + } + /* rate_format sanity check */ + if ((tx_rate_config.rate_format > MLAN_RATE_FORMAT_HT) + ) { + PRINTM(MERROR, "Invalid format selection\n"); + ret = -EINVAL; + goto done; + } + rate->param.rate_cfg.rate_format = + tx_rate_config.rate_format; + + /* rate sanity check */ + if (tx_rate_config.user_data_cnt >= 2) { + if (((tx_rate_config.rate_format == + MLAN_RATE_FORMAT_LG) && + (tx_rate_config.rate > + MLAN_RATE_INDEX_OFDM7)) + || + ((tx_rate_config.rate_format == + MLAN_RATE_FORMAT_HT) && + (tx_rate_config.rate != 32) && + (tx_rate_config.rate > 7) + ) + ) { + PRINTM(MERROR, + "Invalid rate selection\n"); + ret = -EINVAL; + goto done; + } + rate->param.rate_cfg.rate = tx_rate_config.rate; + } + + /* nss sanity check */ + } + } + + status = woal_request_ioctl(priv, mreq, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + if (tx_rate_config.action) { + priv->rate_index = tx_rate_config.action; + } else { + if (rate->param.rate_cfg.is_rate_auto) + tx_rate_config.rate_format = AUTO_RATE; + else { + /* fixed rate */ + tx_rate_config.rate_format = + rate->param.rate_cfg.rate_format; + tx_rate_config.rate = rate->param.rate_cfg.rate; + } + for (i = 0; i < MAX_BITMAP_RATES_SIZE; i++) { + tx_rate_config.bitmap_rates[i] = + rate->param.rate_cfg.bitmap_rates[i]; + } + + if (copy_to_user + (req->ifr_data, &tx_rate_config, sizeof(tx_rate_cfg_t))) { + PRINTM(MERROR, "Copy to user failed\n"); + ret = -EFAULT; + } + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(mreq); + LEAVE(); + return ret; +} + +/** + * @brief Get DFS_REPEATER mode + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * + * @return 0 --success, otherwise fail + */ +static int +woal_uap_dfs_repeater(struct net_device *dev, struct ifreq *req) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + int ret = 0; + dfs_repeater_mode param; + mlan_ds_misc_cfg *misc = NULL; + mlan_ioctl_req *mreq = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "uap_antenna_cfg() corrupt data\n"); + ret = -EFAULT; + goto done; + } + + memset(¶m, 0, sizeof(dfs_repeater_mode)); + /* Get user data */ + if (copy_from_user(¶m, req->ifr_data, sizeof(dfs_repeater_mode))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + mreq = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (mreq == NULL) { + ret = -ENOMEM; + goto done; + } + misc = (mlan_ds_misc_cfg *)mreq->pbuf; + misc->sub_command = MLAN_OID_MISC_DFS_REAPTER_MODE; + mreq->req_id = MLAN_IOCTL_MISC_CFG; + mreq->action = MLAN_ACT_GET; + + status = woal_request_ioctl(priv, mreq, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + param.mode = misc->param.dfs_repeater.mode; + + if (copy_to_user(req->ifr_data, ¶m, sizeof(dfs_repeater_mode))) { + PRINTM(MERROR, "Copy to user failed\n"); + ret = -EFAULT; + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(mreq); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get skip CAC mode + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * + * @return 0 --success, otherwise fail + */ +static int +woal_uap_skip_cac(struct net_device *dev, struct ifreq *req) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + int ret = 0; + skip_cac_para param; + + ENTER(); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "skip_cac() corrupt data\n"); + ret = -EFAULT; + goto done; + } + + memset(¶m, 0, sizeof(skip_cac_para)); + + /* Get user data */ + if (copy_from_user(¶m, req->ifr_data, sizeof(skip_cac_para))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + + /* Currently default action is get */ + if (param.action == 0) { + param.skip_cac = (t_u16)priv->skip_cac; + } else { + priv->skip_cac = param.skip_cac; + } + + if (copy_to_user(req->ifr_data, ¶m, sizeof(skip_cac_para))) { + PRINTM(MERROR, "Copy to user failed\n"); + ret = -EFAULT; + } +done: + + LEAVE(); + return ret; +} + +/** + * @brief Get DFS_REPEATER mode + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * + * @return 0 --success, otherwise fail + */ +static int +woal_uap_cac_timer_status(struct net_device *dev, struct ifreq *req) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + int ret = 0; + cac_timer_status param; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "uap_antenna_cfg() corrupt data\n"); + ret = -EFAULT; + goto done; + } + + memset(¶m, 0, sizeof(cac_timer_status)); + + /* Get user data */ + if (copy_from_user(¶m, req->ifr_data, sizeof(cac_timer_status))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + + /* Currently default action is get */ + param.mode = 0; + + if (priv->phandle->cac_period == MTRUE) { + long cac_left_jiffies; + + cac_left_jiffies = MEAS_REPORT_TIME - + (jiffies - priv->phandle->meas_start_jiffies); + + /* cac_left_jiffies would be negative if timer has already elapsed. + * positive if timer is still yet to lapsed + */ + if (cac_left_jiffies > 0) + param.mode = (t_u32)cac_left_jiffies / HZ; + } + + if (copy_to_user(req->ifr_data, ¶m, sizeof(cac_timer_status))) { + PRINTM(MERROR, "Copy to user failed\n"); + ret = -EFAULT; + } +done: + if (status != MLAN_STATUS_PENDING) + LEAVE(); + return ret; +} + +/** + * @brief set/get uap operation control + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * + * @return 0 --success, otherwise fail + */ +static int +woal_uap_operation_ctrl(struct net_device *dev, struct ifreq *req) +{ + int ret = 0; + moal_private *priv = (moal_private *)netdev_priv(dev); + mlan_uap_oper_ctrl uap_oper; + uap_oper_para_hdr param; + mlan_ds_bss *bss = NULL; + mlan_ioctl_req *ioctl_req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + memset(¶m, 0, sizeof(uap_oper_para_hdr)); + memset(&uap_oper, 0, sizeof(mlan_uap_oper_ctrl)); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "woal_uap_operation_ctrl corrupt data\n"); + ret = -EFAULT; + goto done; + } + if (copy_from_user(¶m, req->ifr_data, sizeof(uap_oper_para_hdr))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (ioctl_req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + bss = (mlan_ds_bss *)ioctl_req->pbuf; + bss->sub_command = MLAN_OID_UAP_OPER_CTRL; + ioctl_req->req_id = MLAN_IOCTL_BSS; + if (copy_from_user(&uap_oper, req->ifr_data + sizeof(uap_oper_para_hdr), + sizeof(mlan_uap_oper_ctrl))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + if (!param.action) { + /* Get uap operation control parameters from FW */ + ioctl_req->action = MLAN_ACT_GET; + } else { + /* Set uap operation control configurations */ + ioctl_req->action = MLAN_ACT_SET; + bss->param.ap_oper_ctrl.ctrl_value = uap_oper.ctrl_value; + if (uap_oper.ctrl_value == 2) + bss->param.ap_oper_ctrl.chan_opt = uap_oper.chan_opt; + if (uap_oper.chan_opt == 3) { + bss->param.ap_oper_ctrl.band_cfg = uap_oper.band_cfg; + bss->param.ap_oper_ctrl.channel = uap_oper.channel; + } + + } + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + /* Copy to user */ + if (copy_to_user(req->ifr_data + sizeof(uap_oper_para_hdr), + &bss->param.ap_oper_ctrl, + sizeof(mlan_uap_oper_ctrl))) { + PRINTM(MERROR, "Copy to user failed!\n"); + ret = -EFAULT; + goto done; + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + LEAVE(); + return ret; +} + +/** + * @brief uap ioctl handler + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int +woal_uap_ioctl(struct net_device *dev, struct ifreq *req) +{ + int ret = 0; + t_u32 subcmd = 0; + ENTER(); + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "uap_ioctl() corrupt data\n"); + ret = -EFAULT; + goto done; + } + if (copy_from_user(&subcmd, req->ifr_data, sizeof(subcmd))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + + PRINTM(MIOCTL, "ioctl subcmd=%d\n", (int)subcmd); + switch (subcmd) { + case UAP_ADDBA_PARA: + ret = woal_uap_addba_param(dev, req); + break; + case UAP_AGGR_PRIOTBL: + ret = woal_uap_aggr_priotbl(dev, req); + break; + case UAP_ADDBA_REJECT: + ret = woal_uap_addba_reject(dev, req); + break; + case UAP_FW_INFO: + ret = woal_uap_get_fw_info(dev, req); + break; + case UAP_DEEP_SLEEP: + ret = woal_uap_deep_sleep(dev, req); + break; + case UAP_TX_DATA_PAUSE: + ret = woal_uap_txdatapause(dev, req); + break; + case UAP_SDCMD52_RW: + ret = woal_uap_sdcmd52_rw(dev, req); + break; + case UAP_SNMP_MIB: + ret = woal_uap_snmp_mib(dev, req); + break; +#ifdef DFS_TESTING_SUPPORT + case UAP_DFS_TESTING: + ret = woal_uap_dfs_testing(dev, req); + break; +#endif + case UAP_CHAN_SWITCH_COUNT_CFG: + ret = woal_uap_chan_switch_count_cfg(dev, req); + break; + case UAP_DOMAIN_INFO: + ret = woal_uap_domain_info(dev, req); + break; + case UAP_TX_BF_CFG: + ret = woal_uap_tx_bf_cfg(dev, req); + break; + case UAP_HT_TX_CFG: + ret = woal_uap_ht_tx_cfg(dev, req); + break; + case UAP_HS_CFG: + ret = woal_uap_hs_cfg(dev, req, MTRUE); + break; + case UAP_HS_SET_PARA: + ret = woal_uap_hs_set_para(dev, req); + break; + case UAP_MGMT_FRAME_CONTROL: + ret = woal_uap_mgmt_frame_control(dev, req); + break; + case UAP_TX_RATE_CFG: + ret = woal_uap_tx_rate_cfg(dev, req); + break; + case UAP_DFS_REPEATER_MODE: + ret = woal_uap_dfs_repeater(dev, req); + break; + case UAP_CAC_TIMER_STATUS: + ret = woal_uap_cac_timer_status(dev, req); + break; + case UAP_SKIP_CAC: + ret = woal_uap_skip_cac(dev, req); + break; + case UAP_OPERATION_CTRL: + ret = woal_uap_operation_ctrl(dev, req); + break; + default: + break; + } +done: + LEAVE(); + return ret; +} + +/** + * @brief uap station deauth ioctl handler + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int +woal_uap_sta_deauth_ioctl(struct net_device *dev, struct ifreq *req) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_bss *bss = NULL; + mlan_deauth_param deauth_param; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + memset(&deauth_param, 0, sizeof(mlan_deauth_param)); + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "uap_sta_deauth_ioctl() corrupt data\n"); + ret = -EFAULT; + goto done; + } + if (copy_from_user + (&deauth_param, req->ifr_data, sizeof(mlan_deauth_param))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + + PRINTM(MIOCTL, "ioctl deauth station: " MACSTR ", reason=%d\n", + MAC2STR(deauth_param.mac_addr), deauth_param.reason_code); + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (ioctl_req == NULL) { + ret = -ENOMEM; + goto done; + } + bss = (mlan_ds_bss *)ioctl_req->pbuf; + + bss->sub_command = MLAN_OID_UAP_DEAUTH_STA; + ioctl_req->req_id = MLAN_IOCTL_BSS; + ioctl_req->action = MLAN_ACT_SET; + + memcpy(&bss->param.deauth_param, &deauth_param, + sizeof(mlan_deauth_param)); + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + if (copy_to_user + (req->ifr_data, &ioctl_req->status_code, sizeof(t_u32))) + PRINTM(MERROR, "Copy to user failed!\n"); + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get radio + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * + * @return 0 --success, otherwise fail + */ +static int +woal_uap_radio_ctl(struct net_device *dev, struct ifreq *req) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + int ret = 0; + mlan_ds_radio_cfg *radio = NULL; + mlan_ioctl_req *mreq = NULL; + int data[2] = { 0, 0 }; + mlan_bss_info bss_info; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "uap_radio_ctl() corrupt data\n"); + ret = -EFAULT; + goto done; + } + + /* Get user data */ + if (copy_from_user(&data, req->ifr_data, sizeof(data))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + + if (data[0]) { + mreq = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_radio_cfg)); + if (mreq == NULL) { + ret = -ENOMEM; + goto done; + } + radio = (mlan_ds_radio_cfg *)mreq->pbuf; + radio->sub_command = MLAN_OID_RADIO_CTRL; + mreq->req_id = MLAN_IOCTL_RADIO_CFG; + mreq->action = MLAN_ACT_SET; + radio->param.radio_on_off = (t_u32)data[1]; + status = woal_request_ioctl(priv, mreq, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) + ret = -EFAULT; + if (status != MLAN_STATUS_PENDING) + kfree(mreq); + } else { + /* Get radio status */ + memset(&bss_info, 0, sizeof(bss_info)); + woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info); + + data[1] = bss_info.radio_on; + if (copy_to_user(req->ifr_data, data, sizeof(data))) { + PRINTM(MERROR, "Copy to user failed\n"); + ret = -EFAULT; + } + } +done: + LEAVE(); + return ret; +} + +/** + * @brief uap bss control ioctl handler + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int +woal_uap_bss_ctrl_ioctl(struct net_device *dev, struct ifreq *req) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + int ret = 0, data = 0; + + ENTER(); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "uap_bss_ctrl() corrupt data\n"); + ret = -EFAULT; + goto done; + } + if (copy_from_user(&data, req->ifr_data, sizeof(data))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + + ret = woal_uap_bss_ctrl(priv, MOAL_IOCTL_WAIT, data); + +done: + LEAVE(); + return ret; +} + +/** + * @brief Set/Get uap power mode + * + * @param priv A pointer to moal_private structure + * @param action Action set or get + * @param ps_mgmt A pointer to mlan_ds_ps_mgmt structure + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +int +woal_set_get_uap_power_mode(moal_private *priv, t_u32 action, + mlan_ds_ps_mgmt *ps_mgmt) +{ + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_pm_cfg *pm_cfg = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + if (!ps_mgmt) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg)); + if (ioctl_req == NULL) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + pm_cfg = (mlan_ds_pm_cfg *)ioctl_req->pbuf; + pm_cfg->sub_command = MLAN_OID_PM_CFG_PS_MODE; + ioctl_req->req_id = MLAN_IOCTL_PM_CFG; + ioctl_req->action = action; + if (action == MLAN_ACT_SET) + memcpy(&pm_cfg->param.ps_mgmt, ps_mgmt, + sizeof(mlan_ds_ps_mgmt)); + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status == MLAN_STATUS_SUCCESS) { + if (action == MLAN_ACT_GET) + memcpy(ps_mgmt, &pm_cfg->param.ps_mgmt, + sizeof(mlan_ds_ps_mgmt)); + } + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + LEAVE(); + return status; +} + +/** + * @brief uap power mode ioctl handler + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int +woal_uap_power_mode_ioctl(struct net_device *dev, struct ifreq *req) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_pm_cfg *pm_cfg = NULL; + mlan_ds_ps_mgmt ps_mgmt; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + memset(&ps_mgmt, 0, sizeof(mlan_ds_ps_mgmt)); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "uap_power_mode_ioctl() corrupt data\n"); + ret = -EFAULT; + goto done; + } + if (copy_from_user(&ps_mgmt, req->ifr_data, sizeof(mlan_ds_ps_mgmt))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + + PRINTM(MIOCTL, + "ioctl power: flag=0x%x ps_mode=%d ctrl_bitmap=%d min_sleep=%d max_sleep=%d " + "inact_to=%d min_awake=%d max_awake=%d\n", ps_mgmt.flags, + (int)ps_mgmt.ps_mode, (int)ps_mgmt.sleep_param.ctrl_bitmap, + (int)ps_mgmt.sleep_param.min_sleep, + (int)ps_mgmt.sleep_param.max_sleep, + (int)ps_mgmt.inact_param.inactivity_to, + (int)ps_mgmt.inact_param.min_awake, + (int)ps_mgmt.inact_param.max_awake); + + if (ps_mgmt. + flags & ~(PS_FLAG_PS_MODE | PS_FLAG_SLEEP_PARAM | + PS_FLAG_INACT_SLEEP_PARAM)) { + PRINTM(MERROR, "Invalid parameter: flags = 0x%x\n", + ps_mgmt.flags); + ret = -EINVAL; + goto done; + } + + if (ps_mgmt.ps_mode > PS_MODE_INACTIVITY) { + PRINTM(MERROR, "Invalid parameter: ps_mode = %d\n", + (int)ps_mgmt.flags); + ret = -EINVAL; + goto done; + } + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg)); + if (ioctl_req == NULL) { + ret = -ENOMEM; + goto done; + } + pm_cfg = (mlan_ds_pm_cfg *)ioctl_req->pbuf; + pm_cfg->sub_command = MLAN_OID_PM_CFG_PS_MODE; + ioctl_req->req_id = MLAN_IOCTL_PM_CFG; + if (ps_mgmt.flags) { + ioctl_req->action = MLAN_ACT_SET; + memcpy(&pm_cfg->param.ps_mgmt, &ps_mgmt, + sizeof(mlan_ds_ps_mgmt)); + } else { + ioctl_req->action = MLAN_ACT_GET; + } + + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + if (copy_to_user + (req->ifr_data, &ioctl_req->status_code, sizeof(t_u32))) + PRINTM(MERROR, "Copy to user failed!\n"); + goto done; + } + if (!ps_mgmt.flags) { + /* Copy to user */ + if (copy_to_user + (req->ifr_data, &pm_cfg->param.ps_mgmt, + sizeof(mlan_ds_ps_mgmt))) { + PRINTM(MERROR, "Copy to user failed!\n"); + ret = -EFAULT; + goto done; + } + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + LEAVE(); + return ret; +} + +/** + * @brief uap BSS config ioctl handler + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int +woal_uap_bss_cfg_ioctl(struct net_device *dev, struct ifreq *req) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + int ret = 0; + mlan_ds_bss *bss = NULL; + mlan_ioctl_req *ioctl_req = NULL; + int offset = 0; + t_u32 action = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "uap_bss_cfg_ioctl() corrupt data\n"); + ret = -EFAULT; + goto done; + } + + /* Get action */ + if (copy_from_user(&action, req->ifr_data + offset, sizeof(action))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + offset += sizeof(action); + + /* Allocate an IOCTL request buffer */ + ioctl_req = + (mlan_ioctl_req *) + woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (ioctl_req == NULL) { + ret = -ENOMEM; + goto done; + } + + bss = (mlan_ds_bss *)ioctl_req->pbuf; + bss->sub_command = MLAN_OID_UAP_BSS_CONFIG; + ioctl_req->req_id = MLAN_IOCTL_BSS; + if (action == 1) + ioctl_req->action = MLAN_ACT_SET; + else + ioctl_req->action = MLAN_ACT_GET; + + if (ioctl_req->action == MLAN_ACT_SET) { + /* Get the BSS config from user */ + if (copy_from_user + (&bss->param.bss_config, req->ifr_data + offset, + sizeof(mlan_uap_bss_param))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + } + + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (ioctl_req->action == MLAN_ACT_GET) { + offset = sizeof(action); + + /* Copy to user : BSS config */ + if (copy_to_user + (req->ifr_data + offset, &bss->param.bss_config, + sizeof(mlan_uap_bss_param))) { + PRINTM(MERROR, "Copy to user failed!\n"); + ret = -EFAULT; + goto done; + } + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + LEAVE(); + return ret; +} + +/** + * @brief uap get station list handler + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int +woal_uap_get_sta_list_ioctl(struct net_device *dev, struct ifreq *req) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + int ret = 0; + mlan_ds_get_info *info = NULL; + mlan_ioctl_req *ioctl_req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "uap_get_sta_list_ioctl() corrupt data\n"); + ret = -EFAULT; + goto done; + } + + /* Allocate an IOCTL request buffer */ + ioctl_req = + (mlan_ioctl_req *) + woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_get_info)); + if (ioctl_req == NULL) { + ret = -ENOMEM; + goto done; + } + + info = (mlan_ds_get_info *)ioctl_req->pbuf; + info->sub_command = MLAN_OID_UAP_STA_LIST; + ioctl_req->req_id = MLAN_IOCTL_GET_INFO; + ioctl_req->action = MLAN_ACT_GET; + + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (ioctl_req->action == MLAN_ACT_GET) { + /* Copy to user : sta_list */ + if (copy_to_user + (req->ifr_data, &info->param.sta_list, + sizeof(mlan_ds_sta_list))) { + PRINTM(MERROR, "Copy to user failed!\n"); + ret = -EFAULT; + goto done; + } + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + LEAVE(); + return ret; +} + +/** + * @brief uAP set WAPI key ioctl + * + * @param priv A pointer to moal_private structure + * @param msg A pointer to wapi_msg structure + * + * @return 0 --success, otherwise fail + */ +static int +woal_uap_set_wapi_key_ioctl(moal_private *priv, wapi_msg *msg) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_sec_cfg *sec = NULL; + int ret = 0; + wapi_key_msg *key_msg = NULL; + t_u8 bcast_addr[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + if (msg->msg_len != sizeof(wapi_key_msg)) { + ret = -EINVAL; + goto done; + } + key_msg = (wapi_key_msg *)msg->msg; + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + sec = (mlan_ds_sec_cfg *)req->pbuf; + sec->sub_command = MLAN_OID_SEC_CFG_ENCRYPT_KEY; + req->req_id = MLAN_IOCTL_SEC_CFG; + req->action = MLAN_ACT_SET; + + sec->param.encrypt_key.is_wapi_key = MTRUE; + sec->param.encrypt_key.key_len = MLAN_MAX_KEY_LENGTH; + memcpy(sec->param.encrypt_key.mac_addr, key_msg->mac_addr, ETH_ALEN); + sec->param.encrypt_key.key_index = key_msg->key_id; + if (0 == memcmp(key_msg->mac_addr, bcast_addr, ETH_ALEN)) + sec->param.encrypt_key.key_flags = KEY_FLAG_GROUP_KEY; + else + sec->param.encrypt_key.key_flags = KEY_FLAG_SET_TX_KEY; + memcpy(sec->param.encrypt_key.key_material, key_msg->key, + sec->param.encrypt_key.key_len); + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) + ret = -EFAULT; +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Enable/Disable wapi in firmware + * + * @param priv A pointer to moal_private structure + * @param enable MTRUE/MFALSE + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail + */ +static mlan_status +woal_enable_wapi(moal_private *priv, t_u8 enable) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_bss *bss = NULL; + mlan_status status; + + ENTER(); + + /* Allocate an IOCTL request buffer */ + req = (mlan_ioctl_req *)woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + status = MLAN_STATUS_FAILURE; + goto done; + } + + /* Fill request buffer */ + bss = (mlan_ds_bss *)req->pbuf; + bss->sub_command = MLAN_OID_UAP_BSS_CONFIG; + req->req_id = MLAN_IOCTL_BSS; + req->action = MLAN_ACT_GET; + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, + "Get AP setting failed! status=%d, error_code=0x%x\n", + status, req->status_code); + } + + /* Change AP default setting */ + req->action = MLAN_ACT_SET; + if (enable == MFALSE) { + bss->param.bss_config.auth_mode = MLAN_AUTH_MODE_OPEN; + bss->param.bss_config.protocol = PROTOCOL_NO_SECURITY; + } else { + bss->param.bss_config.auth_mode = MLAN_AUTH_MODE_OPEN; + bss->param.bss_config.protocol = PROTOCOL_WAPI; + } + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, + "Set AP setting failed! status=%d, error_code=0x%x\n", + status, req->status_code); + } + if (enable) + woal_uap_bss_ctrl(priv, MOAL_IOCTL_WAIT, UAP_BSS_START); +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return status; +} + +/** + * @brief uAP set WAPI flag ioctl + * + * @param priv A pointer to moal_private structure + * @param msg A pointer to wapi_msg structure + * + * @return 0 --success, otherwise fail + */ +static int +woal_uap_set_wapi_flag_ioctl(moal_private *priv, wapi_msg *msg) +{ + t_u8 wapi_psk_ie[] = { + 0x44, 0x14, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x14, + 0x72, 0x02, 0x01, 0x00, + 0x00, 0x14, 0x72, 0x01, + 0x00, 0x14, 0x72, 0x01, + 0x00, 0x00 + }; + t_u8 wapi_cert_ie[] = { + 0x44, 0x14, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x14, + 0x72, 0x01, 0x01, 0x00, + 0x00, 0x14, 0x72, 0x01, + 0x00, 0x14, 0x72, 0x01, + 0x00, 0x00 + }; + mlan_ds_misc_cfg *misc = NULL; + mlan_ioctl_req *req = NULL; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + woal_uap_bss_ctrl(priv, MOAL_IOCTL_WAIT, UAP_BSS_STOP); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + misc = (mlan_ds_misc_cfg *)req->pbuf; + misc->sub_command = MLAN_OID_MISC_GEN_IE; + req->req_id = MLAN_IOCTL_MISC_CFG; + req->action = MLAN_ACT_SET; + + misc->param.gen_ie.type = MLAN_IE_TYPE_GEN_IE; + misc->param.gen_ie.len = sizeof(wapi_psk_ie); + if (msg->msg[0] & WAPI_MODE_PSK) { + memcpy(misc->param.gen_ie.ie_data, wapi_psk_ie, + misc->param.gen_ie.len); + } else if (msg->msg[0] & WAPI_MODE_CERT) { + memcpy(misc->param.gen_ie.ie_data, wapi_cert_ie, + misc->param.gen_ie.len); + } else if (msg->msg[0] == 0) { + /* disable WAPI in driver */ + if (MLAN_STATUS_SUCCESS != + woal_set_wapi_enable(priv, MOAL_IOCTL_WAIT, 0)) + ret = -EFAULT; + woal_enable_wapi(priv, MFALSE); + goto done; + } else { + ret = -EINVAL; + goto done; + } + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + woal_enable_wapi(priv, MTRUE); +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief set wapi ioctl function + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int +woal_uap_set_wapi(struct net_device *dev, struct ifreq *req) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + wapi_msg msg; + int ret = 0; + + ENTER(); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "uap_set_wapi() corrupt data\n"); + ret = -EFAULT; + goto done; + } + + memset(&msg, 0, sizeof(msg)); + + if (copy_from_user(&msg, req->ifr_data, sizeof(msg))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + + PRINTM(MIOCTL, "set wapi msg_type = %d, msg_len=%d\n", msg.msg_type, + msg.msg_len); + DBG_HEXDUMP(MCMD_D, "wapi msg", msg.msg, + MIN(msg.msg_len, sizeof(msg.msg))); + + switch (msg.msg_type) { + case P80211_PACKET_WAPIFLAG: + ret = woal_uap_set_wapi_flag_ioctl(priv, &msg); + break; + case P80211_PACKET_SETKEY: + ret = woal_uap_set_wapi_key_ioctl(priv, &msg); + break; + default: + ret = -EOPNOTSUPP; + break; + } +done: + LEAVE(); + return ret; +} + +/******************************************************** + Global Functions +********************************************************/ +/** + * @brief Initialize the members of mlan_uap_bss_param + * which are uploaded from firmware + * + * @param priv A pointer to moal_private structure + * @param sys_cfg A pointer to mlan_uap_bss_param structure + * @param wait_option Wait option + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +woal_uap_get_bss_param(moal_private *priv, mlan_uap_bss_param *sys_cfg, + t_u8 wait_option) +{ + mlan_ds_bss *info = NULL; + mlan_ioctl_req *req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + info = (mlan_ds_bss *)req->pbuf; + info->sub_command = MLAN_OID_UAP_BSS_CONFIG; + req->req_id = MLAN_IOCTL_BSS; + req->action = MLAN_ACT_GET; + + status = woal_request_ioctl(priv, req, wait_option); + if (status != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "Get bss info failed!\n"); + status = MLAN_STATUS_FAILURE; + goto done; + } + memcpy(sys_cfg, &info->param.bss_config, sizeof(mlan_uap_bss_param)); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return status; +} + +/** + * @brief Set uap httxcfg + * + * @param priv A pointer to moal_private structure + * @param band_cfg Band cfg + * @param en Enabled/Disabled + * + * @return 0 --success, otherwise fail + */ +int +woal_set_uap_ht_tx_cfg(moal_private *priv, Band_Config_t bandcfg, t_u8 en) +{ + int ret = 0; + mlan_ds_11n_cfg *cfg_11n = NULL; + mlan_ioctl_req *ioctl_req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); + if (ioctl_req == NULL) { + ret = -ENOMEM; + goto done; + } + cfg_11n = (mlan_ds_11n_cfg *)ioctl_req->pbuf; + cfg_11n->sub_command = MLAN_OID_11N_CFG_TX; + ioctl_req->req_id = MLAN_IOCTL_11N_CFG; + + /* Set HT Tx configurations */ + if (bandcfg.chanBand == BAND_2GHZ) { + if (en) + cfg_11n->param.tx_cfg.httxcap = 0x20; + else + cfg_11n->param.tx_cfg.httxcap = 0; + cfg_11n->param.tx_cfg.misc_cfg = BAND_SELECT_BG; + } else if (bandcfg.chanBand == BAND_5GHZ) { + if (en) + cfg_11n->param.tx_cfg.httxcap = 0x6f; + else + cfg_11n->param.tx_cfg.httxcap = 0; + cfg_11n->param.tx_cfg.misc_cfg = BAND_SELECT_A; + } + PRINTM(MCMND, "SET: httxcap=0x%x band:0x%x\n", + cfg_11n->param.tx_cfg.httxcap, cfg_11n->param.tx_cfg.misc_cfg); + /* Update 11n tx parameters in MLAN */ + ioctl_req->action = MLAN_ACT_SET; + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + LEAVE(); + return ret; +} + +/** + * @brief Set 11n status based on the configured security mode + * + * @param priv A pointer to moal_private structure + * @param sys_cfg A pointer to mlan_uap_bss_param structure + * @param action MLAN_ACT_DISABLE or MLAN_ACT_ENABLE + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +woal_uap_set_11n_status(moal_private *priv, mlan_uap_bss_param *sys_cfg, + t_u8 action) +{ + mlan_status status = MLAN_STATUS_SUCCESS; + mlan_fw_info fw_info; + + ENTER(); + if (action == MLAN_ACT_DISABLE) { + if ((sys_cfg->supported_mcs_set[0] == 0) + && (sys_cfg->supported_mcs_set[4] == 0) + ) { + goto done; + } else { + sys_cfg->supported_mcs_set[0] = 0; + sys_cfg->supported_mcs_set[4] = 0; + } + } + + if (action == MLAN_ACT_ENABLE) { + woal_request_get_fw_info(priv, MOAL_IOCTL_WAIT, &fw_info); + sys_cfg->supported_mcs_set[0] = 0xFF; + sys_cfg->supported_mcs_set[4] = 0x01; + } + +done: + LEAVE(); + return status; +} + +/** + * @brief Parse AP configuration from ASCII string + * + * @param ap_cfg A pointer to mlan_uap_bss_param structure + * @param buf A pointer to user data + * + * @return 0 --success, otherwise fail + */ +int +woal_uap_ap_cfg_parse_data(mlan_uap_bss_param *ap_cfg, char *buf) +{ + int ret = 0, atoi_ret; + int set_sec = 0, set_key = 0, set_chan = 0; + int set_preamble = 0, set_scb = 0, set_ssid = 0; + char *begin = buf, *value = NULL, *opt = NULL; + + ENTER(); + + while (begin) { + value = woal_strsep(&begin, ',', '/'); + opt = woal_strsep(&value, '=', '/'); + if (opt && !strncmp(opt, "END", strlen("END"))) { + if (!ap_cfg->ssid.ssid_len) { + PRINTM(MERROR, + "Minimum option required is SSID\n"); + ret = -EINVAL; + goto done; + } + PRINTM(MINFO, "Parsing terminated by string END\n"); + break; + } + if (!opt || !value || !value[0]) { + PRINTM(MERROR, "Invalid option\n"); + ret = -EINVAL; + goto done; + } else if (!strncmp(opt, "ASCII_CMD", strlen("ASCII_CMD"))) { + if (strncmp(value, "AP_CFG", strlen("AP_CFG"))) { + PRINTM(MERROR, + "ASCII_CMD: %s not matched with AP_CFG\n", + value); + ret = -EFAULT; + goto done; + } + value = woal_strsep(&begin, ',', '/'); + opt = woal_strsep(&value, '=', '/'); + if (!opt || !value || !value[0]) { + PRINTM(MERROR, + "Minimum option required is SSID\n"); + ret = -EINVAL; + goto done; + } else if (!strncmp(opt, "SSID", strlen("SSID"))) { + if (set_ssid) { + PRINTM(MWARN, + "Skipping SSID, found again!\n"); + continue; + } + if (strlen(value) >= MLAN_MAX_SSID_LENGTH) { + PRINTM(MERROR, + "SSID length exceeds max length\n"); + ret = -EFAULT; + goto done; + } + ap_cfg->ssid.ssid_len = strlen(value); + strncpy((char *)ap_cfg->ssid.ssid, value, + MIN(MLAN_MAX_SSID_LENGTH - 1, + strlen(value))); + PRINTM(MINFO, "ssid=%s, len=%d\n", + ap_cfg->ssid.ssid, + (int)ap_cfg->ssid.ssid_len); + set_ssid = 1; + } else { + PRINTM(MERROR, + "AP_CFG: Invalid option %s, expect SSID\n", + opt); + ret = -EINVAL; + goto done; + } + } else if (!strncmp(opt, "SEC", strlen("SEC"))) { + if (set_sec) { + PRINTM(MWARN, "Skipping SEC, found again!\n"); + continue; + } + if (!strnicmp(value, "open", strlen("open"))) { + ap_cfg->auth_mode = MLAN_AUTH_MODE_OPEN; + if (set_key) + ap_cfg->wpa_cfg.length = 0; + ap_cfg->key_mgmt = KEY_MGMT_NONE; + ap_cfg->protocol = PROTOCOL_NO_SECURITY; + } else if (!strnicmp + (value, "wpa2-psk", strlen("wpa2-psk"))) { + ap_cfg->auth_mode = MLAN_AUTH_MODE_OPEN; + ap_cfg->protocol = PROTOCOL_WPA2; + ap_cfg->key_mgmt = KEY_MGMT_PSK; + ap_cfg->wpa_cfg.pairwise_cipher_wpa = + CIPHER_AES_CCMP; + ap_cfg->wpa_cfg.pairwise_cipher_wpa2 = + CIPHER_AES_CCMP; + ap_cfg->wpa_cfg.group_cipher = CIPHER_AES_CCMP; + } else if (!strnicmp + (value, "wpa-psk", strlen("wpa-psk"))) { + ap_cfg->auth_mode = MLAN_AUTH_MODE_OPEN; + ap_cfg->protocol = PROTOCOL_WPA; + ap_cfg->key_mgmt = KEY_MGMT_PSK; + ap_cfg->wpa_cfg.pairwise_cipher_wpa = + CIPHER_TKIP; + ap_cfg->wpa_cfg.group_cipher = CIPHER_TKIP; + } else if (!strnicmp(value, "wep128", strlen("wep128"))) { + ap_cfg->auth_mode = MLAN_AUTH_MODE_OPEN; + if (set_key) + ap_cfg->wpa_cfg.length = 0; + ap_cfg->key_mgmt = KEY_MGMT_NONE; + ap_cfg->protocol = PROTOCOL_STATIC_WEP; + } else { + PRINTM(MERROR, + "AP_CFG: Invalid value=%s for %s\n", + value, opt); + ret = -EFAULT; + goto done; + } + set_sec = 1; + } else if (!strncmp(opt, "KEY", strlen("KEY"))) { + if (set_key) { + PRINTM(MWARN, "Skipping KEY, found again!\n"); + continue; + } + if (set_sec && ap_cfg->protocol == PROTOCOL_STATIC_WEP) { + if (strlen(value) != MAX_WEP_KEY_SIZE) { + PRINTM(MERROR, + "Invalid WEP KEY length\n"); + ret = -EFAULT; + goto done; + } + ap_cfg->wep_cfg.key0.key_index = 0; + ap_cfg->wep_cfg.key0.is_default = 1; + ap_cfg->wep_cfg.key0.length = strlen(value); + memcpy(ap_cfg->wep_cfg.key0.key, value, + MIN(sizeof(ap_cfg->wep_cfg.key0.key), + strlen(value))); + set_key = 1; + continue; + } + if (set_sec && ap_cfg->protocol != PROTOCOL_WPA2 + && ap_cfg->protocol != PROTOCOL_WPA) { + PRINTM(MWARN, + "Warning! No KEY for open mode\n"); + set_key = 1; + continue; + } + if (strlen(value) < MLAN_MIN_PASSPHRASE_LENGTH || + strlen(value) > MLAN_PMK_HEXSTR_LENGTH) { + PRINTM(MERROR, "Invalid PSK/PMK length\n"); + ret = -EINVAL; + goto done; + } + ap_cfg->wpa_cfg.length = strlen(value); + memcpy(ap_cfg->wpa_cfg.passphrase, value, + MIN(sizeof(ap_cfg->wpa_cfg.passphrase), + strlen(value))); + set_key = 1; + } else if (!strncmp(opt, "CHANNEL", strlen("CHANNEL"))) { + if (set_chan) { + PRINTM(MWARN, + "Skipping CHANNEL, found again!\n"); + continue; + } + if (woal_atoi(&atoi_ret, value)) { + ret = -EINVAL; + goto done; + } + if (atoi_ret < 1 || atoi_ret > MLAN_MAX_CHANNEL) { + PRINTM(MERROR, + "AP_CFG: Channel must be between 1 and %d" + "(both included)\n", MLAN_MAX_CHANNEL); + ret = -EINVAL; + goto done; + } + ap_cfg->channel = atoi_ret; + set_chan = 1; + } else if (!strncmp(opt, "PREAMBLE", strlen("PREAMBLE"))) { + if (set_preamble) { + PRINTM(MWARN, + "Skipping PREAMBLE, found again!\n"); + continue; + } + if (woal_atoi(&atoi_ret, value)) { + ret = -EINVAL; + goto done; + } + /* This is a READ only value from FW, so we + * can not set this and pass it successfully */ + set_preamble = 1; + } else if (!strncmp(opt, "MAX_SCB", strlen("MAX_SCB"))) { + if (set_scb) { + PRINTM(MWARN, + "Skipping MAX_SCB, found again!\n"); + continue; + } + if (woal_atoi(&atoi_ret, value)) { + ret = -EINVAL; + goto done; + } + if (atoi_ret < 1 || atoi_ret > MAX_STA_COUNT) { + PRINTM(MERROR, + "AP_CFG: MAX_SCB must be between 1 to %d " + "(both included)\n", MAX_STA_COUNT); + ret = -EINVAL; + goto done; + } + ap_cfg->max_sta_count = (t_u16)atoi_ret; + set_scb = 1; + } else { + PRINTM(MERROR, "Invalid option %s\n", opt); + ret = -EINVAL; + goto done; + } + } + +done: + LEAVE(); + return ret; +} + +/** + * @brief Set AP configuration + * + * @param priv A pointer to moal_private structure + * @param data A pointer to user data + * @param len Length of buf + * + * @return 0 --success, otherwise fail + */ +int +woal_uap_set_ap_cfg(moal_private *priv, t_u8 *data, int len) +{ + int ret = 0; + static char buf[MAX_BUF_LEN]; + mlan_uap_bss_param *sys_config = NULL; + int restart = 0; + + ENTER(); + +#define MIN_AP_CFG_CMD_LEN 16 /* strlen("ASCII_CMD=AP_CFG") */ + if ((len - 1) <= MIN_AP_CFG_CMD_LEN) { + PRINTM(MERROR, "Invalid length of command\n"); + ret = -EINVAL; + goto done; + } + sys_config = kzalloc(sizeof(mlan_uap_bss_param), GFP_ATOMIC); + if (!sys_config) { + PRINTM(MERROR, "Fail to alloc memory for mlan_uap_bss_param\n"); + ret = -EFAULT; + goto done; + } + + memset(buf, 0, MAX_BUF_LEN); + memcpy(buf, data, MIN(len, (sizeof(buf) - 1))); + + /* Initialize the uap bss values which are uploaded from firmware */ + woal_uap_get_bss_param(priv, sys_config, MOAL_IOCTL_WAIT); + + /* Setting the default values */ + sys_config->channel = 6; + sys_config->preamble_type = 0; + + ret = woal_uap_ap_cfg_parse_data(sys_config, buf); + if (ret) + goto done; + + /* If BSS already started stop it first and restart + * after changing the setting */ + if (priv->bss_started == MTRUE) { + ret = woal_uap_bss_ctrl(priv, MOAL_IOCTL_WAIT, UAP_BSS_STOP); + if (ret) + goto done; + restart = 1; + } + + /* If the security mode is configured as WEP or WPA-PSK, + * it will disable 11n automatically, and if configured as + * open(off) or wpa2-psk, it will automatically enable 11n */ + if ((sys_config->protocol == PROTOCOL_STATIC_WEP) + || (sys_config->protocol == PROTOCOL_WPA)) { + if (MLAN_STATUS_SUCCESS != + woal_uap_set_11n_status(priv, sys_config, + MLAN_ACT_DISABLE)) { + ret = -EFAULT; + goto done; + } + } else { + if (MLAN_STATUS_SUCCESS != + woal_uap_set_11n_status(priv, sys_config, + MLAN_ACT_ENABLE)) { + ret = -EFAULT; + goto done; + } + } + + if (MLAN_STATUS_SUCCESS != + woal_set_get_sys_config(priv, MLAN_ACT_SET, MOAL_IOCTL_WAIT, + sys_config)) { + ret = -EFAULT; + goto done; + } + + /* Start the BSS after successful configuration */ + if (restart) + ret = woal_uap_bss_ctrl(priv, MOAL_IOCTL_WAIT, UAP_BSS_START); + +done: + kfree(sys_config); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get ap scan channel list + * + * @param priv A pointer to moal_private structure + * @param action MLAN_ACT_SET or MLAN_ACT_GET + * @param scan_channels A pointer to mlan_uap_scan_channels structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +woal_set_get_ap_scan_channels(moal_private *priv, t_u16 action, + mlan_uap_scan_channels * scan_channels) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_bss *bss = NULL; + mlan_ioctl_req *req = NULL; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + bss = (mlan_ds_bss *)req->pbuf; + bss->sub_command = MLAN_OID_UAP_SCAN_CHANNELS; + req->req_id = MLAN_IOCTL_BSS; + req->action = action; + + memcpy(&bss->param.ap_scan_channels, scan_channels, + sizeof(mlan_uap_scan_channels)); + + ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (ret != MLAN_STATUS_SUCCESS) + goto done; + if (action == MLAN_ACT_GET) + memcpy(scan_channels, &bss->param.ap_scan_channels, + sizeof(mlan_uap_scan_channels)); + +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get uap channel + * + * @param priv A pointer to moal_private structure + * @param action MLAN_ACT_SET or MLAN_ACT_GET + * @param wait_option wait option + * @param uap_channel A pointer to mlan_uap_channel structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +woal_set_get_ap_channel(moal_private *priv, t_u16 action, t_u8 wait_option, + chan_band_info * uap_channel) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_bss *bss = NULL; + mlan_ioctl_req *req = NULL; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + bss = (mlan_ds_bss *)req->pbuf; + bss->sub_command = MLAN_OID_UAP_CHANNEL; + req->req_id = MLAN_IOCTL_BSS; + req->action = action; + + memcpy(&bss->param.ap_channel, uap_channel, sizeof(chan_band_info)); + ret = woal_request_ioctl(priv, req, wait_option); + if (ret != MLAN_STATUS_SUCCESS) + goto done; + if (action == MLAN_ACT_GET) + memcpy(uap_channel, &bss->param.ap_channel, + sizeof(chan_band_info)); + +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief uap BSS control ioctl handler + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * @param data BSS control type + * @return 0 --success, otherwise fail + */ +int +woal_uap_bss_ctrl(moal_private *priv, t_u8 wait_option, int data) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_bss *bss = NULL; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + PRINTM(MIOCTL, "ioctl bss ctrl=%d\n", data); + if ((data != UAP_BSS_START) && (data != UAP_BSS_STOP) && + (data != UAP_BSS_RESET)) { + PRINTM(MERROR, "Invalid parameter: %d\n", data); + ret = -EINVAL; + goto done; + } + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + bss = (mlan_ds_bss *)req->pbuf; + switch (data) { + case UAP_BSS_START: + if (priv->bss_started == MTRUE) { + PRINTM(MWARN, "Warning: BSS already started!\n"); + /* goto done; */ + } else if (!priv->uap_host_based +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + || dfs_offload +#endif + ) { + /* about to start bss: issue channel check */ + woal_11h_channel_check_ioctl(priv, MOAL_IOCTL_WAIT); + } + bss->sub_command = MLAN_OID_BSS_START; + bss->param.host_based = priv->uap_host_based; + break; + case UAP_BSS_STOP: + if (priv->bss_started == MFALSE) { + PRINTM(MWARN, "Warning: BSS already stopped!\n"); + /* This is a situation where CAC it started and BSS start is dealyed + * and before CAC timer expires BSS stop is triggered. + * + * Do not skip sending the BSS_STOP command since there are many + * routines triggered on BSS_STOP command response. + */ + woal_cancel_cac_block(priv); + } + bss->sub_command = MLAN_OID_BSS_STOP; + break; + case UAP_BSS_RESET: + bss->sub_command = MLAN_OID_UAP_BSS_RESET; + woal_cancel_cac_block(priv); + break; + } + req->req_id = MLAN_IOCTL_BSS; + req->action = MLAN_ACT_SET; + + status = woal_request_ioctl(priv, req, wait_option); + if (status == MLAN_STATUS_FAILURE) { + ret = -EFAULT; + goto done; + } + if (data == UAP_BSS_STOP || data == UAP_BSS_RESET) { + priv->bss_started = MFALSE; + woal_stop_queue(priv->netdev); + if (netif_carrier_ok(priv->netdev)) + netif_carrier_off(priv->netdev); + woal_flush_tcp_sess_queue(priv); + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief This function sets multicast addresses to firmware + * + * @param dev A pointer to net_device structure + * @return N/A + */ +void +woal_uap_set_multicast_list(struct net_device *dev) +{ + ENTER(); + + LEAVE(); +} + +/** + * @brief ioctl function - entry point + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @param cmd Command + * + * @return 0 --success, otherwise fail + */ +int +woal_uap_do_ioctl(struct net_device *dev, struct ifreq *req, int cmd) +{ + int ret = 0; + ENTER(); + PRINTM(MIOCTL, "uap_do_ioctl: ioctl cmd = 0x%x\n", cmd); + switch (cmd) { + case WOAL_ANDROID_DEF_CMD: + /** android default ioctl ID is SIOCDEVPRIVATE + 1 */ + ret = woal_android_priv_cmd(dev, req); + break; + case UAP_IOCTL_CMD: + ret = woal_uap_ioctl(dev, req); + break; + case UAP_POWER_MODE: + ret = woal_uap_power_mode_ioctl(dev, req); + break; + case UAP_BSS_CTRL: + ret = woal_uap_bss_ctrl_ioctl(dev, req); + break; + case UAP_WAPI_MSG: + ret = woal_uap_set_wapi(dev, req); + break; + case UAP_BSS_CONFIG: + ret = woal_uap_bss_cfg_ioctl(dev, req); + break; + case UAP_STA_DEAUTH: + ret = woal_uap_sta_deauth_ioctl(dev, req); + break; + case UAP_RADIO_CTL: + ret = woal_uap_radio_ctl(dev, req); + break; + case UAPHOSTPKTINJECT: + ret = woal_send_host_packet(dev, req); + break; + case UAP_GET_STA_LIST: + ret = woal_uap_get_sta_list_ioctl(dev, req); + break; + case UAP_CUSTOM_IE: + ret = woal_custom_ie_ioctl(dev, req); + break; + case UAP_GET_BSS_TYPE: + ret = woal_get_bss_type(dev, req); + break; + case WOAL_ANDROID_PRIV_CMD: + ret = woal_android_priv_cmd(dev, req); + break; + default: +#ifdef UAP_WEXT + ret = woal_uap_do_priv_ioctl(dev, req, cmd); +#else + ret = -EOPNOTSUPP; +#endif + break; + } + + LEAVE(); + return ret; +} + +#ifdef CONFIG_PROC_FS +/** + * @brief Get version + * + * @param priv A pointer to moal_private structure + * @param version A pointer to version buffer + * @param max_len max length of version buffer + * + * @return N/A + */ +void +woal_uap_get_version(moal_private *priv, char *version, int max_len) +{ + mlan_ds_get_info *info = NULL; + mlan_ioctl_req *req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_get_info)); + if (req == NULL) { + LEAVE(); + return; + } + + info = (mlan_ds_get_info *)req->pbuf; + info->sub_command = MLAN_OID_GET_VER_EXT; + req->req_id = MLAN_IOCTL_GET_INFO; + req->action = MLAN_ACT_GET; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status == MLAN_STATUS_SUCCESS) { + PRINTM(MINFO, "MOAL UAP VERSION: %s\n", + info->param.ver_ext.version_str); + snprintf(version, max_len, priv->phandle->driver_version, + info->param.ver_ext.version_str); + } + + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return; +} +#endif + +/** + * @brief Get uap statistics + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * @param ustats A pointer to mlan_ds_uap_stats structure + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail + */ +mlan_status +woal_uap_get_stats(moal_private *priv, t_u8 wait_option, + mlan_ds_uap_stats *ustats) +{ + mlan_ds_get_info *info = NULL; + mlan_ioctl_req *req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_get_info)); + if (req == NULL) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + info = (mlan_ds_get_info *)req->pbuf; + info->sub_command = MLAN_OID_GET_STATS; + req->req_id = MLAN_IOCTL_GET_INFO; + req->action = MLAN_ACT_GET; + + status = woal_request_ioctl(priv, req, wait_option); + if (status == MLAN_STATUS_SUCCESS) { + if (ustats) + memcpy(ustats, &info->param.ustats, + sizeof(mlan_ds_uap_stats)); +#ifdef UAP_WEXT + priv->w_stats.discard.fragment = + info->param.ustats.fcs_error_count; + priv->w_stats.discard.retries = info->param.ustats.retry_count; + priv->w_stats.discard.misc = + info->param.ustats.ack_failure_count; +#endif + } + + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return status; +} + +/** + * @brief Set/Get system configuration parameters + * + * @param priv A pointer to moal_private structure + * @param action MLAN_ACT_SET or MLAN_ACT_GET + * @param ap_wmm_para A pointer to wmm_parameter_t structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +woal_set_get_ap_wmm_para(moal_private *priv, t_u16 action, + wmm_parameter_t *ap_wmm_para) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_bss *bss = NULL; + mlan_ioctl_req *req = NULL; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + bss = (mlan_ds_bss *)req->pbuf; + bss->sub_command = MLAN_OID_UAP_CFG_WMM_PARAM; + req->req_id = MLAN_IOCTL_BSS; + req->action = action; + + if (action == MLAN_ACT_SET) + memcpy(&bss->param.ap_wmm_para, ap_wmm_para, + sizeof(wmm_parameter_t)); + + ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (ret != MLAN_STATUS_SUCCESS) + goto done; + if (bss->param.ap_wmm_para.reserved != MLAN_STATUS_COMPLETE) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + if (action == MLAN_ACT_GET) + memcpy(ap_wmm_para, &bss->param.ap_wmm_para, + sizeof(wmm_parameter_t)); + +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get system configuration parameters + * + * @param priv A pointer to moal_private structure + * @param action MLAN_ACT_SET or MLAN_ACT_GET + * @param wait_option Wait option + * @param sys_cfg A pointer to mlan_uap_bss_param structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +woal_set_get_sys_config(moal_private *priv, t_u16 action, t_u8 wait_option, + mlan_uap_bss_param *sys_cfg) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_bss *bss = NULL; + mlan_ioctl_req *req = NULL; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + bss = (mlan_ds_bss *)req->pbuf; + bss->sub_command = MLAN_OID_UAP_BSS_CONFIG; + req->req_id = MLAN_IOCTL_BSS; + req->action = action; + + if (action == MLAN_ACT_SET) + memcpy(&bss->param.bss_config, sys_cfg, + sizeof(mlan_uap_bss_param)); + + ret = woal_request_ioctl(priv, req, wait_option); + if (ret != MLAN_STATUS_SUCCESS) + goto done; + + if (action == MLAN_ACT_GET) + memcpy(sys_cfg, &bss->param.bss_config, + sizeof(mlan_uap_bss_param)); + +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set invalid data for each member of mlan_uap_bss_param + * structure + * + * @param config A pointer to mlan_uap_bss_param structure + * + * @return N/A + */ +void +woal_set_sys_config_invalid_data(mlan_uap_bss_param *config) +{ + ENTER(); + + memset(config, 0, sizeof(mlan_uap_bss_param)); + config->bcast_ssid_ctl = 0x7F; + config->radio_ctl = 0x7F; + config->dtim_period = 0x7F; + config->beacon_period = 0x7FFF; + config->tx_data_rate = 0x7FFF; + config->mcbc_data_rate = 0x7FFF; + config->tx_power_level = 0x7F; + config->tx_antenna = 0x7F; + config->rx_antenna = 0x7F; + config->pkt_forward_ctl = 0x7F; + config->max_sta_count = 0x7FFF; + config->auth_mode = 0x7F; + config->sta_ageout_timer = 0x7FFFFFFF; + config->pairwise_update_timeout = 0x7FFFFFFF; + config->pwk_retries = 0x7FFFFFFF; + config->groupwise_update_timeout = 0x7FFFFFFF; + config->gwk_retries = 0x7FFFFFFF; + config->mgmt_ie_passthru_mask = 0x7FFFFFFF; + config->ps_sta_ageout_timer = 0x7FFFFFFF; + config->rts_threshold = 0x7FFF; + config->frag_threshold = 0x7FFF; + config->retry_limit = 0x7FFF; + config->filter.filter_mode = 0x7FFF; + config->filter.mac_count = 0x7FFF; + config->wpa_cfg.rsn_protection = 0x7F; + config->wpa_cfg.gk_rekey_time = 0x7FFFFFFF; + config->enable_2040coex = 0x7F; + config->wmm_para.qos_info = 0x7F; + + LEAVE(); +} diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_uap.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_uap.h new file mode 100644 index 000000000000..9973a1d792ad --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_uap.h @@ -0,0 +1,474 @@ +/** @file moal_uap.h + * + * @brief This file contains uap driver specific defines etc. + * + * Copyright (C) 2009-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +/******************************************************** +Change log: + 02/02/2009: initial version +********************************************************/ + +#ifndef _MOAL_UAP_H +#define _MOAL_UAP_H + +/** Maximum buffer length for WOAL_UAP_SET_GET_256_CHAR */ +#define MAX_BUF_LEN 256 + +/** Private command ID to send ioctl */ +#define UAP_IOCTL_CMD (SIOCDEVPRIVATE + 2) +/** Updating ADDBA variables */ +#define UAP_ADDBA_PARA 0 +/** Updating priority table for AMPDU/AMSDU */ +#define UAP_AGGR_PRIOTBL 1 +/** Updating addbareject table */ + +#define UAP_ADDBA_REJECT 2 +/** Get FW INFO */ +#define UAP_FW_INFO 4 +/** Updating Deep sleep variables */ +#define UAP_DEEP_SLEEP 3 +/** Tx data pause subcommand */ +#define UAP_TX_DATA_PAUSE 5 +/** sdcmd52 read write subcommand */ +#define UAP_SDCMD52_RW 6 +/** snmp mib subcommand */ +#define UAP_SNMP_MIB 7 +/** domain info subcommand */ +#define UAP_DOMAIN_INFO 8 +/** TX beamforming configuration */ +#define UAP_TX_BF_CFG 9 +#ifdef DFS_TESTING_SUPPORT +/** dfs testing subcommand */ +#define UAP_DFS_TESTING 10 +#endif +/** sub command ID to set/get Host Sleep configuration */ +#define UAP_HS_CFG 11 +/** sub command ID to set/get Host Sleep Parameters */ +#define UAP_HS_SET_PARA 12 + +/** Management Frame Control Mask */ +#define UAP_MGMT_FRAME_CONTROL 13 + +#define UAP_TX_RATE_CFG 14 + +#define UAP_DFS_REPEATER_MODE 16 + +#define UAP_CAC_TIMER_STATUS 17 + +/** Skip CAC */ +#define UAP_SKIP_CAC 18 + +#define UAP_HT_TX_CFG 19 + +#define UAP_OPERATION_CTRL 22 + +#define UAP_CHAN_SWITCH_COUNT_CFG 23 + +/** Private command ID to Power Mode */ +#define UAP_POWER_MODE (SIOCDEVPRIVATE + 3) + +/** Private command id to start/stop/reset bss */ +#define UAP_BSS_CTRL (SIOCDEVPRIVATE + 4) +/** BSS START */ +#define UAP_BSS_START 0 +/** BSS STOP */ +#define UAP_BSS_STOP 1 +/** BSS RESET */ +#define UAP_BSS_RESET 2 + +/** wapi_msg */ +typedef struct _wapi_msg { + /** message type */ + t_u16 msg_type; + /** message len */ + t_u16 msg_len; + /** message */ + t_u8 msg[96]; +} wapi_msg; + +/* wapi key msg */ +typedef struct _wapi_key_msg { + /** mac address */ + t_u8 mac_addr[MLAN_MAC_ADDR_LENGTH]; + /** pad */ + t_u8 pad; + /** key id */ + t_u8 key_id; + /** key */ + t_u8 key[32]; +} wapi_key_msg; + +/** Private command ID to set wapi info */ +#define UAP_WAPI_MSG (SIOCDEVPRIVATE + 10) +/** set wapi flag */ +#define P80211_PACKET_WAPIFLAG 0x0001 +/** set wapi key */ +#define P80211_PACKET_SETKEY 0x0003 +/** wapi mode psk */ +#define WAPI_MODE_PSK 0x04 +/** wapi mode certificate */ +#define WAPI_MODE_CERT 0x08 + +typedef struct _tx_rate_cfg_t { + /** sub command */ + int subcmd; + /** Action */ + int action; + /** Rate format */ + int rate_format; + /** Rate configured */ + int rate; + /** user_data_cnt */ + int user_data_cnt; + /** Rate bitmap */ + t_u16 bitmap_rates[MAX_BITMAP_RATES_SIZE]; +} tx_rate_cfg_t; + +/* dfs repeater mode */ +typedef struct _dfs_repeater_mode { + /** subcmd */ + t_u32 subcmd; + /** set/get */ + t_u32 action; + /** mode */ + t_u32 mode; +} dfs_repeater_mode; + +/* */ +typedef struct _cac_timer_status { + /** subcmd */ + t_u32 subcmd; + /** set/get */ + t_u32 action; + /** mode */ + t_u32 mode; +} cac_timer_status; + +/** skip_cac parameters */ +typedef struct _skip_cac_para { + /** subcmd */ + t_u32 subcmd; + /** Set/Get */ + t_u32 action; + /** enable/disable deepsleep*/ + t_u16 skip_cac; +} skip_cac_para; + +/** radio control command */ +#define UAP_RADIO_CTL (SIOCDEVPRIVATE + 5) + +/** Private command ID to BSS config */ +#define UAP_BSS_CONFIG (SIOCDEVPRIVATE + 6) + +/** deauth station */ +#define UAP_STA_DEAUTH (SIOCDEVPRIVATE + 7) + +/** uap get station list */ +#define UAP_GET_STA_LIST (SIOCDEVPRIVATE + 11) +#define UAPHOSTPKTINJECT WOAL_MGMT_FRAME_TX_IOCTL + +/** Private command ID to set/get custom IE buffer */ +#define UAP_CUSTOM_IE (SIOCDEVPRIVATE + 13) + +/** HS WAKE UP event id */ +#define UAP_EVENT_ID_HS_WAKEUP 0x80000001 +/** HS_ACTIVATED event id */ +#define UAP_EVENT_ID_DRV_HS_ACTIVATED 0x80000002 +/** HS DEACTIVATED event id */ +#define UAP_EVENT_ID_DRV_HS_DEACTIVATED 0x80000003 + +/** Host sleep flag set */ +#define HS_CFG_FLAG_GET 0 +/** Host sleep flag get */ +#define HS_CFG_FLAG_SET 1 +/** Host sleep flag for condition */ +#define HS_CFG_FLAG_CONDITION 2 +/** Host sleep flag for GPIO */ +#define HS_CFG_FLAG_GPIO 4 +/** Host sleep flag for Gap */ +#define HS_CFG_FLAG_GAP 8 +/** Host sleep flag for all */ +#define HS_CFG_FLAG_ALL 0x0f +/** Host sleep mask to get condition */ +#define HS_CFG_CONDITION_MASK 0x0f + +/** ds_hs_cfg */ +typedef struct _ds_hs_cfg { + /** subcmd */ + t_u32 subcmd; + /** Bit0: 0 - Get, 1 Set + * Bit1: 1 - conditions is valid + * Bit2: 2 - gpio is valid + * Bit3: 3 - gap is valid + */ + t_u32 flags; + /** Host sleep config condition */ + /** Bit0: non-unicast data + * Bit1: unicast data + * Bit2: mac events + * Bit3: magic packet + */ + t_u32 conditions; + /** GPIO */ + t_u32 gpio; + /** Gap in milliseconds */ + t_u32 gap; +} ds_hs_cfg; + +/** Private command ID to get BSS type */ +#define UAP_GET_BSS_TYPE (SIOCDEVPRIVATE + 15) + +/** addba_param */ +typedef struct _addba_param { + /** subcmd */ + t_u32 subcmd; + /** Set/Get */ + t_u32 action; + /** block ack timeout for ADDBA request */ + t_u32 timeout; + /** Buffer size for ADDBA request */ + t_u32 txwinsize; + /** Buffer size for ADDBA response */ + t_u32 rxwinsize; + /** amsdu for ADDBA request */ + t_u8 txamsdu; + /** amsdu for ADDBA response */ + t_u8 rxamsdu; +} addba_param; + +/** aggr_prio_tbl */ +typedef struct _aggr_prio_tbl { + /** subcmd */ + t_u32 subcmd; + /** Set/Get */ + t_u32 action; + /** ampdu priority table */ + t_u8 ampdu[MAX_NUM_TID]; + /** amsdu priority table */ + t_u8 amsdu[MAX_NUM_TID]; +} aggr_prio_tbl; + +/** addba_reject parameters */ +typedef struct _addba_reject_para { + /** subcmd */ + t_u32 subcmd; + /** Set/Get */ + t_u32 action; + /** BA Reject paramters */ + t_u8 addba_reject[MAX_NUM_TID]; +} addba_reject_para; + +/** fw_info */ +typedef struct _fw_info { + /** subcmd */ + t_u32 subcmd; + /** Get */ + t_u32 action; + /** Firmware release number */ + t_u32 fw_release_number; + /** Device support for MIMO abstraction of MCSs */ + t_u8 hw_dev_mcs_support; + /** fw_bands*/ + t_u8 fw_bands; + /** Region Code */ + t_u16 region_code; + /** 802.11n device capabilities */ + t_u32 hw_dot_11n_dev_cap; +} fw_info; + +typedef struct _ht_tx_cfg_para_hdr { + /** Sub command */ + t_u32 subcmd; + /** Action: Set/Get */ + t_u32 action; +} ht_tx_cfg_para_hdr; + +typedef struct _tx_bf_cfg_para_hdr { + /** Sub command */ + t_u32 subcmd; + /** Action: Set/Get */ + t_u32 action; +} tx_bf_cfg_para_hdr; + +typedef struct _uap_oper_para_hdr { + /** Sub command */ + t_u32 subcmd; + /** Action: Set/Get */ + t_u32 action; +} uap_oper_para_hdr; + +/** sdcmd52rw parameters */ +typedef struct _sdcmd52_para { + /** subcmd */ + t_u32 subcmd; + /** Write /Read */ + t_u32 action; + /** Command 52 paramters */ + t_u8 cmd52_params[3]; +} sdcmd52_para; + +/** deep_sleep parameters */ +typedef struct _deep_sleep_para { + /** subcmd */ + t_u32 subcmd; + /** Set/Get */ + t_u32 action; + /** enable/disable deepsleep*/ + t_u16 deep_sleep; + /** idle_time */ + t_u16 idle_time; +} deep_sleep_para; + +/** tx_data_pause parameters */ +typedef struct _tx_data_pause_para { + /** subcmd */ + t_u32 subcmd; + /** Set/Get */ + t_u32 action; + /** enable/disable Tx data pause*/ + t_u16 txpause; + /** Max number of TX buffer allowed for all PS client*/ + t_u16 txbufcnt; +} tx_data_pause_para; + +/** mgmt_frame_ctrl */ +typedef struct _mgmt_frame_ctrl { + /** subcmd */ + t_u32 subcmd; + /** Set/Get */ + t_u32 action; + /** mask */ + t_u32 mask; +} mgmt_frame_ctrl; + +typedef struct _snmp_mib_para { + /** subcmd */ + t_u32 subcmd; + /** Set/Get */ + t_u32 action; + /** oid to set/get */ + t_u16 oid; + /** length of oid value */ + t_u16 oid_val_len; + /** oid value to set/get */ + t_u8 oid_value[0]; +} snmp_mib_para; + +/** Max length for oid_value field */ +#define MAX_SNMP_VALUE_SIZE 128 + +/** Oid for 802.11D enable/disable */ +#define OID_80211D_ENABLE 0x0009 +/** Oid for 802.11H enable/disable */ +#define OID_80211H_ENABLE 0x000a + +#ifdef DFS_TESTING_SUPPORT +/** dfs_testing parameters */ +typedef struct _dfs_testing_param { + /** subcmd */ + t_u32 subcmd; + /** Set/Get */ + t_u32 action; + /** user CAC period (msec) */ + t_u32 usr_cac_period; + /** user NOP period (sec) */ + t_u16 usr_nop_period; + /** don't change channel on radar */ + t_u8 no_chan_change; + /** fixed channel to change to on radar */ + t_u8 fixed_new_chan; +} dfs_testing_para; +#endif + +/** Channel switch count config */ +typedef struct _cscount_cfg_t { + /** subcmd */ + t_u32 subcmd; + /** Set/Get */ + t_u32 action; + /** user channel switch count */ + t_u8 cs_count; +} cscount_cfg_t; + +/** domain_info parameters */ +typedef struct _domain_info_param { + /** subcmd */ + t_u32 subcmd; + /** Set/Get */ + t_u32 action; + /** domain_param TLV (incl. header) */ + t_u8 tlv[0]; +} domain_info_para; + +/** DOMAIN_INFO param sizes */ +#define TLV_HEADER_LEN (2 + 2) +#define SUB_BAND_LEN 3 +#define MAX_SUB_BANDS 40 + +/** MAX domain TLV length */ +#define MAX_DOMAIN_TLV_LEN (TLV_HEADER_LEN + COUNTRY_CODE_LEN \ + + (SUB_BAND_LEN * MAX_SUB_BANDS)) + +int woal_set_get_uap_power_mode(moal_private *priv, t_u32 action, + mlan_ds_ps_mgmt *ps_mgmt); +void woal_uap_set_multicast_list(struct net_device *dev); +int woal_uap_do_ioctl(struct net_device *dev, struct ifreq *req, int cmd); +int woal_uap_bss_ctrl(moal_private *priv, t_u8 wait_option, int data); +#ifdef UAP_CFG80211 +#if defined(DFS_TESTING_SUPPORT) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) +int woal_uap_get_channel_nop_info(moal_private *priv, t_u8 wait_option, + mlan_ds_11h_chan_nop_info * ch_info); +#endif +#endif +#endif +mlan_status woal_set_get_ap_channel(moal_private *priv, t_u16 action, + t_u8 wait_option, + chan_band_info * uap_channel); +#ifdef CONFIG_PROC_FS +void woal_uap_get_version(moal_private *priv, char *version, int max_len); +#endif +mlan_status woal_uap_get_stats(moal_private *priv, t_u8 wait_option, + mlan_ds_uap_stats *ustats); +#if defined(UAP_WEXT) || defined(UAP_CFG80211) +extern struct iw_handler_def woal_uap_handler_def; +struct iw_statistics *woal_get_uap_wireless_stats(struct net_device *dev); +/** IOCTL function for wireless private IOCTLs */ +int woal_uap_do_priv_ioctl(struct net_device *dev, struct ifreq *req, int cmd); +#endif +/** Set invalid data for each member of mlan_uap_bss_param */ +void woal_set_sys_config_invalid_data(mlan_uap_bss_param *config); +/** Set/Get system configuration parameters */ +mlan_status woal_set_get_sys_config(moal_private *priv, + t_u16 action, t_u8 wait_option, + mlan_uap_bss_param *sys_cfg); +/** Set get AP wmm parameter */ +mlan_status woal_set_get_ap_wmm_para(moal_private *priv, t_u16 action, + wmm_parameter_t *ap_wmm_para); +int woal_uap_set_ap_cfg(moal_private *priv, t_u8 *data, int len); +int woal_set_uap_ht_tx_cfg(moal_private *priv, Band_Config_t bandcfg, t_u8 en); +mlan_status woal_uap_set_11n_status(moal_private *priv, + mlan_uap_bss_param *sys_cfg, t_u8 action); +#ifdef UAP_WEXT +void woal_ioctl_get_uap_info_resp(moal_private *priv, mlan_ds_get_info *info); +int woal_set_get_custom_ie(moal_private *priv, t_u16 mask, t_u8 *ie, + int ie_len); +#endif /* UAP_WEXT */ + +#endif /* _MOAL_UAP_H */ diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_uap_cfg80211.c b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_uap_cfg80211.c new file mode 100644 index 000000000000..a46de1d248c2 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_uap_cfg80211.c @@ -0,0 +1,3167 @@ +/** @file moal_uap_cfg80211.c + * + * @brief This file contains the functions for uAP CFG80211. + * + * Copyright (C) 2011-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +#include "moal_cfg80211.h" +#include "moal_uap_cfg80211.h" +/** deauth reason code */ +#define REASON_CODE_DEAUTH_LEAVING 3 +/******************************************************** + Local Variables +********************************************************/ + +/******************************************************** + Global Variables +********************************************************/ +#ifdef WIFI_DIRECT_SUPPORT +extern int GoAgeoutTime; +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) +extern int dfs_offload; +#endif +/******************************************************** + Local Functions +********************************************************/ + +/******************************************************** + Global Functions +********************************************************/ +/** + * @brief send deauth to station + * + * @param A pointer to moal_private + * @param mac A pointer to station mac address + * @param reason_code ieee deauth reason code + * @return 0 -- success, otherwise fail + */ +static int +woal_deauth_station(moal_private *priv, u8 *mac_addr, u16 reason_code) +{ + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_bss *bss = NULL; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (ioctl_req == NULL) { + ret = -ENOMEM; + goto done; + } + bss = (mlan_ds_bss *)ioctl_req->pbuf; + bss->sub_command = MLAN_OID_UAP_DEAUTH_STA; + ioctl_req->req_id = MLAN_IOCTL_BSS; + ioctl_req->action = MLAN_ACT_SET; + + memcpy(bss->param.deauth_param.mac_addr, mac_addr, + MLAN_MAC_ADDR_LENGTH); + bss->param.deauth_param.reason_code = reason_code; + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + LEAVE(); + return ret; +} + +/** + * @brief send deauth to all station + * + * @param priv A pointer to moal_private structure + * + * @return 0 -- success, otherwise fail + */ +static int +woal_deauth_all_station(moal_private *priv) +{ + int ret = -EFAULT; + int i = 0; + mlan_ds_get_info *info = NULL; + mlan_ioctl_req *ioctl_req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + if (priv->media_connected == MFALSE) { + PRINTM(MINFO, "cfg80211: Media not connected!\n"); + LEAVE(); + return 0; + } + PRINTM(MIOCTL, "del all station\n"); + /* Allocate an IOCTL request buffer */ + ioctl_req = + (mlan_ioctl_req *) + woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_get_info)); + if (ioctl_req == NULL) { + ret = -ENOMEM; + goto done; + } + + info = (mlan_ds_get_info *)ioctl_req->pbuf; + info->sub_command = MLAN_OID_UAP_STA_LIST; + ioctl_req->req_id = MLAN_IOCTL_GET_INFO; + ioctl_req->action = MLAN_ACT_GET; + + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) + goto done; + if (!info->param.sta_list.sta_count) + goto done; + for (i = 0; i < info->param.sta_list.sta_count; i++) { + PRINTM(MIOCTL, "deauth station " MACSTR "\n", + MAC2STR(info->param.sta_list.info[i].mac_address)); + ret = woal_deauth_station(priv, + info->param.sta_list.info[i]. + mac_address, + REASON_CODE_DEAUTH_LEAVING); + } + woal_sched_timeout(200); +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + return ret; +} + +/** + * @brief Verify RSN IE + * + * @param rsn_ie Pointer IEEEtypes_Rsn_t structure + * @param sys_config Pointer to mlan_uap_bss_param structure + * + * @return MTRUE/MFALSE + */ +static t_u8 +woal_check_rsn_ie(IEEEtypes_Rsn_t *rsn_ie, mlan_uap_bss_param *sys_config) +{ + int left = 0; + int count = 0; + int i = 0; + wpa_suite_auth_key_mgmt_t *key_mgmt = NULL; + left = rsn_ie->len + 2; + if (left < sizeof(IEEEtypes_Rsn_t)) + return MFALSE; + sys_config->wpa_cfg.group_cipher = 0; + sys_config->wpa_cfg.pairwise_cipher_wpa2 = 0; + sys_config->key_mgmt = 0; + /* check the group cipher */ + switch (rsn_ie->group_cipher.type) { + case WPA_CIPHER_TKIP: + sys_config->wpa_cfg.group_cipher = CIPHER_TKIP; + break; + case WPA_CIPHER_AES_CCM: + sys_config->wpa_cfg.group_cipher = CIPHER_AES_CCMP; + break; + default: + break; + } + count = le16_to_cpu(rsn_ie->pairwise_cipher.count); + for (i = 0; i < count; i++) { + switch (rsn_ie->pairwise_cipher.list[i].type) { + case WPA_CIPHER_TKIP: + sys_config->wpa_cfg.pairwise_cipher_wpa2 |= CIPHER_TKIP; + break; + case WPA_CIPHER_AES_CCM: + sys_config->wpa_cfg.pairwise_cipher_wpa2 |= + CIPHER_AES_CCMP; + break; + default: + break; + } + } + left -= sizeof(IEEEtypes_Rsn_t) + (count - 1) * sizeof(wpa_suite); + if (left < sizeof(wpa_suite_auth_key_mgmt_t)) + return MFALSE; + key_mgmt = + (wpa_suite_auth_key_mgmt_t *)((u8 *)rsn_ie + + sizeof(IEEEtypes_Rsn_t) + (count - + 1) * + sizeof(wpa_suite)); + count = le16_to_cpu(key_mgmt->count); + if (left < + (sizeof(wpa_suite_auth_key_mgmt_t) + + (count - 1) * sizeof(wpa_suite))) + return MFALSE; + for (i = 0; i < count; i++) { + switch (key_mgmt->list[i].type) { + case RSN_AKM_8021X: + sys_config->key_mgmt |= KEY_MGMT_EAP; + break; + case RSN_AKM_PSK: + sys_config->key_mgmt |= KEY_MGMT_PSK; + break; + case RSN_AKM_PSK_SHA256: + sys_config->key_mgmt |= KEY_MGMT_PSK_SHA256; + break; + } + } + return MTRUE; +} + +/** + * @brief Verify WPA IE + * + * @param wpa_ie Pointer WPA IE + * @param sys_config Pointer to mlan_uap_bss_param structure + * + * @return MTRUE/MFALSE + */ +static t_u8 +woal_check_wpa_ie(IEEEtypes_Wpa_t *wpa_ie, mlan_uap_bss_param *sys_config) +{ + int left = 0; + int count = 0; + int i = 0; + wpa_suite_auth_key_mgmt_t *key_mgmt = NULL; + left = wpa_ie->len + 2; + if (left < sizeof(IEEEtypes_Wpa_t)) + return MFALSE; + sys_config->wpa_cfg.group_cipher = 0; + sys_config->wpa_cfg.pairwise_cipher_wpa = 0; + switch (wpa_ie->group_cipher.type) { + case WPA_CIPHER_TKIP: + sys_config->wpa_cfg.group_cipher = CIPHER_TKIP; + break; + case WPA_CIPHER_AES_CCM: + sys_config->wpa_cfg.group_cipher = CIPHER_AES_CCMP; + break; + default: + break; + } + count = le16_to_cpu(wpa_ie->pairwise_cipher.count); + for (i = 0; i < count; i++) { + switch (wpa_ie->pairwise_cipher.list[i].type) { + case WPA_CIPHER_TKIP: + sys_config->wpa_cfg.pairwise_cipher_wpa |= CIPHER_TKIP; + break; + case WPA_CIPHER_AES_CCM: + sys_config->wpa_cfg.pairwise_cipher_wpa |= + CIPHER_AES_CCMP; + break; + default: + break; + } + } + left -= sizeof(IEEEtypes_Wpa_t) + (count - 1) * sizeof(wpa_suite); + if (left < sizeof(wpa_suite_auth_key_mgmt_t)) + return MFALSE; + key_mgmt = + (wpa_suite_auth_key_mgmt_t *)((u8 *)wpa_ie + + sizeof(IEEEtypes_Wpa_t) + (count - + 1) * + sizeof(wpa_suite)); + count = le16_to_cpu(key_mgmt->count); + if (left < + (sizeof(wpa_suite_auth_key_mgmt_t) + + (count - 1) * sizeof(wpa_suite))) + return MFALSE; + for (i = 0; i < count; i++) { + switch (key_mgmt->list[i].type) { + case RSN_AKM_8021X: + sys_config->key_mgmt = KEY_MGMT_EAP; + break; + case RSN_AKM_PSK: + sys_config->key_mgmt = KEY_MGMT_PSK; + break; + } + } + return MTRUE; +} + +/** + * @brief Find RSN/WPA IES + * + * @param ie Pointer IE buffer + * @param sys_config Pointer to mlan_uap_bss_param structure + * + * @return MTRUE/MFALSE + */ +static t_u8 +woal_find_wpa_ies(const t_u8 *ie, int len, mlan_uap_bss_param *sys_config) +{ + int bytes_left = len; + const t_u8 *pcurrent_ptr = ie; + t_u16 total_ie_len; + t_u8 element_len; + t_u8 wpa2 = 0; + t_u8 wpa = 0; + t_u8 ret = MFALSE; + IEEEtypes_ElementId_e element_id; + IEEEtypes_VendorSpecific_t *pvendor_ie; + const t_u8 wpa_oui[4] = { 0x00, 0x50, 0xf2, 0x01 }; + + while (bytes_left >= 2) { + element_id = (IEEEtypes_ElementId_e)(*((t_u8 *)pcurrent_ptr)); + element_len = *((t_u8 *)pcurrent_ptr + 1); + total_ie_len = element_len + sizeof(IEEEtypes_Header_t); + if (bytes_left < total_ie_len) { + PRINTM(MERROR, + "InterpretIE: Error in processing IE, bytes left < IE length\n"); + bytes_left = 0; + continue; + } + switch (element_id) { + case RSN_IE: + wpa2 = woal_check_rsn_ie((IEEEtypes_Rsn_t *) + pcurrent_ptr, sys_config); + break; + case VENDOR_SPECIFIC_221: + pvendor_ie = (IEEEtypes_VendorSpecific_t *)pcurrent_ptr; + if (!memcmp + (pvendor_ie->vend_hdr.oui, wpa_oui, + sizeof(pvendor_ie->vend_hdr.oui)) && + (pvendor_ie->vend_hdr.oui_type == wpa_oui[3])) { + wpa = woal_check_wpa_ie((IEEEtypes_Wpa_t *) + pcurrent_ptr, + sys_config); + } + break; + default: + break; + } + pcurrent_ptr += element_len + 2; + /* Need to account for IE ID and IE Len */ + bytes_left -= (element_len + 2); + } + if (wpa && wpa2) { + sys_config->protocol = PROTOCOL_WPA | PROTOCOL_WPA2; + ret = MTRUE; + } else if (wpa2) { + sys_config->protocol = PROTOCOL_WPA2; + ret = MTRUE; + } else if (wpa) { + sys_config->protocol = PROTOCOL_WPA; + ret = MTRUE; + } + return ret; +} + +/** + * @brief Find and set WMM IES + * + * @param ie Pointer IE buffer + * @param sys_config Pointer to mlan_uap_bss_param structure + * + * @return N/A + */ +static t_void +woal_set_wmm_ies(const t_u8 *ie, int len, mlan_uap_bss_param *sys_config) +{ + int bytes_left = len; + const t_u8 *pcurrent_ptr = ie; + t_u16 total_ie_len; + t_u8 element_len; + IEEEtypes_VendorSpecific_t *pvendor_ie; + IEEEtypes_ElementId_e element_id; + const t_u8 wmm_oui[4] = { 0x00, 0x50, 0xf2, 0x02 }; + t_u8 *poui; + + while (bytes_left >= 2) { + element_id = (IEEEtypes_ElementId_e)(*((t_u8 *)pcurrent_ptr)); + element_len = *((t_u8 *)pcurrent_ptr + 1); + total_ie_len = element_len + sizeof(IEEEtypes_Header_t); + if (bytes_left < total_ie_len) { + PRINTM(MERROR, + "InterpretIE: Error in processing IE, bytes left < IE length\n"); + bytes_left = 0; + continue; + } + switch (element_id) { + case VENDOR_SPECIFIC_221: + pvendor_ie = (IEEEtypes_VendorSpecific_t *)pcurrent_ptr; + poui = pvendor_ie->vend_hdr.oui; + if (!memcmp(poui, wmm_oui, sizeof(wmm_oui))) { + if (total_ie_len == + sizeof(IEEEtypes_WmmParameter_t)) { + /* + * Only accept and copy the WMM IE if + * it matches the size expected for the + * WMM Parameter IE. + */ + memcpy(&sys_config->wmm_para, + pcurrent_ptr + + sizeof(IEEEtypes_Header_t), + element_len); + } + } + + break; + default: + break; + } + pcurrent_ptr += element_len + 2; + /* Need to account for IE ID and IE Len */ + bytes_left -= (element_len + 2); + } + +} + +/** + * @brief get ht_cap from beacon ie + * + * @param ie Pointer to IEs + * @param len Total length of ie + * + * @return ht_cap + */ +static t_u16 +woal_get_htcap_info(const t_u8 *ie, int len) +{ + t_u16 ht_cap_info = 0; + IEEEtypes_HTCap_t *htcap_ie = NULL; + htcap_ie = + (IEEEtypes_HTCap_t *)woal_parse_ie_tlv(ie, len, HT_CAPABILITY); + if (htcap_ie) { + /* hostap has converted ht_cap_info to little endian, here conver to host endian */ + ht_cap_info = woal_le16_to_cpu(htcap_ie->ht_cap.ht_cap_info); + PRINTM(MMSG, "Get ht_cap from beacon ies: 0x%x\n", ht_cap_info); + } + return ht_cap_info; +} + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) +/** Starting Frequency for 11A band */ +#define START_FREQ_11A_BAND 5000 /* in MHz */ +/** + * @brief convert cfg80211_chan_def to Band_Config + * + * @param bandcfg A pointer to (Band_Config_t structure + * @param chandef A pointer to cfg80211_chan_def structure + * + * @return N/A + */ +static void +woal_convert_chan_to_bandconfig(Band_Config_t *bandcfg, + struct cfg80211_chan_def *chandef) +{ + ENTER(); + if (chandef->chan->hw_value <= MAX_BG_CHANNEL) + bandcfg->chanBand = BAND_2GHZ; + else + bandcfg->chanBand = BAND_5GHZ; + switch (chandef->width) { + case NL80211_CHAN_WIDTH_20_NOHT: + case NL80211_CHAN_WIDTH_20: + bandcfg->chanWidth = CHAN_BW_20MHZ; + break; + case NL80211_CHAN_WIDTH_40: + bandcfg->chanWidth = CHAN_BW_40MHZ; + if (chandef->center_freq1 > chandef->chan->center_freq) + bandcfg->chan2Offset = SEC_CHAN_ABOVE; + else + bandcfg->chan2Offset = SEC_CHAN_BELOW; + break; + default: + break; + } + LEAVE(); + return; +} + +/** + * @brief Enable radar detect for DFS channel + * + * @param priv A pointer to moal private structure + * @param chandef A pointer to cfg80211_chan_def structure + * @return N/A + */ +static void +woal_enable_dfs_support(moal_private *priv, struct cfg80211_chan_def *chandef) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_11h_chan_rep_req *pchan_rpt_req = NULL; + mlan_ds_11h_cfg *p11h_cfg = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + ENTER(); + if (!(chandef->chan->flags & IEEE80211_CHAN_RADAR)) { + PRINTM(MIOCTL, "No radar channel\n"); + LEAVE(); + return; + } + PRINTM(MIOCTL, "start Radar detect, chan %d , Bw %d \n", + chandef->chan->hw_value, chandef->width); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11h_cfg)); + if (NULL == req) { + PRINTM(MIOCTL, "No Memory to allocate ioctl buffer\n"); + LEAVE(); + return; + } + p11h_cfg = (mlan_ds_11h_cfg *)req->pbuf; + pchan_rpt_req = &p11h_cfg->param.chan_rpt_req; + pchan_rpt_req->startFreq = 5000; + pchan_rpt_req->chanNum = (t_u8)chandef->chan->hw_value; + woal_convert_chan_to_bandconfig(&pchan_rpt_req->bandcfg, chandef); + pchan_rpt_req->host_based = MTRUE; + pchan_rpt_req->millisec_dwell_time = 0; + + p11h_cfg->sub_command = MLAN_OID_11H_CHANNEL_CHECK; + req->req_id = MLAN_IOCTL_11H_CFG; + req->action = MLAN_ACT_SET; + /* Send Channel Check command and wait until the report is ready */ + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return; +} +#endif + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) +/** + * @brief initialize AP or GO bss config + * + * @param priv A pointer to moal private structure + * @param params A pointer to cfg80211_ap_settings structure + * @return 0 -- success, otherwise fail + */ +static int +woal_cfg80211_beacon_config(moal_private *priv, + struct cfg80211_ap_settings *params) +#else +/** + * @brief initialize AP or GO bss config + * + * @param priv A pointer to moal private structure + * @param params A pointer to beacon_parameters structure + * @return 0 -- success, otherwise fail + */ +static int +woal_cfg80211_beacon_config(moal_private *priv, + struct beacon_parameters *params) +#endif +{ + struct wiphy *wiphy = NULL; + const t_u8 *ie = NULL; + int ret = 0, ie_len; + mlan_uap_bss_param *sys_config = NULL; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) + int i = 0; +#else + t_u8 wpa_ies; + const t_u8 *ssid_ie = NULL; + struct ieee80211_mgmt *head = NULL; + t_u16 capab_info = 0; +#endif + t_u8 rates_bg[13] = { + 0x82, 0x84, 0x8b, 0x96, + 0x0c, 0x12, 0x18, 0x24, + 0x30, 0x48, 0x60, 0x6c, + 0x00 + }; + t_u8 rates_a[9] = { + 0x8c, 0x12, 0x98, 0x24, + 0xb0, 0x48, 0x60, 0x6c, + 0x00 + }; +#ifdef WIFI_DIRECT_SUPPORT + t_u8 rates_wfd[9] = { + 0x8c, 0x12, 0x18, 0x24, + 0x30, 0x48, 0x60, 0x6c, + 0x00 + }; +#endif + t_u8 chan2Offset = SEC_CHAN_NONE; + t_u8 enable_11n = MTRUE; + t_u16 ht_cap = 0; + t_u8 *wapi_ie = NULL; + int wapi_ie_len = 0; +#if defined(DFS_TESTING_SUPPORT) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) + mlan_ds_11h_chan_nop_info chan_nop_info; + Band_Config_t bandcfg; +#endif +#endif + ENTER(); + + if (!params) { + ret = -EFAULT; + goto done; + } +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) + ie = ((struct cfg80211_ap_settings *)params)->beacon.tail; + ie_len = ((struct cfg80211_ap_settings *)params)->beacon.tail_len; +#else + ie = ((struct beacon_parameters *)params)->tail; + ie_len = ((struct beacon_parameters *)params)->tail_len; +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) + wapi_ie = (t_u8 *)woal_parse_ie_tlv(params->beacon.tail, + params->beacon.tail_len, WAPI_IE); +#else + wapi_ie = (t_u8 *)woal_parse_ie_tlv(params->tail, + params->tail_len, WAPI_IE); +#endif + if (wapi_ie) { + wapi_ie_len = *(wapi_ie + 1) + 2; + woal_set_get_gen_ie(priv, MLAN_ACT_SET, wapi_ie, &wapi_ie_len, + MOAL_IOCTL_WAIT); + } + wiphy = priv->phandle->wiphy; + if (priv->bss_type != MLAN_BSS_TYPE_UAP +#ifdef WIFI_DIRECT_SUPPORT + && priv->bss_type != MLAN_BSS_TYPE_WIFIDIRECT +#endif + ) { + ret = -EFAULT; + goto done; + } + sys_config = kzalloc(sizeof(mlan_uap_bss_param), GFP_ATOMIC); + if (!sys_config) { + PRINTM(MERROR, "Fail to alloc memory for mlan_uap_bss_param\n"); + ret = -EFAULT; + goto done; + } + + /* Initialize the uap bss values which are uploaded from firmware */ + if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv, + MLAN_ACT_GET, + MOAL_IOCTL_WAIT, + sys_config)) { + PRINTM(MERROR, "Error getting AP confiruration\n"); + ret = -EFAULT; + goto done; + } + + /* Setting the default values */ + sys_config->channel = 6; + sys_config->preamble_type = 0; + sys_config->mgmt_ie_passthru_mask = priv->mgmt_subtype_mask; + memcpy(sys_config->mac_addr, priv->current_addr, ETH_ALEN); + +#ifdef WIFI_DIRECT_SUPPORT + if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT && GoAgeoutTime) { + sys_config->sta_ageout_timer = GoAgeoutTime; + sys_config->ps_sta_ageout_timer = GoAgeoutTime; + } +#endif + /* Set frag_threshold, rts_threshold, and retry limit */ + sys_config->frag_threshold = wiphy->frag_threshold; + sys_config->rts_threshold = wiphy->rts_threshold; + sys_config->retry_limit = wiphy->retry_long; + if (sys_config->frag_threshold == MLAN_FRAG_RTS_DISABLED) { + sys_config->frag_threshold = MLAN_FRAG_MAX_VALUE; + } + if (sys_config->rts_threshold == MLAN_FRAG_RTS_DISABLED) { + sys_config->rts_threshold = MLAN_RTS_MAX_VALUE; + } + + if (priv->bss_type == MLAN_BSS_TYPE_UAP) { +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) + if (params->beacon_interval) + sys_config->beacon_period = params->beacon_interval; +#else + if (params->interval) + sys_config->beacon_period = params->interval; +#endif + if (params->dtim_period) + sys_config->dtim_period = params->dtim_period; + } +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + /** back up ap's channel */ + memcpy(&priv->chan, ¶ms->chandef, sizeof(struct cfg80211_chan_def)); +#endif + +#if defined(DFS_TESTING_SUPPORT) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) + PRINTM(MCMND, "Checking if AP's channel %d is under NOP\n", + priv->channel); + woal_convert_chan_to_bandconfig(&bandcfg, ¶ms->chandef); + memset(&chan_nop_info, 0, sizeof(chan_nop_info)); + chan_nop_info.curr_chan = priv->channel; + chan_nop_info.chan_width = bandcfg.chanWidth; + if (params->chandef.width >= NL80211_CHAN_WIDTH_20) + chan_nop_info.new_chan.is_11n_enabled = MTRUE; + chan_nop_info.new_chan.bandcfg = bandcfg; + woal_uap_get_channel_nop_info(priv, MOAL_IOCTL_WAIT, &chan_nop_info); + if (chan_nop_info.chan_under_nop) { + PRINTM(MCMND, + "cfg80211: Channel %d is under NOP, New channel=%d\n", + priv->channel, chan_nop_info.new_chan.channel); + priv->chan_under_nop = chan_nop_info.chan_under_nop; + priv->channel = chan_nop_info.new_chan.channel; + woal_chandef_create(priv, &priv->chan, &chan_nop_info.new_chan); + } +#endif +#endif + + if (priv->channel) { + memset(sys_config->rates, 0, sizeof(sys_config->rates)); +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + switch (priv->chan.width) { +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 11, 0) + case NL80211_CHAN_WIDTH_5: + case NL80211_CHAN_WIDTH_10: +#endif + case NL80211_CHAN_WIDTH_20_NOHT: + enable_11n = MFALSE; + break; + case NL80211_CHAN_WIDTH_20: + break; + case NL80211_CHAN_WIDTH_40: + if (priv->chan.center_freq1 < + priv->chan.chan->center_freq) + chan2Offset = SEC_CHAN_BELOW; + else + chan2Offset = SEC_CHAN_ABOVE; + break; + case NL80211_CHAN_WIDTH_80: + case NL80211_CHAN_WIDTH_80P80: + case NL80211_CHAN_WIDTH_160: + chan2Offset = + woal_get_second_channel_offset(priv->channel); + break; + default: + PRINTM(MWARN, "Unknown channel width: %d\n", + priv->chan.width); + break; + } +#else + switch (params->channel_type) { + case NL80211_CHAN_NO_HT: + enable_11n = MFALSE; + break; + case NL80211_CHAN_HT20: + break; + case NL80211_CHAN_HT40PLUS: + chan2Offset = SEC_CHAN_ABOVE; + break; + case NL80211_CHAN_HT40MINUS: + chan2Offset = SEC_CHAN_BELOW; + break; + default: + PRINTM(MWARN, "Unknown channel type: %d\n", + params->channel_type); + break; + } +#endif +#endif /* CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) */ + sys_config->channel = priv->channel; + if (priv->channel <= MAX_BG_CHANNEL) { + sys_config->bandcfg.chanBand = BAND_2GHZ; +#ifdef WIFI_DIRECT_SUPPORT + if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT) + memcpy(sys_config->rates, rates_wfd, + sizeof(rates_wfd)); + else +#endif + memcpy(sys_config->rates, rates_bg, + sizeof(rates_bg)); + } else { + sys_config->bandcfg.chanBand = BAND_5GHZ; +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 6, 0) + chan2Offset = + woal_get_second_channel_offset(priv->channel); +#endif + +#ifdef WIFI_DIRECT_SUPPORT +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) + + /* Force enable 40MHZ on WFD interface */ + if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT) + chan2Offset = + woal_get_second_channel_offset(priv-> + channel); +#endif +#endif + +#ifdef WIFI_DIRECT_SUPPORT + if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT) + memcpy(sys_config->rates, rates_wfd, + sizeof(rates_wfd)); + else +#endif + memcpy(sys_config->rates, rates_a, + sizeof(rates_a)); + } + /* Disable GreenField by default */ + sys_config->ht_cap_info = 0x10c; + if (enable_11n) + sys_config->ht_cap_info |= 0x20; + if (chan2Offset) { + sys_config->bandcfg.chan2Offset = chan2Offset; + sys_config->ht_cap_info |= 0x1042; + sys_config->ampdu_param = 3; + } else { + sys_config->bandcfg.chan2Offset = 0; + } + if (priv->bss_type == MLAN_BSS_TYPE_UAP) { + ht_cap = woal_get_htcap_info(ie, ie_len); + if (ht_cap) + sys_config->ht_cap_info = + (ht_cap & + (wiphy->bands[IEEE80211_BAND_2GHZ]-> + ht_cap.cap & 0x13ff)) | 0x0c; + PRINTM(MCMND, + "11n=%d, ht_cap=0x%x, channel=%d, bandcfg:chanBand=0x%x chanWidth=0x%x chan2Offset=0x%x scanMode=0x%x\n", + enable_11n, sys_config->ht_cap_info, + priv->channel, sys_config->bandcfg.chanBand, + sys_config->bandcfg.chanWidth, + sys_config->bandcfg.chan2Offset, + sys_config->bandcfg.scanMode); + } + } +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) + if (!params->ssid || !params->ssid_len) { + ret = -EINVAL; + goto done; + } + memcpy(sys_config->ssid.ssid, params->ssid, + MIN(MLAN_MAX_SSID_LENGTH, params->ssid_len)); + sys_config->ssid.ssid_len = MIN(MLAN_MAX_SSID_LENGTH, params->ssid_len); + /** + * hidden_ssid=0: broadcast SSID in beacons. + * hidden_ssid=1: send empty SSID (length=0) in beacon. + * hidden_ssid=2: clear SSID (ACSII 0), but keep the original length + */ + if (!params->hidden_ssid) + sys_config->bcast_ssid_ctl = 1; + else if (params->hidden_ssid == 1) + sys_config->bcast_ssid_ctl = 0; + else if (params->hidden_ssid == 2) + sys_config->bcast_ssid_ctl = 2; + switch (params->auth_type) { + case NL80211_AUTHTYPE_SHARED_KEY: + sys_config->auth_mode = MLAN_AUTH_MODE_SHARED; + break; + case NL80211_AUTHTYPE_AUTOMATIC: + sys_config->auth_mode = MLAN_AUTH_MODE_AUTO; + break; + case NL80211_AUTHTYPE_OPEN_SYSTEM: + default: + sys_config->auth_mode = MLAN_AUTH_MODE_OPEN; + break; + } + + sys_config->protocol = PROTOCOL_NO_SECURITY; + if (params->crypto.n_akm_suites) + woal_find_wpa_ies(ie, ie_len, sys_config); + for (i = 0; i < params->crypto.n_akm_suites; i++) { + switch (params->crypto.akm_suites[i]) { + case WLAN_AKM_SUITE_8021X: + sys_config->key_mgmt |= KEY_MGMT_EAP; + if ((params->crypto. + wpa_versions & NL80211_WPA_VERSION_1) && + (params->crypto. + wpa_versions & NL80211_WPA_VERSION_2)) + sys_config->protocol = + PROTOCOL_WPA | PROTOCOL_WPA2; + else if (params->crypto. + wpa_versions & NL80211_WPA_VERSION_2) + sys_config->protocol = PROTOCOL_WPA2; + else if (params->crypto. + wpa_versions & NL80211_WPA_VERSION_1) + sys_config->protocol = PROTOCOL_WPA; + break; + case WLAN_AKM_SUITE_PSK: + sys_config->key_mgmt |= KEY_MGMT_PSK; + if ((params->crypto. + wpa_versions & NL80211_WPA_VERSION_1) && + (params->crypto. + wpa_versions & NL80211_WPA_VERSION_2)) + sys_config->protocol = + PROTOCOL_WPA | PROTOCOL_WPA2; + else if (params->crypto. + wpa_versions & NL80211_WPA_VERSION_2) + sys_config->protocol = PROTOCOL_WPA2; + else if (params->crypto. + wpa_versions & NL80211_WPA_VERSION_1) + sys_config->protocol = PROTOCOL_WPA; + break; + } + } + sys_config->wpa_cfg.pairwise_cipher_wpa = 0; + sys_config->wpa_cfg.pairwise_cipher_wpa2 = 0; + for (i = 0; i < params->crypto.n_ciphers_pairwise; i++) { + switch (params->crypto.ciphers_pairwise[i]) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + break; + case WLAN_CIPHER_SUITE_TKIP: + if (params->crypto.wpa_versions & NL80211_WPA_VERSION_1) + sys_config->wpa_cfg.pairwise_cipher_wpa |= + CIPHER_TKIP; + if (params->crypto.wpa_versions & NL80211_WPA_VERSION_2) + sys_config->wpa_cfg.pairwise_cipher_wpa2 |= + CIPHER_TKIP; + break; + case WLAN_CIPHER_SUITE_CCMP: + if (params->crypto.wpa_versions & NL80211_WPA_VERSION_1) + sys_config->wpa_cfg.pairwise_cipher_wpa |= + CIPHER_AES_CCMP; + if (params->crypto.wpa_versions & NL80211_WPA_VERSION_2) + sys_config->wpa_cfg.pairwise_cipher_wpa2 |= + CIPHER_AES_CCMP; + break; + case WLAN_CIPHER_SUITE_SMS4: + sys_config->protocol = PROTOCOL_WAPI; + break; + } + } + switch (params->crypto.cipher_group) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + if ((priv->cipher == WLAN_CIPHER_SUITE_WEP40) || + (priv->cipher == WLAN_CIPHER_SUITE_WEP104)) { + sys_config->protocol = PROTOCOL_STATIC_WEP; + sys_config->key_mgmt = KEY_MGMT_NONE; + sys_config->wpa_cfg.length = 0; + memcpy(&sys_config->wep_cfg.key0, &priv->uap_wep_key[0], + sizeof(wep_key)); + memcpy(&sys_config->wep_cfg.key1, &priv->uap_wep_key[1], + sizeof(wep_key)); + memcpy(&sys_config->wep_cfg.key2, &priv->uap_wep_key[2], + sizeof(wep_key)); + memcpy(&sys_config->wep_cfg.key3, &priv->uap_wep_key[3], + sizeof(wep_key)); + } + break; + case WLAN_CIPHER_SUITE_TKIP: + sys_config->wpa_cfg.group_cipher = CIPHER_TKIP; + break; + case WLAN_CIPHER_SUITE_CCMP: + sys_config->wpa_cfg.group_cipher = CIPHER_AES_CCMP; + break; + case WLAN_CIPHER_SUITE_SMS4: + sys_config->protocol = PROTOCOL_WAPI; + break; + } +#else + /* Since in Android ICS 4.0.1's wpa_supplicant, there is no way to set ssid + * when GO (AP) starts up, so get it from beacon head parameter + * TODO: right now use hard code + * 24 -- ieee80211 header lenth, 12 -- fixed element length for beacon + */ +#define BEACON_IE_OFFSET 36 + /* Find SSID in head + * SSID IE id: 0, right now use hard code + */ + ssid_ie = woal_parse_ie_tlv(params->head + BEACON_IE_OFFSET, + params->head_len - BEACON_IE_OFFSET, 0); + + if (!ssid_ie) { + PRINTM(MERROR, "No ssid IE found.\n"); + ret = -EFAULT; + goto done; + } + if (*(ssid_ie + 1) > 32) { + PRINTM(MERROR, "ssid len error: %d\n", *(ssid_ie + 1)); + ret = -EFAULT; + goto done; + } + memcpy(sys_config->ssid.ssid, ssid_ie + 2, *(ssid_ie + 1)); + sys_config->ssid.ssid_len = *(ssid_ie + 1); + head = (struct ieee80211_mgmt *)params->head; + + capab_info = le16_to_cpu(head->u.beacon.capab_info); + PRINTM(MIOCTL, "capab_info=0x%x\n", head->u.beacon.capab_info); + sys_config->auth_mode = MLAN_AUTH_MODE_OPEN; + /** For ICS, we don't support OPEN mode */ + if ((priv->cipher == WLAN_CIPHER_SUITE_WEP40) || + (priv->cipher == WLAN_CIPHER_SUITE_WEP104)) { + sys_config->protocol = PROTOCOL_STATIC_WEP; + sys_config->key_mgmt = KEY_MGMT_NONE; + sys_config->.wpa_cfg.length = 0; + memcpy(&sys_config->wep_cfg.key0, &priv->uap_wep_key[0], + sizeof(wep_key)); + memcpy(&sys_config->wep_cfg.key1, &priv->uap_wep_key[1], + sizeof(wep_key)); + memcpy(&sys_config->wep_cfg.key2, &priv->uap_wep_key[2], + sizeof(wep_key)); + memcpy(&sys_config->wep_cfg.key3, &priv->uap_wep_key[3], + sizeof(wep_key)); + } else { + /** Get cipher and key_mgmt from RSN/WPA IE */ + if (capab_info & WLAN_CAPABILITY_PRIVACY) { + wpa_ies = + woal_find_wpa_ies(params->tail, + params->tail_len, sys_config); + if (wpa_ies == MFALSE) { + /* hard code setting to wpa2-psk */ + sys_config->protocol = PROTOCOL_WPA2; + sys_config->key_mgmt = KEY_MGMT_PSK; + sys_config->wpa_cfg.pairwise_cipher_wpa2 = + CIPHER_AES_CCMP; + sys_config->wpa_cfg.group_cipher = + CIPHER_AES_CCMP; + } + } + } +#endif + + if (priv->bss_type == MLAN_BSS_TYPE_UAP) { + /*find and set wmm ie */ + woal_set_wmm_ies(ie, ie_len, sys_config); + } + /* If the security mode is configured as WEP or WPA-PSK, + * it will disable 11n automatically, and if configured as + * open(off) or wpa2-psk, it will automatically enable 11n */ + if ((sys_config->protocol == PROTOCOL_STATIC_WEP) || + (sys_config->protocol == PROTOCOL_WPA)) + enable_11n = MFALSE; + if (!enable_11n) { + woal_set_uap_ht_tx_cfg(priv, sys_config->bandcfg, MFALSE); + woal_uap_set_11n_status(priv, sys_config, MLAN_ACT_DISABLE); + } else { + woal_set_uap_ht_tx_cfg(priv, sys_config->bandcfg, MTRUE); + woal_uap_set_11n_status(priv, sys_config, MLAN_ACT_ENABLE); + woal_set_get_tx_bf_cap(priv, MLAN_ACT_GET, + &sys_config->tx_bf_cap); + } + if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv, + MLAN_ACT_SET, + MOAL_IOCTL_WAIT, + sys_config)) { + ret = -EFAULT; + goto done; + } +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) + woal_enable_dfs_support(priv, &priv->chan); +#endif +done: + kfree(sys_config); + LEAVE(); + return ret; +} + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) +/** + * @brief Request the driver to add a monitor interface + * + * @param wiphy A pointer to wiphy structure + * @param name Virtual interface name + * @param name_assign_type Interface name assignment type + * @param flags Flags for the virtual interface + * @param params A pointer to vif_params structure + * @param new_dev Netdevice to be passed out + * + * @return 0 -- success, otherwise fail + */ +static int +woal_cfg80211_add_mon_if(struct wiphy *wiphy, + const char *name, + unsigned char name_assign_type, + u32 *flags, struct vif_params *params, + struct net_device **new_dev) +#else +/** + * @brief Request the driver to add a monitor interface + * + * @param wiphy A pointer to wiphy structure + * @param name Virtual interface name + * @param flags Flags for the virtual interface + * @param params A pointer to vif_params structure + * @param new_dev Netdevice to be passed out + * + * @return 0 -- success, otherwise fail + */ +static int +woal_cfg80211_add_mon_if(struct wiphy *wiphy, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 7, 0) + const +#endif + char *name, u32 *flags, struct vif_params *params, + struct net_device **new_dev) +#endif +{ + int ret = 0; + moal_handle *handle = (moal_handle *)woal_get_wiphy_priv(wiphy); + moal_private *priv = + (moal_private *)woal_get_priv(handle, MLAN_BSS_ROLE_STA); + monitor_iface *mon_if = NULL; + struct net_device *ndev = NULL; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + chan_band_info chan_info; +#endif + unsigned char name_assign_type_tmp = 0; + + ENTER(); + + ASSERT_RTNL(); + + if (handle->mon_if) { + PRINTM(MERROR, "%s: monitor interface exist: %s basedev %s\n", + __func__, handle->mon_if->mon_ndev->name, + handle->mon_if->base_ndev->name); + ret = -EFAULT; + goto fail; + } + + if (!priv) { + PRINTM(MERROR, "add_mon_if: priv is NULL\n"); + ret = -EFAULT; + goto fail; + } +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) + name_assign_type_tmp = name_assign_type; +#endif + mon_if = woal_prepare_mon_if(priv, name, name_assign_type_tmp, + CHANNEL_SPEC_SNIFFER_MODE); + if (!mon_if) { + PRINTM(MFATAL, "Prepare mon_if fail.\n"); + goto fail; + } + + ndev = mon_if->mon_ndev; + dev_net_set(ndev, wiphy_net(wiphy)); + memcpy(ndev->perm_addr, wiphy->perm_addr, ETH_ALEN); + memcpy(ndev->dev_addr, ndev->perm_addr, ETH_ALEN); + SET_NETDEV_DEV(ndev, wiphy_dev(wiphy)); + ndev->ieee80211_ptr = &mon_if->wdev; + mon_if->wdev.iftype = NL80211_IFTYPE_MONITOR; + mon_if->wdev.wiphy = wiphy; + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + /* Set default band channel config */ + mon_if->band_chan_cfg.band = BAND_B | BAND_G; + mon_if->band_chan_cfg.band |= BAND_GN; + mon_if->band_chan_cfg.channel = 1; + mon_if->band_chan_cfg.chan_bandwidth = CHANNEL_BW_20MHZ; + memset(&chan_info, 0x00, sizeof(chan_info)); + chan_info.channel = 1; + chan_info.is_11n_enabled = MTRUE; + if (MLAN_STATUS_FAILURE == + woal_chandef_create(priv, &mon_if->chandef, &chan_info)) { + ret = -EFAULT; + goto fail; + } + if (MLAN_STATUS_SUCCESS != woal_set_net_monitor(priv, MOAL_IOCTL_WAIT, + CHANNEL_SPEC_SNIFFER_MODE, + 0x7, + &mon_if-> + band_chan_cfg)) { + PRINTM(MERROR, "%s: woal_set_net_monitor fail\n", __func__); + ret = -EFAULT; + goto fail; + } +#endif + + ret = register_netdevice(ndev); + if (ret) { + PRINTM(MFATAL, "register net_device failed, ret=%d\n", ret); + free_netdev(ndev); + goto fail; + } + + handle->mon_if = mon_if; + + if (new_dev) + *new_dev = ndev; + +fail: + LEAVE(); + return ret; +} + +#if defined(WIFI_DIRECT_SUPPORT) +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION +/** + * @brief Callback function for virtual interface + * setup + * + * @param dev A pointer to structure net_device + * + * @return N/A + */ +static void +woal_virt_if_setup(struct net_device *dev) +{ + ENTER(); + ether_setup(dev); +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 11, 9) + dev->needs_free_netdev = true; +#else + dev->destructor = free_netdev; +#endif + LEAVE(); +} + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) +/** + * @brief This function adds a new interface. It will + * allocate, initialize and register the device. + * + * @param handle A pointer to moal_handle structure + * @param bss_index BSS index number + * @param name_assign_type Interface name assignment type + * @param bss_type BSS type + * + * @return A pointer to the new priv structure + */ +moal_private * +woal_alloc_virt_interface(moal_handle *handle, t_u8 bss_index, + unsigned char name_assign_type, + t_u8 bss_type, const char *name) +#else +/** + * @brief This function adds a new interface. It will + * allocate, initialize and register the device. + * + * @param handle A pointer to moal_handle structure + * @param bss_index BSS index number + * @param bss_type BSS type + * + * @return A pointer to the new priv structure + */ +moal_private * +woal_alloc_virt_interface(moal_handle *handle, t_u8 bss_index, t_u8 bss_type, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 7, 0) + const +#endif + char *name) +#endif +{ + struct net_device *dev = NULL; + moal_private *priv = NULL; + ENTER(); + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) +#ifndef MAX_WMM_QUEUE +#define MAX_WMM_QUEUE 4 +#endif + /* Allocate an Ethernet device */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) + dev = alloc_netdev_mq(sizeof(moal_private), name, name_assign_type, + woal_virt_if_setup, MAX_WMM_QUEUE); +#else + dev = alloc_netdev_mq(sizeof(moal_private), name, NET_NAME_UNKNOWN, + woal_virt_if_setup, MAX_WMM_QUEUE); +#endif +#else + dev = alloc_netdev_mq(sizeof(moal_private), name, woal_virt_if_setup, + MAX_WMM_QUEUE); +#endif +#else + dev = alloc_netdev(sizeof(moal_private), name, woal_virt_if_setup); +#endif + if (!dev) { + PRINTM(MFATAL, "Init virtual ethernet device failed\n"); + goto error; + } + /* Allocate device name */ + if ((dev_alloc_name(dev, name) < 0)) { + PRINTM(MERROR, "Could not allocate device name\n"); + goto error; + } + + priv = (moal_private *)netdev_priv(dev); + /* Save the priv to handle */ + handle->priv[bss_index] = priv; + + /* Use the same handle structure */ + priv->phandle = handle; + priv->netdev = dev; + priv->bss_index = bss_index; + priv->bss_type = bss_type; + priv->bss_role = MLAN_BSS_ROLE_STA; + + INIT_LIST_HEAD(&priv->tcp_sess_queue); + spin_lock_init(&priv->tcp_sess_lock); + + INIT_LIST_HEAD(&priv->tx_stat_queue); + spin_lock_init(&priv->tx_stat_lock); + spin_lock_init(&priv->connect_lock); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24) + SET_MODULE_OWNER(dev); +#endif + + PRINTM(MCMND, "Alloc virtual interface%s\n", dev->name); + + LEAVE(); + return priv; +error: + if (dev) + free_netdev(dev); + LEAVE(); + return NULL; +} + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) +/** + * @brief Request the driver to add a virtual interface + * + * @param wiphy A pointer to wiphy structure + * @param name Virtual interface name + * @param name_assign_type Interface name assignment type + * @param type Virtual interface type + * @param flags Flags for the virtual interface + * @param params A pointer to vif_params structure + * @param new_dev new net_device to return + * + * @return 0 -- success, otherwise fail + */ +int +woal_cfg80211_add_virt_if(struct wiphy *wiphy, + const char *name, + unsigned char name_assign_type, + enum nl80211_iftype type, u32 *flags, + struct vif_params *params, + struct net_device **new_dev) +#else +/** + * @brief Request the driver to add a virtual interface + * + * @param wiphy A pointer to wiphy structure + * @param name Virtual interface name + * @param type Virtual interface type + * @param flags Flags for the virtual interface + * @param params A pointer to vif_params structure + * @param new_dev new net_device to return + * + * @return 0 -- success, otherwise fail + */ +int +woal_cfg80211_add_virt_if(struct wiphy *wiphy, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 7, 0) + const +#endif + char *name, enum nl80211_iftype type, u32 *flags, + struct vif_params *params, + struct net_device **new_dev) +#endif +{ + int ret = 0; + struct net_device *ndev = NULL; + moal_private *priv = NULL, *new_priv = NULL; + moal_handle *handle = (moal_handle *)woal_get_wiphy_priv(wiphy); + struct wireless_dev *wdev = NULL; + moal_private *vir_priv; + int i = 0; + + ENTER(); + ASSERT_RTNL(); + priv = (moal_private *)woal_get_priv_bss_type(handle, + MLAN_BSS_TYPE_WIFIDIRECT); + if (!priv || !priv->phandle) { + PRINTM(MERROR, "priv or handle is NULL\n"); + LEAVE(); + return -EFAULT; + } + if (priv->phandle->drv_mode.intf_num == priv->phandle->priv_num) { + PRINTM(MERROR, "max virtual interface limit reached\n"); + for (i = 0; i < priv->phandle->priv_num; i++) { + vir_priv = priv->phandle->priv[i]; + if (vir_priv->bss_virtual) { + woal_cfg80211_del_virt_if(wiphy, + vir_priv->netdev); + break; + } + } + if (priv->phandle->drv_mode.intf_num == priv->phandle->priv_num) { + LEAVE(); + return -ENOMEM; + } + } + PRINTM(MMSG, "Add virtual interface %s\n", name); + if ((type != NL80211_IFTYPE_P2P_CLIENT) && + (type != NL80211_IFTYPE_P2P_GO)) { + PRINTM(MERROR, "Invalid iftype: %d\n", type); + LEAVE(); + return -EINVAL; + } + + handle = priv->phandle; + /* Cancel previous scan req */ + woal_cancel_scan(priv, MOAL_IOCTL_WAIT); +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) + new_priv = + woal_alloc_virt_interface(handle, handle->priv_num, + name_assign_type, + MLAN_BSS_TYPE_WIFIDIRECT, name); +#else + new_priv = + woal_alloc_virt_interface(handle, handle->priv_num, + MLAN_BSS_TYPE_WIFIDIRECT, name); +#endif + if (!new_priv) { + PRINTM(MERROR, "Add virtual interface fail."); + LEAVE(); + return -EFAULT; + } + handle->priv_num++; + + wdev = (struct wireless_dev *)&new_priv->w_dev; + memset(wdev, 0, sizeof(struct wireless_dev)); + ndev = new_priv->netdev; + SET_NETDEV_DEV(ndev, wiphy_dev(wiphy)); + ndev->ieee80211_ptr = wdev; + wdev->iftype = type; + wdev->wiphy = wiphy; + new_priv->wdev = wdev; + new_priv->bss_virtual = MTRUE; + new_priv->pa_netdev = priv->netdev; + + woal_init_sta_dev(ndev, new_priv); + + /* Initialize priv structure */ + woal_init_priv(new_priv, MOAL_IOCTL_WAIT); + /** Init to GO/CLIENT mode */ + if (type == NL80211_IFTYPE_P2P_CLIENT) + woal_cfg80211_init_p2p_client(new_priv); + else if (type == NL80211_IFTYPE_P2P_GO) + woal_cfg80211_init_p2p_go(new_priv); + ret = register_netdevice(ndev); + if (ret) { + handle->priv[new_priv->bss_index] = NULL; + handle->priv_num--; + if (ndev->reg_state == NETREG_REGISTERED) { + unregister_netdevice(ndev); + free_netdev(ndev); + ndev = NULL; + } + PRINTM(MFATAL, "register net_device failed, ret=%d\n", ret); + goto done; + } + netif_carrier_off(ndev); + woal_stop_queue(ndev); + if (new_dev) + *new_dev = ndev; +#ifdef CONFIG_PROC_FS + woal_create_proc_entry(new_priv); +#ifdef PROC_DEBUG + woal_debug_entry(new_priv); +#endif /* PROC_DEBUG */ +#endif /* CONFIG_PROC_FS */ +done: + LEAVE(); + return ret; +} + +/** + * @brief Notify mlan BSS will be removed. + * + * @param priv A pointer to moal_private structure + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail + */ +mlan_status +woal_bss_remove(moal_private *priv) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_bss *bss = NULL; + mlan_status status; + + ENTER(); + + /* Allocate an IOCTL request buffer */ + req = (mlan_ioctl_req *)woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + status = MLAN_STATUS_FAILURE; + goto done; + } + + /* Fill request buffer */ + bss = (mlan_ds_bss *)req->pbuf; + bss->sub_command = MLAN_OID_BSS_REMOVE; + req->req_id = MLAN_IOCTL_BSS; + req->action = MLAN_ACT_SET; + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return status; +} + +/** + * @brief This function removes an virtual interface. + * + * @param wiphy A pointer to the wiphy structure + * @param dev A pointer to the net_device structure + * + * @return 0 -- success, otherwise fail + */ +int +woal_cfg80211_del_virt_if(struct wiphy *wiphy, struct net_device *dev) +{ + int ret = 0; + int i = 0; + moal_private *priv = NULL; + moal_private *vir_priv = NULL; + moal_private *remain_priv = NULL; + moal_handle *handle = (moal_handle *)woal_get_wiphy_priv(wiphy); + + for (i = 0; i < handle->priv_num; i++) { + vir_priv = handle->priv[i]; + if (vir_priv) { + if (vir_priv->netdev == dev) { + PRINTM(MMSG, + "Del virtual interface %s, index=%d\n", + dev->name, i); + break; + } + } + } + + priv = (moal_private *)woal_get_priv_bss_type(handle, + MLAN_BSS_TYPE_WIFIDIRECT); + if (!priv) + return ret; + if (vir_priv && vir_priv->netdev == dev) { + woal_stop_queue(dev); + netif_carrier_off(dev); + netif_device_detach(dev); + if (handle->is_remain_timer_set) { + woal_cancel_timer(&handle->remain_timer); + woal_remain_timer_func(handle); + } + + /*** cancel pending scan */ + woal_cancel_scan(vir_priv, MOAL_IOCTL_WAIT); + + woal_flush_tx_stat_queue(vir_priv); + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + /* cancel previous remain on channel to avoid firmware hang */ + if (priv->phandle->remain_on_channel) { + t_u8 channel_status; + remain_priv = + priv->phandle->priv[priv->phandle-> + remain_bss_index]; + if (remain_priv) { + if (woal_cfg80211_remain_on_channel_cfg + (remain_priv, MOAL_IOCTL_WAIT, MTRUE, + &channel_status, NULL, 0, 0)) + PRINTM(MERROR, + "del_virt_if: Fail to cancel remain on channel\n"); + + if (priv->phandle->cookie) { + cfg80211_remain_on_channel_expired( +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 6, 0) + remain_priv-> + netdev, +#else + remain_priv-> + wdev, +#endif + priv-> + phandle-> + cookie, + &priv-> + phandle-> + chan, +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 8, 0) + priv-> + phandle-> + channel_type, +#endif + GFP_ATOMIC); + priv->phandle->cookie = 0; + } + priv->phandle->remain_on_channel = MFALSE; + } + } +#endif + woal_clear_all_mgmt_ies(vir_priv, MOAL_IOCTL_WAIT); + woal_cfg80211_deinit_p2p(vir_priv); + woal_bss_remove(vir_priv); +#ifdef CONFIG_PROC_FS +#ifdef PROC_DEBUG + /* Remove proc debug */ + woal_debug_remove(vir_priv); +#endif /* PROC_DEBUG */ + woal_proc_remove(vir_priv); +#endif /* CONFIG_PROC_FS */ + /* Last reference is our one */ +#if CFG80211_VERSION_CODE < KERNEL_VERSION(2, 6, 37) + PRINTM(MINFO, "refcnt = %d\n", atomic_read(&dev->refcnt)); +#else + PRINTM(MINFO, "refcnt = %d\n", netdev_refcnt_read(dev)); +#endif + PRINTM(MINFO, "netdev_finish_unregister: %s\n", dev->name); + /* Clear the priv in handle */ + vir_priv->phandle->priv[vir_priv->bss_index] = NULL; + priv->phandle->priv_num--; + if (dev->reg_state == NETREG_REGISTERED) + unregister_netdevice(dev); + } + return ret; +} +#endif +#endif + +/** + * @brief This function removes an virtual interface. + * + * @param handle A pointer to the moal_handle structure + * + * @return N/A + */ +void +woal_remove_virtual_interface(moal_handle *handle) +{ +#ifdef WIFI_DIRECT_SUPPORT + moal_private *priv = NULL; + int vir_intf = 0; + int i = 0; +#endif + ENTER(); + rtnl_lock(); +#ifdef WIFI_DIRECT_SUPPORT + for (i = 0; i < handle->priv_num; i++) { + priv = handle->priv[i]; + if (priv) { + if (priv->bss_virtual) { + PRINTM(MCMND, "Remove virtual interface %s\n", + priv->netdev->name); +#ifdef CONFIG_PROC_FS +#ifdef PROC_DEBUG + /* Remove proc debug */ + woal_debug_remove(priv); +#endif /* PROC_DEBUG */ + woal_proc_remove(priv); +#endif /* CONFIG_PROC_FS */ + netif_device_detach(priv->netdev); + if (priv->netdev->reg_state == + NETREG_REGISTERED) + unregister_netdevice(priv->netdev); + handle->priv[i] = NULL; + vir_intf++; + } + } + } +#endif + if (handle->mon_if) { + netif_device_detach(handle->mon_if->mon_ndev); + if (handle->mon_if->mon_ndev->reg_state == NETREG_REGISTERED) + unregister_netdevice(handle->mon_if->mon_ndev); + handle->mon_if = NULL; + } + rtnl_unlock(); +#ifdef WIFI_DIRECT_SUPPORT + handle->priv_num -= vir_intf; +#endif + LEAVE(); +} + +/** + * @brief This function check if uap interface is ready + * + * @param wiphy A pointer to wiphy structure + * @param name Virtual interface name + * + * @return MTRUE/MFALSE; + */ +static t_u8 +woal_uap_interface_ready(struct wiphy *wiphy, char *name, + struct net_device **new_dev) +{ + moal_handle *handle = (moal_handle *)woal_get_wiphy_priv(wiphy); + moal_private *priv = NULL; + int i; + + for (i = 0; i < handle->priv_num; i++) { + priv = handle->priv[i]; + if (priv && (priv->bss_type == MLAN_BSS_TYPE_UAP) && + !strcmp(priv->netdev->name, name)) { + priv->wdev->iftype = NL80211_IFTYPE_AP; + *new_dev = priv->netdev; + break; + } + } + if (priv && *new_dev) + return MTRUE; + else + return MFALSE; +} + +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 6, 0) +#if CFG80211_VERSION_CODE > KERNEL_VERSION(2, 6, 37) +/** + * @brief Request the driver to add a virtual interface + * + * @param wiphy A pointer to wiphy structure + * @param name Virtual interface name + * @param type Virtual interface type + * @param flags Flags for the virtual interface + * @param params A pointer to vif_params structure + * + * @return A pointer to net_device -- success, otherwise null + */ +struct net_device * +woal_cfg80211_add_virtual_intf(struct wiphy *wiphy, + char *name, enum nl80211_iftype type, u32 *flags, + struct vif_params *params) +#else +/** + * @brief Request the driver to add a virtual interface + * + * @param wiphy A pointer to wiphy structure + * @param name Virtual interface name + * @param type Virtual interface type + * @param flags Flags for the virtual interface + * @param params A pointer to vif_params structure + * + * @return 0 -- success, otherwise fail + */ +int +woal_cfg80211_add_virtual_intf(struct wiphy *wiphy, + char *name, enum nl80211_iftype type, u32 *flags, + struct vif_params *params) +#endif +#else +#if CFG80211_VERSION_CODE < KERNEL_VERSION(4, 1, 0) +/** + * @brief Request the driver to add a virtual interface + * + * @param wiphy A pointer to wiphy structure + * @param name Virtual interface name + * @param type Virtual interface type + * @param flags Flags for the virtual interface + * @param params A pointer to vif_params structure + * + * @return A pointer to wireless_dev -- success, otherwise null + */ +struct wireless_dev * +woal_cfg80211_add_virtual_intf(struct wiphy *wiphy, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 7, 0) + const +#endif + char *name, enum nl80211_iftype type, u32 *flags, + struct vif_params *params) +#else +/** + * @brief Request the driver to add a virtual interface + * + * @param wiphy A pointer to wiphy structure + * @param name Virtual interface name + * @param name_assign_type Interface name assignment type + * @param type Virtual interface type + * @param flags Flags for the virtual interface + * @param params A pointer to vif_params structure + * + * @return A pointer to wireless_dev -- success, otherwise null + */ +struct wireless_dev * +woal_cfg80211_add_virtual_intf(struct wiphy *wiphy, + const char *name, + unsigned char name_assign_type, + enum nl80211_iftype type, +#if CFG80211_VERSION_CODE < KERNEL_VERSION(4, 12, 0) + u32 *flags, +#endif + struct vif_params *params) +#endif +#endif +{ + struct net_device *ndev = NULL; + int ret = 0; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) + u32 *flags = ¶ms->flags; +#endif + + ENTER(); + PRINTM(MIOCTL, "add virtual intf: %d name: %s\n", type, name); + switch (type) { + case NL80211_IFTYPE_MONITOR: +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) + ret = woal_cfg80211_add_mon_if(wiphy, name, name_assign_type, + flags, params, &ndev); +#else + ret = woal_cfg80211_add_mon_if(wiphy, name, flags, params, + &ndev); +#endif + break; +#if defined(WIFI_DIRECT_SUPPORT) +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION + case NL80211_IFTYPE_P2P_CLIENT: + case NL80211_IFTYPE_P2P_GO: +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) + ret = woal_cfg80211_add_virt_if(wiphy, name, name_assign_type, + type, flags, params, &ndev); +#else + ret = woal_cfg80211_add_virt_if(wiphy, name, type, flags, + params, &ndev); +#endif + break; +#endif +#endif + case NL80211_IFTYPE_AP: + if (!woal_uap_interface_ready(wiphy, (char *)name, &ndev)) { + PRINTM(MMSG, + "Not support dynamically create %s UAP interface\n", + name); + ret = -EFAULT; + } + break; + default: + PRINTM(MWARN, "Not supported if type: %d\n", type); + ret = -EFAULT; + break; + } + LEAVE(); +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 6, 0) +#if CFG80211_VERSION_CODE > KERNEL_VERSION(2, 6, 37) + if (ret) + return ERR_PTR(ret); + else + return ndev; +#else + return ret; +#endif +#else + if (ret) + return ERR_PTR(ret); + else + return ndev->ieee80211_ptr; +#endif +} + +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 6, 0) +/** + * @brief Request the driver to del a virtual interface + * + * @param wiphy A pointer to wiphy structure + * @param dev The pointer to net_device + * + * @return 0 -- success, otherwise fail + */ +int +woal_cfg80211_del_virtual_intf(struct wiphy *wiphy, struct net_device *dev) +#else +/** + * @brief Request the driver to del a virtual interface + * + * @param wiphy A pointer to wiphy structure + * @param wdev The pointer to wireless_dev + * + * @return 0 -- success, otherwise fail + */ +int +woal_cfg80211_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev) +#endif +{ + int ret = 0; + moal_handle *handle = (moal_handle *)woal_get_wiphy_priv(wiphy); + int i; + moal_private *vir_priv = NULL; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) + struct net_device *dev = wdev->netdev; +#endif + ENTER(); + + PRINTM(MIOCTL, "del virtual intf %s\n", dev->name); + ASSERT_RTNL(); + if (dev->ieee80211_ptr->iftype == NL80211_IFTYPE_MONITOR) { + if ((handle->mon_if) && (handle->mon_if->mon_ndev == dev)) { +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + if (MLAN_STATUS_SUCCESS != + woal_set_net_monitor(handle->mon_if->priv, + MOAL_IOCTL_WAIT, MFALSE, 0, + NULL)) { + PRINTM(MERROR, + "%s: woal_set_net_monitor fail\n", + __func__); + ret = -EFAULT; + } +#endif + handle->mon_if = NULL; + } + unregister_netdevice(dev); + LEAVE(); + return ret; + } + if (dev->ieee80211_ptr->iftype == NL80211_IFTYPE_AP) { + for (i = 0; i < handle->priv_num; i++) { + vir_priv = handle->priv[i]; + if (vir_priv) { + if (vir_priv->netdev == dev) { + PRINTM(MMSG, + "Del virtual interface %s, index=%d\n", + dev->name, i); + break; + } + } + } + if (vir_priv && vir_priv->bss_type == MLAN_BSS_TYPE_UAP) { + woal_cfg80211_del_beacon(wiphy, dev); +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) + vir_priv->wdev->beacon_interval = 0; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 15, 0) + memset(&vir_priv->wdev->chandef, 0, + sizeof(vir_priv->wdev->chandef)); +#endif +#endif + vir_priv->wdev->ssid_len = 0; + PRINTM(MMSG, "Skip del UAP virtual interface %s", + dev->name); + } + LEAVE(); + return ret; + } +#if defined(WIFI_DIRECT_SUPPORT) +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION + ret = woal_cfg80211_del_virt_if(wiphy, dev); +#endif +#endif + LEAVE(); + return ret; +} + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) +/** + * @brief initialize AP or GO parameters + + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param params A pointer to cfg80211_ap_settings structure + * @return 0 -- success, otherwise fail + */ +int +woal_cfg80211_add_beacon(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_ap_settings *params) +#else +/** + * @brief initialize AP or GO parameters + + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param params A pointer to beacon_parameters structure + * @return 0 -- success, otherwise fail + */ +int +woal_cfg80211_add_beacon(struct wiphy *wiphy, + struct net_device *dev, + struct beacon_parameters *params) +#endif +{ + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + int ret = 0; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + t_u8 wait_option = MOAL_IOCTL_WAIT; +#endif + ENTER(); + + PRINTM(MMSG, "wlan: Starting AP\n"); +#ifdef STA_CFG80211 + /*** cancel pending scan */ + woal_cancel_scan(priv, MOAL_IOCTL_WAIT); +#endif + + if (!params) { + LEAVE(); + return -EFAULT; + } +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + priv->channel = + ieee80211_frequency_to_channel(params->chandef.chan-> + center_freq); +#else + priv->channel = + ieee80211_frequency_to_channel(params->channel->center_freq); +#endif +#endif + /* bss config */ + if (MLAN_STATUS_SUCCESS != woal_cfg80211_beacon_config(priv, params)) { + ret = -EFAULT; + goto done; + } + + /* set mgmt frame ies */ + ret = woal_cfg80211_mgmt_frame_ie(priv, +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 2, 0) + params->tail, params->tail_len, NULL, + 0, NULL, 0, NULL, 0, MGMT_MASK_BEACON +#else +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) + params->beacon.tail, + params->beacon.tail_len, + params->beacon.proberesp_ies, + params->beacon.proberesp_ies_len, + params->beacon.assocresp_ies, + params->beacon.assocresp_ies_len, +#else + params->tail, params->tail_len, + params->proberesp_ies, + params->proberesp_ies_len, + params->assocresp_ies, + params->assocresp_ies_len, +#endif + NULL, 0, + MGMT_MASK_BEACON | + MGMT_MASK_PROBE_RESP | + MGMT_MASK_ASSOC_RESP +#endif + , MOAL_IOCTL_WAIT); + if (ret) + goto done; + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) + if (params->beacon.beacon_ies && params->beacon.beacon_ies_len) { + ret = woal_cfg80211_mgmt_frame_ie(priv, + params->beacon.beacon_ies, + params->beacon.beacon_ies_len, + NULL, 0, NULL, 0, NULL, 0, + MGMT_MASK_BEACON_WPS_P2P, + MOAL_IOCTL_WAIT); + if (ret) { + PRINTM(MERROR, "Failed to set beacon wps/p2p ie\n"); + goto done; + } + } +#else + if (params->beacon_ies && params->beacon_ies_len) { + ret = woal_cfg80211_mgmt_frame_ie(priv, + params->beacon_ies, + params->beacon_ies_len, NULL, + 0, NULL, 0, NULL, 0, + MGMT_MASK_BEACON_WPS_P2P, + MOAL_IOCTL_WAIT); + if (ret) { + PRINTM(MERROR, "Failed to set beacon wps/p2p ie\n"); + goto done; + } + } +#endif +#endif + priv->uap_host_based = MTRUE; + + /* if the bss is stopped, then start it */ + if (priv->bss_started == MFALSE) { +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + if (dfs_offload) + wait_option = MOAL_NO_WAIT; +#endif + if (MLAN_STATUS_SUCCESS != + woal_uap_bss_ctrl(priv, MOAL_IOCTL_WAIT, UAP_BSS_START)) { + priv->uap_host_based = MFALSE; + ret = -EFAULT; + goto done; + } + } + PRINTM(MMSG, "wlan: AP started\n"); +done: + LEAVE(); + return ret; +} + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) +/** + * @brief set AP or GO parameter + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param params A pointer to cfg80211_beacon_data structure + * @return 0 -- success, otherwise fail + */ +int +woal_cfg80211_set_beacon(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_beacon_data *params) +#else +/** + * @brief set AP or GO parameter + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param params A pointer to beacon_parameters structure + * @return 0 -- success, otherwise fail + */ +int +woal_cfg80211_set_beacon(struct wiphy *wiphy, + struct net_device *dev, + struct beacon_parameters *params) +#endif +{ + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + int ret = 0; + + ENTER(); + + PRINTM(MIOCTL, "set beacon\n"); + if (params != NULL) { +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 2, 0) + if (params->tail && params->tail_len) { + ret = woal_cfg80211_mgmt_frame_ie(priv, + params->tail, + params->tail_len, + NULL, 0, NULL, 0, + NULL, 0, + MGMT_MASK_BEACON, + MOAL_IOCTL_WAIT); + if (ret) + goto done; + } +#else + t_u16 mask = 0; + if (params->tail && params->tail_len) + mask |= MGMT_MASK_BEACON; + if (params->proberesp_ies && params->proberesp_ies_len) + mask |= MGMT_MASK_PROBE_RESP; + if (params->assocresp_ies && params->assocresp_ies_len) + mask |= MGMT_MASK_ASSOC_RESP; + PRINTM(MIOCTL, "Set beacon: mask=0x%x\n", mask); + if (mask) { + ret = woal_cfg80211_mgmt_frame_ie(priv, params->tail, + params->tail_len, + params->proberesp_ies, + params-> + proberesp_ies_len, + params->assocresp_ies, + params-> + assocresp_ies_len, + NULL, 0, mask, + MOAL_IOCTL_WAIT); + if (ret) + goto done; + } + if (params->beacon_ies && params->beacon_ies_len) { + ret = woal_cfg80211_mgmt_frame_ie(priv, + params->beacon_ies, + params-> + beacon_ies_len, NULL, + 0, NULL, 0, NULL, 0, + MGMT_MASK_BEACON_WPS_P2P, + MOAL_IOCTL_WAIT); + if (ret) { + PRINTM(MERROR, + "Failed to set beacon wps/p2p ie\n"); + goto done; + } + } +#endif + } + +done: + LEAVE(); + return ret; +} + +/** + * @brief reset AP or GO parameters + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * + * @return 0 -- success, otherwise fail + */ +int +woal_cfg80211_del_beacon(struct wiphy *wiphy, struct net_device *dev) +{ + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + int ret = 0; +#ifdef STA_SUPPORT + moal_private *pmpriv = NULL; +#endif + + ENTER(); + + priv->phandle->driver_state = woal_check_driver_status(priv->phandle); + if (priv->phandle->driver_state) { + PRINTM(MERROR, + "Block woal_cfg80211_del_beacon in abnormal driver state\n"); + LEAVE(); + return -EFAULT; + } + priv->uap_host_based = MFALSE; + PRINTM(MMSG, "wlan: Stoping AP\n"); +#ifdef STA_SUPPORT + woal_cancel_scan(priv, MOAL_IOCTL_WAIT); +#endif + memset(priv->dscp_map, 0xFF, sizeof(priv->dscp_map)); + woal_deauth_all_station(priv); +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + if (dfs_offload) + woal_cancel_cac_block(priv); +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) + memset(&priv->chan, 0, sizeof(struct cfg80211_chan_def)); + if (priv->phandle->is_cac_timer_set && + priv->bss_index == priv->phandle->cac_bss_index) { + woal_cancel_timer(&priv->phandle->cac_timer); + priv->phandle->is_cac_timer_set = MFALSE; + /* Make sure Chan Report is cancelled */ + woal_11h_cancel_chan_report_ioctl(priv, MOAL_IOCTL_WAIT); +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + cfg80211_cac_event(priv->netdev, &priv->phandle->dfs_channel, + NL80211_RADAR_CAC_ABORTED, GFP_KERNEL); +#else + cfg80211_cac_event(priv->netdev, NL80211_RADAR_CAC_ABORTED, + GFP_KERNEL); +#endif + memset(&priv->phandle->dfs_channel, 0, + sizeof(struct cfg80211_chan_def)); + priv->phandle->cac_bss_index = 0xff; + } + if (priv->csa_workqueue) + flush_workqueue(priv->csa_workqueue); +#endif + /* if the bss is still running, then stop it */ + if (priv->bss_started == MTRUE) { + if (MLAN_STATUS_SUCCESS != + woal_uap_bss_ctrl(priv, MOAL_IOCTL_WAIT, UAP_BSS_STOP)) { + ret = -EFAULT; + goto done; + } + if (MLAN_STATUS_SUCCESS != + woal_uap_bss_ctrl(priv, MOAL_IOCTL_WAIT, UAP_BSS_RESET)) { + ret = -EFAULT; + goto done; + } + /* Set WLAN MAC addresses */ + if (MLAN_STATUS_SUCCESS != woal_request_set_mac_address(priv)) { + PRINTM(MERROR, "Set MAC address failed\n"); + ret = -EFAULT; + goto done; + } + } + woal_clear_all_mgmt_ies(priv, MOAL_IOCTL_WAIT); +#ifdef STA_SUPPORT + if (!woal_is_any_interface_active(priv->phandle)) { + pmpriv = woal_get_priv((moal_handle *)priv->phandle, + MLAN_BSS_ROLE_STA); + if (pmpriv) + woal_set_scan_time(pmpriv, ACTIVE_SCAN_CHAN_TIME, + PASSIVE_SCAN_CHAN_TIME, + SPECIFIC_SCAN_CHAN_TIME); + } +#endif + + priv->cipher = 0; + memset(priv->uap_wep_key, 0, sizeof(priv->uap_wep_key)); + priv->channel = 0; + PRINTM(MMSG, "wlan: AP stopped\n"); +done: + LEAVE(); + return ret; +} + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) +/** + * @brief change BSS + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param params A pointer to bss_parameters structure + * + * @return 0 -- success, otherwise fail + */ +int +woal_cfg80211_change_bss(struct wiphy *wiphy, struct net_device *dev, + struct bss_parameters *params) +{ + int ret = 0; + t_u8 change = MFALSE; + mlan_uap_bss_param *sys_config = NULL; + u8 bss_started = MFALSE; + t_u8 pkt_forward_ctl; + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + + ENTER(); + PRINTM(MIOCTL, "isolate=%d\n", params->ap_isolate); + + sys_config = kzalloc(sizeof(mlan_uap_bss_param), GFP_ATOMIC); + if (!sys_config) { + PRINTM(MERROR, "Fail to alloc memory for mlan_uap_bss_param\n"); + LEAVE(); + return -EFAULT; + } + + if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv, + MLAN_ACT_GET, + MOAL_IOCTL_WAIT, + sys_config)) { + PRINTM(MERROR, "Error getting AP confiruration\n"); + ret = -EFAULT; + goto done; + } + + pkt_forward_ctl = sys_config->pkt_forward_ctl; + if (params->ap_isolate) { + /** disable packet forwarding */ + sys_config->pkt_forward_ctl |= PKT_FWD_INTRA_BCAST; + sys_config->pkt_forward_ctl |= PKT_FWD_INTRA_UCAST; + } else { + sys_config->pkt_forward_ctl &= ~PKT_FWD_INTRA_BCAST; + sys_config->pkt_forward_ctl &= ~PKT_FWD_INTRA_UCAST; + } + if (pkt_forward_ctl != sys_config->pkt_forward_ctl) { + change = MTRUE; + PRINTM(MIOCTL, "ap_isolate=%xd\n", params->ap_isolate); + } + if (change) { + if (priv->bss_started == MTRUE) { + bss_started = MTRUE; + woal_uap_bss_ctrl(priv, MOAL_IOCTL_WAIT, UAP_BSS_STOP); + } + if (MLAN_STATUS_SUCCESS == woal_set_get_sys_config(priv, + MLAN_ACT_SET, + MOAL_IOCTL_WAIT, + sys_config)) + ret = 0; + if (bss_started) + woal_uap_bss_ctrl(priv, MOAL_IOCTL_WAIT, UAP_BSS_START); + } +done: + kfree(sys_config); + LEAVE(); + return ret; +} +#endif + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) +/** + * @brief del station + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param param A pointer tostation_del_parameters structure + * + * @return 0 -- success, otherwise fail + */ +#else +/** + * @brief del station + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param mac_addr A pointer to station mac address + * + * @return 0 -- success, otherwise fail + */ +#endif +int +woal_cfg80211_del_station(struct wiphy *wiphy, struct net_device *dev, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) + struct station_del_parameters *param) +#else +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 16, 0) + const u8 *mac_addr) +#else + u8 *mac_addr) +#endif +#endif +{ +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) + const u8 *mac_addr = NULL; +#endif + u16 reason_code = REASON_CODE_DEAUTH_LEAVING; + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + ENTER(); +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) + if (priv->phandle->is_cac_timer_set && + priv->bss_index == priv->phandle->cac_bss_index) { + woal_cancel_timer(&priv->phandle->cac_timer); + priv->phandle->is_cac_timer_set = MFALSE; + /* Make sure Chan Report is cancelled */ + woal_11h_cancel_chan_report_ioctl(priv, MOAL_IOCTL_WAIT); +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + cfg80211_cac_event(priv->netdev, &priv->phandle->dfs_channel, + NL80211_RADAR_CAC_ABORTED, GFP_KERNEL); +#else + cfg80211_cac_event(priv->netdev, NL80211_RADAR_CAC_ABORTED, + GFP_KERNEL); +#endif + memset(&priv->phandle->dfs_channel, 0, + sizeof(struct cfg80211_chan_def)); + priv->phandle->cac_bss_index = 0xff; + } +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + if (dfs_offload) + woal_cancel_cac_block(priv); +#endif + + if (priv->media_connected == MFALSE) { + PRINTM(MINFO, "cfg80211: Media not connected!\n"); + LEAVE(); + return 0; + } +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) + if (param) { + mac_addr = param->mac; + reason_code = param->reason_code; + } +#endif + /** we will not send deauth to p2p interface, it might cause WPS failure */ + if (mac_addr) { + PRINTM(MMSG, "wlan: deauth station " MACSTR "\n", + MAC2STR(mac_addr)); +#ifdef WIFI_DIRECT_SUPPORT + if (!priv->phandle->is_go_timer_set || + priv->bss_type != MLAN_BSS_TYPE_WIFIDIRECT) +#endif + woal_deauth_station(priv, (u8 *)mac_addr, reason_code); + } else { + PRINTM(MIOCTL, "del all station\n"); + } + LEAVE(); + return 0; + +} + +/** + * @brief Get station info + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param mac A pointer to station mac address + * @param stainfo A pointer to station_info structure + * + * @return 0 -- success, otherwise fail + */ +int +woal_uap_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 16, 0) + const u8 *mac, +#else + u8 *mac, +#endif + struct station_info *stainfo) +{ + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + int ret = -EFAULT; + int i = 0; + mlan_ds_get_info *info = NULL; + mlan_ioctl_req *ioctl_req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + if (priv->media_connected == MFALSE) { + PRINTM(MINFO, "cfg80211: Media not connected!\n"); + LEAVE(); + return -ENOENT; + } + + /* Allocate an IOCTL request buffer */ + ioctl_req = + (mlan_ioctl_req *) + woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_get_info)); + if (ioctl_req == NULL) { + ret = -ENOMEM; + goto done; + } + + info = (mlan_ds_get_info *)ioctl_req->pbuf; + info->sub_command = MLAN_OID_UAP_STA_LIST; + ioctl_req->req_id = MLAN_IOCTL_GET_INFO; + ioctl_req->action = MLAN_ACT_GET; + + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) + goto done; + for (i = 0; i < info->param.sta_list.sta_count; i++) { + if (!memcmp + (info->param.sta_list.info[i].mac_address, mac, ETH_ALEN)) { + PRINTM(MIOCTL, "Get station: " MACSTR " RSSI=%d\n", + MAC2STR(mac), + (int)info->param.sta_list.info[i].rssi); +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) + stainfo->filled = BIT(NL80211_STA_INFO_INACTIVE_TIME) | + BIT(NL80211_STA_INFO_SIGNAL); +#else + stainfo->filled = + STATION_INFO_INACTIVE_TIME | + STATION_INFO_SIGNAL; +#endif + stainfo->inactive_time = 0; + stainfo->signal = info->param.sta_list.info[i].rssi; + ret = 0; + break; + } + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + LEAVE(); + return ret; +} + +/** + * @brief Request the driver to dump the station information + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param idx Station index + * @param mac MAC address of the station + * @param sinfo A pointer to station_info structure + * + * @return 0 -- success, otherwise fail + */ +int +woal_uap_cfg80211_dump_station(struct wiphy *wiphy, + struct net_device *dev, int idx, + t_u8 *mac, struct station_info *sinfo) +{ + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + int ret = -EFAULT; + mlan_ds_get_info *info = NULL; + mlan_ioctl_req *ioctl_req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + if (priv->media_connected == MFALSE) { + PRINTM(MINFO, "cfg80211: Media not connected!\n"); + LEAVE(); + return -ENOENT; + } + + /* Allocate an IOCTL request buffer */ + ioctl_req = + (mlan_ioctl_req *) + woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_get_info)); + if (ioctl_req == NULL) { + ret = -ENOMEM; + goto done; + } + + info = (mlan_ds_get_info *)ioctl_req->pbuf; + info->sub_command = MLAN_OID_UAP_STA_LIST; + ioctl_req->req_id = MLAN_IOCTL_GET_INFO; + ioctl_req->action = MLAN_ACT_GET; + + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) + goto done; + if (idx >= info->param.sta_list.sta_count) { + ret = -EFAULT; + goto done; + } + ret = 0; + memcpy(mac, info->param.sta_list.info[idx].mac_address, ETH_ALEN); + PRINTM(MIOCTL, "Dump station: " MACSTR " RSSI=%d\n", MAC2STR(mac), + (int)info->param.sta_list.info[idx].rssi); +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) + sinfo->filled = + BIT(NL80211_STA_INFO_INACTIVE_TIME) | + BIT(NL80211_STA_INFO_SIGNAL); +#else + sinfo->filled = STATION_INFO_INACTIVE_TIME | STATION_INFO_SIGNAL; +#endif + sinfo->inactive_time = 0; + sinfo->signal = info->param.sta_list.info[idx].rssi; +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + LEAVE(); + return ret; +} + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 9, 0) +/** + * @brief set mac filter + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param params A pointer to cfg80211_acl_data structure + * + * @return 0 -- success, otherwise fail + */ +int +woal_cfg80211_set_mac_acl(struct wiphy *wiphy, struct net_device *dev, + const struct cfg80211_acl_data *params) +{ + int ret = -EFAULT; + mlan_uap_bss_param *sys_config = NULL; + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + u8 bss_started = MFALSE; + ENTER(); + + PRINTM(MIOCTL, "Set mac acl, entries=%d, policy=%d\n", + params->n_acl_entries, params->acl_policy); + sys_config = kzalloc(sizeof(mlan_uap_bss_param), GFP_ATOMIC); + if (!sys_config) { + PRINTM(MERROR, "Fail to alloc memory for mlan_uap_bss_param\n"); + LEAVE(); + return -EFAULT; + } + /* Initialize the uap bss values which are uploaded from firmware */ + if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv, + MLAN_ACT_GET, + MOAL_IOCTL_WAIT, + sys_config)) { + PRINTM(MERROR, "Error getting AP confiruration\n"); + ret = -EFAULT; + goto done; + } + memset(&sys_config->filter, 0, sizeof(mac_filter)); + if (params->n_acl_entries <= MAX_MAC_FILTER_NUM) + sys_config->filter.mac_count = params->n_acl_entries; + else + sys_config->filter.mac_count = MAX_MAC_FILTER_NUM; + + if (params->acl_policy == NL80211_ACL_POLICY_DENY_UNLESS_LISTED) + sys_config->filter.filter_mode = MAC_FILTER_MODE_ALLOW_MAC; + else if (params->acl_policy == NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED) + sys_config->filter.filter_mode = MAC_FILTER_MODE_BLOCK_MAC; + memcpy(sys_config->filter.mac_list, params->mac_addrs, + sys_config->filter.mac_count * sizeof(mlan_802_11_mac_addr)); + if (priv->bss_started == MTRUE) { + bss_started = MTRUE; + woal_uap_bss_ctrl(priv, MOAL_IOCTL_WAIT, UAP_BSS_STOP); + } + if (MLAN_STATUS_SUCCESS == woal_set_get_sys_config(priv, + MLAN_ACT_SET, + MOAL_IOCTL_WAIT, + sys_config)) + ret = 0; +done: + kfree(sys_config); + if (bss_started) + woal_uap_bss_ctrl(priv, MOAL_IOCTL_WAIT, UAP_BSS_START); + LEAVE(); + return ret; +} +#endif + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 1, 0) +/** + * @brief Set txq parameters + + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param params A pointer to ieee80211_txq_params structure + * + * @return 0 -- success, otherwise fail + */ +int +woal_cfg80211_set_txq_params(struct wiphy *wiphy, struct net_device *dev, + struct ieee80211_txq_params *params) +{ + int ret = 0; + u8 ac = 0; + wmm_parameter_t ap_wmm_para; + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + + ENTER(); + + /* AC_BE: 0, AC_BK:1, AC_VI: 2, AC_VO:3 */ +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 5, 0) + switch (params->ac) { + case NL80211_AC_VO: + ac = 3; + break; + case NL80211_AC_VI: + ac = 2; + break; + case NL80211_AC_BK: + ac = 1; + break; + case NL80211_AC_BE: + ac = 0; + break; + default: + break; + } +#else + switch (params->queue) { + case NL80211_TXQ_Q_VO: + ac = 3; + break; + case NL80211_TXQ_Q_VI: + ac = 2; + break; + case NL80211_TXQ_Q_BK: + ac = 1; + break; + case NL80211_TXQ_Q_BE: + ac = 0; + break; + default: + break; + } +#endif + + PRINTM(MMSG, "Set AC=%d, txop=%d cwmin=%d, cwmax=%d aifs=%d\n", ac, + params->txop, params->cwmin, params->cwmax, params->aifs); + + memset(&ap_wmm_para, 0, sizeof(wmm_parameter_t)); + + if (MLAN_STATUS_SUCCESS != + woal_set_get_ap_wmm_para(priv, MLAN_ACT_GET, &ap_wmm_para)) { + PRINTM(MERROR, "wlan: We don't support AP WMM parameter\n"); + LEAVE(); + return ret; + } + ap_wmm_para.ac_params[ac].aci_aifsn.aifsn = params->aifs; + ap_wmm_para.ac_params[ac].ecw.ecw_max = ilog2(params->cwmax + 1); + ap_wmm_para.ac_params[ac].ecw.ecw_min = ilog2(params->cwmin + 1); + ap_wmm_para.ac_params[ac].tx_op_limit = params->txop; + if (MLAN_STATUS_SUCCESS != + woal_set_get_ap_wmm_para(priv, MLAN_ACT_SET, &ap_wmm_para)) { + PRINTM(MERROR, "wlan: Fail to set AP WMM parameter\n"); + ret = -EFAULT; + } + LEAVE(); + return ret; +} +#endif + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) +/** + * @brief cac timer call back function. + * + * @param context a pointer to moal_handle + * + * @return N/A + */ +void +woal_cac_timer_func(void *context) +{ + moal_handle *handle = (moal_handle *)context; + moal_private *priv = handle->priv[handle->cac_bss_index]; + + PRINTM(MEVENT, "cac_timer fired.\n"); +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + cfg80211_cac_event(priv->netdev, &handle->dfs_channel, + NL80211_RADAR_CAC_ABORTED, GFP_KERNEL); +#else + cfg80211_cac_event(priv->netdev, NL80211_RADAR_CAC_ABORTED, GFP_KERNEL); +#endif + handle->is_cac_timer_set = MFALSE; + memset(&handle->dfs_channel, 0, sizeof(struct cfg80211_chan_def)); + handle->cac_bss_index = 0xff; +} + +/** + * @brief This function switch AP's channel + * 1. clear mgmt IEs 2. stop uAP + * 3. set beacon after 4. set new channel + * 5. start uAP 6. notify cfg80211 + * + * @param priv a pointer to moal_private + * @param wait_option wait option + * + * @return N/A + */ +void +woal_switch_uap_channel(moal_private *priv, t_u8 wait_option) +{ + + chan_band_info uap_channel; + t_u8 chan2Offset = SEC_CHAN_NONE; + ENTER(); + woal_clear_all_mgmt_ies(priv, MOAL_IOCTL_WAIT); + if (MLAN_STATUS_SUCCESS != woal_uap_bss_ctrl(priv, + wait_option, + UAP_BSS_STOP)) { + PRINTM(MERROR, "%s: stop uap failed \n", __func__); + goto done; + } + if (woal_cfg80211_set_beacon(priv->wdev->wiphy, priv->netdev, + &priv->beacon_after)) { + PRINTM(MERROR, "%s: set mgmt ies failed \n", __func__); + goto done; + } + + uap_channel.channel = + ieee80211_frequency_to_channel(priv->csa_chan.chan-> + center_freq); + switch (priv->csa_chan.width) { + case NL80211_CHAN_WIDTH_5: + case NL80211_CHAN_WIDTH_10: + case NL80211_CHAN_WIDTH_20_NOHT: + break; + case NL80211_CHAN_WIDTH_20: + break; + case NL80211_CHAN_WIDTH_40: + if (priv->csa_chan.center_freq1 < + priv->csa_chan.chan->center_freq) + chan2Offset = SEC_CHAN_BELOW; + else + chan2Offset = SEC_CHAN_ABOVE; + break; + case NL80211_CHAN_WIDTH_80: + case NL80211_CHAN_WIDTH_80P80: + case NL80211_CHAN_WIDTH_160: + chan2Offset = + woal_get_second_channel_offset(uap_channel.channel); + break; + default: + PRINTM(MWARN, "Unknown channel width: %d\n", + priv->csa_chan.width); + break; + } + if (priv->csa_chan.chan->band == IEEE80211_BAND_2GHZ) + uap_channel.bandcfg.chanBand = BAND_2GHZ; + else if (priv->csa_chan.chan->band == IEEE80211_BAND_5GHZ) + uap_channel.bandcfg.chanBand = BAND_5GHZ; + uap_channel.bandcfg.chan2Offset = chan2Offset; + if (MLAN_STATUS_SUCCESS != + woal_set_get_ap_channel(priv, MLAN_ACT_SET, wait_option, + &uap_channel)) { + PRINTM(MERROR, "Fail to set ap channel \n"); + goto done; + } + if (MLAN_STATUS_SUCCESS != + woal_uap_bss_ctrl(priv, wait_option, UAP_BSS_START)) { + PRINTM(MERROR, "%s: start uap failed \n", __func__); + goto done; + } + PRINTM(MMSG, "CSA: old chan %d => new chan %d \n", priv->channel, + uap_channel.channel); + priv->channel = uap_channel.channel; + memcpy(&priv->chan, &priv->csa_chan, sizeof(struct cfg80211_chan_def)); + cfg80211_ch_switch_notify(priv->netdev, &priv->chan); + if (priv->uap_tx_blocked) { + if (!netif_carrier_ok(priv->netdev)) + netif_carrier_on(priv->netdev); + woal_start_queue(priv->netdev); + priv->uap_tx_blocked = MFALSE; + } +done: + LEAVE(); + return; +} + +/** + * @brief csa work handler + * + * @param work a pointer to work_struct + * + * @return 0 -- success, otherwise fail + */ +void +woal_csa_work_queue(struct work_struct *work) +{ + struct delayed_work *delayed_work = + container_of(work, struct delayed_work, work); + moal_private *priv = container_of(delayed_work, moal_private, csa_work); + ENTER(); + if (priv->bss_started == MTRUE) + woal_switch_uap_channel(priv, MOAL_IOCTL_WAIT); + LEAVE(); +} + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 15, 0) +/** + * @brief start radar detection + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param chandef A pointer to cfg80211_chan_def structure + * @param cac_time_ms A cac dwell time + * @return 0 -- success, otherwise fail + */ + +int +woal_cfg80211_start_radar_detection(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_chan_def *chandef, + u32 cac_time_ms) +#else +/** + * @brief start radar detection + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param chandef A pointer to cfg80211_chan_def structure + * @return 0 -- success, otherwise fail + */ + +int +woal_cfg80211_start_radar_detection(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_chan_def *chandef) +#endif +{ + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + moal_handle *handle = priv->phandle; + mlan_ioctl_req *req = NULL; + mlan_ds_11h_chan_rep_req *pchan_rpt_req = NULL; + mlan_ds_11h_cfg *p11h_cfg = NULL; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 15, 0) + PRINTM(MIOCTL, "start Radar detect, chan %d , Bw %d , Time %d \n", + chandef->chan->hw_value, chandef->width, cac_time_ms); +#else + PRINTM(MIOCTL, "start Radar detect, chan %d , Bw %d \n", + chandef->chan->hw_value, chandef->width); +#endif + + if (priv->bss_started == MTRUE) { + PRINTM(MERROR, "recv CAC request when bss already started \n"); + ret = -EFAULT; + goto done; + } + if (priv->phandle->cac_period || handle->is_cac_timer_set) { + PRINTM(MERROR, + "Maybe other interface is doing CAC, please defer your oper\n"); + ret = -EBUSY; + goto done; + } + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11h_cfg)); + if (NULL == req) { + ret = -ENOMEM; + goto done; + } + + p11h_cfg = (mlan_ds_11h_cfg *)req->pbuf; + pchan_rpt_req = &p11h_cfg->param.chan_rpt_req; + pchan_rpt_req->startFreq = START_FREQ_11A_BAND; + pchan_rpt_req->chanNum = (t_u8)chandef->chan->hw_value; + woal_convert_chan_to_bandconfig(&pchan_rpt_req->bandcfg, chandef); + pchan_rpt_req->host_based = MTRUE; + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 15, 0) + pchan_rpt_req->millisec_dwell_time = cac_time_ms; +#else + pchan_rpt_req->millisec_dwell_time = IEEE80211_DFS_MIN_CAC_TIME_MS; + + if ((woal_is_etsi_country(priv->phandle->country_code) == MTRUE)) { + if (chandef->chan->hw_value == 120 || + chandef->chan->hw_value == 124 || + chandef->chan->hw_value == 128) { + pchan_rpt_req->millisec_dwell_time = + IEEE80211_DFS_MIN_CAC_TIME_MS * 10; + } + if (chandef->chan->hw_value == 116 && + ((chandef->width == NL80211_CHAN_WIDTH_40) || + (chandef->width == NL80211_CHAN_WIDTH_80))) { + pchan_rpt_req->millisec_dwell_time = + IEEE80211_DFS_MIN_CAC_TIME_MS * 10; + } + } +#endif +#if defined(DFS_TESTING_SUPPORT) + if (priv->user_cac_period_msec) { + pchan_rpt_req->millisec_dwell_time = priv->user_cac_period_msec; + PRINTM(MCMD_D, + "cfg80211 dfstesting: User CAC Period=%d (msec) \n", + pchan_rpt_req->millisec_dwell_time); + } +#endif + + p11h_cfg->sub_command = MLAN_OID_11H_CHANNEL_CHECK; + req->req_id = MLAN_IOCTL_11H_CFG; + req->action = MLAN_ACT_SET; + + /* Send Channel Check command and wait until the report is ready */ + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "Fail to start radar detection\n"); + ret = -EFAULT; + } else { + memcpy(&handle->dfs_channel, chandef, + sizeof(struct cfg80211_chan_def)); + handle->cac_bss_index = priv->bss_index; + handle->is_cac_timer_set = MTRUE; + /* avoid EVENT_CHANNEL_RAPORT_READY missing, add 1s gap */ + woal_mod_timer(&handle->cac_timer, + pchan_rpt_req->millisec_dwell_time + 1000); + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief channel switch + + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param params A pointer to cfg80211_csa_settings structure + * + * @return 0 -- success, otherwise fail + */ +int +woal_cfg80211_channel_switch(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_csa_settings *params) +{ + int ret = 0; + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + t_u32 chsw_msec; + mlan_uap_bss_param *bss_cfg = NULL; + + ENTER(); + + if (!params) { + ret = -EINVAL; + goto done; + } + + /* TODO: support this case in next version */ + if (params->radar_required) { + PRINTM(MMSG, + " hostapd handle this case by disable and re-enable interface\n"); + ret = -ENOTSUPP; + goto done; + } + + /* actually hostapd would always choose one diff channel */ + if (cfg80211_chandef_identical(¶ms->chandef, &priv->chan)) { + PRINTM(MMSG, + "csa channel is same with current channel, invaild\n"); + ret = -EINVAL; + goto done; + } + bss_cfg = kzalloc(sizeof(mlan_uap_bss_param), GFP_ATOMIC); + if (!bss_cfg) { + PRINTM(MERROR, "Fail to alloc memory for mlan_uap_bss_param\n"); + ret = -EFAULT; + goto done; + } + + if (params->block_tx) { + if (netif_carrier_ok(dev)) + netif_carrier_off(dev); + woal_stop_queue(dev); + priv->uap_tx_blocked = MTRUE; + } + + woal_clear_all_mgmt_ies(priv, MOAL_IOCTL_WAIT); + if (woal_cfg80211_set_beacon(wiphy, dev, ¶ms->beacon_csa)) { + PRINTM(MERROR, "%s: setting csa mgmt ies failed\n", __func__); + goto done; + } + + memcpy(&priv->csa_chan, ¶ms->chandef, + sizeof(struct cfg80211_chan_def)); + memcpy(&priv->beacon_after, ¶ms->beacon_after, + sizeof(struct cfg80211_beacon_data)); + + if (!priv->phandle->fw_ecsa_enable) { + if (MLAN_STATUS_SUCCESS != + woal_set_get_sys_config(priv, MLAN_ACT_GET, MOAL_IOCTL_WAIT, + bss_cfg)) { + PRINTM(MERROR, "%s: get uap config failed\n", __func__); + ret = -EFAULT; + goto done; + } + chsw_msec = params->count * bss_cfg->beacon_period; + queue_delayed_work(priv->csa_workqueue, &priv->csa_work, + msecs_to_jiffies(chsw_msec)); + } +done: + kfree(bss_cfg); + LEAVE(); + return ret; +} +#endif + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 5, 0) +/** + * @brief Notify cfg80211 uap channel changed + * + * @param priv A pointer moal_private structure + * @param pchan_info A pointer to chan_band structure + * + * @return N/A + */ +void +woal_cfg80211_notify_uap_channel(moal_private *priv, + chan_band_info * pchan_info) +{ +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + struct cfg80211_chan_def chandef; +#else +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 5, 0) + enum nl80211_channel_type type; + enum ieee80211_band band; + int freq = 0; +#endif +#endif + ENTER(); + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + if (MLAN_STATUS_SUCCESS == + woal_chandef_create(priv, &chandef, pchan_info)) + cfg80211_ch_switch_notify(priv->netdev, &chandef); +#else +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 5, 0) + if (pchan_info->bandcfg.chanBand == BAND_2GHZ) + band = IEEE80211_BAND_2GHZ; + else if (pchan_info->bandcfg.chanBand == BAND_5GHZ) + band = IEEE80211_BAND_5GHZ; + else { + LEAVE(); + return; + } + freq = ieee80211_channel_to_frequency(pchan_info->channel, band); + switch (pchan_info->bandcfg.chanWidth) { + case CHAN_BW_20MHZ: + if (pchan_info->is_11n_enabled) + type = NL80211_CHAN_HT20; + else + type = NL80211_CHAN_NO_HT; + break; + default: + if (pchan_info->bandcfg.chan2Offset == SEC_CHAN_ABOVE) + type = NL80211_CHAN_HT40PLUS; + else if (pchan_info->bandcfg.chan2Offset == SEC_CHAN_BELOW) + type = NL80211_CHAN_HT40MINUS; + else + type = NL80211_CHAN_HT20; + break; + } + cfg80211_ch_switch_notify(priv->netdev, freq, type); +#endif +#endif + LEAVE(); +} +#endif + +/** + * @brief Register the device with cfg80211 + * + * @param dev A pointer to net_device structure + * @param bss_type BSS type + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +woal_register_uap_cfg80211(struct net_device *dev, t_u8 bss_type) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + moal_private *priv = (moal_private *)netdev_priv(dev); + struct wireless_dev *wdev = NULL; + + ENTER(); + + wdev = (struct wireless_dev *)&priv->w_dev; + memset(wdev, 0, sizeof(struct wireless_dev)); + + wdev->wiphy = priv->phandle->wiphy; + if (!wdev->wiphy) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + if (bss_type == MLAN_BSS_TYPE_UAP) + wdev->iftype = NL80211_IFTYPE_AP; + + dev_net_set(dev, wiphy_net(wdev->wiphy)); + dev->ieee80211_ptr = wdev; + SET_NETDEV_DEV(dev, wiphy_dev(wdev->wiphy)); + priv->wdev = wdev; + + LEAVE(); + return ret; +} diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_uap_cfg80211.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_uap_cfg80211.h new file mode 100644 index 000000000000..12b5abe40d75 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_uap_cfg80211.h @@ -0,0 +1,29 @@ +/** @file moal_uap_cfg80211.h + * + * @brief This file contains the uAP CFG80211 specific defines. + * + * Copyright (C) 2014-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +#ifndef _MOAL_UAP_CFG80211_H_ +#define _MOAL_UAP_CFG80211_H_ + +#include "moal_uap.h" + +mlan_status woal_register_uap_cfg80211(struct net_device *dev, t_u8 bss_type); + +#endif /* _MOAL_UAP_CFG80211_H_ */ diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_uap_priv.c b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_uap_priv.c new file mode 100644 index 000000000000..30a8a5ae7a78 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_uap_priv.c @@ -0,0 +1,180 @@ +/** @file moal_uap_priv.c + * + * @brief This file contains standard ioctl functions + * + * Copyright (C) 2010-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +/************************************************************************ +Change log: + 08/06/2010: initial version +************************************************************************/ + +#include "moal_main.h" +#include "moal_uap.h" +#include "moal_uap_priv.h" + +/******************************************************** + Local Variables +********************************************************/ + +/******************************************************** + Global Variables +********************************************************/ + +/******************************************************** + Local Functions +********************************************************/ + +/******************************************************** + Global Functions +********************************************************/ + +/** + * @brief ioctl function for wireless IOCTLs + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @param cmd Command + * + * @return 0 --success, otherwise fail + */ +int +woal_uap_do_priv_ioctl(struct net_device *dev, struct ifreq *req, int cmd) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + struct iwreq *wrq = (struct iwreq *)req; + int ret = 0; + + ENTER(); + + switch (cmd) { + case WOAL_UAP_SETNONE_GETNONE: + switch (wrq->u.data.flags) { + case WOAL_UAP_START: + break; + case WOAL_UAP_STOP: + ret = woal_uap_bss_ctrl(priv, MOAL_IOCTL_WAIT, + UAP_BSS_STOP); + break; + case WOAL_AP_BSS_START: + ret = woal_uap_bss_ctrl(priv, MOAL_IOCTL_WAIT, + UAP_BSS_START); + break; + case WOAL_AP_BSS_STOP: + ret = woal_uap_bss_ctrl(priv, MOAL_IOCTL_WAIT, + UAP_BSS_STOP); + break; + default: + ret = -EINVAL; + break; + } + break; + case WOAL_UAP_SETONEINT_GETWORDCHAR: + switch (wrq->u.data.flags) { + case WOAL_UAP_VERSION: + ret = woal_get_driver_version(priv, req); + break; + case WOAL_UAP_VEREXT: + ret = woal_get_driver_verext(priv, req); + break; + default: + ret = -EOPNOTSUPP; + break; + } + break; + case WOAL_UAP_SET_GET_256_CHAR: + switch (wrq->u.data.flags) { + case WOAL_WL_FW_RELOAD: + break; + case WOAL_AP_SET_CFG: + ret = woal_uap_set_ap_cfg(priv, wrq->u.data.pointer, + wrq->u.data.length); + break; + default: + ret = -EINVAL; + break; + } + break; +#if defined(WIFI_DIRECT_SUPPORT) +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) + case WOAL_UAP_SETONEINT_GETONEINT: + switch (wrq->u.data.flags) { + case WOAL_UAP_SET_GET_BSS_ROLE: + ret = woal_set_get_bss_role(priv, wrq); + break; + default: + ret = -EINVAL; + break; + } + break; +#endif +#endif + case WOAL_UAP_HOST_CMD: + ret = woal_host_command(priv, wrq); + break; + case WOAL_UAP_FROYO_START: + break; + case WOAL_UAP_FROYO_STOP: + ret = woal_uap_bss_ctrl(priv, MOAL_IOCTL_WAIT, UAP_BSS_STOP); + break; + case WOAL_UAP_FROYO_AP_BSS_START: + ret = woal_uap_bss_ctrl(priv, MOAL_IOCTL_WAIT, UAP_BSS_START); + break; + case WOAL_UAP_FROYO_AP_BSS_STOP: + ret = woal_uap_bss_ctrl(priv, MOAL_IOCTL_WAIT, UAP_BSS_STOP); + break; + case WOAL_UAP_FROYO_WL_FW_RELOAD: + break; + case WOAL_UAP_FROYO_AP_SET_CFG: + ret = woal_uap_set_ap_cfg(priv, wrq->u.data.pointer, + wrq->u.data.length); + break; + default: + ret = -EINVAL; + break; + } + + LEAVE(); + return ret; +} + +/** + * @brief Handle get info resp + * + * @param priv Pointer to moal_private structure + * @param info Pointer to mlan_ds_get_info structure + * + * @return N/A + */ +void +woal_ioctl_get_uap_info_resp(moal_private *priv, mlan_ds_get_info *info) +{ + ENTER(); + switch (info->sub_command) { + case MLAN_OID_GET_STATS: + priv->w_stats.discard.fragment = + info->param.ustats.fcs_error_count; + priv->w_stats.discard.retries = info->param.ustats.retry_count; + priv->w_stats.discard.misc = + info->param.ustats.ack_failure_count; + break; + default: + break; + } + LEAVE(); +} diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_uap_priv.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_uap_priv.h new file mode 100644 index 000000000000..d6804accfa81 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_uap_priv.h @@ -0,0 +1,194 @@ +/** @file moal_uap_priv.h + * + * @brief This file contains definition for extended private IOCTL call. + * + * Copyright (C) 2010-2017, Marvell International Lt. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +/************************************************************************ +Change log: + 08/06/2010: initial version +************************************************************************/ + +#ifndef _MOAL_UAP_PRIV_H_ +#define _MOAL_UAP_PRIV_H_ + +/** Private command ID */ +#define WOAL_UAP_IOCTL 0x8BE0 + +/** Private command to get/set 256 chars */ +#define WOAL_UAP_SET_GET_256_CHAR (WOAL_UAP_IOCTL + 1) +/** Private command ID to FW reload */ +#define WOAL_WL_FW_RELOAD 1 +/** Private command ID to set AP configuration */ +#define WOAL_AP_SET_CFG 2 + +/** Private command ID to set/get none */ +#define WOAL_UAP_SETNONE_GETNONE (WOAL_UAP_IOCTL + 2) +/** Private command ID to start UAP */ +#define WOAL_UAP_START 1 +/** Private command ID to stop UAP */ +#define WOAL_UAP_STOP 2 +/** Private command ID to start AP BSS */ +#define WOAL_AP_BSS_START 3 +/** Private command ID to stop AP BSS */ +#define WOAL_AP_BSS_STOP 4 + +/** Private command ID to set one int/get word char */ +#define WOAL_UAP_SETONEINT_GETWORDCHAR (WOAL_UAP_IOCTL + 3) +/** Private command ID to get version */ +#define WOAL_UAP_VERSION 1 +/** Private command ID to get extended version */ +#define WOAL_UAP_VEREXT 2 + +#if defined(WIFI_DIRECT_SUPPORT) +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) +/** Private command ID to set one int/get one int */ +#define WOAL_UAP_SETONEINT_GETONEINT (WOAL_UAP_IOCTL + 5) +/** Private command ID for set/get BSS role */ +#define WOAL_UAP_SET_GET_BSS_ROLE 1 +#endif +#endif + +/** Private command ID for hostcmd */ +#define WOAL_UAP_HOST_CMD (WOAL_UAP_IOCTL + 17) + +/** The following command IDs are for Froyo app */ +/** Private command ID to start AP BSS */ +#define WOAL_UAP_FROYO_AP_BSS_START (WOAL_UAP_IOCTL + 24) +/** Private command ID to stop AP BSS */ +#define WOAL_UAP_FROYO_AP_BSS_STOP (WOAL_UAP_IOCTL + 26) +/** Private command ID to set AP config */ +#define WOAL_UAP_FROYO_AP_SET_CFG (WOAL_UAP_IOCTL + 27) +/** Private command ID to start driver */ +#define WOAL_UAP_FROYO_START (WOAL_UAP_IOCTL + 28) +/** Private command ID to reload FW */ +#define WOAL_UAP_FROYO_WL_FW_RELOAD (WOAL_UAP_IOCTL + 29) +/** Private command ID to stop driver */ +#define WOAL_UAP_FROYO_STOP (WOAL_UAP_IOCTL + 30) + +/** + * iwpriv ioctl handlers + */ +static const struct iw_priv_args woal_uap_priv_args[] = { + { + WOAL_UAP_SETNONE_GETNONE, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_NONE, + ""}, + { + WOAL_UAP_START, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_NONE, + "start"}, + { + WOAL_UAP_STOP, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_NONE, + "stop"}, + { + WOAL_AP_BSS_START, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_NONE, + "bssstart"}, + { + WOAL_AP_BSS_STOP, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_NONE, + "bssstop"}, + { + WOAL_UAP_SETONEINT_GETWORDCHAR, + IW_PRIV_TYPE_INT | 1, + IW_PRIV_TYPE_CHAR | 128, + ""}, + { + WOAL_UAP_VERSION, + IW_PRIV_TYPE_INT | 1, + IW_PRIV_TYPE_CHAR | 128, + "version"}, + { + WOAL_UAP_VEREXT, + IW_PRIV_TYPE_INT | 1, + IW_PRIV_TYPE_CHAR | 128, + "verext"}, +#if defined(WIFI_DIRECT_SUPPORT) +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) + { + WOAL_UAP_SETONEINT_GETONEINT, + IW_PRIV_TYPE_INT | 1, + IW_PRIV_TYPE_INT | 1, + ""}, + { + WOAL_UAP_SET_GET_BSS_ROLE, + IW_PRIV_TYPE_INT | 1, + IW_PRIV_TYPE_INT | 1, + "bssrole"}, +#endif +#endif + { + WOAL_UAP_SET_GET_256_CHAR, + IW_PRIV_TYPE_CHAR | 256, + IW_PRIV_TYPE_CHAR | 256, + ""}, + { + WOAL_WL_FW_RELOAD, + IW_PRIV_TYPE_CHAR | 256, + IW_PRIV_TYPE_CHAR | 256, + "fwreload"}, + { + WOAL_AP_SET_CFG, + IW_PRIV_TYPE_CHAR | 256, + IW_PRIV_TYPE_CHAR | 256, + "apcfg"}, + { + WOAL_UAP_HOST_CMD, + IW_PRIV_TYPE_BYTE | 2047, + IW_PRIV_TYPE_BYTE | 2047, + "hostcmd"}, + { + WOAL_UAP_FROYO_START, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_NONE, + "START"}, + { + WOAL_UAP_FROYO_STOP, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_NONE, + "STOP"}, + { + WOAL_UAP_FROYO_AP_BSS_START, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_NONE, + "AP_BSS_START"}, + { + WOAL_UAP_FROYO_AP_BSS_STOP, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_NONE, + "AP_BSS_STOP"}, + { + WOAL_UAP_FROYO_WL_FW_RELOAD, + IW_PRIV_TYPE_CHAR | 256, + IW_PRIV_TYPE_CHAR | 256, + "WL_FW_RELOAD"}, + { + WOAL_UAP_FROYO_AP_SET_CFG, + IW_PRIV_TYPE_CHAR | 256, + IW_PRIV_TYPE_CHAR | 256, + "AP_SET_CFG"}, +}; + +#endif /* _MOAL_UAP_PRIV_H_ */ diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_uap_wext.c b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_uap_wext.c new file mode 100644 index 000000000000..d3bd9dee5a8f --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_uap_wext.c @@ -0,0 +1,1879 @@ +/** @file moal_uap_wext.c + * + * @brief This file contains wireless extension standard ioctl functions + * + * Copyright (C) 2010-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +/************************************************************************ +Change log: + 08/06/2010: initial version +************************************************************************/ + +#include "moal_main.h" +#include "moal_uap.h" +#include "moal_wext.h" +#include "moal_uap_priv.h" + +/******************************************************** + Global Variables +********************************************************/ +typedef struct _chan_to_freq_t { + /** Channel */ + t_u16 channel; + /** Frequency */ + t_u32 freq; + /** Band */ + t_u8 band; +} chan_to_freq_t; + +const chan_to_freq_t chan_to_freq[] = { + {1, 2412, 0}, + {2, 2417, 0}, + {3, 2422, 0}, + {4, 2427, 0}, + {5, 2432, 0}, + {6, 2437, 0}, + {7, 2442, 0}, + {8, 2447, 0}, + {9, 2452, 0}, + {10, 2457, 0}, + {11, 2462, 0}, + {12, 2467, 0}, + {13, 2472, 0}, + {14, 2484, 0}, + {183, 4915, 1}, + {184, 4920, 1}, + {185, 4925, 1}, + {187, 4935, 1}, + {188, 4940, 1}, + {189, 4945, 1}, + {192, 4960, 1}, + {196, 4980, 1}, + {7, 5035, 1}, + {8, 5040, 1}, + {9, 5045, 1}, + {11, 5055, 1}, + {12, 5060, 1}, + {16, 5080, 1}, + {34, 5170, 1}, + {36, 5180, 1}, + {38, 5190, 1}, + {40, 5200, 1}, + {42, 5210, 1}, + {44, 5220, 1}, + {46, 5230, 1}, + {48, 5240, 1}, + {52, 5260, 1}, + {56, 5280, 1}, + {60, 5300, 1}, + {64, 5320, 1}, + {100, 5500, 1}, + {104, 5520, 1}, + {108, 5540, 1}, + {112, 5560, 1}, + {116, 5580, 1}, + {120, 5600, 1}, + {124, 5620, 1}, + {128, 5640, 1}, + {132, 5660, 1}, + {136, 5680, 1}, + {140, 5700, 1}, + {144, 5720, 1}, + {149, 5745, 1}, + {153, 5765, 1}, + {157, 5785, 1}, + {161, 5805, 1}, + {165, 5825, 1}, +}; + +/** Convertion from frequency to channel */ +#define freq_to_chan(x) ((((x) - 2412) / 5) + 1) + +/******************************************************** + Local Functions +********************************************************/ + +/** + * @brief Sort Channels + * + * @param freq A pointer to iw_freq structure + * @param num Number of Channels + * + * @return N/A + */ +static inline void +woal_sort_channels(struct iw_freq *freq, int num) +{ + int i, j; + struct iw_freq temp; + + for (i = 0; i < num; i++) + for (j = i + 1; j < num; j++) + if (freq[i].i > freq[j].i) { + temp.i = freq[i].i; + temp.m = freq[i].m; + + freq[i].i = freq[j].i; + freq[i].m = freq[j].m; + + freq[j].i = temp.i; + freq[j].m = temp.m; + } +} + +/** + * @brief Get frequency for channel in given band + * + * @param channel channel + * @param band band + * + * @return freq + */ +static int +channel_to_frequency(t_u16 channel, t_u8 band) +{ + int i = 0; + + ENTER(); + for (i = 0; i < ARRAY_SIZE(chan_to_freq); i++) { + if (channel == chan_to_freq[i].channel && + band == chan_to_freq[i].band) { + LEAVE(); + return chan_to_freq[i].freq; + } + } + LEAVE(); + return 0; +} + +/** + * @brief Commit handler: called after a bunch of SET operations + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param cwrq A pointer to char buffer + * @param extra A pointer to extra data buf + * + * @return 0 --success + */ +static int +woal_config_commit(struct net_device *dev, + struct iw_request_info *info, char *cwrq, char *extra) +{ + ENTER(); + + LEAVE(); + return 0; +} + +/** + * @brief Get name + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param cwrq A pointer to char buffer + * @param extra A pointer to extra data buf + * + * @return 0 --success + */ +static int +woal_get_name(struct net_device *dev, struct iw_request_info *info, + char *cwrq, char *extra) +{ + ENTER(); + strcpy(cwrq, "IEEE 802.11-DS"); + LEAVE(); + return 0; +} + +/** + * @brief Get current BSSID + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param awrq A pointer to sockaddr structure + * @param extra A pointer to extra data buf + * + * @return 0 --success + */ +static int +woal_get_wap(struct net_device *dev, struct iw_request_info *info, + struct sockaddr *awrq, char *extra) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + int ret = 0; + + ENTER(); + + if (priv->bss_started) + memcpy(awrq->sa_data, priv->current_addr, MLAN_MAC_ADDR_LENGTH); + else + memset(awrq->sa_data, 0, MLAN_MAC_ADDR_LENGTH); + awrq->sa_family = ARPHRD_ETHER; + + LEAVE(); + return ret; +} + +/** + * @brief Change the AP BSSID + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param awrq A pointer to iw_param structure + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +static int +woal_set_wap(struct net_device *dev, struct iw_request_info *info, + struct sockaddr *awrq, char *extra) +{ + int ret = 0; + moal_private *priv = (moal_private *)netdev_priv(dev); + const t_u8 zero_mac[MLAN_MAC_ADDR_LENGTH] = { 0, 0, 0, 0, 0, 0 }; + + ENTER(); + + if (awrq->sa_family != ARPHRD_ETHER) { + ret = -EINVAL; + goto done; + } + + PRINTM(MINFO, "ASSOC: WAP: uAP bss : " MACSTR "\n", + MAC2STR((t_u8 *)awrq->sa_data)); + + /* + * Using this ioctl to start/stop the BSS, return if bss + * is already started/stopped. + */ + if (memcmp(zero_mac, awrq->sa_data, MLAN_MAC_ADDR_LENGTH)) { + if (priv->bss_started == MFALSE) + ret = woal_uap_bss_ctrl(priv, MOAL_IOCTL_WAIT, + UAP_BSS_START); + else + PRINTM(MINFO, "BSS is already started.\n"); + } else { + /* zero_mac means bss_stop */ + if (priv->bss_started == MTRUE) + ret = woal_uap_bss_ctrl(priv, MOAL_IOCTL_WAIT, + UAP_BSS_STOP); + else + PRINTM(MINFO, "BSS is already stopped.\n"); + } + +done: + LEAVE(); + return ret; +} + +/** + * @brief Set frequency/channel + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param fwrq A pointer to iw_freq structure + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +static int +woal_set_freq(struct net_device *dev, struct iw_request_info *info, + struct iw_freq *fwrq, char *extra) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + mlan_uap_bss_param *sys_cfg = NULL, *ap_cfg = NULL; + int ret = 0, chan = 0, i = 0; + + ENTER(); + + ap_cfg = kmalloc(sizeof(mlan_uap_bss_param), GFP_KERNEL); + if (ap_cfg == NULL) { + ret = -ENOMEM; + goto done; + } + + sys_cfg = kmalloc(sizeof(mlan_uap_bss_param), GFP_KERNEL); + if (sys_cfg == NULL) { + ret = -ENOMEM; + goto done; + } + + if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv, + MLAN_ACT_GET, + MOAL_IOCTL_WAIT, + ap_cfg)) { + PRINTM(MERROR, "Error getting AP confiruration\n"); + ret = -EFAULT; + goto done; + } + i = ap_cfg->num_of_chan; + + /* Initialize the invalid values so that the correct values + * below are downloaded to firmware */ + woal_set_sys_config_invalid_data(sys_cfg); + + /* If setting by frequency, convert to a channel */ + if (fwrq->e == 1) + chan = freq_to_chan(fwrq->m / 100000); + else + chan = fwrq->m; + if (chan > 0 && chan < MLAN_MAX_CHANNEL) + sys_cfg->channel = chan; + else { + ret = -EINVAL; + goto done; + } + for (i = 0; i < ap_cfg->num_of_chan; i++) + if (ap_cfg->chan_list[i].chan_number == chan) + break; + if (i == ap_cfg->num_of_chan) { + PRINTM(MERROR, "Channel %d is not supported\n", chan); + ret = -EINVAL; + goto done; + } + + if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv, + MLAN_ACT_SET, + MOAL_IOCTL_WAIT, + sys_cfg)) { + PRINTM(MERROR, "Error setting AP confiruration\n"); + ret = -EFAULT; + goto done; + } + +done: + kfree(sys_cfg); + kfree(ap_cfg); + LEAVE(); + return ret; +} + +/** + * @brief Get frequency and channel + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param fwrq A pointer to iw_freq structure + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +static int +woal_get_freq(struct net_device *dev, struct iw_request_info *info, + struct iw_freq *fwrq, char *extra) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + mlan_uap_bss_param *ap_cfg = NULL; + t_u8 band = 0; + int ret = 0; + + ENTER(); + + ap_cfg = kzalloc(sizeof(mlan_uap_bss_param), GFP_ATOMIC); + if (!ap_cfg) { + PRINTM(MERROR, "Fail to alloc memory for mlan_uap_bss_param\n"); + return -EFAULT; + } + + if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv, + MLAN_ACT_GET, + MOAL_IOCTL_WAIT, + ap_cfg)) { + PRINTM(MERROR, "Error getting AP confiruration\n"); + kfree(ap_cfg); + LEAVE(); + return -EFAULT; + } + + band = (ap_cfg->bandcfg.chanBand == BAND_5GHZ); + fwrq->m = (long)channel_to_frequency(ap_cfg->channel, band); + fwrq->i = (long)ap_cfg->channel; + fwrq->e = 6; + + kfree(ap_cfg); + LEAVE(); + return ret; +} + +/** + * @brief Set wlan mode + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param uwrq A pointer to t_u32 string + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +static int +woal_set_bss_mode(struct net_device *dev, struct iw_request_info *info, + t_u32 *uwrq, char *extra) +{ + int ret = 0; + ENTER(); + + switch (*uwrq) { + case IW_MODE_AUTO: + case IW_MODE_MASTER: + PRINTM(MINFO, "This is correct mode in AP mode\n"); + break; + default: + PRINTM(MERROR, "Invalid mode for AP\n"); + ret = -EINVAL; + } + + LEAVE(); + return ret; +} + +/** + * @brief Get wlan mode + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param uwrq A pointer to t_u32 string + * @param extra A pointer to extra data buf + * + * @return 0 --success + */ +static int +woal_get_bss_mode(struct net_device *dev, struct iw_request_info *info, + t_u32 *uwrq, char *extra) +{ + ENTER(); + + *uwrq = IW_MODE_MASTER; + + LEAVE(); + return 0; +} + +/** + * @brief Set encryption key + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param dwrq A pointer to iw_point structure + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +static int +woal_set_encode(struct net_device *dev, struct iw_request_info *info, + struct iw_point *dwrq, char *extra) +{ + int ret = 0; + moal_private *priv = (moal_private *)netdev_priv(dev); + mlan_uap_bss_param *sys_cfg = NULL, *ap_cfg = NULL; + wep_key *pkey = NULL; + int key_index = 0; + + ENTER(); + + /* Check index */ + key_index = (dwrq->flags & IW_ENCODE_INDEX) - 1; + if (key_index > 3) { + PRINTM(MERROR, "Key index #%d out of range\n", key_index); + ret = -EINVAL; + goto done; + } + + ap_cfg = kmalloc(sizeof(mlan_uap_bss_param), GFP_KERNEL); + if (ap_cfg == NULL) { + ret = -ENOMEM; + goto done; + } + + sys_cfg = kmalloc(sizeof(mlan_uap_bss_param), GFP_KERNEL); + if (sys_cfg == NULL) { + ret = -ENOMEM; + goto done; + } + + if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv, + MLAN_ACT_GET, + MOAL_IOCTL_WAIT, + ap_cfg)) { + PRINTM(MERROR, "Error getting AP confiruration\n"); + ret = -EFAULT; + goto done; + } + + /* Initialize the invalid values so that the correct values + * below are downloaded to firmware */ + woal_set_sys_config_invalid_data(sys_cfg); + sys_cfg->wep_cfg.key0.key_index = 0; + sys_cfg->wep_cfg.key1.key_index = 1; + sys_cfg->wep_cfg.key2.key_index = 2; + sys_cfg->wep_cfg.key3.key_index = 3; + + if (key_index >= 0 && key_index <= 3) { + if (key_index == 0) + pkey = &sys_cfg->wep_cfg.key0; + else if (key_index == 1) + pkey = &sys_cfg->wep_cfg.key1; + else if (key_index == 2) + pkey = &sys_cfg->wep_cfg.key2; + else if (key_index == 3) + pkey = &sys_cfg->wep_cfg.key3; + } + + if (!(dwrq->flags & IW_ENCODE_NOKEY) && dwrq->length) { + if (dwrq->length > MAX_WEP_KEY_SIZE) { + PRINTM(MERROR, "Key length (%d) out of range\n", + dwrq->length); + ret = -E2BIG; + goto done; + } + if (key_index < 0) { + /* Get current default key index */ + if (ap_cfg->wep_cfg.key0.is_default) + pkey = &sys_cfg->wep_cfg.key0; + if (ap_cfg->wep_cfg.key1.is_default) + pkey = &sys_cfg->wep_cfg.key1; + if (ap_cfg->wep_cfg.key2.is_default) + pkey = &sys_cfg->wep_cfg.key2; + if (ap_cfg->wep_cfg.key3.is_default) + pkey = &sys_cfg->wep_cfg.key3; + else { /* Something wrong, select first key as default */ + PRINTM(MERROR, + "No default key set! Selecting first key.\n"); + pkey = &sys_cfg->wep_cfg.key0; + } + } + + sys_cfg->protocol = PROTOCOL_STATIC_WEP; + if (extra) + memcpy(pkey->key, extra, dwrq->length); + /* Set the length */ + if (dwrq->length > MIN_WEP_KEY_SIZE) + pkey->length = MAX_WEP_KEY_SIZE; + else + pkey->length = MIN_WEP_KEY_SIZE; + /* Set current key index as default */ + pkey->is_default = MTRUE; + } else { + /* + * No key provided so it is either enable key, + * on or off + */ + if (dwrq->flags & IW_ENCODE_DISABLED) { + PRINTM(MINFO, "*** iwconfig mlanX key off ***\n"); + sys_cfg->protocol = PROTOCOL_NO_SECURITY; + } else { + /* + * iwconfig mlanX key [n] + * iwconfig mlanX key on + * Do we want to just set the transmit key index ? + */ + if (key_index < 0) { + PRINTM(MINFO, + "*** iwconfig mlanX key on ***\n"); + } else { + /* Get current key configuration at key_index */ + if (key_index == 0) + memcpy(pkey, &ap_cfg->wep_cfg.key0, + sizeof(wep_key)); + if (key_index == 1) + memcpy(pkey, &ap_cfg->wep_cfg.key1, + sizeof(wep_key)); + if (key_index == 2) + memcpy(pkey, &ap_cfg->wep_cfg.key2, + sizeof(wep_key)); + if (key_index == 3) + memcpy(pkey, &ap_cfg->wep_cfg.key3, + sizeof(wep_key)); + /* Set current key index as default */ + pkey->is_default = MTRUE; + } + } + } + if (dwrq->flags & (IW_ENCODE_RESTRICTED | IW_ENCODE_OPEN)) { + switch (dwrq->flags & 0xf000) { + case IW_ENCODE_RESTRICTED: + /* iwconfig mlanX restricted key [1] */ + sys_cfg->auth_mode = MLAN_AUTH_MODE_SHARED; + PRINTM(MINFO, "Auth mode restricted!\n"); + break; + case IW_ENCODE_OPEN: + /* iwconfig mlanX key [2] open */ + sys_cfg->auth_mode = MLAN_AUTH_MODE_OPEN; + PRINTM(MINFO, "Auth mode open!\n"); + break; + case IW_ENCODE_RESTRICTED | IW_ENCODE_OPEN: + default: + /* iwconfig mlanX key [2] open restricted */ + PRINTM(MINFO, "Auth mode auto!\n"); + break; + } + } + + if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv, + MLAN_ACT_SET, + MOAL_IOCTL_WAIT, + sys_cfg)) { + PRINTM(MERROR, "Error setting AP confiruration\n"); + ret = -EFAULT; + goto done; + } + +done: + kfree(sys_cfg); + kfree(ap_cfg); + LEAVE(); + return ret; +} + +/** + * @brief Get encryption key + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param dwrq A pointer to iw_point structure + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +static int +woal_get_encode(struct net_device *dev, struct iw_request_info *info, + struct iw_point *dwrq, char *extra) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + int index = (dwrq->flags & IW_ENCODE_INDEX); + wep_key *pkey = NULL; + mlan_uap_bss_param *ap_cfg = NULL; + int ret = 0; + + ENTER(); + if (index < 0 || index > 4) { + PRINTM(MERROR, "Key index #%d out of range\n", index); + ret = -EINVAL; + goto done; + } + ap_cfg = kzalloc(sizeof(mlan_uap_bss_param), GFP_ATOMIC); + if (!ap_cfg) { + PRINTM(MERROR, "Fail to alloc memory for mlan_uap_bss_param\n"); + ret = -EFAULT; + goto done; + } + + if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv, + MLAN_ACT_GET, + MOAL_IOCTL_WAIT, + ap_cfg)) { + PRINTM(MERROR, "Error getting AP confiruration\n"); + ret = -EFAULT; + goto done; + } + + dwrq->flags = 0; + /* + * Check encryption mode + */ + switch (ap_cfg->auth_mode) { + case MLAN_AUTH_MODE_OPEN: + dwrq->flags = IW_ENCODE_OPEN; + break; + case MLAN_AUTH_MODE_SHARED: + case MLAN_AUTH_MODE_NETWORKEAP: + dwrq->flags = IW_ENCODE_RESTRICTED; + break; + default: + dwrq->flags = IW_ENCODE_DISABLED | IW_ENCODE_OPEN; + break; + } + + switch (ap_cfg->protocol) { + case PROTOCOL_NO_SECURITY: + dwrq->flags |= IW_ENCODE_DISABLED; + break; + case PROTOCOL_STATIC_WEP: + if (ap_cfg->wep_cfg.key0.is_default) + pkey = &ap_cfg->wep_cfg.key0; + else if (ap_cfg->wep_cfg.key1.is_default) + pkey = &ap_cfg->wep_cfg.key1; + else if (ap_cfg->wep_cfg.key2.is_default) + pkey = &ap_cfg->wep_cfg.key2; + else if (ap_cfg->wep_cfg.key3.is_default) + pkey = &ap_cfg->wep_cfg.key3; + if (pkey) { + dwrq->flags |= (pkey->key_index + 1); + dwrq->length = pkey->length; + memcpy(extra, pkey->key, pkey->length); + dwrq->flags &= ~IW_ENCODE_DISABLED; + } else { + ret = -EFAULT; + } + break; + case PROTOCOL_WPA: + case PROTOCOL_WPA2: + case PROTOCOL_WPA2_MIXED: + memcpy(extra, ap_cfg->wpa_cfg.passphrase, + ap_cfg->wpa_cfg.length); + dwrq->length = ap_cfg->wpa_cfg.length; + dwrq->flags |= 1; + dwrq->flags &= ~IW_ENCODE_DISABLED; + break; + default: + dwrq->flags &= ~IW_ENCODE_DISABLED; + break; + } + dwrq->flags |= IW_ENCODE_NOKEY; + +done: + kfree(ap_cfg); + LEAVE(); + return ret; +} + +#if (WIRELESS_EXT >= 18) +/** + * @brief Get IE + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param dwrq A pointer to iw_point structure + * @param extra A pointer to extra data buf + * + * @return -EOPNOTSUPP + */ +static int +woal_get_gen_ie(struct net_device *dev, struct iw_request_info *info, + struct iw_point *dwrq, char *extra) +{ + ENTER(); + LEAVE(); + return -EOPNOTSUPP; +} + +/** + * @brief Set IE + * + * Pass an opaque block of data, expected to be IEEE IEs, to the driver + * for eventual passthrough to the firmware in an associate/join + * (and potentially start) command. + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param dwrq A pointer to iw_point structure + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +static int +woal_set_gen_ie(struct net_device *dev, struct iw_request_info *info, + struct iw_point *dwrq, char *extra) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + mlan_uap_bss_param *sys_cfg = NULL; + IEEEtypes_Header_t *tlv = NULL; + int tlv_hdr_len = sizeof(IEEEtypes_Header_t), tlv_buf_left = 0; + int ret = 0; + + ENTER(); + + sys_cfg = kzalloc(sizeof(mlan_uap_bss_param), GFP_ATOMIC); + if (!sys_cfg) { + PRINTM(MERROR, "Fail to alloc memory for mlan_uap_bss_param\n"); + LEAVE(); + return -EFAULT; + } + /* Initialize the invalid values so that the correct values + * below are downloaded to firmware */ + woal_set_sys_config_invalid_data(sys_cfg); + + tlv_buf_left = dwrq->length; + tlv = (IEEEtypes_Header_t *)extra; + while (tlv_buf_left >= tlv_hdr_len) { + if (tlv->element_id == WPA_IE) { + sys_cfg->protocol |= PROTOCOL_WPA; + if (priv->pairwise_cipher == CIPHER_TKIP) { + sys_cfg->wpa_cfg.pairwise_cipher_wpa = + CIPHER_TKIP; + PRINTM(MINFO, "Set IE Cipher TKIP\n"); + } + if (priv->pairwise_cipher == CIPHER_AES_CCMP) { + sys_cfg->wpa_cfg.pairwise_cipher_wpa = + CIPHER_AES_CCMP; + PRINTM(MINFO, "Set IE Cipher CCMP\n"); + } + if (priv->pairwise_cipher == + (CIPHER_TKIP | CIPHER_AES_CCMP)) { + sys_cfg->wpa_cfg.pairwise_cipher_wpa = + CIPHER_TKIP | CIPHER_AES_CCMP; + PRINTM(MINFO, "Set IE Cipher TKIP + CCMP\n"); + } + memcpy(priv->bcn_ie_buf + priv->bcn_ie_len, + ((t_u8 *)tlv), + sizeof(IEEEtypes_Header_t) + tlv->len); + priv->bcn_ie_len += + sizeof(IEEEtypes_Header_t) + tlv->len; + } + if (tlv->element_id == RSN_IE) { + sys_cfg->protocol |= PROTOCOL_WPA2; + if (priv->pairwise_cipher == CIPHER_TKIP) { + sys_cfg->wpa_cfg.pairwise_cipher_wpa2 = + CIPHER_TKIP; + } + if (priv->pairwise_cipher == CIPHER_AES_CCMP) { + sys_cfg->wpa_cfg.pairwise_cipher_wpa2 = + CIPHER_AES_CCMP; + } + if (priv->pairwise_cipher == + (CIPHER_TKIP | CIPHER_AES_CCMP)) { + sys_cfg->wpa_cfg.pairwise_cipher_wpa2 = + (CIPHER_TKIP | CIPHER_AES_CCMP); + } + memcpy(priv->bcn_ie_buf + priv->bcn_ie_len, + ((t_u8 *)tlv), + sizeof(IEEEtypes_Header_t) + tlv->len); + priv->bcn_ie_len += + sizeof(IEEEtypes_Header_t) + tlv->len; + } + if (priv->group_cipher == CIPHER_TKIP) + sys_cfg->wpa_cfg.group_cipher = CIPHER_TKIP; + if (priv->group_cipher == CIPHER_AES_CCMP) + sys_cfg->wpa_cfg.group_cipher = CIPHER_AES_CCMP; + tlv_buf_left -= (tlv_hdr_len + tlv->len); + tlv = (IEEEtypes_Header_t *)((t_u8 *)tlv + tlv_hdr_len + + tlv->len); + } + sys_cfg->key_mgmt = priv->uap_key_mgmt; + if (sys_cfg->key_mgmt & KEY_MGMT_PSK) + sys_cfg->key_mgmt_operation |= 0x01; + if (sys_cfg->key_mgmt & KEY_MGMT_EAP) + sys_cfg->key_mgmt_operation |= 0x03; + + if (sys_cfg->protocol) { + if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv, + MLAN_ACT_SET, + MOAL_IOCTL_WAIT, + sys_cfg)) { + PRINTM(MERROR, "Error setting AP configuration\n"); + ret = -EFAULT; + goto done; + } + priv->pairwise_cipher = 0; + priv->group_cipher = 0; + + /* custom IE command to set priv->bcn_ie_buf */ + if (MLAN_STATUS_SUCCESS != +#define UAP_RSN_MASK (BIT(8) | BIT(5) | BIT(1) | BIT(3)) + woal_set_get_custom_ie(priv, UAP_RSN_MASK, priv->bcn_ie_buf, + priv->bcn_ie_len)) { + PRINTM(MERROR, "Error setting wpa-rsn IE\n"); + ret = -EFAULT; + } + } else if (dwrq->length == 0) { + /* custom IE command to re-set priv->bcn_ie_buf */ + if (MLAN_STATUS_SUCCESS != + woal_set_get_custom_ie(priv, 0, priv->bcn_ie_buf, + priv->bcn_ie_len)) { + PRINTM(MERROR, "Error resetting wpa-rsn IE\n"); + ret = -EFAULT; + } + priv->bcn_ie_len = 0; + } + +done: + kfree(sys_cfg); + LEAVE(); + return ret; +} + +/** + * @brief Extended version of encoding configuration + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param dwrq A pointer to iw_point structure + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +static int +woal_set_encode_ext(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, char *extra) +{ + struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; + moal_private *priv = (moal_private *)netdev_priv(dev); + int key_index; + t_u8 *pkey_material = NULL; + mlan_ioctl_req *req = NULL; + mlan_ds_sec_cfg *sec = NULL; + mlan_uap_bss_param *sys_cfg = NULL; + wep_key *pwep_key = NULL; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + key_index = (dwrq->flags & IW_ENCODE_INDEX) - 1; + if (key_index < 0 || key_index > 5) { + ret = -EINVAL; + goto done; + } + if (ext->key_len > (dwrq->length - sizeof(struct iw_encode_ext))) { + ret = -EINVAL; + goto done; + } + sys_cfg = kzalloc(sizeof(mlan_uap_bss_param), GFP_ATOMIC); + if (!sys_cfg) { + PRINTM(MERROR, "Fail to alloc memory for mlan_uap_bss_param\n"); + ret = -EFAULT; + goto done; + } + + /* Initialize the invalid values so that the correct values + * below are downloaded to firmware */ + woal_set_sys_config_invalid_data(sys_cfg); + + pkey_material = (t_u8 *)(ext + 1); + /* Disable Key */ + if ((dwrq->flags & IW_ENCODE_DISABLED) && !ext->key_len) { + sys_cfg->protocol = PROTOCOL_NO_SECURITY; + } else if (ext->alg == IW_ENCODE_ALG_WEP) { + sys_cfg->protocol = PROTOCOL_STATIC_WEP; + /* Set WEP key */ + switch (key_index) { + case 0: + pwep_key = &sys_cfg->wep_cfg.key0; + break; + case 1: + pwep_key = &sys_cfg->wep_cfg.key1; + break; + case 2: + pwep_key = &sys_cfg->wep_cfg.key2; + break; + case 3: + pwep_key = &sys_cfg->wep_cfg.key3; + break; + } + if (pwep_key) { + pwep_key->key_index = key_index; + pwep_key->is_default = MTRUE; + pwep_key->length = ext->key_len; + memcpy(pwep_key->key, pkey_material, ext->key_len); + } + } else { + /* Set GTK/PTK key */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + sec = (mlan_ds_sec_cfg *)req->pbuf; + sec->sub_command = MLAN_OID_SEC_CFG_ENCRYPT_KEY; + req->req_id = MLAN_IOCTL_SEC_CFG; + req->action = MLAN_ACT_SET; + sec->param.encrypt_key.key_len = ext->key_len; + sec->param.encrypt_key.key_index = key_index; + memcpy(sec->param.encrypt_key.key_material, pkey_material, + ext->key_len); + memcpy(sec->param.encrypt_key.mac_addr, ext->addr.sa_data, + ETH_ALEN); + sec->param.encrypt_key.key_flags = ext->ext_flags; + if (ext->ext_flags & IW_ENCODE_EXT_RX_SEQ_VALID) { + memcpy(sec->param.encrypt_key.pn, (t_u8 *)ext->rx_seq, + SEQ_MAX_SIZE); + DBG_HEXDUMP(MCMD_D, "Uap Rx PN", + sec->param.encrypt_key.pn, SEQ_MAX_SIZE); + } + if (ext->ext_flags & IW_ENCODE_EXT_TX_SEQ_VALID) { + memcpy(sec->param.encrypt_key.pn, (t_u8 *)ext->tx_seq, + SEQ_MAX_SIZE); + DBG_HEXDUMP(MCMD_D, "Uap Tx PN", + sec->param.encrypt_key.pn, SEQ_MAX_SIZE); + } + PRINTM(MIOCTL, + "set uap wpa key key_index=%d, key_len=%d key_flags=0x%x " + MACSTR "\n", key_index, ext->key_len, + sec->param.encrypt_key.key_flags, + MAC2STR(sec->param.encrypt_key.mac_addr)); + DBG_HEXDUMP(MCMD_D, "uap wpa key", pkey_material, ext->key_len); +#define IW_ENCODE_ALG_AES_CMAC 5 + + if (ext->alg == IW_ENCODE_ALG_AES_CMAC) + sec->param.encrypt_key.key_flags |= + KEY_FLAG_AES_MCAST_IGTK; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) + ret = -EFAULT; + /* Cipher set will be done in set generic IE */ + priv->pairwise_cipher = ext->alg; + priv->group_cipher = ext->alg; + goto done; /* No AP configuration */ + } + if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv, + MLAN_ACT_SET, + MOAL_IOCTL_WAIT, + sys_cfg)) { + PRINTM(MERROR, "Error setting AP confiruration\n"); + ret = -EFAULT; + goto done; + } + +done: + kfree(sys_cfg); + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Extended version of encoding configuration + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param dwrq A pointer to iw_point structure + * @param extra A pointer to extra data buf + * + * @return -EOPNOTSUPP + */ +static int +woal_get_encode_ext(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, char *extra) +{ + ENTER(); + LEAVE(); + return -EOPNOTSUPP; +} + +/** + * @brief Request MLME operation + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param dwrq A pointer to iw_point structure + * @param extra A pointer to extra data buf + * + * @return 0--success, otherwise fail + */ +static int +woal_set_mlme(struct net_device *dev, + struct iw_request_info *info, struct iw_point *dwrq, char *extra) +{ + struct iw_mlme *mlme = (struct iw_mlme *)extra; + moal_private *priv = (moal_private *)netdev_priv(dev); + mlan_ds_bss *bss = NULL; + mlan_ds_get_info *pinfo = NULL; + mlan_ioctl_req *req = NULL; + mlan_ds_sta_list *sta_list = NULL; + const t_u8 bc_addr[] = { 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF }; + t_u8 sta_addr[ETH_ALEN]; + int ret = 0, i; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + memset(sta_addr, 0, ETH_ALEN); + if ((mlme->cmd == IW_MLME_DEAUTH) || (mlme->cmd == IW_MLME_DISASSOC)) { + memcpy(sta_addr, (t_u8 *)mlme->addr.sa_data, ETH_ALEN); + PRINTM(MIOCTL, + "Deauth station: " MACSTR ", reason=%d\n", + MAC2STR(sta_addr), mlme->reason_code); + + /* FIXME: For flushing all stations we need to use zero MAC, + * but right now the FW does not support this. So, manually + * delete each one individually. + */ + /* If deauth all station, get the connected STA list first */ + if (!memcmp(bc_addr, sta_addr, ETH_ALEN)) { + PRINTM(MIOCTL, "Deauth all stations\n"); + req = woal_alloc_mlan_ioctl_req(sizeof + (mlan_ds_get_info)); + if (req == NULL) { + LEAVE(); + return -ENOMEM; + } + pinfo = (mlan_ds_get_info *)req->pbuf; + pinfo->sub_command = MLAN_OID_UAP_STA_LIST; + req->req_id = MLAN_IOCTL_GET_INFO; + req->action = MLAN_ACT_GET; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + sta_list = + kmalloc(sizeof(mlan_ds_sta_list), GFP_KERNEL); + if (sta_list == NULL) { + PRINTM(MERROR, "Memory allocation failed!\n"); + ret = -ENOMEM; + goto done; + } + memcpy(sta_list, &pinfo->param.sta_list, + sizeof(mlan_ds_sta_list)); + kfree(req); + req = NULL; + } + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + bss = (mlan_ds_bss *)req->pbuf; + bss->sub_command = MLAN_OID_UAP_DEAUTH_STA; + req->req_id = MLAN_IOCTL_BSS; + req->action = MLAN_ACT_SET; + + if (!memcmp(bc_addr, sta_addr, ETH_ALEN)) { + for (i = 0; i < sta_list->sta_count; i++) { + memcpy(bss->param.deauth_param.mac_addr, + sta_list->info[i].mac_address, ETH_ALEN); + bss->param.deauth_param.reason_code = + mlme->reason_code; + + status = woal_request_ioctl(priv, req, + MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + } + } else { + memcpy(bss->param.deauth_param.mac_addr, sta_addr, + ETH_ALEN); + bss->param.deauth_param.reason_code = mlme->reason_code; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + } + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + kfree(sta_list); + LEAVE(); + return ret; +} + +/** @brief Set authentication mode parameters + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param vwrq A pointer to iw_param structure + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +static int +woal_set_auth(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + int ret = 0; + moal_private *priv = (moal_private *)netdev_priv(dev); + mlan_uap_bss_param *sys_cfg = NULL; + + ENTER(); + + sys_cfg = kzalloc(sizeof(mlan_uap_bss_param), GFP_ATOMIC); + if (!sys_cfg) { + PRINTM(MERROR, "Fail to alloc memory for mlan_uap_bss_param\n"); + LEAVE(); + return -EFAULT; + } + /* Initialize the invalid values so that the correct values + * below are downloaded to firmware */ + woal_set_sys_config_invalid_data(sys_cfg); + + switch (vwrq->flags & IW_AUTH_INDEX) { + case IW_AUTH_CIPHER_PAIRWISE: + /* Rest are not supported now */ + if (vwrq->value & IW_AUTH_CIPHER_NONE) + /* XXX Do not delete no-operation line */ + ; + else if (vwrq->value & IW_AUTH_CIPHER_WEP40) + /* XXX Do not delete no-operation line */ + ; + else if (vwrq->value & IW_AUTH_CIPHER_WEP104) + /* XXX Do not delete no-operation line */ + ; + else if (vwrq->value == IW_AUTH_CIPHER_TKIP) { + sys_cfg->wpa_cfg.pairwise_cipher_wpa = CIPHER_TKIP; + sys_cfg->wpa_cfg.pairwise_cipher_wpa2 = CIPHER_TKIP; + priv->pairwise_cipher = CIPHER_TKIP; + if (!priv->uap_key_mgmt) + priv->uap_key_mgmt = KEY_MGMT_PSK; + PRINTM(MINFO, "Set Auth Cipher TKIP\n"); + } else if (vwrq->value == IW_AUTH_CIPHER_CCMP) { + sys_cfg->wpa_cfg.pairwise_cipher_wpa = CIPHER_AES_CCMP; + sys_cfg->wpa_cfg.pairwise_cipher_wpa2 = CIPHER_AES_CCMP; + priv->pairwise_cipher = CIPHER_AES_CCMP; + if (!priv->uap_key_mgmt) + priv->uap_key_mgmt = KEY_MGMT_PSK; + PRINTM(MINFO, "Set Auth Cipher CCMP\n"); + } else if (vwrq->value == + (IW_AUTH_CIPHER_TKIP | IW_AUTH_CIPHER_CCMP)) { + sys_cfg->wpa_cfg.pairwise_cipher_wpa = + (CIPHER_TKIP | CIPHER_AES_CCMP); + sys_cfg->wpa_cfg.pairwise_cipher_wpa2 = + (CIPHER_TKIP | CIPHER_AES_CCMP); + priv->pairwise_cipher = (CIPHER_TKIP | CIPHER_AES_CCMP); + if (!priv->uap_key_mgmt) + priv->uap_key_mgmt = KEY_MGMT_PSK; + PRINTM(MINFO, "Set Auth Cipher TKIP + CCMP\n"); + } + break; + case IW_AUTH_CIPHER_GROUP: + /* Rest are not supported now */ + if (vwrq->value & IW_AUTH_CIPHER_NONE) + /* XXX Do not delete no-operation line */ + ; + else if (vwrq->value & IW_AUTH_CIPHER_WEP40) + /* XXX Do not delete no-operation line */ + ; + else if (vwrq->value & IW_AUTH_CIPHER_WEP104) + /* XXX Do not delete no-operation line */ + ; + else if (vwrq->value & IW_AUTH_CIPHER_TKIP) { + sys_cfg->wpa_cfg.group_cipher = CIPHER_TKIP; + priv->group_cipher = CIPHER_TKIP; + if (!priv->uap_key_mgmt) + priv->uap_key_mgmt = KEY_MGMT_PSK; + PRINTM(MINFO, "Set Auth Cipher TKIP\n"); + } else if (vwrq->value & IW_AUTH_CIPHER_CCMP) { + sys_cfg->wpa_cfg.group_cipher = CIPHER_AES_CCMP; + priv->group_cipher = CIPHER_AES_CCMP; + if (!priv->uap_key_mgmt) + priv->uap_key_mgmt = KEY_MGMT_PSK; + PRINTM(MINFO, "Set Auth Cipher CCMP\n"); + } + break; + case IW_AUTH_80211_AUTH_ALG: + switch (vwrq->value) { + case IW_AUTH_ALG_SHARED_KEY: + PRINTM(MINFO, "Auth mode shared key!\n"); + sys_cfg->auth_mode = MLAN_AUTH_MODE_SHARED; + break; + case IW_AUTH_ALG_LEAP: + break; + case IW_AUTH_ALG_OPEN_SYSTEM: + PRINTM(MINFO, "Auth mode open!\n"); + sys_cfg->auth_mode = MLAN_AUTH_MODE_OPEN; + break; + default: + PRINTM(MINFO, "Auth mode auto!\n"); + break; + } + break; + case IW_AUTH_WPA_VERSION: + switch (vwrq->value) { + case IW_AUTH_WPA_VERSION_DISABLED: + sys_cfg->protocol = PROTOCOL_NO_SECURITY; + break; + case IW_AUTH_WPA_VERSION_WPA: + sys_cfg->protocol = PROTOCOL_WPA; + break; + case IW_AUTH_WPA_VERSION_WPA2: + sys_cfg->protocol = PROTOCOL_WPA2; + break; + case IW_AUTH_WPA_VERSION_WPA | IW_AUTH_WPA_VERSION_WPA2: + sys_cfg->protocol = PROTOCOL_WPA2_MIXED; + break; + default: + break; + } + priv->uap_protocol = sys_cfg->protocol; + break; + case IW_AUTH_KEY_MGMT: + switch (vwrq->value) { + case IW_AUTH_KEY_MGMT_802_1X: + sys_cfg->key_mgmt |= KEY_MGMT_EAP; + priv->uap_key_mgmt |= KEY_MGMT_EAP; + break; + case IW_AUTH_KEY_MGMT_PSK: + sys_cfg->key_mgmt |= KEY_MGMT_PSK; + priv->uap_key_mgmt |= KEY_MGMT_PSK; + break; + default: + break; + } + break; + case IW_AUTH_WPA_ENABLED: + case IW_AUTH_TKIP_COUNTERMEASURES: + case IW_AUTH_DROP_UNENCRYPTED: + case IW_AUTH_RX_UNENCRYPTED_EAPOL: + case IW_AUTH_ROAMING_CONTROL: + case IW_AUTH_PRIVACY_INVOKED: + default: + kfree(sys_cfg); + LEAVE(); + return -EOPNOTSUPP; /* No AP configuration */ + } + if (!sys_cfg->key_mgmt) + sys_cfg->key_mgmt = priv->uap_key_mgmt; + if (sys_cfg->key_mgmt & KEY_MGMT_PSK) + sys_cfg->key_mgmt_operation |= 0x01; + if (sys_cfg->key_mgmt & KEY_MGMT_EAP) + sys_cfg->key_mgmt_operation |= 0x03; + if (!sys_cfg->protocol) + sys_cfg->protocol = priv->uap_protocol; + + /* Set AP configuration */ + if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv, + MLAN_ACT_SET, + MOAL_IOCTL_WAIT, + sys_cfg)) { + PRINTM(MERROR, "Error setting AP confiruration\n"); + ret = -EFAULT; + goto done; + } + +done: + kfree(sys_cfg); + LEAVE(); + return 0; +} + +/** + * @brief Get authentication mode parameters + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param vwrq A pointer to iw_param structure + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +static int +woal_get_auth(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + mlan_uap_bss_param *ap_cfg = NULL; + + ENTER(); + + ap_cfg = kzalloc(sizeof(mlan_uap_bss_param), GFP_ATOMIC); + if (!ap_cfg) { + PRINTM(MERROR, "Fail to alloc memory for mlan_uap_bss_param\n"); + LEAVE(); + return -EFAULT; + } + + if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv, + MLAN_ACT_GET, + MOAL_IOCTL_WAIT, + ap_cfg)) { + PRINTM(MERROR, "Error getting AP confiruration\n"); + kfree(ap_cfg); + LEAVE(); + return -EFAULT; + } + switch (vwrq->flags & IW_AUTH_INDEX) { + case IW_AUTH_CIPHER_PAIRWISE: + if (ap_cfg->wpa_cfg.pairwise_cipher_wpa == CIPHER_TKIP || + ap_cfg->wpa_cfg.pairwise_cipher_wpa2 == CIPHER_TKIP) + vwrq->value = IW_AUTH_CIPHER_TKIP; + else if (ap_cfg->wpa_cfg.pairwise_cipher_wpa == CIPHER_AES_CCMP + || ap_cfg->wpa_cfg.pairwise_cipher_wpa2 == + CIPHER_AES_CCMP) + vwrq->value = IW_AUTH_CIPHER_CCMP; + else + vwrq->value = IW_AUTH_CIPHER_NONE; + break; + case IW_AUTH_CIPHER_GROUP: + if (ap_cfg->wpa_cfg.group_cipher == CIPHER_TKIP) + vwrq->value = IW_AUTH_CIPHER_TKIP; + else if (ap_cfg->wpa_cfg.group_cipher == CIPHER_AES_CCMP) + vwrq->value = IW_AUTH_CIPHER_CCMP; + else + vwrq->value = IW_AUTH_CIPHER_NONE; + break; + case IW_AUTH_80211_AUTH_ALG: + if (ap_cfg->auth_mode == MLAN_AUTH_MODE_SHARED) + vwrq->value = IW_AUTH_ALG_SHARED_KEY; + else if (ap_cfg->auth_mode == MLAN_AUTH_MODE_NETWORKEAP) + vwrq->value = IW_AUTH_ALG_LEAP; + else + vwrq->value = IW_AUTH_ALG_OPEN_SYSTEM; + break; + case IW_AUTH_WPA_ENABLED: + if (ap_cfg->protocol == PROTOCOL_WPA || + ap_cfg->protocol == PROTOCOL_WPA2 || + ap_cfg->protocol == PROTOCOL_WPA2_MIXED) + vwrq->value = 1; + else + vwrq->value = 0; + break; + case IW_AUTH_KEY_MGMT: + if (ap_cfg->key_mgmt & KEY_MGMT_EAP) + vwrq->value |= IW_AUTH_KEY_MGMT_802_1X; + if (ap_cfg->key_mgmt & KEY_MGMT_PSK) + vwrq->value |= IW_AUTH_KEY_MGMT_PSK; + break; + case IW_AUTH_WPA_VERSION: + case IW_AUTH_TKIP_COUNTERMEASURES: + case IW_AUTH_DROP_UNENCRYPTED: + case IW_AUTH_RX_UNENCRYPTED_EAPOL: + case IW_AUTH_ROAMING_CONTROL: + case IW_AUTH_PRIVACY_INVOKED: + default: + kfree(ap_cfg); + LEAVE(); + return -EOPNOTSUPP; + } + kfree(ap_cfg); + LEAVE(); + return 0; +} +#endif /* WE >= 18 */ + +/* Data rate listing + * MULTI_BANDS: + * abg a b b/g + * Infra G(12) A(8) B(4) G(12) + * Adhoc A+B(12) A(8) B(4) B(4) + * non-MULTI_BANDS: + b b/g + * Infra B(4) G(12) + * Adhoc B(4) B(4) + */ +/** + * @brief Get Range Info + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param dwrq A pointer to iw_point structure + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +static int +woal_get_range(struct net_device *dev, struct iw_request_info *info, + struct iw_point *dwrq, char *extra) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + mlan_uap_bss_param *ap_cfg = NULL; + struct iw_range *range = (struct iw_range *)extra; + t_u8 band = 0; + int i; + + ENTER(); + + ap_cfg = kzalloc(sizeof(mlan_uap_bss_param), GFP_ATOMIC); + if (!ap_cfg) { + PRINTM(MERROR, "Fail to alloc memory for mlan_uap_bss_param\n"); + LEAVE(); + return -EFAULT; + } + if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv, + MLAN_ACT_GET, + MOAL_IOCTL_WAIT, + ap_cfg)) { + PRINTM(MERROR, "Error getting AP confiruration\n"); + kfree(ap_cfg); + LEAVE(); + return -EFAULT; + } + dwrq->length = sizeof(struct iw_range); + memset(range, 0, sizeof(struct iw_range)); + + range->min_nwid = 0; + range->max_nwid = 0; + + range->num_bitrates = MAX_DATA_RATES; + for (i = 0; i < MIN(MAX_DATA_RATES, IW_MAX_BITRATES) && + ap_cfg->rates[i]; i++) { + range->bitrate[i] = (ap_cfg->rates[i] & 0x7f) * 500000; + } + range->num_bitrates = i; + PRINTM(MINFO, "IW_MAX_BITRATES=%d num_bitrates=%d\n", + IW_MAX_BITRATES, range->num_bitrates); + + range->num_frequency = MIN(ap_cfg->num_of_chan, IW_MAX_FREQUENCIES); + + for (i = 0; i < range->num_frequency; i++) { + range->freq[i].i = (long)ap_cfg->chan_list[i].chan_number; + band = (ap_cfg->chan_list[i].bandcfg.chanBand == BAND_5GHZ); + range->freq[i].m = + (long)channel_to_frequency(ap_cfg->chan_list[i]. + chan_number, band) * 100000; + range->freq[i].e = 1; + } + + PRINTM(MINFO, "IW_MAX_FREQUENCIES=%d num_frequency=%d\n", + IW_MAX_FREQUENCIES, range->num_frequency); + + range->num_channels = range->num_frequency; + + woal_sort_channels(&range->freq[0], range->num_frequency); + + /* + * Set an indication of the max TCP throughput in bit/s that we can + * expect using this interface + */ + if (i > 2) + range->throughput = 5000 * 1000; + else + range->throughput = 1500 * 1000; + + range->min_rts = MLAN_RTS_MIN_VALUE; + range->max_rts = MLAN_RTS_MAX_VALUE; + range->min_frag = MLAN_FRAG_MIN_VALUE; + range->max_frag = MLAN_FRAG_MAX_VALUE; + + range->encoding_size[0] = 5; + range->encoding_size[1] = 13; + range->num_encoding_sizes = 2; + range->max_encoding_tokens = 4; + +/** Minimum power period */ +#define IW_POWER_PERIOD_MIN 1000000 /* 1 sec */ +/** Maximum power period */ +#define IW_POWER_PERIOD_MAX 120000000 /* 2 min */ +/** Minimum power timeout value */ +#define IW_POWER_TIMEOUT_MIN 1000 /* 1 ms */ +/** Maximim power timeout value */ +#define IW_POWER_TIMEOUT_MAX 1000000 /* 1 sec */ + + /* Power Management duration & timeout */ + range->min_pmp = IW_POWER_PERIOD_MIN; + range->max_pmp = IW_POWER_PERIOD_MAX; + range->min_pmt = IW_POWER_TIMEOUT_MIN; + range->max_pmt = IW_POWER_TIMEOUT_MAX; + range->pmp_flags = IW_POWER_PERIOD; + range->pmt_flags = IW_POWER_TIMEOUT; + range->pm_capa = IW_POWER_PERIOD | IW_POWER_TIMEOUT | IW_POWER_ALL_R; + + /* + * Minimum version we recommend + */ + range->we_version_source = 15; + + /* + * Version we are compiled with + */ + range->we_version_compiled = WIRELESS_EXT; + + range->retry_capa = IW_RETRY_LIMIT; + range->retry_flags = IW_RETRY_LIMIT | IW_RETRY_MAX; + + range->min_retry = MLAN_TX_RETRY_MIN; + range->max_retry = MLAN_TX_RETRY_MAX; + +#if (WIRELESS_EXT >= 18) + if (ap_cfg->protocol & PROTOCOL_WPA) + range->enc_capa |= IW_ENC_CAPA_WPA; + if (ap_cfg->protocol & PROTOCOL_WPA2) + range->enc_capa |= IW_ENC_CAPA_WPA2; + if (ap_cfg->wpa_cfg.pairwise_cipher_wpa == CIPHER_TKIP || + ap_cfg->wpa_cfg.pairwise_cipher_wpa2 == CIPHER_TKIP || + ap_cfg->wpa_cfg.group_cipher == CIPHER_TKIP) + range->enc_capa |= IW_ENC_CAPA_CIPHER_TKIP; + if (ap_cfg->wpa_cfg.pairwise_cipher_wpa == CIPHER_AES_CCMP || + ap_cfg->wpa_cfg.pairwise_cipher_wpa2 == CIPHER_AES_CCMP || + ap_cfg->wpa_cfg.group_cipher == CIPHER_AES_CCMP) + range->enc_capa |= IW_ENC_CAPA_CIPHER_CCMP; +#endif + kfree(ap_cfg); + LEAVE(); + return 0; +} + +/** + * @brief Set priv command + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param dwrq A pointer to iw_point structure + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +static int +woal_set_priv(struct net_device *dev, struct iw_request_info *info, + struct iw_point *dwrq, char *extra) +{ + ENTER(); + LEAVE(); + return -EOPNOTSUPP; +} + +/** + * @brief Set essid + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param dwrq A pointer to iw_point structure + * @param extra A pointer to extra data buf + * + * @return 0--success, otherwise fail + */ +static int +woal_set_essid(struct net_device *dev, struct iw_request_info *info, + struct iw_point *dwrq, char *extra) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + mlan_uap_bss_param *sys_cfg = NULL; + int ret = 0; + + ENTER(); + + /* Check the size of the string */ + if (dwrq->length > IW_ESSID_MAX_SIZE + 1) { + ret = -E2BIG; + goto done; + } + sys_cfg = kzalloc(sizeof(mlan_uap_bss_param), GFP_ATOMIC); + if (!sys_cfg) { + PRINTM(MERROR, "Fail to alloc memory for mlan_uap_bss_param\n"); + ret = -EFAULT; + goto done; + } + + /* Initialize the invalid values so that the correct values + * below are downloaded to firmware */ + woal_set_sys_config_invalid_data(sys_cfg); + + /* Set the SSID */ +#if WIRELESS_EXT > 20 + sys_cfg->ssid.ssid_len = dwrq->length; +#else + sys_cfg->ssid.ssid_len = dwrq->length - 1; +#endif + + memcpy(sys_cfg->ssid.ssid, extra, + MIN(sys_cfg->ssid.ssid_len, MLAN_MAX_SSID_LENGTH)); + if (!sys_cfg->ssid.ssid_len || sys_cfg->ssid.ssid[0] < 0x20) { + PRINTM(MERROR, "Invalid SSID - aborting set_essid\n"); + ret = -EINVAL; + goto done; + } + PRINTM(MINFO, "Requested new SSID = %s\n", + (sys_cfg->ssid.ssid_len > 0) ? + (char *)sys_cfg->ssid.ssid : "NULL"); + + /* Set AP configuration */ + if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv, + MLAN_ACT_SET, + MOAL_IOCTL_WAIT, + sys_cfg)) { + PRINTM(MERROR, "Error setting AP confiruration\n"); + ret = -EFAULT; + goto done; + } + +done: + kfree(sys_cfg); + LEAVE(); + return ret; +} + +/** + * @brief Get current essid + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param dwrq A pointer to iw_point structure + * @param extra A pointer to extra data buf + * + * @return 0--success, otherwise fail + */ +static int +woal_get_essid(struct net_device *dev, struct iw_request_info *info, + struct iw_point *dwrq, char *extra) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + mlan_uap_bss_param *ap_cfg = NULL; + + ENTER(); + + ap_cfg = kzalloc(sizeof(mlan_uap_bss_param), GFP_ATOMIC); + if (!ap_cfg) { + PRINTM(MERROR, "Fail to alloc memory for mlan_uap_bss_param\n"); + return -EFAULT; + } + + if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv, + MLAN_ACT_GET, + MOAL_IOCTL_WAIT, + ap_cfg)) { + PRINTM(MERROR, "Error getting AP confiruration\n"); + kfree(ap_cfg); + LEAVE(); + return -EFAULT; + } + + if (priv->bss_started) { + dwrq->length = MIN(dwrq->length, ap_cfg->ssid.ssid_len); + memcpy(extra, ap_cfg->ssid.ssid, dwrq->length); + } else + dwrq->length = 0; + + dwrq->flags = 1; + kfree(ap_cfg); + LEAVE(); + return 0; +} + +/** + * iwconfig settable callbacks + */ +static const iw_handler woal_handler[] = { + (iw_handler) woal_config_commit, /* SIOCSIWCOMMIT */ + (iw_handler) woal_get_name, /* SIOCGIWNAME */ + (iw_handler) NULL, /* SIOCSIWNWID */ + (iw_handler) NULL, /* SIOCGIWNWID */ + (iw_handler) woal_set_freq, /* SIOCSIWFREQ */ + (iw_handler) woal_get_freq, /* SIOCGIWFREQ */ + (iw_handler) woal_set_bss_mode, /* SIOCSIWMODE */ + (iw_handler) woal_get_bss_mode, /* SIOCGIWMODE */ + (iw_handler) NULL, /* SIOCSIWSENS */ + (iw_handler) NULL, /* SIOCGIWSENS */ + (iw_handler) NULL, /* SIOCSIWRANGE */ + (iw_handler) woal_get_range, /* SIOCGIWRANGE */ + (iw_handler) woal_set_priv, /* SIOCSIWPRIV */ + (iw_handler) NULL, /* SIOCGIWPRIV */ + (iw_handler) NULL, /* SIOCSIWSTATS */ + (iw_handler) NULL, /* SIOCGIWSTATS */ +#if WIRELESS_EXT > 15 +#ifdef CONFIG_WEXT_SPY + iw_handler_set_spy, /* SIOCSIWSPY */ + iw_handler_get_spy, /* SIOCGIWSPY */ + iw_handler_set_thrspy, /* SIOCSIWTHRSPY */ + iw_handler_get_thrspy, /* SIOCGIWTHRSPY */ +#else + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) NULL, /* -- hole -- */ +#endif +#else /* WIRELESS_EXT > 15 */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) NULL, /* -- hole -- */ +#endif /* WIRELESS_EXT > 15 */ + (iw_handler) woal_set_wap, /* SIOCSIWAP */ + (iw_handler) woal_get_wap, /* SIOCGIWAP */ +#if WIRELESS_EXT >= 18 + (iw_handler) woal_set_mlme, /* SIOCSIWMLME */ +#else + (iw_handler) NULL, /* -- hole -- */ +#endif + /* (iw_handler) wlan_get_aplist, *//* SIOCGIWAPLIST */ + NULL, /* SIOCGIWAPLIST */ +#if WIRELESS_EXT > 13 + (iw_handler) NULL, /* SIOCSIWSCAN */ + (iw_handler) NULL, /* SIOCGIWSCAN */ +#else /* WIRELESS_EXT > 13 */ + (iw_handler) NULL, /* SIOCSIWSCAN */ + (iw_handler) NULL, /* SIOCGIWSCAN */ +#endif /* WIRELESS_EXT > 13 */ + (iw_handler) woal_set_essid, /* SIOCSIWESSID */ + (iw_handler) woal_get_essid, /* SIOCGIWESSID */ + (iw_handler) NULL, /* SIOCSIWNICKN */ + (iw_handler) NULL, /* SIOCGIWNICKN */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) NULL, /* SIOCSIWRATE */ + (iw_handler) NULL, /* SIOCGIWRATE */ + (iw_handler) NULL, /* SIOCSIWRTS */ + (iw_handler) NULL, /* SIOCGIWRTS */ + (iw_handler) NULL, /* SIOCSIWFRAG */ + (iw_handler) NULL, /* SIOCGIWFRAG */ + (iw_handler) NULL, /* SIOCSIWTXPOW */ + (iw_handler) NULL, /* SIOCGIWTXPOW */ + (iw_handler) NULL, /* SIOCSIWRETRY */ + (iw_handler) NULL, /* SIOCGIWRETRY */ + (iw_handler) woal_set_encode, /* SIOCSIWENCODE */ + (iw_handler) woal_get_encode, /* SIOCGIWENCODE */ + (iw_handler) NULL, /* SIOCSIWPOWER */ + (iw_handler) NULL, /* SIOCGIWPOWER */ +#if (WIRELESS_EXT >= 18) + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) woal_set_gen_ie, /* SIOCSIWGENIE */ + (iw_handler) woal_get_gen_ie, /* SIOCGIWGENIE */ + (iw_handler) woal_set_auth, /* SIOCSIWAUTH */ + (iw_handler) woal_get_auth, /* SIOCGIWAUTH */ + (iw_handler) woal_set_encode_ext, /* SIOCSIWENCODEEXT */ + (iw_handler) woal_get_encode_ext, /* SIOCGIWENCODEEXT */ +#endif /* WIRELESSS_EXT >= 18 */ +}; + +/** + * iwpriv settable callbacks + */ +static const iw_handler woal_private_handler[] = { + NULL, /* SIOCIWFIRSTPRIV */ +}; + +/******************************************************** + Global Functions +********************************************************/ + +/** wlan_handler_def */ +struct iw_handler_def woal_uap_handler_def = { +num_standard:ARRAY_SIZE(woal_handler), +num_private:ARRAY_SIZE(woal_private_handler), +num_private_args:ARRAY_SIZE(woal_uap_priv_args), +standard:(iw_handler *) woal_handler, +private:(iw_handler *) woal_private_handler, +private_args:(struct iw_priv_args *)woal_uap_priv_args, +#if WIRELESS_EXT > 20 +get_wireless_stats:woal_get_uap_wireless_stats, +#endif +}; + +/** + * @brief Get wireless statistics + * + * @param dev A pointer to net_device structure + * + * @return A pointer to iw_statistics buf + */ +struct iw_statistics * +woal_get_uap_wireless_stats(struct net_device *dev) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + t_u16 wait_option = MOAL_IOCTL_WAIT; + + ENTER(); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) + /* + * Since schedule() is not allowed from an atomic context + * such as when dev_base_lock for netdevices is acquired + * for reading/writing in kernel before this call, HostCmd + * is issued in non-blocking way in such contexts and + * blocking in other cases. + */ + if (in_atomic() || !write_can_lock(&dev_base_lock)) + wait_option = MOAL_NO_WAIT; +#endif + + priv->w_stats.qual.qual = 0; + priv->w_stats.qual.level = 0; + priv->w_stats.discard.code = 0; + priv->w_stats.status = IW_MODE_MASTER; + woal_uap_get_stats(priv, wait_option, NULL); + + LEAVE(); + return &priv->w_stats; +} diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_wext.c b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_wext.c new file mode 100644 index 000000000000..ddcd8c52192b --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_wext.c @@ -0,0 +1,3148 @@ +/** @file moal_wext.c + * + * @brief This file contains wireless extension standard ioctl functions + * + * Copyright (C) 2008-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +/************************************************************************ +Change log: + 10/21/2008: initial version +************************************************************************/ + +#include "moal_main.h" + +#ifdef STA_SUPPORT +/** Approximate amount of data needed to pass a scan result back to iwlist */ +#define MAX_SCAN_CELL_SIZE \ + (IW_EV_ADDR_LEN \ + + MLAN_MAX_SSID_LENGTH \ + + IW_EV_UINT_LEN \ + + IW_EV_FREQ_LEN \ + + IW_EV_QUAL_LEN \ + + MLAN_MAX_SSID_LENGTH \ + + IW_EV_PARAM_LEN \ + + 40) /* 40 for WPAIE */ +/** Macro for minimum size of scan buffer */ +#define MIN_ACCEPTED_GET_SCAN_BUF 8000 + +/******************************************************** + Global Variables +********************************************************/ +extern int hw_test; +/******************************************************** + Local Functions +********************************************************/ + +/** + * @brief Compare two SSIDs + * + * @param ssid1 A pointer to ssid to compare + * @param ssid2 A pointer to ssid to compare + * + * @return 0--ssid is same, otherwise is different + */ +static t_s32 +woal_ssid_cmp(mlan_802_11_ssid *ssid1, mlan_802_11_ssid *ssid2) +{ + ENTER(); + + if (!ssid1 || !ssid2) { + LEAVE(); + return -1; + } + if (ssid1->ssid_len != ssid2->ssid_len) { + LEAVE(); + return -1; + } + + LEAVE(); + return memcmp(ssid1->ssid, ssid2->ssid, ssid1->ssid_len); +} + +/** + * @brief Sort Channels + * + * @param freq A pointer to iw_freq structure + * @param num Number of Channels + * + * @return N/A + */ +static inline void +woal_sort_channels(struct iw_freq *freq, int num) +{ + int i, j; + struct iw_freq temp; + + for (i = 0; i < num; i++) + for (j = i + 1; j < num; j++) + if (freq[i].i > freq[j].i) { + temp.i = freq[i].i; + temp.m = freq[i].m; + + freq[i].i = freq[j].i; + freq[i].m = freq[j].m; + + freq[j].i = temp.i; + freq[j].m = temp.m; + } +} + +/** + * @brief Convert RSSI to quality + * + * @param rssi RSSI in dBm + * + * @return Quality of the link (0-5) + */ +static t_u8 +woal_rssi_to_quality(t_s16 rssi) +{ +/** Macro for RSSI range */ +#define MOAL_RSSI_NO_SIGNAL -90 +#define MOAL_RSSI_VERY_LOW -80 +#define MOAL_RSSI_LOW -70 +#define MOAL_RSSI_GOOD -60 +#define MOAL_RSSI_VERY_GOOD -50 +#define MOAL_RSSI_INVALID 0 + if (rssi <= MOAL_RSSI_NO_SIGNAL || rssi == MOAL_RSSI_INVALID) + return 0; + else if (rssi <= MOAL_RSSI_VERY_LOW) + return 1; + else if (rssi <= MOAL_RSSI_LOW) + return 2; + else if (rssi <= MOAL_RSSI_GOOD) + return 3; + else if (rssi <= MOAL_RSSI_VERY_GOOD) + return 4; + else + return 5; +} + +/** + * @brief Set Adapter Node Name + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param dwrq A pointer to iw_point structure + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +static int +woal_set_nick(struct net_device *dev, struct iw_request_info *info, + struct iw_point *dwrq, char *extra) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + ENTER(); + /* + * Check the size of the string + */ + if (dwrq->length > 16) { + LEAVE(); + return -E2BIG; + } + memset(priv->nick_name, 0, sizeof(priv->nick_name)); + memcpy(priv->nick_name, extra, dwrq->length); + LEAVE(); + return 0; +} + +/** + * @brief Get Adapter Node Name + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param dwrq A pointer to iw_point structure + * @param extra A pointer to extra data buf + * + * @return 0 --success + */ +static int +woal_get_nick(struct net_device *dev, struct iw_request_info *info, + struct iw_point *dwrq, char *extra) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + ENTER(); + /* + * Get the Nick Name saved + */ + strncpy(extra, (char *)priv->nick_name, 16); + extra[16] = '\0'; + /* + * If none, we may want to get the one that was set + */ + + /* + * Push it out ! + */ + dwrq->length = strlen(extra) + 1; + LEAVE(); + return 0; +} + +/** + * @brief Commit handler: called after a bunch of SET operations + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param cwrq A pointer to char buffer + * @param extra A pointer to extra data buf + * + * @return 0 --success + */ +static int +woal_config_commit(struct net_device *dev, + struct iw_request_info *info, char *cwrq, char *extra) +{ + ENTER(); + + LEAVE(); + return 0; +} + +/** + * @brief Get name + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param cwrq A pointer to char buffer + * @param extra A pointer to extra data buf + * + * @return 0 --success + */ +static int +woal_get_name(struct net_device *dev, struct iw_request_info *info, + char *cwrq, char *extra) +{ + ENTER(); + strcpy(cwrq, "IEEE 802.11-DS"); + LEAVE(); + return 0; +} + +/** + * @brief Set frequency + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param fwrq A pointer to iw_freq structure + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +static int +woal_set_freq(struct net_device *dev, struct iw_request_info *info, + struct iw_freq *fwrq, char *extra) +{ + int ret = 0; + moal_private *priv = (moal_private *)netdev_priv(dev); + mlan_ds_bss *bss = NULL; + mlan_ioctl_req *req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + bss = (mlan_ds_bss *)req->pbuf; + /* + * If setting by frequency, convert to a channel + */ + if (fwrq->e == 1) { + long f = fwrq->m / 100000; + bss->param.bss_chan.freq = f; + } else + bss->param.bss_chan.channel = fwrq->m; + + bss->sub_command = MLAN_OID_BSS_CHANNEL; + req->req_id = MLAN_IOCTL_BSS; + req->action = MLAN_ACT_SET; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + if (MLAN_STATUS_SUCCESS != + woal_change_adhoc_chan(priv, bss->param.bss_chan.channel, + MOAL_IOCTL_WAIT)) + ret = -EFAULT; + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Get frequency + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param fwrq A pointer to iw_freq structure + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +static int +woal_get_freq(struct net_device *dev, struct iw_request_info *info, + struct iw_freq *fwrq, char *extra) +{ + int ret = 0; + moal_private *priv = (moal_private *)netdev_priv(dev); + mlan_ds_bss *bss = NULL; + mlan_ioctl_req *req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + bss = (mlan_ds_bss *)req->pbuf; + bss->sub_command = MLAN_OID_BSS_CHANNEL; + req->req_id = MLAN_IOCTL_BSS; + req->action = MLAN_ACT_GET; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + fwrq->m = (long)bss->param.bss_chan.freq; + fwrq->i = (long)bss->param.bss_chan.channel; + fwrq->e = 6; + fwrq->flags = IW_FREQ_FIXED; +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set wlan mode + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param uwrq Wireless mode to set + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +static int +woal_set_bss_mode(struct net_device *dev, struct iw_request_info *info, + t_u32 *uwrq, char *extra) +{ + int ret = 0; + moal_private *priv = (moal_private *)netdev_priv(dev); + mlan_ds_bss *bss = NULL; + mlan_ioctl_req *req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + bss = (mlan_ds_bss *)req->pbuf; + bss->sub_command = MLAN_OID_BSS_MODE; + req->req_id = MLAN_IOCTL_BSS; + req->action = MLAN_ACT_SET; + + switch (*uwrq) { + case IW_MODE_INFRA: + bss->param.bss_mode = MLAN_BSS_MODE_INFRA; + break; + case IW_MODE_ADHOC: + bss->param.bss_mode = MLAN_BSS_MODE_IBSS; + break; + case IW_MODE_AUTO: + bss->param.bss_mode = MLAN_BSS_MODE_AUTO; + break; + default: + ret = -EINVAL; + break; + } + if (ret) + goto done; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Get current BSSID + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param awrq A pointer to sockaddr structure + * @param extra A pointer to extra data buf + * + * @return 0 --success + */ +static int +woal_get_wap(struct net_device *dev, struct iw_request_info *info, + struct sockaddr *awrq, char *extra) +{ + int ret = 0; + moal_private *priv = (moal_private *)netdev_priv(dev); + mlan_bss_info bss_info; + + ENTER(); + + memset(&bss_info, 0, sizeof(bss_info)); + + woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info); + + if (bss_info.media_connected == MTRUE) + memcpy(awrq->sa_data, &bss_info.bssid, MLAN_MAC_ADDR_LENGTH); + else + memset(awrq->sa_data, 0, MLAN_MAC_ADDR_LENGTH); + awrq->sa_family = ARPHRD_ETHER; + + LEAVE(); + return ret; +} + +/** + * @brief Connect to the AP or Ad-hoc Network with specific bssid + * + * NOTE: Scan should be issued by application before this function is called + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param awrq A pointer to sockaddr structure + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +static int +woal_set_wap(struct net_device *dev, struct iw_request_info *info, + struct sockaddr *awrq, char *extra) +{ + int ret = 0; + const t_u8 bcast[MLAN_MAC_ADDR_LENGTH] = { + 255, 255, 255, 255, 255, 255 + }; + const t_u8 zero_mac[MLAN_MAC_ADDR_LENGTH] = { + 0, 0, 0, 0, 0, 0 + }; + moal_private *priv = (moal_private *)netdev_priv(dev); + mlan_ssid_bssid ssid_bssid; + mlan_bss_info bss_info; + + ENTER(); + + if (awrq->sa_family != ARPHRD_ETHER) { + ret = -EINVAL; + goto done; + } + + PRINTM(MINFO, "ASSOC: WAP: sa_data: " MACSTR "\n", + MAC2STR((t_u8 *)awrq->sa_data)); + + if (MLAN_STATUS_SUCCESS != + woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info)) { + ret = -EFAULT; + goto done; + } +#ifdef REASSOCIATION + /* Cancel re-association */ + priv->reassoc_required = MFALSE; +#endif + + /* zero_mac means disconnect */ + if (!memcmp(zero_mac, awrq->sa_data, MLAN_MAC_ADDR_LENGTH)) { + woal_disconnect(priv, MOAL_IOCTL_WAIT, NULL, + DEF_DEAUTH_REASON_CODE); + goto done; + } + + /* Broadcast MAC means search for best network */ + memset(&ssid_bssid, 0, sizeof(mlan_ssid_bssid)); + + if (memcmp(bcast, awrq->sa_data, MLAN_MAC_ADDR_LENGTH)) { + /* Check if we are already assoicated to the AP */ + if (bss_info.media_connected == MTRUE) { + if (!memcmp(awrq->sa_data, &bss_info.bssid, ETH_ALEN)) + goto done; + } + memcpy(&ssid_bssid.bssid, awrq->sa_data, ETH_ALEN); + } + + if (MLAN_STATUS_SUCCESS != woal_find_best_network(priv, + MOAL_IOCTL_WAIT, + &ssid_bssid)) { + PRINTM(MERROR, + "ASSOC: WAP: MAC address not found in BSSID List\n"); + ret = -ENETUNREACH; + goto done; + } + /* Zero SSID implies use BSSID to connect */ + memset(&ssid_bssid.ssid, 0, sizeof(mlan_802_11_ssid)); + if (MLAN_STATUS_SUCCESS != woal_bss_start(priv, + MOAL_IOCTL_WAIT, + &ssid_bssid)) { + ret = -EFAULT; + goto done; + } +#ifdef REASSOCIATION + memset(&bss_info, 0, sizeof(bss_info)); + if (MLAN_STATUS_SUCCESS != woal_get_bss_info(priv, + MOAL_IOCTL_WAIT, + &bss_info)) { + ret = -EFAULT; + goto done; + } + memcpy(&priv->prev_ssid_bssid.ssid, &bss_info.ssid, + sizeof(mlan_802_11_ssid)); + memcpy(&priv->prev_ssid_bssid.bssid, &bss_info.bssid, + MLAN_MAC_ADDR_LENGTH); +#endif /* REASSOCIATION */ + +done: + + LEAVE(); + return ret; +} + +/** + * @brief Get wlan mode + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param uwrq A pointer to t_u32 string + * @param extra A pointer to extra data buf + * + * @return 0 --success + */ +static int +woal_get_bss_mode(struct net_device *dev, struct iw_request_info *info, + t_u32 *uwrq, char *extra) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + ENTER(); + *uwrq = woal_get_mode(priv, MOAL_IOCTL_WAIT); + LEAVE(); + return 0; +} + +/** + * @brief Set sensitivity + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param vwrq A pointer to iw_param structure + * @param extra A pointer to extra data buf + * + * @return 0 --success + */ +static int +woal_set_sens(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + int ret = 0; + + ENTER(); + + LEAVE(); + return ret; +} + +/** + * @brief Get sensitivity + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param vwrq A pointer to iw_param structure + * @param extra A pointer to extra data buf + * + * @return -1 + */ +static int +woal_get_sens(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + int ret = -1; + + ENTER(); + + LEAVE(); + return ret; +} + +/** + * @brief Set Tx power + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param vwrq A pointer to iw_param structure + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +static int +woal_set_txpow(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + int ret = 0; + moal_private *priv = (moal_private *)netdev_priv(dev); + mlan_power_cfg_t power_cfg; + + ENTER(); + if (vwrq->disabled) { + woal_set_radio(priv, 0); + goto done; + } + woal_set_radio(priv, 1); + + if (!vwrq->fixed) + power_cfg.is_power_auto = 1; + else { + power_cfg.is_power_auto = 0; + power_cfg.power_level = vwrq->value; + } + + if (MLAN_STATUS_SUCCESS != + woal_set_get_tx_power(priv, MLAN_ACT_SET, &power_cfg)) { + ret = -EFAULT; + goto done; + } + +done: + LEAVE(); + return ret; +} + +/** + * @brief Get Tx power + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param vwrq A pointer to iw_param structure + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +static int +woal_get_txpow(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + int ret = 0; + moal_private *priv = (moal_private *)netdev_priv(dev); + mlan_power_cfg_t power_cfg; + mlan_bss_info bss_info; + + ENTER(); + + memset(&power_cfg, 0, sizeof(mlan_power_cfg_t)); + memset(&bss_info, 0, sizeof(bss_info)); + woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info); + + if (MLAN_STATUS_SUCCESS != + woal_set_get_tx_power(priv, MLAN_ACT_GET, &power_cfg)) { + ret = -EFAULT; + goto done; + } + + vwrq->value = power_cfg.power_level; + if (power_cfg.is_power_auto) + vwrq->fixed = 0; + else + vwrq->fixed = 1; + if (bss_info.radio_on) { + vwrq->disabled = 0; + vwrq->flags = IW_TXPOW_DBM; + } else { + vwrq->disabled = 1; + } + +done: + LEAVE(); + return ret; +} + +/** + * @brief Set power management + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param vwrq A pointer to iw_param structure + * @param extra A pointer to extra data buf + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +static int +woal_set_power(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + int ret = 0, disabled; + moal_private *priv = (moal_private *)netdev_priv(dev); + + ENTER(); + + if (hw_test) { + PRINTM(MIOCTL, "block set power in hw_test mode\n"); + LEAVE(); + return ret; + } + disabled = vwrq->disabled; + + if (MLAN_STATUS_SUCCESS != woal_set_get_power_mgmt(priv, + MLAN_ACT_SET, + &disabled, + vwrq->flags, + MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + } + + LEAVE(); + return ret; +} + +/** + * @brief Get power management + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param vwrq A pointer to iw_param structure + * @param extra A pointer to extra data buf + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +static int +woal_get_power(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + int ret = 0, ps_mode; + moal_private *priv = (moal_private *)netdev_priv(dev); + + ENTER(); + + if (MLAN_STATUS_SUCCESS != woal_set_get_power_mgmt(priv, + MLAN_ACT_GET, + &ps_mode, 0, + MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + } + + if (ps_mode) + vwrq->disabled = 0; + else + vwrq->disabled = 1; + + vwrq->value = 0; + + LEAVE(); + return ret; +} + +/** + * @brief Set Tx retry count + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param vwrq A pointer to iw_param structure + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +static int +woal_set_retry(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + int ret = 0, retry_val = vwrq->value; + moal_private *priv = (moal_private *)netdev_priv(dev); + + ENTER(); + + if (vwrq->flags == IW_RETRY_LIMIT) { + /* + * The MAC has a 4-bit Total_Tx_Count register + * Total_Tx_Count = 1 + Tx_Retry_Count + */ + + if (MLAN_STATUS_SUCCESS != + woal_set_get_retry(priv, MLAN_ACT_SET, + MOAL_IOCTL_WAIT, &retry_val)) { + ret = -EFAULT; + goto done; + } + } else { + ret = -EOPNOTSUPP; + goto done; + } + +done: + LEAVE(); + return ret; +} + +/** + * @brief Get Tx retry count + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param vwrq A pointer to iw_param structure + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +static int +woal_get_retry(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + int retry_val, ret = 0; + moal_private *priv = (moal_private *)netdev_priv(dev); + + ENTER(); + + if (MLAN_STATUS_SUCCESS != + woal_set_get_retry(priv, MLAN_ACT_GET, + MOAL_IOCTL_WAIT, &retry_val)) { + ret = -EFAULT; + goto done; + } + + vwrq->disabled = 0; + if (!vwrq->flags) { + vwrq->flags = IW_RETRY_LIMIT; + /* Get Tx retry count */ + vwrq->value = retry_val; + } + +done: + LEAVE(); + return ret; +} + +/** + * @brief Set encryption key + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param dwrq A pointer to iw_point structure + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +static int +woal_set_encode(struct net_device *dev, struct iw_request_info *info, + struct iw_point *dwrq, char *extra) +{ + int ret = 0; + moal_private *priv = (moal_private *)netdev_priv(dev); + mlan_ds_sec_cfg *sec = NULL; + mlan_ioctl_req *req = NULL; + int index = 0; + t_u32 auth_mode = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + sec = (mlan_ds_sec_cfg *)req->pbuf; + sec->sub_command = MLAN_OID_SEC_CFG_ENCRYPT_KEY; + req->req_id = MLAN_IOCTL_SEC_CFG; + req->action = MLAN_ACT_SET; + + /* Check index */ + index = (dwrq->flags & IW_ENCODE_INDEX) - 1; + if (index > 3) { + PRINTM(MERROR, "Key index #%d out of range\n", index); + ret = -EINVAL; + goto done; + } + + sec->param.encrypt_key.key_len = 0; + if (!(dwrq->flags & IW_ENCODE_NOKEY) && dwrq->length) { + if (dwrq->length > MAX_WEP_KEY_SIZE) { + PRINTM(MERROR, "Key length (%d) out of range\n", + dwrq->length); + ret = -EINVAL; + goto done; + } + if (index < 0) + sec->param.encrypt_key.key_index = + MLAN_KEY_INDEX_DEFAULT; + else + sec->param.encrypt_key.key_index = index; + memcpy(sec->param.encrypt_key.key_material, extra, + dwrq->length); + /* Set the length */ + if (dwrq->length > MIN_WEP_KEY_SIZE) + sec->param.encrypt_key.key_len = MAX_WEP_KEY_SIZE; + else + sec->param.encrypt_key.key_len = MIN_WEP_KEY_SIZE; + } else { + /* + * No key provided so it is either enable key, + * on or off + */ + if (dwrq->flags & IW_ENCODE_DISABLED) { + PRINTM(MINFO, "*** iwconfig mlanX key off ***\n"); + sec->param.encrypt_key.key_disable = MTRUE; + } else { + /* + * iwconfig mlanX key [n] + * iwconfig mlanX key on + * iwconfig mlanX key open + * iwconfig mlanX key restricted + * Do we want to just set the transmit key index ? + */ + if (index < 0) { + PRINTM(MINFO, + "*** iwconfig mlanX key on ***\n"); + sec->param.encrypt_key.key_index = + MLAN_KEY_INDEX_DEFAULT; + } else + sec->param.encrypt_key.key_index = index; + sec->param.encrypt_key.is_current_wep_key = MTRUE; + } + } + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + if (dwrq->flags & (IW_ENCODE_RESTRICTED | IW_ENCODE_OPEN)) { + switch (dwrq->flags & 0xf000) { + case IW_ENCODE_RESTRICTED: + /* iwconfig mlanX restricted key [1] */ + auth_mode = MLAN_AUTH_MODE_SHARED; + PRINTM(MINFO, "Auth mode restricted!\n"); + break; + case IW_ENCODE_OPEN: + /* iwconfig mlanX key [2] open */ + auth_mode = MLAN_AUTH_MODE_OPEN; + PRINTM(MINFO, "Auth mode open!\n"); + break; + case IW_ENCODE_RESTRICTED | IW_ENCODE_OPEN: + default: + /* iwconfig mlanX key [2] open restricted */ + auth_mode = MLAN_AUTH_MODE_AUTO; + PRINTM(MINFO, "Auth mode auto!\n"); + break; + } + if (MLAN_STATUS_SUCCESS != + woal_set_auth_mode(priv, MOAL_IOCTL_WAIT, auth_mode)) + ret = -EFAULT; + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Get encryption key + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param dwrq A pointer to iw_point structure + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +static int +woal_get_encode(struct net_device *dev, struct iw_request_info *info, + struct iw_point *dwrq, char *extra) +{ + int ret = 0; + moal_private *priv = (moal_private *)netdev_priv(dev); + mlan_ds_sec_cfg *sec = NULL; + mlan_ioctl_req *req = NULL; + t_u32 auth_mode; + int index = (dwrq->flags & IW_ENCODE_INDEX); + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + if (index < 0 || index > 4) { + PRINTM(MERROR, "Key index #%d out of range\n", index); + ret = -EINVAL; + goto done; + } + if (MLAN_STATUS_SUCCESS != + woal_get_auth_mode(priv, MOAL_IOCTL_WAIT, &auth_mode)) { + ret = -EFAULT; + goto done; + } + dwrq->flags = 0; + /* + * Check encryption mode + */ + switch (auth_mode) { + case MLAN_AUTH_MODE_OPEN: + dwrq->flags = IW_ENCODE_OPEN; + break; + + case MLAN_AUTH_MODE_SHARED: + case MLAN_AUTH_MODE_NETWORKEAP: + dwrq->flags = IW_ENCODE_RESTRICTED; + break; + + case MLAN_AUTH_MODE_AUTO: + dwrq->flags = IW_ENCODE_OPEN | IW_ENCODE_RESTRICTED; + break; + + default: + dwrq->flags = IW_ENCODE_DISABLED | IW_ENCODE_OPEN; + break; + } + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + sec = (mlan_ds_sec_cfg *)req->pbuf; + sec->sub_command = MLAN_OID_SEC_CFG_ENCRYPT_KEY; + req->req_id = MLAN_IOCTL_SEC_CFG; + req->action = MLAN_ACT_GET; + + if (!index) + sec->param.encrypt_key.key_index = MLAN_KEY_INDEX_DEFAULT; + else + sec->param.encrypt_key.key_index = index - 1; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + memset(extra, 0, 16); + if (sec->param.encrypt_key.key_len) { + memcpy(extra, sec->param.encrypt_key.key_material, + sec->param.encrypt_key.key_len); + dwrq->length = sec->param.encrypt_key.key_len; + dwrq->flags |= (sec->param.encrypt_key.key_index + 1); + dwrq->flags &= ~IW_ENCODE_DISABLED; + } else if (sec->param.encrypt_key.key_disable) + dwrq->flags |= IW_ENCODE_DISABLED; + else + dwrq->flags &= ~IW_ENCODE_DISABLED; + + dwrq->flags |= IW_ENCODE_NOKEY; +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set data rate + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param vwrq A pointer to iw_param structure + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +static int +woal_set_rate(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + int ret = 0; + moal_private *priv = (moal_private *)netdev_priv(dev); + mlan_rate_cfg_t rate_cfg; + + ENTER(); + + if (vwrq->value == -1) { + rate_cfg.is_rate_auto = 1; + } else { + rate_cfg.is_rate_auto = 0; + rate_cfg.rate_type = MLAN_RATE_VALUE; + rate_cfg.rate = vwrq->value / 500000; + } + if (MLAN_STATUS_SUCCESS != woal_set_get_data_rate(priv, + MLAN_ACT_SET, + &rate_cfg)) { + ret = -EFAULT; + } + + LEAVE(); + return ret; +} + +/** + * @brief Get data rate + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param vwrq A pointer to iw_param structure + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +static int +woal_get_rate(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + int ret = 0; + moal_private *priv = (moal_private *)netdev_priv(dev); + mlan_rate_cfg_t rate_cfg; + + ENTER(); + + if (MLAN_STATUS_SUCCESS != woal_set_get_data_rate(priv, + MLAN_ACT_GET, + &rate_cfg)) { + ret = -EFAULT; + goto done; + } + + if (rate_cfg.is_rate_auto) + vwrq->fixed = 0; + else + vwrq->fixed = 1; + vwrq->value = rate_cfg.rate * 500000; +done: + LEAVE(); + return ret; +} + +/** + * @brief Set RTS threshold + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param vwrq A pointer to iw_param structure + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +static int +woal_set_rts(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + int ret = 0; + moal_private *priv = (moal_private *)netdev_priv(dev); + int rthr = vwrq->value; + + ENTER(); + + if (vwrq->disabled) { + rthr = MLAN_RTS_MAX_VALUE; + } else { + if (rthr < MLAN_RTS_MIN_VALUE || rthr > MLAN_RTS_MAX_VALUE) { + ret = -EINVAL; + goto done; + } + } + + if (MLAN_STATUS_SUCCESS != + woal_set_get_rts(priv, MLAN_ACT_SET, MOAL_IOCTL_WAIT, &rthr)) { + ret = -EFAULT; + goto done; + } + +done: + LEAVE(); + return ret; +} + +/** + * @brief Get RTS threshold + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param vwrq A pointer to iw_param structure + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +static int +woal_get_rts(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + int rthr, ret = 0; + moal_private *priv = (moal_private *)netdev_priv(dev); + + ENTER(); + + if (MLAN_STATUS_SUCCESS != + woal_set_get_rts(priv, MLAN_ACT_GET, MOAL_IOCTL_WAIT, &rthr)) { + ret = -EFAULT; + goto done; + } + + vwrq->value = rthr; + vwrq->disabled = ((vwrq->value < MLAN_RTS_MIN_VALUE) + || (vwrq->value > MLAN_RTS_MAX_VALUE)); + vwrq->fixed = 1; + +done: + LEAVE(); + return ret; +} + +/** + * @brief Set Fragment threshold + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param vwrq A pointer to iw_param structure + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +static int +woal_set_frag(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + int ret = 0; + moal_private *priv = (moal_private *)netdev_priv(dev); + int fthr = vwrq->value; + + ENTER(); + + if (vwrq->disabled) { + fthr = MLAN_FRAG_MAX_VALUE; + } else { + if (fthr < MLAN_FRAG_MIN_VALUE || fthr > MLAN_FRAG_MAX_VALUE) { + ret = -EINVAL; + goto done; + } + } + + if (MLAN_STATUS_SUCCESS != + woal_set_get_frag(priv, MLAN_ACT_SET, MOAL_IOCTL_WAIT, &fthr)) { + ret = -EFAULT; + goto done; + } + +done: + LEAVE(); + return ret; +} + +/** + * @brief Get Fragment threshold + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param vwrq A pointer to iw_param structure + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +static int +woal_get_frag(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + int ret = 0, fthr; + moal_private *priv = (moal_private *)netdev_priv(dev); + + ENTER(); + + if (MLAN_STATUS_SUCCESS != + woal_set_get_frag(priv, MLAN_ACT_GET, MOAL_IOCTL_WAIT, &fthr)) { + ret = -EFAULT; + goto done; + } + + vwrq->value = fthr; + vwrq->disabled = ((vwrq->value < MLAN_FRAG_MIN_VALUE) + || (vwrq->value > MLAN_FRAG_MAX_VALUE)); + vwrq->fixed = 1; + +done: + LEAVE(); + return ret; +} + +#if (WIRELESS_EXT >= 18) +/** + * @brief Get IE + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param dwrq A pointer to iw_point structure + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +static int +woal_get_gen_ie(struct net_device *dev, struct iw_request_info *info, + struct iw_point *dwrq, char *extra) +{ + int ret = 0; + moal_private *priv = (moal_private *)netdev_priv(dev); + int copy_size = 0, ie_len; + t_u8 ie[MAX_IE_SIZE]; + + ENTER(); + + if (MLAN_STATUS_SUCCESS != + woal_set_get_gen_ie(priv, MLAN_ACT_GET, ie, &ie_len, + MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + + copy_size = MIN(ie_len, dwrq->length); + memcpy(extra, ie, copy_size); + dwrq->length = copy_size; + +done: + LEAVE(); + return ret; +} + +/** + * @brief Set IE + * + * Pass an opaque block of data, expected to be IEEE IEs, to the driver + * for eventual passthrough to the firmware in an associate/join + * (and potentially start) command. + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param dwrq A pointer to iw_point structure + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +static int +woal_set_gen_ie(struct net_device *dev, struct iw_request_info *info, + struct iw_point *dwrq, char *extra) +{ + int ret = 0; + moal_private *priv = (moal_private *)netdev_priv(dev); + int ie_len = dwrq->length; + const t_u8 wps_oui[] = { 0x00, 0x50, 0xf2, 0x04 }; + mlan_ds_wps_cfg *pwps = NULL; + mlan_ioctl_req *req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + /* extra + 2 to skip element id and length */ + if (!memcmp((t_u8 *)(extra + 2), wps_oui, sizeof(wps_oui))) { + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_wps_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + pwps = (mlan_ds_wps_cfg *)req->pbuf; + req->req_id = MLAN_IOCTL_WPS_CFG; + req->action = MLAN_ACT_SET; + pwps->sub_command = MLAN_OID_WPS_CFG_SESSION; + pwps->param.wps_session = MLAN_WPS_CFG_SESSION_START; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + } + + if (MLAN_STATUS_SUCCESS != + woal_set_get_gen_ie(priv, MLAN_ACT_SET, (t_u8 *)extra, &ie_len, + MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + + LEAVE(); + return ret; +} + +/** + * @brief Extended version of encoding configuration + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param dwrq A pointer to iw_point structure + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +static int +woal_set_encode_ext(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, char *extra) +{ + struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; + moal_private *priv = (moal_private *)netdev_priv(dev); + int key_index; + t_u8 *pkey_material = NULL; + mlan_ioctl_req *req = NULL; + mlan_ds_sec_cfg *sec = NULL; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + key_index = (dwrq->flags & IW_ENCODE_INDEX) - 1; + if (key_index < 0 || key_index > 5) { + ret = -EINVAL; + goto done; + } + if (ext->key_len > (dwrq->length - sizeof(struct iw_encode_ext))) { + ret = -EINVAL; + goto done; + } + if (ext->key_len > (MLAN_MAX_KEY_LENGTH)) { + ret = -EINVAL; + goto done; + } + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + sec = (mlan_ds_sec_cfg *)req->pbuf; + sec->sub_command = MLAN_OID_SEC_CFG_ENCRYPT_KEY; + req->req_id = MLAN_IOCTL_SEC_CFG; + req->action = MLAN_ACT_SET; + pkey_material = (t_u8 *)(ext + 1); + sec->param.encrypt_key.key_len = ext->key_len; + memcpy(sec->param.encrypt_key.mac_addr, (u8 *)ext->addr.sa_data, + ETH_ALEN); + /* Disable and Remove Key */ + if ((dwrq->flags & IW_ENCODE_DISABLED) && !ext->key_len) { + sec->param.encrypt_key.key_remove = MTRUE; + sec->param.encrypt_key.key_index = key_index; + sec->param.encrypt_key.key_flags = KEY_FLAG_REMOVE_KEY; + PRINTM(MIOCTL, + "Remove key key_index=%d, dwrq->flags=0x%x " MACSTR "\n", + key_index, dwrq->flags, + MAC2STR(sec->param.encrypt_key.mac_addr)); + } else if (ext->key_len <= MAX_WEP_KEY_SIZE) { + /* Set WEP key */ + sec->param.encrypt_key.key_index = key_index; + sec->param.encrypt_key.key_flags = ext->ext_flags; + memcpy(sec->param.encrypt_key.key_material, pkey_material, + ext->key_len); + if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) + sec->param.encrypt_key.is_current_wep_key = MTRUE; + } else { + /* Set WPA key */ + sec->param.encrypt_key.key_index = key_index; + sec->param.encrypt_key.key_flags = ext->ext_flags; + if (ext->ext_flags & IW_ENCODE_EXT_RX_SEQ_VALID) { + memcpy(sec->param.encrypt_key.pn, (t_u8 *)ext->rx_seq, + SEQ_MAX_SIZE); + DBG_HEXDUMP(MCMD_D, "Rx PN", sec->param.encrypt_key.pn, + SEQ_MAX_SIZE); + } + if (ext->ext_flags & IW_ENCODE_EXT_TX_SEQ_VALID) { + memcpy(sec->param.encrypt_key.pn, (t_u8 *)ext->tx_seq, + SEQ_MAX_SIZE); + DBG_HEXDUMP(MCMD_D, "Tx PN", sec->param.encrypt_key.pn, + SEQ_MAX_SIZE); + } + memcpy(sec->param.encrypt_key.key_material, pkey_material, + ext->key_len); + PRINTM(MIOCTL, + "set wpa key key_index=%d, key_len=%d key_flags=0x%x " + MACSTR "\n", key_index, ext->key_len, + sec->param.encrypt_key.key_flags, + MAC2STR(sec->param.encrypt_key.mac_addr)); + DBG_HEXDUMP(MCMD_D, "wpa key", pkey_material, ext->key_len); +#define IW_ENCODE_ALG_AES_CMAC 5 + if (ext->alg == IW_ENCODE_ALG_AES_CMAC) + sec->param.encrypt_key.key_flags |= + KEY_FLAG_AES_MCAST_IGTK; +#define IW_ENCODE_ALG_SMS4 0x20 + /* Set WAPI key */ + if (ext->alg == IW_ENCODE_ALG_SMS4) { + sec->param.encrypt_key.is_wapi_key = MTRUE; + memcpy(sec->param.encrypt_key.pn, (t_u8 *)ext->tx_seq, + SEQ_MAX_SIZE); + memcpy(&sec->param.encrypt_key.pn[SEQ_MAX_SIZE], + (t_u8 *)ext->rx_seq, SEQ_MAX_SIZE); + DBG_HEXDUMP(MCMD_D, "WAPI PN", + sec->param.encrypt_key.pn, PN_SIZE); + } + } + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) + ret = -EFAULT; +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Extended version of encoding configuration + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param dwrq A pointer to iw_point structure + * @param extra A pointer to extra data buf + * + * @return -EOPNOTSUPP + */ +static int +woal_get_encode_ext(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, char *extra) +{ + ENTER(); + LEAVE(); + return -EOPNOTSUPP; +} + +/** + * @brief Request MLME operation + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param dwrq A pointer to iw_point structure + * @param extra A pointer to extra data buf + * + * @return 0--success, otherwise fail + */ +static int +woal_set_mlme(struct net_device *dev, + struct iw_request_info *info, struct iw_point *dwrq, char *extra) +{ + struct iw_mlme *mlme = (struct iw_mlme *)extra; + moal_private *priv = (moal_private *)netdev_priv(dev); + int ret = 0; + + ENTER(); + if ((mlme->cmd == IW_MLME_DEAUTH) || (mlme->cmd == IW_MLME_DISASSOC)) { + + if (MLAN_STATUS_SUCCESS != + woal_disconnect(priv, MOAL_IOCTL_WAIT, + (t_u8 *)mlme->addr.sa_data, + DEF_DEAUTH_REASON_CODE)) + ret = -EFAULT; + } + LEAVE(); + return ret; +} + +/** @brief Set authentication mode parameters + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param vwrq A pointer to iw_param structure + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +static int +woal_set_auth(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + int ret = 0; + moal_private *priv = (moal_private *)netdev_priv(dev); + t_u32 auth_mode = 0; + t_u32 encrypt_mode = 0; + ENTER(); + + switch (vwrq->flags & IW_AUTH_INDEX) { + case IW_AUTH_CIPHER_PAIRWISE: + case IW_AUTH_CIPHER_GROUP: + if (vwrq->value & IW_AUTH_CIPHER_NONE) + encrypt_mode = MLAN_ENCRYPTION_MODE_NONE; + else if (vwrq->value & IW_AUTH_CIPHER_WEP40) + encrypt_mode = MLAN_ENCRYPTION_MODE_WEP40; + else if (vwrq->value & IW_AUTH_CIPHER_WEP104) + encrypt_mode = MLAN_ENCRYPTION_MODE_WEP104; + else if (vwrq->value & IW_AUTH_CIPHER_TKIP) + encrypt_mode = MLAN_ENCRYPTION_MODE_TKIP; + else if (vwrq->value & IW_AUTH_CIPHER_CCMP) + encrypt_mode = MLAN_ENCRYPTION_MODE_CCMP; + if (MLAN_STATUS_SUCCESS != + woal_set_encrypt_mode(priv, MOAL_IOCTL_WAIT, encrypt_mode)) + ret = -EFAULT; + break; + case IW_AUTH_80211_AUTH_ALG: + switch (vwrq->value) { + case IW_AUTH_ALG_SHARED_KEY: + PRINTM(MINFO, "Auth mode shared key!\n"); + auth_mode = MLAN_AUTH_MODE_SHARED; + break; + case IW_AUTH_ALG_LEAP: + PRINTM(MINFO, "Auth mode LEAP!\n"); + auth_mode = MLAN_AUTH_MODE_NETWORKEAP; + break; + case IW_AUTH_ALG_OPEN_SYSTEM: + PRINTM(MINFO, "Auth mode open!\n"); + auth_mode = MLAN_AUTH_MODE_OPEN; + break; + case IW_AUTH_ALG_SHARED_KEY | IW_AUTH_ALG_OPEN_SYSTEM: + default: + PRINTM(MINFO, "Auth mode auto!\n"); + auth_mode = MLAN_AUTH_MODE_AUTO; + break; + } + if (MLAN_STATUS_SUCCESS != + woal_set_auth_mode(priv, MOAL_IOCTL_WAIT, auth_mode)) + ret = -EFAULT; + break; + case IW_AUTH_WPA_ENABLED: + if (MLAN_STATUS_SUCCESS != + woal_set_wpa_enable(priv, MOAL_IOCTL_WAIT, vwrq->value)) + ret = -EFAULT; + break; +#define IW_AUTH_WAPI_ENABLED 0x20 + case IW_AUTH_WAPI_ENABLED: + if (MLAN_STATUS_SUCCESS != + woal_set_wapi_enable(priv, MOAL_IOCTL_WAIT, vwrq->value)) + ret = -EFAULT; + break; + case IW_AUTH_WPA_VERSION: + /* set WPA_VERSION_DISABLED/VERSION_WPA/VERSION_WP2 */ + priv->wpa_version = vwrq->value; + break; + case IW_AUTH_KEY_MGMT: + /* set KEY_MGMT_802_1X/KEY_MGMT_PSK */ + priv->key_mgmt = vwrq->value; + break; + case IW_AUTH_TKIP_COUNTERMEASURES: + case IW_AUTH_DROP_UNENCRYPTED: + case IW_AUTH_RX_UNENCRYPTED_EAPOL: + case IW_AUTH_ROAMING_CONTROL: + case IW_AUTH_PRIVACY_INVOKED: +#if CFG80211_VERSION_CODE > KERNEL_VERSION(2, 6, 29) + case IW_AUTH_MFP: +#endif + break; + default: + ret = -EOPNOTSUPP; + break; + } + LEAVE(); + return ret; +} + +/** + * @brief Get authentication mode parameters + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param vwrq A pointer to iw_param structure + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +static int +woal_get_auth(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + int ret = 0; + moal_private *priv = (moal_private *)netdev_priv(dev); + t_u32 encrypt_mode = 0; + t_u32 auth_mode; + t_u32 wpa_enable; + ENTER(); + switch (vwrq->flags & IW_AUTH_INDEX) { + case IW_AUTH_CIPHER_PAIRWISE: + case IW_AUTH_CIPHER_GROUP: + if (MLAN_STATUS_SUCCESS != + woal_get_encrypt_mode(priv, MOAL_IOCTL_WAIT, &encrypt_mode)) + ret = -EFAULT; + else { + if (encrypt_mode == MLAN_ENCRYPTION_MODE_NONE) + vwrq->value = IW_AUTH_CIPHER_NONE; + else if (encrypt_mode == MLAN_ENCRYPTION_MODE_WEP40) + vwrq->value = IW_AUTH_CIPHER_WEP40; + else if (encrypt_mode == MLAN_ENCRYPTION_MODE_TKIP) + vwrq->value = IW_AUTH_CIPHER_TKIP; + else if (encrypt_mode == MLAN_ENCRYPTION_MODE_CCMP) + vwrq->value = IW_AUTH_CIPHER_CCMP; + else if (encrypt_mode == MLAN_ENCRYPTION_MODE_WEP104) + vwrq->value = IW_AUTH_CIPHER_WEP104; + } + break; + case IW_AUTH_80211_AUTH_ALG: + if (MLAN_STATUS_SUCCESS != + woal_get_auth_mode(priv, MOAL_IOCTL_WAIT, &auth_mode)) + ret = -EFAULT; + else { + if (auth_mode == MLAN_AUTH_MODE_SHARED) + vwrq->value = IW_AUTH_ALG_SHARED_KEY; + else if (auth_mode == MLAN_AUTH_MODE_NETWORKEAP) + vwrq->value = IW_AUTH_ALG_LEAP; + else + vwrq->value = IW_AUTH_ALG_OPEN_SYSTEM; + } + break; + case IW_AUTH_WPA_ENABLED: + if (MLAN_STATUS_SUCCESS != + woal_get_wpa_enable(priv, MOAL_IOCTL_WAIT, &wpa_enable)) + ret = -EFAULT; + else + vwrq->value = wpa_enable; + break; + case IW_AUTH_WPA_VERSION: + vwrq->value = priv->wpa_version; + break; + case IW_AUTH_KEY_MGMT: + vwrq->value = priv->key_mgmt; + break; + case IW_AUTH_TKIP_COUNTERMEASURES: + case IW_AUTH_DROP_UNENCRYPTED: + case IW_AUTH_RX_UNENCRYPTED_EAPOL: + case IW_AUTH_ROAMING_CONTROL: + case IW_AUTH_PRIVACY_INVOKED: + default: + ret = -EOPNOTSUPP; + goto done; + } + +done: + LEAVE(); + return ret; +} + +/** + * @brief Set PMKSA Cache + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param vwrq A pointer to iw_param structure + * @param extra A pointer to extra data buf + * + * @return -EOPNOTSUPP + */ +static int +woal_set_pmksa(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + ENTER(); + LEAVE(); + return -EOPNOTSUPP; +} + +#endif /* WE >= 18 */ + +/* Data rate listing + * MULTI_BANDS: + * abg a b b/g + * Infra G(12) A(8) B(4) G(12) + * Adhoc A+B(12) A(8) B(4) B(4) + * non-MULTI_BANDS: + b b/g + * Infra B(4) G(12) + * Adhoc B(4) B(4) + */ +/** + * @brief Get Range Info + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param dwrq A pointer to iw_point structure + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +static int +woal_get_range(struct net_device *dev, struct iw_request_info *info, + struct iw_point *dwrq, char *extra) +{ + int i; + moal_private *priv = (moal_private *)netdev_priv(dev); + struct iw_range *range = (struct iw_range *)extra; + moal_802_11_rates rates; + mlan_chan_list *pchan_list = NULL; + mlan_bss_info bss_info; + gfp_t flag; + + ENTER(); + + flag = (in_atomic() || irqs_disabled())? GFP_ATOMIC : GFP_KERNEL; + pchan_list = kzalloc(sizeof(mlan_chan_list), flag); + if (!pchan_list) { + LEAVE(); + return -ENOMEM; + } + + dwrq->length = sizeof(struct iw_range); + memset(range, 0, sizeof(struct iw_range)); + + range->min_nwid = 0; + range->max_nwid = 0; + + memset(&rates, 0, sizeof(rates)); + woal_get_data_rates(priv, MOAL_IOCTL_WAIT, &rates); + range->num_bitrates = rates.num_of_rates; + + for (i = 0; + i < MIN(range->num_bitrates, IW_MAX_BITRATES) && rates.rates[i]; + i++) { + range->bitrate[i] = (rates.rates[i] & 0x7f) * 500000; + } + range->num_bitrates = i; + PRINTM(MINFO, "IW_MAX_BITRATES=%d num_bitrates=%d\n", IW_MAX_BITRATES, + range->num_bitrates); + + range->num_frequency = 0; + + woal_get_channel_list(priv, MOAL_IOCTL_WAIT, pchan_list); + + range->num_frequency = MIN(pchan_list->num_of_chan, IW_MAX_FREQUENCIES); + + for (i = 0; i < range->num_frequency; i++) { + range->freq[i].i = (long)pchan_list->cf[i].channel; + range->freq[i].m = (long)pchan_list->cf[i].freq * 100000; + range->freq[i].e = 1; + } + kfree(pchan_list); + + PRINTM(MINFO, "IW_MAX_FREQUENCIES=%d num_frequency=%d\n", + IW_MAX_FREQUENCIES, range->num_frequency); + + range->num_channels = range->num_frequency; + + woal_sort_channels(&range->freq[0], range->num_frequency); + + /* + * Set an indication of the max TCP throughput in bit/s that we can + * expect using this interface + */ + if (i > 2) + range->throughput = 5000 * 1000; + else + range->throughput = 1500 * 1000; + + range->min_rts = MLAN_RTS_MIN_VALUE; + range->max_rts = MLAN_RTS_MAX_VALUE; + range->min_frag = MLAN_FRAG_MIN_VALUE; + range->max_frag = MLAN_FRAG_MAX_VALUE; + + range->encoding_size[0] = 5; + range->encoding_size[1] = 13; + range->num_encoding_sizes = 2; + range->max_encoding_tokens = 4; + +/** Minimum power period */ +#define IW_POWER_PERIOD_MIN 1000000 /* 1 sec */ +/** Maximum power period */ +#define IW_POWER_PERIOD_MAX 120000000 /* 2 min */ +/** Minimum power timeout value */ +#define IW_POWER_TIMEOUT_MIN 1000 /* 1 ms */ +/** Maximim power timeout value */ +#define IW_POWER_TIMEOUT_MAX 1000000 /* 1 sec */ + + /* Power Management duration & timeout */ + range->min_pmp = IW_POWER_PERIOD_MIN; + range->max_pmp = IW_POWER_PERIOD_MAX; + range->min_pmt = IW_POWER_TIMEOUT_MIN; + range->max_pmt = IW_POWER_TIMEOUT_MAX; + range->pmp_flags = IW_POWER_PERIOD; + range->pmt_flags = IW_POWER_TIMEOUT; + range->pm_capa = IW_POWER_PERIOD | IW_POWER_TIMEOUT | IW_POWER_ALL_R; + + /* + * Minimum version we recommend + */ + range->we_version_source = 15; + + /* + * Version we are compiled with + */ + range->we_version_compiled = WIRELESS_EXT; + + range->retry_capa = IW_RETRY_LIMIT; + range->retry_flags = IW_RETRY_LIMIT | IW_RETRY_MAX; + + range->min_retry = MLAN_TX_RETRY_MIN; + range->max_retry = MLAN_TX_RETRY_MAX; + + /* + * Set the qual, level and noise range values + */ + /* + * need to put the right values here + */ +/** Maximum quality percentage */ +#define IW_MAX_QUAL_PERCENT 5 +/** Average quality percentage */ +#define IW_AVG_QUAL_PERCENT 3 + range->max_qual.qual = IW_MAX_QUAL_PERCENT; + range->max_qual.level = 0; + range->max_qual.noise = 0; + + range->avg_qual.qual = IW_AVG_QUAL_PERCENT; + range->avg_qual.level = 0; + range->avg_qual.noise = 0; + + range->sensitivity = 0; + + /* + * Setup the supported power level ranges + */ + memset(range->txpower, 0, sizeof(range->txpower)); + + memset(&bss_info, 0, sizeof(bss_info)); + + woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info); + + range->txpower[0] = bss_info.min_power_level; + range->txpower[1] = bss_info.max_power_level; + range->num_txpower = 2; + range->txpower_capa = IW_TXPOW_DBM | IW_TXPOW_RANGE; + +#if (WIRELESS_EXT >= 18) + range->enc_capa = IW_ENC_CAPA_WPA | + IW_ENC_CAPA_WPA2 | + IW_ENC_CAPA_CIPHER_CCMP | IW_ENC_CAPA_CIPHER_TKIP; +#endif + + LEAVE(); + return 0; +} + +#ifdef MEF_CFG_RX_FILTER +/** + * @brief Enable/disable Rx broadcast/multicast filter in non-HS mode + * + * @param priv A pointer to moal_private structure + * @param enable MTRUE/MFALSE: enable/disable + * + * @return 0 -- success, otherwise fail + */ +static int +woal_set_rxfilter(moal_private *priv, BOOLEAN enable) +{ + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *misc = NULL; + mlan_ds_misc_mef_cfg *mef_cfg = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + misc = (mlan_ds_misc_cfg *)req->pbuf; + mef_cfg = &misc->param.mef_cfg; + req->req_id = MLAN_IOCTL_MISC_CFG; + misc->sub_command = MLAN_OID_MISC_MEF_CFG; + req->action = MLAN_ACT_SET; + + mef_cfg->sub_id = (enable ? MEF_CFG_RX_FILTER_ENABLE : MEF_CFG_DISABLE); + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} +#endif + +/** + * @brief Set priv command + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param dwrq A pointer to iw_point structure + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +static int +woal_set_priv(struct net_device *dev, struct iw_request_info *info, + struct iw_point *dwrq, char *extra) +{ + int ret = 0; + moal_private *priv = (moal_private *)netdev_priv(dev); + char *buf = NULL; + int power_mode = 0; + int band = 0; + char *pband = NULL; + mlan_bss_info bss_info; + mlan_ds_get_signal signal; + mlan_rate_cfg_t rate; + char *pdata = NULL; + t_u8 country_code[COUNTRY_CODE_LEN]; + int len = 0; + gfp_t flag; + ENTER(); + if (!priv || !priv->phandle) { + PRINTM(MERROR, "priv or handle is NULL\n"); + ret = -EFAULT; + goto done; + } + flag = (in_atomic() || irqs_disabled())? GFP_ATOMIC : GFP_KERNEL; + buf = kzalloc(dwrq->length + 1, flag); + if (!buf) { + ret = -ENOMEM; + goto done; + } + if (copy_from_user(buf, dwrq->pointer, dwrq->length)) { + ret = -EFAULT; + goto done; + } + PRINTM(MIOCTL, "SIOCSIWPRIV request = %s\n", buf); + if (strncmp(buf, "RSSILOW-THRESHOLD", strlen("RSSILOW-THRESHOLD")) == 0) { + if (dwrq->length > strlen("RSSILOW-THRESHOLD") + 1) { + pdata = buf + strlen("RSSILOW-THRESHOLD") + 1; + if (MLAN_STATUS_SUCCESS != + woal_set_rssi_low_threshold(priv, pdata, + MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + len = sprintf(buf, "OK\n") + 1; + } else { + ret = -EFAULT; + goto done; + } + } else if (strncmp(buf, "RSSI", strlen("RSSI")) == 0) { + if (MLAN_STATUS_SUCCESS != woal_get_bss_info(priv, + MOAL_IOCTL_WAIT, + &bss_info)) { + ret = -EFAULT; + goto done; + } + if (bss_info.media_connected) { + if (MLAN_STATUS_SUCCESS != + woal_get_signal_info(priv, MOAL_IOCTL_WAIT, + &signal)) { + ret = -EFAULT; + goto done; + } + len = sprintf(buf, "%s rssi %d\n", bss_info.ssid.ssid, + signal.bcn_rssi_avg) + 1; + } else { + len = sprintf(buf, "OK\n") + 1; + } + } else if (strncmp(buf, "LINKSPEED", strlen("LINKSPEED")) == 0) { + if (MLAN_STATUS_SUCCESS != + woal_set_get_data_rate(priv, MLAN_ACT_GET, &rate)) { + ret = -EFAULT; + goto done; + } + PRINTM(MIOCTL, "tx rate=%d\n", (int)rate.rate); + len = sprintf(buf, "LinkSpeed %d\n", + (int)(rate.rate * 500000 / 1000000)) + 1; + } else if (strncmp(buf, "MACADDR", strlen("MACADDR")) == 0) { + len = sprintf(buf, "Macaddr = %02X:%02X:%02X:%02X:%02X:%02X\n", + priv->current_addr[0], priv->current_addr[1], + priv->current_addr[2], priv->current_addr[3], + priv->current_addr[4], priv->current_addr[5]) + 1; + } else if (strncmp(buf, "GETPOWER", strlen("GETPOWER")) == 0) { + if (MLAN_STATUS_SUCCESS != + woal_get_powermode(priv, &power_mode)) { + ret = -EFAULT; + goto done; + } + len = sprintf(buf, "powermode = %d\n", power_mode) + 1; + } else if (strncmp(buf, "SCAN-ACTIVE", strlen("SCAN-ACTIVE")) == 0) { + if (MLAN_STATUS_SUCCESS != + woal_set_scan_type(priv, MLAN_SCAN_TYPE_ACTIVE)) { + ret = -EFAULT; + goto done; + } + priv->scan_type = MLAN_SCAN_TYPE_ACTIVE; + PRINTM(MIOCTL, "Set Active Scan\n"); + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "SCAN-PASSIVE", strlen("SCAN-PASSIVE")) == 0) { + if (MLAN_STATUS_SUCCESS != + woal_set_scan_type(priv, MLAN_SCAN_TYPE_PASSIVE)) { + ret = -EFAULT; + goto done; + } + priv->scan_type = MLAN_SCAN_TYPE_PASSIVE; + PRINTM(MIOCTL, "Set Passive Scan\n"); + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "POWERMODE", strlen("POWERMODE")) == 0) { + if (dwrq->length > strlen("POWERMODE") + 1) { + pdata = buf + strlen("POWERMODE") + 1; + if (!hw_test) { + if (MLAN_STATUS_SUCCESS != + woal_set_powermode(priv, pdata)) { + ret = -EFAULT; + goto done; + } + } + len = sprintf(buf, "OK\n") + 1; + } else { + ret = -EFAULT; + goto done; + } + } else if (strncmp(buf, "COUNTRY", strlen("COUNTRY")) == 0) { + memset(country_code, 0, sizeof(country_code)); + if ((strlen(buf) - strlen("COUNTRY") - 1) > COUNTRY_CODE_LEN) { + ret = -EFAULT; + goto done; + } + memcpy(country_code, buf + strlen("COUNTRY") + 1, + COUNTRY_CODE_LEN - 1); + PRINTM(MIOCTL, "Set COUNTRY %s\n", country_code); + if (MLAN_STATUS_SUCCESS != + woal_set_region_code(priv, country_code)) { + ret = -EFAULT; + goto done; + } + len = sprintf(buf, "OK\n") + 1; + } else if (memcmp(buf, WEXT_CSCAN_HEADER, strlen(WEXT_CSCAN_HEADER)) == + 0) { + PRINTM(MIOCTL, "Set Combo Scan\n"); + if (MLAN_STATUS_SUCCESS != + woal_set_combo_scan(priv, buf, dwrq->length)) { + ret = -EFAULT; + goto done; + } + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "GETBAND", strlen("GETBAND")) == 0) { + if (MLAN_STATUS_SUCCESS != woal_get_band(priv, &band)) { + ret = -EFAULT; + goto done; + } + len = sprintf(buf, "Band %d\n", band) + 1; + } else if (strncmp(buf, "SETBAND", strlen("SETBAND")) == 0) { + pband = buf + strlen("SETBAND") + 1; + if (MLAN_STATUS_SUCCESS != woal_set_band(priv, pband)) { + ret = -EFAULT; + goto done; + } + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "START", strlen("START")) == 0) { + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "STOP", strlen("STOP")) == 0) { + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "SETSUSPENDOPT", strlen("SETSUSPENDOPT")) == 0) { + /* it will be done by GUI */ + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "BTCOEXMODE", strlen("BTCOEXMODE")) == 0) { + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "BTCOEXSCAN-START", strlen("BTCOEXSCAN-START")) + == 0) { + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "BTCOEXSCAN-STOP", strlen("BTCOEXSCAN-STOP")) == + 0) { + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "BGSCAN-START", strlen("BGSCAN-START")) == 0) { + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "BGSCAN-CONFIG", strlen("BGSCAN-CONFIG")) == 0) { + if (MLAN_STATUS_SUCCESS != + woal_set_bg_scan(priv, buf, dwrq->length)) { + ret = -EFAULT; + goto done; + } + priv->bg_scan_start = MTRUE; + priv->bg_scan_reported = MFALSE; + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "BGSCAN-STOP", strlen("BGSCAN-STOP")) == 0) { + if (priv->bg_scan_start && !priv->scan_cfg.rssi_threshold) { + if (MLAN_STATUS_SUCCESS != + woal_stop_bg_scan(priv, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + priv->bg_scan_start = MFALSE; + priv->bg_scan_reported = MFALSE; + } + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "RXFILTER-START", strlen("RXFILTER-START")) == + 0) { +#ifdef MEF_CFG_RX_FILTER + ret = woal_set_rxfilter(priv, MTRUE); + if (ret) + goto done; +#endif + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "RXFILTER-STOP", strlen("RXFILTER-STOP")) == 0) { +#ifdef MEF_CFG_RX_FILTER + ret = woal_set_rxfilter(priv, MFALSE); + if (ret) + goto done; +#endif + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "RXFILTER-ADD", strlen("RXFILTER-ADD")) == 0) { + if (dwrq->length > strlen("RXFILTER-ADD") + 1) { + pdata = buf + strlen("RXFILTER-ADD") + 1; + if (MLAN_STATUS_SUCCESS != + woal_add_rxfilter(priv, pdata)) { + ret = -EFAULT; + goto done; + } + len = sprintf(buf, "OK\n") + 1; + } else { + ret = -EFAULT; + goto done; + } + } else if (strncmp(buf, "RXFILTER-REMOVE", strlen("RXFILTER-REMOVE")) == + 0) { + if (dwrq->length > strlen("RXFILTER-REMOVE") + 1) { + pdata = buf + strlen("RXFILTER-REMOVE") + 1; + if (MLAN_STATUS_SUCCESS != + woal_remove_rxfilter(priv, pdata)) { + ret = -EFAULT; + goto done; + } + len = sprintf(buf, "OK\n") + 1; + } else { + ret = -EFAULT; + goto done; + } + } else if (strncmp(buf, "QOSINFO", strlen("QOSINFO")) == 0) { + if (dwrq->length > strlen("QOSINFO") + 1) { + pdata = buf + strlen("QOSINFO") + 1; + if (MLAN_STATUS_SUCCESS != + woal_priv_qos_cfg(priv, MLAN_ACT_SET, pdata)) { + ret = -EFAULT; + goto done; + } + len = sprintf(buf, "OK\n") + 1; + } else { + ret = -EFAULT; + goto done; + } + } else if (strncmp(buf, "SLEEPPD", strlen("SLEEPPD")) == 0) { + if (dwrq->length > strlen("SLEEPPD") + 1) { + pdata = buf + strlen("SLEEPPD") + 1; + if (MLAN_STATUS_SUCCESS != + woal_set_sleeppd(priv, pdata)) { + ret = -EFAULT; + goto done; + } + len = sprintf(buf, "OK\n") + 1; + } else { + ret = -EFAULT; + goto done; + } + } else { + PRINTM(MIOCTL, "Unknow PRIVATE command: %s, ignored\n", buf); + ret = -EFAULT; + goto done; + } + PRINTM(MIOCTL, "PRIV Command return: %s, length=%d\n", buf, len); + dwrq->length = (t_u16)len; + if (copy_to_user(dwrq->pointer, buf, dwrq->length)) + ret = -EFAULT; +done: + kfree(buf); + LEAVE(); + return ret; +} + +/** + * @brief Scan Network + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param vwrq A pointer to iw_param structure + * @param extra A pointer to extra data buf + * + * @return 0--success, otherwise fail + */ +static int +woal_set_scan(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + int ret = 0; + moal_private *priv = (moal_private *)netdev_priv(dev); + moal_handle *handle = priv->phandle; +#if WIRELESS_EXT >= 18 + struct iw_scan_req *req; + struct iw_point *dwrq = (struct iw_point *)vwrq; +#endif + mlan_802_11_ssid req_ssid; + + ENTER(); + if (handle->scan_pending_on_block == MTRUE) { + PRINTM(MINFO, "scan already in processing...\n"); + LEAVE(); + return ret; + } +#ifdef REASSOCIATION + if (MOAL_ACQ_SEMAPHORE_BLOCK(&handle->reassoc_sem)) { + PRINTM(MERROR, "Acquire semaphore error, woal_set_scan\n"); + LEAVE(); + return -EBUSY; + } +#endif /* REASSOCIATION */ + priv->report_scan_result = MTRUE; + + memset(&req_ssid, 0x00, sizeof(mlan_802_11_ssid)); + +#if WIRELESS_EXT >= 18 + if ((dwrq->flags & IW_SCAN_THIS_ESSID) && + (dwrq->length == sizeof(struct iw_scan_req))) { + req = (struct iw_scan_req *)extra; + + if (req->essid_len <= MLAN_MAX_SSID_LENGTH) { + + req_ssid.ssid_len = req->essid_len; + memcpy(req_ssid.ssid, + (t_u8 *)req->essid, req->essid_len); + if (MLAN_STATUS_SUCCESS != + woal_request_scan(priv, MOAL_NO_WAIT, &req_ssid)) { + ret = -EFAULT; + goto done; + } + } + } else { +#endif + if (MLAN_STATUS_SUCCESS != + woal_request_scan(priv, MOAL_NO_WAIT, NULL)) { + ret = -EFAULT; + goto done; + } +#if WIRELESS_EXT >= 18 + } +#endif + + if (priv->phandle->surprise_removed) { + ret = -EFAULT; + goto done; + } + +done: +#ifdef REASSOCIATION + MOAL_REL_SEMAPHORE(&handle->reassoc_sem); +#endif + + LEAVE(); + return ret; +} + +/** + * @brief Set essid + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param dwrq A pointer to iw_point structure + * @param extra A pointer to extra data buf + * + * @return 0--success, otherwise fail + */ +static int +woal_set_essid(struct net_device *dev, struct iw_request_info *info, + struct iw_point *dwrq, char *extra) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + mlan_802_11_ssid req_ssid; + mlan_ssid_bssid ssid_bssid; +#ifdef REASSOCIATION + moal_handle *handle = priv->phandle; + mlan_bss_info bss_info; +#endif + int ret = 0; + t_u32 mode = 0; + + ENTER(); + +#ifdef REASSOCIATION + /* Cancel re-association */ + priv->reassoc_required = MFALSE; + + if (MOAL_ACQ_SEMAPHORE_BLOCK(&handle->reassoc_sem)) { + PRINTM(MERROR, "Acquire semaphore error, woal_set_essid\n"); + LEAVE(); + return -EBUSY; + } +#endif /* REASSOCIATION */ + + /* Check the size of the string */ + if (dwrq->length > IW_ESSID_MAX_SIZE + 1) { + ret = -E2BIG; + goto setessid_ret; + } + if (priv->scan_type == MLAN_SCAN_TYPE_PASSIVE) + woal_set_scan_type(priv, MLAN_SCAN_TYPE_ACTIVE); + memset(&req_ssid, 0, sizeof(mlan_802_11_ssid)); + memset(&ssid_bssid, 0, sizeof(mlan_ssid_bssid)); + +#if WIRELESS_EXT > 20 + req_ssid.ssid_len = dwrq->length; +#else + req_ssid.ssid_len = dwrq->length - 1; +#endif + + /* + * Check if we asked for `any' or 'particular' + */ + if (!dwrq->flags) { +#ifdef REASSOCIATION + if (!req_ssid.ssid_len) { + memset(&priv->prev_ssid_bssid.ssid, 0x00, + sizeof(mlan_802_11_ssid)); + memset(&priv->prev_ssid_bssid.bssid, 0x00, + MLAN_MAC_ADDR_LENGTH); + goto setessid_ret; + } +#endif + /* Do normal SSID scanning */ + if (MLAN_STATUS_SUCCESS != + woal_request_scan(priv, MOAL_IOCTL_WAIT, NULL)) { + ret = -EFAULT; + goto setessid_ret; + } + } else { + /* Set the SSID */ + memcpy(req_ssid.ssid, extra, + MIN(req_ssid.ssid_len, MLAN_MAX_SSID_LENGTH)); + if (!req_ssid.ssid_len || + (MFALSE == woal_ssid_valid(&req_ssid))) { + PRINTM(MERROR, "Invalid SSID - aborting set_essid\n"); + ret = -EINVAL; + goto setessid_ret; + } + + PRINTM(MINFO, "Requested new SSID = %s\n", + (char *)req_ssid.ssid); + memcpy(&ssid_bssid.ssid, &req_ssid, sizeof(mlan_802_11_ssid)); + if (MTRUE == woal_is_connected(priv, &ssid_bssid)) { + PRINTM(MIOCTL, "Already connect to the network\n"); + goto setessid_ret; + } + + if (dwrq->flags != 0xFFFF) { + if (MLAN_STATUS_SUCCESS != + woal_find_essid(priv, &ssid_bssid, + MOAL_IOCTL_WAIT)) { + /* Do specific SSID scanning */ + if (MLAN_STATUS_SUCCESS != + woal_request_scan(priv, MOAL_IOCTL_WAIT, + &req_ssid)) { + ret = -EFAULT; + goto setessid_ret; + } + } + } + + } + + mode = woal_get_mode(priv, MOAL_IOCTL_WAIT); + if (mode == IW_MODE_ADHOC) + /* disconnect before try to associate */ + woal_disconnect(priv, MOAL_IOCTL_WAIT, NULL, + DEF_DEAUTH_REASON_CODE); + + if (mode != IW_MODE_ADHOC) { + if (MLAN_STATUS_SUCCESS != + woal_find_best_network(priv, MOAL_IOCTL_WAIT, + &ssid_bssid)) { + ret = -EFAULT; + goto setessid_ret; + } + if (MLAN_STATUS_SUCCESS != + woal_11d_check_ap_channel(priv, MOAL_IOCTL_WAIT, + &ssid_bssid)) { + PRINTM(MERROR, + "The AP's channel is invalid for current region\n"); + ret = -EFAULT; + goto setessid_ret; + } + } else if (MLAN_STATUS_SUCCESS != + woal_find_best_network(priv, MOAL_IOCTL_WAIT, &ssid_bssid)) + /* Adhoc start, Check the channel command */ + woal_11h_channel_check_ioctl(priv, MOAL_IOCTL_WAIT); + + /* Connect to BSS by ESSID */ + memset(&ssid_bssid.bssid, 0, MLAN_MAC_ADDR_LENGTH); + + if (MLAN_STATUS_SUCCESS != woal_bss_start(priv, + MOAL_IOCTL_WAIT, + &ssid_bssid)) { + ret = -EFAULT; + goto setessid_ret; + } +#ifdef REASSOCIATION + memset(&bss_info, 0, sizeof(bss_info)); + if (MLAN_STATUS_SUCCESS != woal_get_bss_info(priv, + MOAL_IOCTL_WAIT, + &bss_info)) { + ret = -EFAULT; + goto setessid_ret; + } + memcpy(&priv->prev_ssid_bssid.ssid, &bss_info.ssid, + sizeof(mlan_802_11_ssid)); + memcpy(&priv->prev_ssid_bssid.bssid, &bss_info.bssid, + MLAN_MAC_ADDR_LENGTH); +#endif /* REASSOCIATION */ + +setessid_ret: + if (priv->scan_type == MLAN_SCAN_TYPE_PASSIVE) + woal_set_scan_type(priv, MLAN_SCAN_TYPE_PASSIVE); +#ifdef REASSOCIATION + MOAL_REL_SEMAPHORE(&handle->reassoc_sem); +#endif + + LEAVE(); + return ret; +} + +/** + * @brief Get current essid + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param dwrq A pointer to iw_point structure + * @param extra A pointer to extra data buf + * + * @return 0--success, otherwise fail + */ +static int +woal_get_essid(struct net_device *dev, struct iw_request_info *info, + struct iw_point *dwrq, char *extra) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + mlan_bss_info bss_info; + int ret = 0; + + ENTER(); + + memset(&bss_info, 0, sizeof(bss_info)); + + if (MLAN_STATUS_SUCCESS != + woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info)) { + ret = -EFAULT; + goto done; + } + + if (bss_info.media_connected) { + dwrq->length = MIN(dwrq->length, bss_info.ssid.ssid_len); + memcpy(extra, bss_info.ssid.ssid, dwrq->length); + } else + dwrq->length = 0; + + if (bss_info.scan_table_idx) + dwrq->flags = (bss_info.scan_table_idx + 1) & IW_ENCODE_INDEX; + else + dwrq->flags = 1; + +done: + LEAVE(); + return ret; +} + +/** + * @brief Retrieve the scan table entries via wireless tools IOCTL call + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param dwrq A pointer to iw_point structure + * @param extra A pointer to extra data buf + * + * @return 0--success, otherwise fail + */ +static int +woal_get_scan(struct net_device *dev, struct iw_request_info *info, + struct iw_point *dwrq, char *extra) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + int ret = 0; + char *current_ev = extra; + char *end_buf = extra + IW_SCAN_MAX_DATA; + char *current_val; /* For rates */ + struct iw_event iwe; /* Temporary buffer */ + unsigned int i; + unsigned int j; + mlan_scan_resp scan_resp; + mlan_bss_info bss_info; + BSSDescriptor_t *scan_table; + mlan_ds_get_signal rssi; + t_u16 buf_size = 16 + 256 * 2; + char *buf = NULL; + char *ptr; +#if WIRELESS_EXT >= 18 + t_u8 *praw_data; +#endif + int beacon_size; + t_u8 *pbeacon; + IEEEtypes_ElementId_e element_id; + t_u8 element_len; + gfp_t flag; + + ENTER(); + + if (priv->phandle->scan_pending_on_block == MTRUE) { + LEAVE(); + return -EAGAIN; + } + flag = (in_atomic() || irqs_disabled())? GFP_ATOMIC : GFP_KERNEL; + buf = kzalloc((buf_size), flag); + if (!buf) { + PRINTM(MERROR, "Cannot allocate buffer!\n"); + ret = -EFAULT; + goto done; + } + + memset(&bss_info, 0, sizeof(bss_info)); + if (MLAN_STATUS_SUCCESS != + woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info)) { + ret = -EFAULT; + goto done; + } + memset(&scan_resp, 0, sizeof(scan_resp)); + if (MLAN_STATUS_SUCCESS != woal_get_scan_table(priv, + MOAL_IOCTL_WAIT, + &scan_resp)) { + ret = -EFAULT; + goto done; + } + scan_table = (BSSDescriptor_t *)scan_resp.pscan_table; + if (dwrq->length) + end_buf = extra + dwrq->length; + if (priv->media_connected == MTRUE) + PRINTM(MINFO, "Current Ssid: %-32s\n", bss_info.ssid.ssid); + PRINTM(MINFO, "Scan: Get: NumInScanTable = %d\n", + (int)scan_resp.num_in_scan_table); + +#if WIRELESS_EXT > 13 + /* The old API using SIOCGIWAPLIST had a hard limit of + * IW_MAX_AP. The new API using SIOCGIWSCAN is only + * limited by buffer size WE-14 -> WE-16 the buffer is + * limited to IW_SCAN_MAX_DATA bytes which is 4096. + */ + for (i = 0; i < MIN(scan_resp.num_in_scan_table, 64); i++) { + if ((current_ev + MAX_SCAN_CELL_SIZE) >= end_buf) { + PRINTM(MINFO, + "i=%d break out: current_ev=%p end_buf=%p " + "MAX_SCAN_CELL_SIZE=%d\n", i, current_ev, + end_buf, (t_u32)MAX_SCAN_CELL_SIZE); + ret = -E2BIG; + break; + } + if (!scan_table[i].freq) { + PRINTM(MWARN, "Invalid channel number %d\n", + (int)scan_table[i].channel); + continue; + } + PRINTM(MINFO, "i=%d Ssid: %-32s\n", i, + scan_table[i].ssid.ssid); + + /* check ssid is valid or not, ex. hidden ssid will be filter out */ + if (woal_ssid_valid(&scan_table[i].ssid) == MFALSE) + continue; + + /* First entry *MUST* be the AP MAC address */ + iwe.cmd = SIOCGIWAP; + iwe.u.ap_addr.sa_family = ARPHRD_ETHER; + memcpy(iwe.u.ap_addr.sa_data, &scan_table[i].mac_address, + ETH_ALEN); + + iwe.len = IW_EV_ADDR_LEN; + current_ev = + IWE_STREAM_ADD_EVENT(info, current_ev, end_buf, &iwe, + iwe.len); + + /* Add the ESSID */ + iwe.u.data.length = scan_table[i].ssid.ssid_len; + + if (iwe.u.data.length > 32) + iwe.u.data.length = 32; + + iwe.cmd = SIOCGIWESSID; + iwe.u.essid.flags = (i + 1) & IW_ENCODE_INDEX; + iwe.len = IW_EV_POINT_LEN + iwe.u.data.length; + current_ev = + IWE_STREAM_ADD_POINT(info, current_ev, end_buf, &iwe, + (char *)scan_table[i].ssid.ssid); + + /* Add mode */ + iwe.cmd = SIOCGIWMODE; + if (scan_table[i].bss_mode == MLAN_BSS_MODE_IBSS) + iwe.u.mode = IW_MODE_ADHOC; + else if (scan_table[i].bss_mode == MLAN_BSS_MODE_INFRA) + iwe.u.mode = IW_MODE_MASTER; + else + iwe.u.mode = IW_MODE_AUTO; + + iwe.len = IW_EV_UINT_LEN; + current_ev = + IWE_STREAM_ADD_EVENT(info, current_ev, end_buf, &iwe, + iwe.len); + + /* Frequency */ + iwe.cmd = SIOCGIWFREQ; + iwe.u.freq.m = (long)scan_table[i].freq; + iwe.u.freq.e = 6; + iwe.u.freq.flags = IW_FREQ_FIXED; + iwe.len = IW_EV_FREQ_LEN; + current_ev = + IWE_STREAM_ADD_EVENT(info, current_ev, end_buf, &iwe, + iwe.len); + + memset(&iwe, 0, sizeof(iwe)); + /* Add quality statistics */ + iwe.cmd = IWEVQUAL; + iwe.u.qual.level = SCAN_RSSI(scan_table[i].rssi); + if (!bss_info.bcn_nf_last) + iwe.u.qual.noise = MRVDRV_NF_DEFAULT_SCAN_VALUE; + else + iwe.u.qual.noise = bss_info.bcn_nf_last; + + if ((bss_info.bss_mode == MLAN_BSS_MODE_IBSS) && + !woal_ssid_cmp(&bss_info.ssid, &scan_table[i].ssid) + && bss_info.adhoc_state == ADHOC_STARTED) { + memset(&rssi, 0, sizeof(mlan_ds_get_signal)); + if (MLAN_STATUS_SUCCESS != + woal_get_signal_info(priv, MOAL_IOCTL_WAIT, + &rssi)) { + ret = -EFAULT; + break; + } + iwe.u.qual.level = rssi.data_rssi_avg; + } + iwe.u.qual.qual = + woal_rssi_to_quality((t_s16)(iwe.u.qual.level - 0x100)); + iwe.len = IW_EV_QUAL_LEN; + current_ev = + IWE_STREAM_ADD_EVENT(info, current_ev, end_buf, &iwe, + iwe.len); + + /* Add encryption capability */ + iwe.cmd = SIOCGIWENCODE; + if (scan_table[i].privacy) + iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; + else + iwe.u.data.flags = IW_ENCODE_DISABLED; + + iwe.u.data.length = 0; + iwe.len = IW_EV_POINT_LEN + iwe.u.data.length; + current_ev = + IWE_STREAM_ADD_POINT(info, current_ev, end_buf, &iwe, + NULL); + + current_val = current_ev + IW_EV_LCP_LEN; + + iwe.cmd = SIOCGIWRATE; + + iwe.u.bitrate.fixed = 0; + iwe.u.bitrate.disabled = 0; + iwe.u.bitrate.value = 0; + + /* Bit rate given in 500 kb/s units (+ 0x80) */ + for (j = 0; j < sizeof(scan_table[i].supported_rates); j++) { + if (!scan_table[i].supported_rates[j]) + break; + + iwe.u.bitrate.value = + (scan_table[i].supported_rates[j] & 0x7f) * + 500000; + iwe.len = IW_EV_PARAM_LEN; + current_val = + IWE_STREAM_ADD_VALUE(info, current_ev, + current_val, end_buf, &iwe, + iwe.len); + + } + if ((bss_info.bss_mode == MLAN_BSS_MODE_IBSS) && + !woal_ssid_cmp(&bss_info.ssid, &scan_table[i].ssid) + && bss_info.adhoc_state == ADHOC_STARTED) { + iwe.u.bitrate.value = 22 * 500000; + iwe.len = IW_EV_PARAM_LEN; + current_val = + IWE_STREAM_ADD_VALUE(info, current_ev, + current_val, end_buf, &iwe, + iwe.len); + } + + /* Check if an event is added */ + if ((unsigned int)(current_val - current_ev) >= IW_EV_PARAM_LEN) + current_ev = current_val; + + /* Beacon Interval */ + memset(&iwe, 0, sizeof(iwe)); + ptr = buf; + ptr += sprintf(ptr, "Beacon interval=%d", + scan_table[i].beacon_period); + + iwe.u.data.length = strlen(buf); + iwe.cmd = IWEVCUSTOM; + iwe.len = IW_EV_POINT_LEN + iwe.u.data.length; + current_ev = + IWE_STREAM_ADD_POINT(info, current_ev, end_buf, &iwe, + buf); + current_val = current_ev + IW_EV_LCP_LEN + strlen(buf); + + /* Parse and send the IEs */ + pbeacon = scan_table[i].pbeacon_buf; + beacon_size = scan_table[i].beacon_buf_size; + + /* Skip time stamp, beacon interval and capability */ + if (pbeacon) { + pbeacon += sizeof(scan_table[i].beacon_period) + + sizeof(scan_table[i].time_stamp) + + sizeof(scan_table[i].cap_info); + beacon_size -= sizeof(scan_table[i].beacon_period) + + sizeof(scan_table[i].time_stamp) + + sizeof(scan_table[i].cap_info); + + while ((unsigned int)beacon_size >= + sizeof(IEEEtypes_Header_t)) { + element_id = + (IEEEtypes_ElementId_e)(*(t_u8 *) + pbeacon); + element_len = *((t_u8 *)pbeacon + 1); + if ((unsigned int)beacon_size < + (unsigned int)element_len + + sizeof(IEEEtypes_Header_t)) { + PRINTM(MERROR, + "Get scan: Error in processing IE, " + "bytes left < IE length\n"); + break; + } + + switch (element_id) { +#if WIRELESS_EXT >= 18 + case VENDOR_SPECIFIC_221: + case RSN_IE: + case WAPI_IE: + praw_data = (t_u8 *)pbeacon; + memset(&iwe, 0, sizeof(iwe)); + memset(buf, 0, buf_size); + ptr = buf; + memcpy(buf, praw_data, + element_len + + sizeof(IEEEtypes_Header_t)); + iwe.cmd = IWEVGENIE; + iwe.u.data.length = + element_len + + sizeof(IEEEtypes_Header_t); + iwe.len = + IW_EV_POINT_LEN + + iwe.u.data.length; + current_ev = + IWE_STREAM_ADD_POINT(info, + current_ev, + end_buf, + &iwe, buf); + current_val = + current_ev + IW_EV_LCP_LEN + + strlen(buf); + break; +#endif + default: + break; + } + pbeacon += + element_len + + sizeof(IEEEtypes_Header_t); + beacon_size -= + element_len + + sizeof(IEEEtypes_Header_t); + } + } +#if WIRELESS_EXT > 14 + memset(&iwe, 0, sizeof(iwe)); + memset(buf, 0, buf_size); + ptr = buf; + ptr += sprintf(ptr, "band="); + memset(&iwe, 0, sizeof(iwe)); + if (scan_table[i].bss_band == BAND_A) + ptr += sprintf(ptr, "a"); + else + ptr += sprintf(ptr, "bg"); + iwe.u.data.length = strlen(buf); + PRINTM(MINFO, "iwe.u.data.length %d\n", iwe.u.data.length); + PRINTM(MINFO, "BUF: %s\n", buf); + iwe.cmd = IWEVCUSTOM; + iwe.len = IW_EV_POINT_LEN + iwe.u.data.length; + current_ev = + IWE_STREAM_ADD_POINT(info, current_ev, end_buf, &iwe, + buf); + current_val = current_ev + IW_EV_LCP_LEN + strlen(buf); +#endif + current_val = current_ev + IW_EV_LCP_LEN; + + /* + * Check if we added any event + */ + if ((unsigned int)(current_val - current_ev) > IW_EV_LCP_LEN) + current_ev = current_val; + } + + dwrq->length = (current_ev - extra); + dwrq->flags = 0; +#endif + +done: + kfree(buf); + LEAVE(); + return ret; +} + +/** + * iwconfig settable callbacks + */ +static const iw_handler woal_handler[] = { + (iw_handler) woal_config_commit, /* SIOCSIWCOMMIT */ + (iw_handler) woal_get_name, /* SIOCGIWNAME */ + (iw_handler) NULL, /* SIOCSIWNWID */ + (iw_handler) NULL, /* SIOCGIWNWID */ + (iw_handler) woal_set_freq, /* SIOCSIWFREQ */ + (iw_handler) woal_get_freq, /* SIOCGIWFREQ */ + (iw_handler) woal_set_bss_mode, /* SIOCSIWMODE */ + (iw_handler) woal_get_bss_mode, /* SIOCGIWMODE */ + (iw_handler) woal_set_sens, /* SIOCSIWSENS */ + (iw_handler) woal_get_sens, /* SIOCGIWSENS */ + (iw_handler) NULL, /* SIOCSIWRANGE */ + (iw_handler) woal_get_range, /* SIOCGIWRANGE */ + (iw_handler) woal_set_priv, /* SIOCSIWPRIV */ + (iw_handler) NULL, /* SIOCGIWPRIV */ + (iw_handler) NULL, /* SIOCSIWSTATS */ + (iw_handler) NULL, /* SIOCGIWSTATS */ +#if WIRELESS_EXT > 15 +#ifdef CONFIG_WEXT_SPY + iw_handler_set_spy, /* SIOCSIWSPY */ + iw_handler_get_spy, /* SIOCGIWSPY */ + iw_handler_set_thrspy, /* SIOCSIWTHRSPY */ + iw_handler_get_thrspy, /* SIOCGIWTHRSPY */ +#else + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) NULL, /* -- hole -- */ +#endif +#else /* WIRELESS_EXT > 15 */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) NULL, /* -- hole -- */ +#endif /* WIRELESS_EXT > 15 */ + (iw_handler) woal_set_wap, /* SIOCSIWAP */ + (iw_handler) woal_get_wap, /* SIOCGIWAP */ +#if WIRELESS_EXT >= 18 + (iw_handler) woal_set_mlme, /* SIOCSIWMLME */ +#else + (iw_handler) NULL, /* -- hole -- */ +#endif + /* (iw_handler) wlan_get_aplist, *//* SIOCGIWAPLIST */ + NULL, /* SIOCGIWAPLIST */ +#if WIRELESS_EXT > 13 + (iw_handler) woal_set_scan, /* SIOCSIWSCAN */ + (iw_handler) woal_get_scan, /* SIOCGIWSCAN */ +#else /* WIRELESS_EXT > 13 */ + (iw_handler) NULL, /* SIOCSIWSCAN */ + (iw_handler) NULL, /* SIOCGIWSCAN */ +#endif /* WIRELESS_EXT > 13 */ + (iw_handler) woal_set_essid, /* SIOCSIWESSID */ + (iw_handler) woal_get_essid, /* SIOCGIWESSID */ + (iw_handler) woal_set_nick, /* SIOCSIWNICKN */ + (iw_handler) woal_get_nick, /* SIOCGIWNICKN */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) woal_set_rate, /* SIOCSIWRATE */ + (iw_handler) woal_get_rate, /* SIOCGIWRATE */ + (iw_handler) woal_set_rts, /* SIOCSIWRTS */ + (iw_handler) woal_get_rts, /* SIOCGIWRTS */ + (iw_handler) woal_set_frag, /* SIOCSIWFRAG */ + (iw_handler) woal_get_frag, /* SIOCGIWFRAG */ + (iw_handler) woal_set_txpow, /* SIOCSIWTXPOW */ + (iw_handler) woal_get_txpow, /* SIOCGIWTXPOW */ + (iw_handler) woal_set_retry, /* SIOCSIWRETRY */ + (iw_handler) woal_get_retry, /* SIOCGIWRETRY */ + (iw_handler) woal_set_encode, /* SIOCSIWENCODE */ + (iw_handler) woal_get_encode, /* SIOCGIWENCODE */ + (iw_handler) woal_set_power, /* SIOCSIWPOWER */ + (iw_handler) woal_get_power, /* SIOCGIWPOWER */ +#if (WIRELESS_EXT >= 18) + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) woal_set_gen_ie, /* SIOCSIWGENIE */ + (iw_handler) woal_get_gen_ie, /* SIOCGIWGENIE */ + (iw_handler) woal_set_auth, /* SIOCSIWAUTH */ + (iw_handler) woal_get_auth, /* SIOCGIWAUTH */ + (iw_handler) woal_set_encode_ext, /* SIOCSIWENCODEEXT */ + (iw_handler) woal_get_encode_ext, /* SIOCGIWENCODEEXT */ + (iw_handler) woal_set_pmksa, /* SIOCSIWPMKSA */ +#endif /* WIRELESSS_EXT >= 18 */ +}; + +/** + * iwpriv settable callbacks + */ +static const iw_handler woal_private_handler[] = { + NULL, /* SIOCIWFIRSTPRIV */ +}; +#endif /* STA_SUPPORT */ + +/******************************************************** + Global Functions +********************************************************/ + +#if WIRELESS_EXT > 14 + +/** + * @brief This function sends customized event to application. + * + * @param priv A pointer to moal_private structure + * @param str A pointer to event string + * + * @return N/A + */ +void +woal_send_iwevcustom_event(moal_private *priv, char *str) +{ + union iwreq_data iwrq; + char buf[IW_CUSTOM_MAX]; + + ENTER(); + + memset(&iwrq, 0, sizeof(union iwreq_data)); + memset(buf, 0, sizeof(buf)); + + snprintf(buf, sizeof(buf) - 1, "%s", str); + + iwrq.data.pointer = buf; + iwrq.data.length = strlen(buf) + 1; + + /* Send Event to upper layer */ + wireless_send_event(priv->netdev, IWEVCUSTOM, &iwrq, buf); + PRINTM(MINFO, "Wireless event %s is sent to application\n", str); + + LEAVE(); + return; +} +#endif + +#if WIRELESS_EXT >= 18 +/** + * @brief This function sends mic error event to application. + * + * @param priv A pointer to moal_private structure + * @param event MIC MERROR EVENT. + * + * @return N/A + */ +void +woal_send_mic_error_event(moal_private *priv, t_u32 event) +{ + union iwreq_data iwrq; + struct iw_michaelmicfailure mic; + + ENTER(); + + memset(&iwrq, 0, sizeof(iwrq)); + memset(&mic, 0, sizeof(mic)); + if (event == MLAN_EVENT_ID_FW_MIC_ERR_UNI) + mic.flags = IW_MICFAILURE_PAIRWISE; + else + mic.flags = IW_MICFAILURE_GROUP; + iwrq.data.pointer = &mic; + iwrq.data.length = sizeof(mic); + + wireless_send_event(priv->netdev, IWEVMICHAELMICFAILURE, &iwrq, + (char *)&mic); + + LEAVE(); + return; +} +#endif + +#ifdef STA_SUPPORT +/** wlan_handler_def */ +struct iw_handler_def woal_handler_def = { +num_standard:ARRAY_SIZE(woal_handler), +num_private:ARRAY_SIZE(woal_private_handler), +num_private_args:ARRAY_SIZE(woal_private_args), +standard:(iw_handler *) woal_handler, +private:(iw_handler *) woal_private_handler, +private_args:(struct iw_priv_args *)woal_private_args, +#if WIRELESS_EXT > 20 +get_wireless_stats:woal_get_wireless_stats, +#endif +}; + +/** + * @brief Get wireless statistics + * + * @param dev A pointer to net_device structure + * + * @return A pointer to iw_statistics buf + */ +struct iw_statistics * +woal_get_wireless_stats(struct net_device *dev) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + t_u16 wait_option = MOAL_IOCTL_WAIT; + + ENTER(); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) + /* + * Since schedule() is not allowed from an atomic context + * such as when dev_base_lock for netdevices is acquired + * for reading/writing in kernel before this call, HostCmd + * is issued in non-blocking way in such contexts and + * blocking in other cases. + */ + if (in_atomic() || !write_can_lock(&dev_base_lock)) + wait_option = MOAL_NO_WAIT; +#endif + + priv->w_stats.status = woal_get_mode(priv, wait_option); + priv->w_stats.discard.retries = priv->stats.tx_errors; + priv->w_stats.qual.qual = 0; + + /* Send RSSI command to get beacon RSSI/NF, valid only if associated */ + if (priv->media_connected == MTRUE) { + if (MLAN_STATUS_SUCCESS == + woal_get_signal_info(priv, wait_option, NULL)) + priv->w_stats.qual.qual = + woal_rssi_to_quality((t_s16) + (priv->w_stats.qual.level - + 0x100)); + } +#if WIRELESS_EXT > 18 + priv->w_stats.qual.updated |= (IW_QUAL_ALL_UPDATED | IW_QUAL_DBM); +#else + priv->w_stats.qual.updated |= 7; +#endif + if (!priv->w_stats.qual.noise && priv->media_connected == MTRUE) + priv->w_stats.qual.noise = MRVDRV_NF_DEFAULT_SCAN_VALUE; + + PRINTM(MINFO, "Link Quality = %#x\n", priv->w_stats.qual.qual); + PRINTM(MINFO, "Signal Level = %#x\n", priv->w_stats.qual.level); + PRINTM(MINFO, "Noise = %#x\n", priv->w_stats.qual.noise); + priv->w_stats.discard.code = 0; + woal_get_stats_info(priv, wait_option, NULL); + + LEAVE(); + return &priv->w_stats; +} +#endif /* STA_SUPPORT */ diff --git a/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_wext.h b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_wext.h new file mode 100644 index 000000000000..7266596871a1 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/mvl88w8977/mlinux/moal_wext.h @@ -0,0 +1,51 @@ +/** @file moal_wext.h + * + * @brief This file contains definition for wireless extension IOCTL call. + * + * Copyright (C) 2008-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +/******************************************************** +Change log: + 10/21/2008: initial version +********************************************************/ + +#ifndef _WOAL_WEXT_H_ +#define _WOAL_WEXT_H_ + +/** NF value for default scan */ +#define MRVDRV_NF_DEFAULT_SCAN_VALUE (-96) + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) +/** Add event */ +#define IWE_STREAM_ADD_EVENT(i, c, e, w, l) iwe_stream_add_event((i), (c), (e), (w), (l)) +/** Add point */ +#define IWE_STREAM_ADD_POINT(i, c, e, w, p) iwe_stream_add_point((i), (c), (e), (w), (p)) +/** Add value */ +#define IWE_STREAM_ADD_VALUE(i, c, v, e, w, l) iwe_stream_add_value((i), (c), (v), (e), (w), (l)) +#else +/** Add event */ +#define IWE_STREAM_ADD_EVENT(i, c, e, w, l) iwe_stream_add_event((c), (e), (w), (l)) +/** Add point */ +#define IWE_STREAM_ADD_POINT(i, c, e, w, p) iwe_stream_add_point((c), (e), (w), (p)) +/** Add value */ +#define IWE_STREAM_ADD_VALUE(i, c, v, e, w, l) iwe_stream_add_value((c), (v), (e), (w), (l)) +#endif + +extern struct iw_handler_def woal_handler_def; +struct iw_statistics *woal_get_wireless_stats(struct net_device *dev); +#endif /* _WOAL_WEXT_H_ */ diff --git a/include/linux/rfkill-wlan.h b/include/linux/rfkill-wlan.h index f529cfd43b78..649dcdcaef5e 100644 --- a/include/linux/rfkill-wlan.h +++ b/include/linux/rfkill-wlan.h @@ -73,6 +73,7 @@ enum { WIFI_RTL8812AU, WIFI_RTL_SERIES, WIFI_ESP8089, + WIFI_MVL88W8977, TYPE_MAX, }; diff --git a/net/rfkill/rfkill-wlan.c b/net/rfkill/rfkill-wlan.c index a4b6e5d11c08..104fc17ee14c 100644 --- a/net/rfkill/rfkill-wlan.c +++ b/net/rfkill/rfkill-wlan.c @@ -93,6 +93,8 @@ static const char wlan_name[] = "ap6476" #elif defined(CONFIG_AP6493) "ap6493" +#elif defined(CONFIG_MVL88W8977) + "mvl88w8977" #else "wlan_default" #endif @@ -154,6 +156,8 @@ int get_wifi_chip_type(void) type = WIFI_RTL8812AU; } else if (strcmp(wifi_chip_type_string, "esp8089") == 0) { type = WIFI_ESP8089; + } else if (strcmp(wifi_chip_type_string, "mvl88w8977") == 0) { + type = WIFI_MVL88W8977; } else { type = WIFI_AP6210; }