From b5c77922776bc0d3cd5cb31c0794406638841013 Mon Sep 17 00:00:00 2001 From: Mohan T Date: Thu, 26 Apr 2012 10:53:57 +0530 Subject: net: wireless: sd8797: Marvell sd8797 Wi-Fi driver Initial commit for Marvell sd8797 Wi-Fi driver Package Ver: T3T-14.69.11.p111-M2614303_A0B0-MGPL Bug 954218 Change-Id: I76fcadb5cda054d1e489c4cff77a3c461bdac742 Signed-off-by: Mohan T Reviewed-on: http://git-master/r/97305 Reviewed-by: Bharat Nihalani --- drivers/net/wireless/sd8797/Makefile | 474 ++ drivers/net/wireless/sd8797/mlan/mlan.h | 35 + drivers/net/wireless/sd8797/mlan/mlan_11d.c | 1514 +++++ drivers/net/wireless/sd8797/mlan/mlan_11h.c | 3180 ++++++++++ drivers/net/wireless/sd8797/mlan/mlan_11h.h | 158 + drivers/net/wireless/sd8797/mlan/mlan_11n.c | 1937 ++++++ drivers/net/wireless/sd8797/mlan/mlan_11n.h | 420 ++ drivers/net/wireless/sd8797/mlan/mlan_11n_aggr.c | 508 ++ drivers/net/wireless/sd8797/mlan/mlan_11n_aggr.h | 37 + .../net/wireless/sd8797/mlan/mlan_11n_rxreorder.c | 1135 ++++ .../net/wireless/sd8797/mlan/mlan_11n_rxreorder.h | 102 + drivers/net/wireless/sd8797/mlan/mlan_cfp.c | 1148 ++++ drivers/net/wireless/sd8797/mlan/mlan_cmdevt.c | 2927 +++++++++ drivers/net/wireless/sd8797/mlan/mlan_decl.h | 883 +++ drivers/net/wireless/sd8797/mlan/mlan_fw.h | 4630 ++++++++++++++ drivers/net/wireless/sd8797/mlan/mlan_ieee.h | 1318 ++++ drivers/net/wireless/sd8797/mlan/mlan_init.c | 1038 +++ drivers/net/wireless/sd8797/mlan/mlan_init.h | 88 + drivers/net/wireless/sd8797/mlan/mlan_ioctl.h | 2918 +++++++++ drivers/net/wireless/sd8797/mlan/mlan_join.c | 1853 ++++++ drivers/net/wireless/sd8797/mlan/mlan_join.h | 40 + drivers/net/wireless/sd8797/mlan/mlan_main.h | 2693 ++++++++ drivers/net/wireless/sd8797/mlan/mlan_meas.c | 464 ++ drivers/net/wireless/sd8797/mlan/mlan_meas.h | 54 + drivers/net/wireless/sd8797/mlan/mlan_misc.c | 2153 +++++++ drivers/net/wireless/sd8797/mlan/mlan_module.c | 47 + drivers/net/wireless/sd8797/mlan/mlan_scan.c | 4155 ++++++++++++ drivers/net/wireless/sd8797/mlan/mlan_sdio.c | 1661 +++++ drivers/net/wireless/sd8797/mlan/mlan_sdio.h | 307 + drivers/net/wireless/sd8797/mlan/mlan_shim.c | 939 +++ drivers/net/wireless/sd8797/mlan/mlan_sta_cmd.c | 1888 ++++++ .../net/wireless/sd8797/mlan/mlan_sta_cmdresp.c | 1640 +++++ drivers/net/wireless/sd8797/mlan/mlan_sta_event.c | 595 ++ drivers/net/wireless/sd8797/mlan/mlan_sta_ioctl.c | 5257 ++++++++++++++++ drivers/net/wireless/sd8797/mlan/mlan_sta_rx.c | 287 + drivers/net/wireless/sd8797/mlan/mlan_sta_tx.c | 276 + drivers/net/wireless/sd8797/mlan/mlan_txrx.c | 374 ++ drivers/net/wireless/sd8797/mlan/mlan_uap.h | 104 + .../net/wireless/sd8797/mlan/mlan_uap_cmdevent.c | 3021 +++++++++ drivers/net/wireless/sd8797/mlan/mlan_uap_ioctl.c | 1393 +++++ drivers/net/wireless/sd8797/mlan/mlan_uap_txrx.c | 556 ++ drivers/net/wireless/sd8797/mlan/mlan_util.h | 526 ++ drivers/net/wireless/sd8797/mlan/mlan_wmm.c | 2534 ++++++++ drivers/net/wireless/sd8797/mlan/mlan_wmm.h | 182 + drivers/net/wireless/sd8797/mlinux/moal_cfg80211.c | 1263 ++++ drivers/net/wireless/sd8797/mlinux/moal_cfg80211.h | 195 + drivers/net/wireless/sd8797/mlinux/moal_debug.c | 674 ++ .../net/wireless/sd8797/mlinux/moal_eth_ioctl.c | 1143 ++++ .../net/wireless/sd8797/mlinux/moal_eth_ioctl.h | 69 + drivers/net/wireless/sd8797/mlinux/moal_ioctl.c | 4307 +++++++++++++ drivers/net/wireless/sd8797/mlinux/moal_main.c | 3773 +++++++++++ drivers/net/wireless/sd8797/mlinux/moal_main.h | 1510 +++++ drivers/net/wireless/sd8797/mlinux/moal_priv.c | 6596 ++++++++++++++++++++ drivers/net/wireless/sd8797/mlinux/moal_priv.h | 715 +++ drivers/net/wireless/sd8797/mlinux/moal_proc.c | 631 ++ drivers/net/wireless/sd8797/mlinux/moal_sdio.h | 140 + drivers/net/wireless/sd8797/mlinux/moal_sdio_mmc.c | 728 +++ drivers/net/wireless/sd8797/mlinux/moal_shim.c | 1458 +++++ drivers/net/wireless/sd8797/mlinux/moal_shim.h | 95 + .../net/wireless/sd8797/mlinux/moal_sta_cfg80211.c | 2476 ++++++++ .../net/wireless/sd8797/mlinux/moal_sta_cfg80211.h | 41 + drivers/net/wireless/sd8797/mlinux/moal_uap.c | 2692 ++++++++ drivers/net/wireless/sd8797/mlinux/moal_uap.h | 410 ++ .../net/wireless/sd8797/mlinux/moal_uap_cfg80211.c | 1762 ++++++ .../net/wireless/sd8797/mlinux/moal_uap_cfg80211.h | 30 + drivers/net/wireless/sd8797/mlinux/moal_uap_priv.c | 175 + drivers/net/wireless/sd8797/mlinux/moal_uap_priv.h | 194 + drivers/net/wireless/sd8797/mlinux/moal_uap_wext.c | 1766 ++++++ drivers/net/wireless/sd8797/mlinux/moal_wext.c | 3048 +++++++++ drivers/net/wireless/sd8797/mlinux/moal_wext.h | 116 + 70 files changed, 93456 insertions(+) create mode 100644 drivers/net/wireless/sd8797/Makefile create mode 100644 drivers/net/wireless/sd8797/mlan/mlan.h create mode 100644 drivers/net/wireless/sd8797/mlan/mlan_11d.c create mode 100644 drivers/net/wireless/sd8797/mlan/mlan_11h.c create mode 100644 drivers/net/wireless/sd8797/mlan/mlan_11h.h create mode 100644 drivers/net/wireless/sd8797/mlan/mlan_11n.c create mode 100644 drivers/net/wireless/sd8797/mlan/mlan_11n.h create mode 100644 drivers/net/wireless/sd8797/mlan/mlan_11n_aggr.c create mode 100644 drivers/net/wireless/sd8797/mlan/mlan_11n_aggr.h create mode 100644 drivers/net/wireless/sd8797/mlan/mlan_11n_rxreorder.c create mode 100644 drivers/net/wireless/sd8797/mlan/mlan_11n_rxreorder.h create mode 100644 drivers/net/wireless/sd8797/mlan/mlan_cfp.c create mode 100644 drivers/net/wireless/sd8797/mlan/mlan_cmdevt.c create mode 100644 drivers/net/wireless/sd8797/mlan/mlan_decl.h create mode 100644 drivers/net/wireless/sd8797/mlan/mlan_fw.h create mode 100644 drivers/net/wireless/sd8797/mlan/mlan_ieee.h create mode 100644 drivers/net/wireless/sd8797/mlan/mlan_init.c create mode 100644 drivers/net/wireless/sd8797/mlan/mlan_init.h create mode 100644 drivers/net/wireless/sd8797/mlan/mlan_ioctl.h create mode 100644 drivers/net/wireless/sd8797/mlan/mlan_join.c create mode 100644 drivers/net/wireless/sd8797/mlan/mlan_join.h create mode 100644 drivers/net/wireless/sd8797/mlan/mlan_main.h create mode 100644 drivers/net/wireless/sd8797/mlan/mlan_meas.c create mode 100644 drivers/net/wireless/sd8797/mlan/mlan_meas.h create mode 100644 drivers/net/wireless/sd8797/mlan/mlan_misc.c create mode 100644 drivers/net/wireless/sd8797/mlan/mlan_module.c create mode 100644 drivers/net/wireless/sd8797/mlan/mlan_scan.c create mode 100644 drivers/net/wireless/sd8797/mlan/mlan_sdio.c create mode 100644 drivers/net/wireless/sd8797/mlan/mlan_sdio.h create mode 100644 drivers/net/wireless/sd8797/mlan/mlan_shim.c create mode 100644 drivers/net/wireless/sd8797/mlan/mlan_sta_cmd.c create mode 100644 drivers/net/wireless/sd8797/mlan/mlan_sta_cmdresp.c create mode 100644 drivers/net/wireless/sd8797/mlan/mlan_sta_event.c create mode 100644 drivers/net/wireless/sd8797/mlan/mlan_sta_ioctl.c create mode 100644 drivers/net/wireless/sd8797/mlan/mlan_sta_rx.c create mode 100644 drivers/net/wireless/sd8797/mlan/mlan_sta_tx.c create mode 100644 drivers/net/wireless/sd8797/mlan/mlan_txrx.c create mode 100644 drivers/net/wireless/sd8797/mlan/mlan_uap.h create mode 100644 drivers/net/wireless/sd8797/mlan/mlan_uap_cmdevent.c create mode 100644 drivers/net/wireless/sd8797/mlan/mlan_uap_ioctl.c create mode 100644 drivers/net/wireless/sd8797/mlan/mlan_uap_txrx.c create mode 100644 drivers/net/wireless/sd8797/mlan/mlan_util.h create mode 100644 drivers/net/wireless/sd8797/mlan/mlan_wmm.c create mode 100644 drivers/net/wireless/sd8797/mlan/mlan_wmm.h create mode 100644 drivers/net/wireless/sd8797/mlinux/moal_cfg80211.c create mode 100644 drivers/net/wireless/sd8797/mlinux/moal_cfg80211.h create mode 100644 drivers/net/wireless/sd8797/mlinux/moal_debug.c create mode 100644 drivers/net/wireless/sd8797/mlinux/moal_eth_ioctl.c create mode 100644 drivers/net/wireless/sd8797/mlinux/moal_eth_ioctl.h create mode 100644 drivers/net/wireless/sd8797/mlinux/moal_ioctl.c create mode 100644 drivers/net/wireless/sd8797/mlinux/moal_main.c create mode 100644 drivers/net/wireless/sd8797/mlinux/moal_main.h create mode 100644 drivers/net/wireless/sd8797/mlinux/moal_priv.c create mode 100644 drivers/net/wireless/sd8797/mlinux/moal_priv.h create mode 100644 drivers/net/wireless/sd8797/mlinux/moal_proc.c create mode 100644 drivers/net/wireless/sd8797/mlinux/moal_sdio.h create mode 100644 drivers/net/wireless/sd8797/mlinux/moal_sdio_mmc.c create mode 100644 drivers/net/wireless/sd8797/mlinux/moal_shim.c create mode 100644 drivers/net/wireless/sd8797/mlinux/moal_shim.h create mode 100644 drivers/net/wireless/sd8797/mlinux/moal_sta_cfg80211.c create mode 100644 drivers/net/wireless/sd8797/mlinux/moal_sta_cfg80211.h create mode 100644 drivers/net/wireless/sd8797/mlinux/moal_uap.c create mode 100644 drivers/net/wireless/sd8797/mlinux/moal_uap.h create mode 100644 drivers/net/wireless/sd8797/mlinux/moal_uap_cfg80211.c create mode 100644 drivers/net/wireless/sd8797/mlinux/moal_uap_cfg80211.h create mode 100644 drivers/net/wireless/sd8797/mlinux/moal_uap_priv.c create mode 100644 drivers/net/wireless/sd8797/mlinux/moal_uap_priv.h create mode 100644 drivers/net/wireless/sd8797/mlinux/moal_uap_wext.c create mode 100644 drivers/net/wireless/sd8797/mlinux/moal_wext.c create mode 100644 drivers/net/wireless/sd8797/mlinux/moal_wext.h (limited to 'drivers') diff --git a/drivers/net/wireless/sd8797/Makefile b/drivers/net/wireless/sd8797/Makefile new file mode 100644 index 000000000000..52e3a3df3988 --- /dev/null +++ b/drivers/net/wireless/sd8797/Makefile @@ -0,0 +1,474 @@ +# File: Makefile +# +# Copyright (C) 2008-2012, 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. + +CC= $(CROSS_COMPILE)gcc +LD= $(CROSS_COMPILE)ld +BACKUP= /root/backup +YMD= `date +%Y%m%d%H%M` + +############################################################################# +# Configuration Options +############################################################################# + +# Debug Option +# DEBUG LEVEL n/1/2: +# n: NO DEBUG +# 1: 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 + +# Enable WEXT for STA +CONFIG_STA_WEXT=y + +# Enable WEXT for uAP +CONFIG_UAP_WEXT=y + +# 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 + +# 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 + +# Manufacturing firmware support +CONFIG_MFG_CMD_SUPPORT=y + +# Big-endian platform +CONFIG_BIG_ENDIAN=n + +# 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 + +# Use static link for app build +export CONFIG_STATIC_LINK=y + +############################################################################# +# Select Platform Tools +############################################################################# + +MODEXT = ko +EXTRA_CFLAGS += -I$(M)/mlan +EXTRA_CFLAGS += -DLINUX + +# KERNELDIR point to the installed kernel directory. +# KERNELDIR can be set on the command line, +# make KERNELDIR=/usr/src/arm/ +# Alternatively KERNELDIR can be set in the environment. +# Default value for KERNELDIR is set below. +KERNELDIR ?= /usr/src/arm/linux-2.6.39-T3T + +# CROSS_COMPILE specify the prefix used for all executables used +# during compilation. Only gcc and related bin-utils executables +# CROSS_COMPILE can be set on the command line +# make CROSS_COMPILE=arm-linux- +# Alternatively CROSS_COMPILE can be set in the environment. +# Default value for CROSS_COMPILE is set below. +CROSS_COMPILE ?= /usr/local/arm-eabi-4.4.3/bin/arm-eabi- + +# INSTALLDIR specify the path to install the kernel module after +# succesful compilation. +# INSTALLDIR can be set on the command line +# make INSTALLDIR=/tftpboot/ +# Alternatively INSTALLDIR can be set in the environment. +# Default value for INSTALL is set below. +INSTALLDIR ?= /tftpboot/pxa9xx/root + +# ARCH specifies the architecture of the target processor, this kernel +# module will run. +# ARCH can be set on the command line +# make ARCH= +# Alternatively ARCH can be set in the environment +# Default values of ARCH for specific platform are set below. +ARCH ?= arm + +LD += -S + +BINDIR = ../bin_sd8797 +APPDIR= $(shell if test -d "mapp"; then echo mapp; fi) + +############################################################################# +# Compiler Flags +############################################################################# + + EXTRA_CFLAGS += -I$(KERNELDIR)/include + + EXTRA_CFLAGS += -DFPNUM='"69"' + +ifeq ($(CONFIG_DEBUG),1) + EXTRA_CFLAGS += -DDEBUG_LEVEL1 +endif + +ifeq ($(CONFIG_DEBUG),2) + EXTRA_CFLAGS += -DDEBUG_LEVEL1 + EXTRA_CFLAGS += -DDEBUG_LEVEL2 + DBG= -dbg +endif + +ifeq ($(CONFIG_PROC_DEBUG),y) + EXTRA_CFLAGS += -DPROC_DEBUG + export CONFIG_PROC_DEBUG +endif + +ifeq ($(CONFIG_64BIT), y) + EXTRA_CFLAGS += -DMLAN_64BIT +endif + +ifeq ($(CONFIG_STA_SUPPORT),y) + EXTRA_CFLAGS += -DSTA_SUPPORT +ifeq ($(CONFIG_REASSOCIATION),y) + EXTRA_CFLAGS += -DREASSOCIATION +endif +ifeq ($(CONFIG_STA_WEXT),y) + EXTRA_CFLAGS += -DSTA_WEXT +endif +ifeq ($(CONFIG_STA_CFG80211),y) + EXTRA_CFLAGS += -DSTA_CFG80211 +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) + EXTRA_CFLAGS += -DUAP_SUPPORT +ifeq ($(CONFIG_UAP_WEXT),y) + EXTRA_CFLAGS += -DUAP_WEXT +endif +ifeq ($(CONFIG_UAP_CFG80211),y) + EXTRA_CFLAGS += -DUAP_CFG80211 +endif +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) + EXTRA_CFLAGS += -DWIFI_DIRECT_SUPPORT +endif +ifeq ($(CONFIG_WIFI_DISPLAY_SUPPORT),y) + EXTRA_CFLAGS += -DWIFI_DISPLAY_SUPPORT +endif + +ifeq ($(CONFIG_MFG_CMD_SUPPORT),y) + EXTRA_CFLAGS += -DMFG_CMD_SUPPORT +endif + +ifeq ($(CONFIG_BIG_ENDIAN),y) + EXTRA_CFLAGS += -DBIG_ENDIAN_SUPPORT +endif + +ifeq ($(CONFIG_SDIO_MULTI_PORT_TX_AGGR),y) + EXTRA_CFLAGS += -DSDIO_MULTI_PORT_TX_AGGR +endif + +ifeq ($(CONFIG_SDIO_MULTI_PORT_RX_AGGR),y) + EXTRA_CFLAGS += -DSDIO_MULTI_PORT_RX_AGGR +endif + + +ifeq ($(CONFIG_SDIO_SUSPEND_RESUME),y) + EXTRA_CFLAGS += -DSDIO_SUSPEND_RESUME +endif + +ifeq ($(CONFIG_DFS_TESTING_SUPPORT),y) + EXTRA_CFLAGS += -DDFS_TESTING_SUPPORT +endif + +############################################################################# +# Make Targets +############################################################################# + +ifneq ($(KERNELRELEASE),) + +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 +ifeq ($(CONFIG_STA_WEXT),y) +MOALOBJS += mlinux/moal_wext.o +endif +endif +ifeq ($(CONFIG_STA_CFG80211),y) +MOALOBJS += mlinux/moal_cfg80211.o +MOALOBJS += mlinux/moal_sta_cfg80211.o +endif +ifeq ($(CONFIG_UAP_CFG80211),y) +MOALOBJS += mlinux/moal_cfg80211.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 + +obj-m := mlan.o +mlan-objs := $(MLANOBJS) +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: +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 + $(MAKE) -C $(KERNELDIR) M=$(PWD) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) modules + +endif + +############################################################### + +export CC LD EXTRA_CFLAGS KERNELDIR + +ifeq ($(CONFIG_STA_SUPPORT),y) +ifeq ($(CONFIG_UAP_SUPPORT),y) +.PHONY: mapp/mlanconfig mapp/mlan2040coex mapp/mlanevent mapp/uaputl mapp/mlanutl clean distclean +else +.PHONY: mapp/mlanconfig mapp/mlanevent mapp/mlan2040coex mapp/mlanutl clean distclean +endif +else +ifeq ($(CONFIG_UAP_SUPPORT),y) +.PHONY: mapp/mlanevent mapp/uaputl clean distclean +endif +endif + @echo "Finished Making Marvell Wlan Linux Driver" + +ifeq ($(CONFIG_STA_SUPPORT),y) +mapp/mlanconfig: + $(MAKE) -C $@ +mapp/mlanutl: + $(MAKE) -C $@ +mapp/mlan2040coex: + $(MAKE) -C $@ +endif +ifeq ($(CONFIG_UAP_SUPPORT),y) +mapp/uaputl: + $(MAKE) -C $@ +endif +ifeq ($(CONFIG_WIFI_DIRECT_SUPPORT),y) +mapp/wifidirectutl: + $(MAKE) -C $@ +endif +mapp/mlanevent: + $(MAKE) -C $@ + +echo: + +build: echo default + + @if [ ! -d $(BINDIR) ]; then \ + mkdir $(BINDIR); \ + fi + + cp -f mlan.$(MODEXT) $(BINDIR)/mlan$(DBG).$(MODEXT) + cp -f sd8xxx.$(MODEXT) $(BINDIR)/sd8797$(DBG).$(MODEXT) + +ifeq ($(CONFIG_STA_SUPPORT),y) + cp -f README $(BINDIR) + cp -f README_RBC $(BINDIR) +ifneq ($(APPDIR),) + $(MAKE) -C mapp/mlanconfig $@ INSTALLDIR=$(BINDIR) + $(MAKE) -C mapp/mlanutl $@ INSTALLDIR=$(BINDIR) + $(MAKE) -C mapp/mlan2040coex $@ INSTALLDIR=$(BINDIR) +endif +endif +ifeq ($(CONFIG_UAP_SUPPORT),y) + cp -f README_UAP $(BINDIR) +ifneq ($(APPDIR),) + $(MAKE) -C mapp/uaputl $@ INSTALLDIR=$(BINDIR) +endif +endif +ifeq ($(CONFIG_WIFI_DIRECT_SUPPORT),y) + cp -f README_WIFIDIRECT $(BINDIR) + cp -rpf script/wifidirect $(BINDIR) +ifeq ($(CONFIG_WIFI_DISPLAY_SUPPORT),y) + cp -rpf script/wifidisplay $(BINDIR) +endif +ifneq ($(APPDIR),) + $(MAKE) -C mapp/wifidirectutl $@ INSTALLDIR=$(BINDIR) +endif +endif +ifneq ($(APPDIR),) + $(MAKE) -C mapp/mlanevent $@ INSTALLDIR=$(BINDIR) +endif + +clean: + -find . -name "*.o" -exec rm {} \; + -find . -name "*.ko" -exec rm {} \; + -find . -name ".*.cmd" -exec rm {} \; + -find . -name "*.mod.c" -exec rm {} \; + -find . -name "Module.symvers" -exec rm {} \; + -find . -name "Module.markers" -exec rm {} \; + -find . -name "modules.order" -exec rm {} \; + -rm -rf .tmp_versions +ifneq ($(APPDIR),) +ifeq ($(CONFIG_STA_SUPPORT),y) + $(MAKE) -C mapp/mlanconfig $@ + $(MAKE) -C mapp/mlanutl $@ + $(MAKE) -C mapp/mlan2040coex $@ +endif +ifeq ($(CONFIG_UAP_SUPPORT),y) + $(MAKE) -C mapp/uaputl $@ +endif +ifeq ($(CONFIG_WIFI_DIRECT_SUPPORT),y) + $(MAKE) -C mapp/wifidirectutl $@ +endif + $(MAKE) -C mapp/mlanevent $@ +endif + +install: default + + cp -f mlan.$(MODEXT) $(INSTALLDIR)/mlan$(DBG).$(MODEXT) + cp -f sd8xxx.$(MODEXT) $(INSTALLDIR)/sd8797$(DBG).$(MODEXT) + echo "sd8797 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 {} \; + -rm -rf .tmp_versions +ifneq ($(APPDIR),) +ifeq ($(CONFIG_STA_SUPPORT),y) + $(MAKE) -C mapp/mlanconfig $@ + $(MAKE) -C mapp/mlanutl $@ + $(MAKE) -C mapp/mlan2040coex $@ +endif +ifeq ($(CONFIG_UAP_SUPPORT),y) + $(MAKE) -C mapp/uaputl $@ +endif +ifeq ($(CONFIG_WIFI_DIRECT_SUPPORT),y) + $(MAKE) -C mapp/wifidirectutl $@ +endif + $(MAKE) -C mapp/mlanevent $@ +endif + +# End of file diff --git a/drivers/net/wireless/sd8797/mlan/mlan.h b/drivers/net/wireless/sd8797/mlan/mlan.h new file mode 100644 index 000000000000..a341e1547099 --- /dev/null +++ b/drivers/net/wireless/sd8797/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-2011, 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/sd8797/mlan/mlan_11d.c b/drivers/net/wireless/sd8797/mlan/mlan_11d.c new file mode 100644 index 000000000000..809de4cc9ede --- /dev/null +++ b/drivers/net/wireless/sd8797/mlan/mlan_11d.c @@ -0,0 +1,1514 @@ +/** @file mlan_11d.c + * + * @brief This file contains functions for 802.11D. + * + * Copyright (C) 2008-2011, 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 +********************************************************/ + +/** 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 ", 0xFF}, /* Japan special */ +}; + +#ifdef STA_SUPPORT +/** 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 +********************************************************/ + +/** + * @brief This function converts region string to integer code + * + * @param pmadapter A pointer to mlan_adapter structure + * @param region Region string + * @param code [output] Region code + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status +wlan_11d_region_2_code(pmlan_adapter pmadapter, t_u8 * region, OUT t_u8 * code) +{ + t_u8 i; + t_u8 size = sizeof(region_code_mapping) / sizeof(region_code_mapping_t); + + ENTER(); + + /* Look for code in mapping table */ + for (i = 0; i < size; i++) { + if (!memcmp(pmadapter, region_code_mapping[i].region, + region, COUNTRY_CODE_LEN)) { + *code = region_code_mapping[i].code; + LEAVE(); + return MLAN_STATUS_SUCCESS; + } + } + + LEAVE(); + return MLAN_STATUS_FAILURE; +} + +#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; + t_u8 size = sizeof(region_code_mapping) / sizeof(region_code_mapping_t); + + ENTER(); + + /* Look for code in mapping table */ + for (i = 0; i < size; 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 */ + if ((pmpriv = wlan_get_priv(pmadapter, MLAN_BSS_ROLE_STA)) + && 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 = sizeof(channel_freq_power_UN_BG) / sizeof(chan_freq_power_t); + } else if (band & (BAND_A | BAND_AN)) { + cfp = channel_freq_power_UN_AJ; + cfp_no = sizeof(channel_freq_power_UN_AJ) / sizeof(chan_freq_power_t); + } 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 < 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 = sizeof(channel_freq_power_UN_AJ) / sizeof(chan_freq_power_t); + } else { + cf = channel_freq_power_UN_BG; + cnt = sizeof(channel_freq_power_UN_BG) / sizeof(chan_freq_power_t); + } + + /* 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 size = sizeof(chan_freq_power_t); + 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 = + (t_u8) (sizeof(channel_freq_power_UN_BG) / size); + 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 = + sizeof(channel_freq_power_UN_AJ) / size; + 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 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 region_code = 0; + + ENTER(); + + cfg_11d = (mlan_ds_11d_cfg *) pioctl_req->pbuf; + domain_info = &cfg_11d->param.domain_info; + + /* Update region code and table based on country code */ + if (wlan_11d_region_2_code(pmadapter, domain_info->country_code, + ®ion_code) == MLAN_STATUS_SUCCESS) { + pmadapter->region_code = region_code; + ret = wlan_set_regiontable(pmpriv, region_code, pmadapter->fw_bands); + if (ret != MLAN_STATUS_SUCCESS) + goto done; + } + + wlan_11d_set_domain_info(pmpriv, domain_info->band, + 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; + + 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 region_code = 0; + + ENTER(); + + pdomain_tlv = (MrvlIEtypes_DomainParamSet_t *) domain_tlv; + + // update region code & table based on country string + if (wlan_11d_region_2_code(pmadapter, pdomain_tlv->country_code, + ®ion_code) == MLAN_STATUS_SUCCESS) { + pmadapter->region_code = region_code; + ret = wlan_set_regiontable(pmpriv, region_code, pmadapter->fw_bands); + } + + 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); + + LEAVE(); + return ret; +} +#endif diff --git a/drivers/net/wireless/sd8797/mlan/mlan_11h.c b/drivers/net/wireless/sd8797/mlan/mlan_11h.c new file mode 100644 index 000000000000..23970861db1e --- /dev/null +++ b/drivers/net/wireless/sd8797/mlan/mlan_11h.c @@ -0,0 +1,3180 @@ +/** @file mlan_11h.c + * + * @brief This file contains functions for 802.11H. + * + * Copyright (C) 2008-2011, 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" +#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 */ + +/** 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 (500) // 5 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 ((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 0x32: /* France */ + 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 0x32: /* France */ + 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 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; + break; + case 0x50: /* China */ + 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; + } + } + + /* + * 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; + + ENTER(); + + if (pstate_dfs->dfs_check_pending) { + 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)); + + /* if DFS channel, add BASIC report TLV, and set radar bit */ + if (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_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 *startPtr = MNULL; + + ENTER(); + + /* Null Checks */ + if ((ppbuffer == MNULL) || (((t_u8 *) (*ppbuffer)) == MNULL)) { + LEAVE(); + return 0; + } + + startPtr = (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 - startPtr); +} + +/** + * @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 = 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.band_config & BAND_CONFIG_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 + */ +static 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", + __FUNCTION__, 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", __FUNCTION__, + 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", + __FUNCTION__, 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 Determine if mlan_private list only contains UAP interface(s) + * + * @param priv_list List of mlan_private pointers + * @param priv_list_count Number of mlan_privates in above list + * + * @return MTRUE or MFALSE + */ +static t_bool +wlan_only_uap_priv_in_list(mlan_private ** priv_list, t_u8 priv_list_count) +{ +#if defined(STA_SUPPORT) && !defined(UAP_SUPPORT) + return MFALSE; +#else + t_u8 uap_count = 0; + t_u8 sta_count = 0; + t_u8 i; + + ENTER(); + for (i = 0; i < priv_list_count; i++) { + if (GET_BSS_ROLE(priv_list[i]) == MLAN_BSS_ROLE_UAP) + uap_count++; + else + sta_count++; + } + + LEAVE(); + return ((uap_count > 0) && (sta_count == 0)) ? MTRUE : MFALSE; +#endif +} + +/** + * @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", __FUNCTION__); + 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 = 0; /* simplification */ + + 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 +/** + * @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, t_u8 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 & UAP_BAND_CONFIG_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). + */ + 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) + && (++rand_tries < MAX_RANDOM_CHANNEL_RETRIES)); + } + } + } + } + + 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", __FUNCTION__); + 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; + + 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; + + 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", __FUNCTION__); + + 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) { + 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) { + 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); + + /* + * 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; + pstate_rdh->uap_band_cfg = 0; + pstate_rdh->max_bcn_dtim_ms = 0; + memset(adapter, pstate_rdh->priv_list, 0, sizeof(pstate_rdh->priv_list)); + +#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; + + 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 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); + + 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 + * + * @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) +{ + t_s32 ret; + HostCmd_DS_CHAN_RPT_REQ chan_rpt_req; + + 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; + chan_rpt_req.chan_desc.chanWidth = 0; // 1 for 40Mhz + chan_rpt_req.chan_desc.chanNum = channel; + chan_rpt_req.millisec_dwell_time = + WLAN_11H_CHANNEL_AVAIL_CHECK_DURATION; +#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", __FUNCTION__); + 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 { + // 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: + PRINTM(MINFO, "11h: Ret ChanRptReq. Set dfs_check_pending and wait" + " for EVENT_CHANNEL_REPORT.\n"); + priv->adapter->state_dfs.dfs_check_pending = MTRUE; + 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; + ENTER(); + priv->adapter->state_11h.recvd_chanswann_event = MTRUE; + +#ifdef STA_SUPPORT + /* do directed deauth. priv flag above will cause different reason code */ + PRINTM(MINFO, "11h: handle_event_chanswann() - sending deauth\n"); + ret = wlan_disconnect(priv, MNULL, + &priv->curr_bss_params.bss_descriptor.mac_address); + + /* 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); +#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; +} +#endif // DFS_TESTING_SUPPORT + +/** + * @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 *pChanRptRsp; + MrvlIEtypes_Data_t *pTlv; + MeasRptBasicMap_t *pMeasRptBasic; + t_u8 *pBuffer; + t_s32 evtLen; + t_u16 tlvLen; + t_u32 sec, uSec; + wlan_dfs_device_state_t *pstate_dfs = &priv->adapter->state_dfs; + + ENTER(); + pChanRptRsp = (HostCmd_DS_CHAN_RPT_RSP *) & pevent->event_buf; + DBG_HEXDUMP(MCMD_D, "11h: Event ChanRptReady (HostCmd_DS_CHAN_RPT_RSP)", + (t_u8 *) pChanRptRsp, wlan_le32_to_cpu(pevent->event_len)); + + if (wlan_le32_to_cpu(pChanRptRsp->cmd_result) == MLAN_CMD_RESULT_SUCCESS) { + pBuffer = (t_u8 *) & pChanRptRsp->tlv_buffer; + evtLen = wlan_le32_to_cpu(pevent->event_len); + evtLen -= + sizeof(HostCmd_DS_CHAN_RPT_RSP) - sizeof(pChanRptRsp->tlv_buffer); + + while (evtLen >= sizeof(MrvlIEtypesHeader_t)) { + pTlv = (MrvlIEtypes_Data_t *) pBuffer; + tlvLen = wlan_le16_to_cpu(pTlv->header.len); + + switch (wlan_le16_to_cpu(pTlv->header.type)) { + case TLV_TYPE_CHANRPT_11H_BASIC: + pMeasRptBasic = (MeasRptBasicMap_t *) & pTlv->data; + if (pMeasRptBasic->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 += (tlvLen + sizeof(pTlv->header)); + evtLen -= (tlvLen + sizeof(pTlv->header)); + evtLen = (evtLen > 0) ? evtLen : 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; + + 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) +{ + 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); + LEAVE(); + return ret; +} + +/** + * @brief Driver handling for RADAR_DETECTED event + * + * @param pmadapter Pointer to mlan_adapter + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE or MLAN_STATUS_PENDING + */ +mlan_status +wlan_11h_radar_detected_handling(mlan_adapter * pmadapter) +{ +#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; + mlan_private *pmpriv = MNULL; + t_u32 i; + wlan_radar_det_hndlg_state_t *pstate_rdh = &pmadapter->state_rdh; + + ENTER(); + + switch (pstate_rdh->stage) { + case RDH_CHK_INTFS: + PRINTM(MCMD_D, "%s(): stage(%d)=%s\n", + __FUNCTION__, 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", __FUNCTION__, + pstate_rdh->priv_list_count); + for (i = 0; i < pstate_rdh->priv_list_count; i++) + PRINTM(MINFO, "%s(): priv_list[%d] = %p\n", + __FUNCTION__, 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", + __FUNCTION__, 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; + pstate_rdh->uap_band_cfg = 0; + 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", + __FUNCTION__, 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", + __FUNCTION__, 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) { + if (wlan_only_uap_priv_in_list(pstate_rdh->priv_list, + pstate_rdh->priv_list_count)) { +#ifdef UAP_SUPPORT + /* Assume all UAPs on same channel, use first UAP */ + pmpriv = pstate_rdh->priv_list[0]; + pstate_rdh->priv_curr_idx = 0; + /* 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 +#endif + } else { + /* 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 */ + pmpriv = pstate_rdh->priv_list[pstate_rdh->priv_curr_idx]; + pstate_rdh->curr_channel = pmpriv->uap_state_chan_cb.channel; + pstate_rdh->uap_band_cfg = pmpriv->uap_state_chan_cb.band_config; + PRINTM(MCMD_D, "%s(): uap_band_cfg=0x%02x\n", + __FUNCTION__, pstate_rdh->uap_band_cfg); + } +#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 */ + i = 0; + do { +#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. + band_config); + else +#endif + pstate_rdh->new_channel = + wlan_11h_get_adhoc_start_channel(pmpriv); + } while ((pstate_rdh->new_channel == pstate_rdh->curr_channel) && (++i < MAX_RANDOM_CHANNEL_RETRIES)); /* avoid + deadloop + */ + if (i >= MAX_RANDOM_CHANNEL_RETRIES) /* report error */ + PRINTM(MERROR, "%s(): ERROR - could not choose new_chan" + " (!= curr_chan) !!\n", __FUNCTION__); + +#ifdef DFS_TESTING_SUPPORT + if (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; + } +#endif + PRINTM(MCMD_D, "%s(): curr_chan=%d, new_chan=%d\n", + __FUNCTION__, 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", + __FUNCTION__, 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", + __FUNCTION__, 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; + + if (wlan_11h_is_dfs_master(pmpriv)) { /* adhoc creator */ + 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", + __FUNCTION__, 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", + __FUNCTION__, 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 preparng CHAN_SW IE.\n", + __FUNCTION__); + break; // EXIT CASE + } + + PRINTM(MMSG, + "11h: Radar Detected - adding CHAN_SW IE to interfaces.\n"); + 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", + __FUNCTION__, 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", + __FUNCTION__, 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((5 * 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 preparng CHAN_SW IE.\n", + __FUNCTION__); + 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", + __FUNCTION__, 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_STOP_INTFS; +#ifdef DFS_TESTING_SUPPORT + if (pmadapter->dfs_test_params.no_channel_change_on_radar) { + PRINTM(MCMD_D, "dfs_testing - no channel change on radar." + " Also skip stop/restart interface stages.\n", + pmadapter->dfs_test_params.no_channel_change_on_radar); + pstate_rdh->priv_curr_idx = RDH_STAGE_FIRST_ENTRY_PRIV_IDX; + pstate_rdh->stage = RDH_RESTART_TRAFFIC; + goto rdh_restart_traffic; // skip several stages + } +#endif + // FALL THROUGH TO NEXT STAGE + + case RDH_STOP_INTFS: + PRINTM(MCMD_D, "%s(): stage(%d)=%s, priv_idx=%d\n", + __FUNCTION__, 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) + break; // EXIT CASE + // else + pstate_rdh->priv_curr_idx = RDH_STAGE_FIRST_ENTRY_PRIV_IDX; + pstate_rdh->stage = RDH_SET_NEW_CHANNEL; + // FALL THROUGH TO NEXT STAGE + + case RDH_SET_NEW_CHANNEL: + PRINTM(MCMD_D, "%s(): stage(%d)=%s, priv_idx=%d\n", + __FUNCTION__, 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; + pstate_rdh->uap_band_cfg |= UAP_BAND_CONFIG_5GHZ; /* DFS + only + in + 5GHz + */ + 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) + 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: + PRINTM(MCMD_D, "%s(): stage(%d)=%s, priv_idx=%d\n", + __FUNCTION__, 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); + ret = wlan_11h_config_master_radar_det(pmpriv, MTRUE); + ret = wlan_11h_check_update_radar_det_state(pmpriv); + } + 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) + 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: +#ifdef DFS_TESTING_SUPPORT + rdh_restart_traffic: +#endif + PRINTM(MCMD_D, "%s(): stage(%d)=%s\n", + __FUNCTION__, pstate_rdh->stage, + RDH_stage_str[pstate_rdh->stage]); + + /* 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", + __FUNCTION__, 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", + __FUNCTION__, 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", + __FUNCTION__, 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", + __FUNCTION__, 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; + } + 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(); + + /* 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].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].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/sd8797/mlan/mlan_11h.h b/drivers/net/wireless/sd8797/mlan/mlan_11h.h new file mode 100644 index 000000000000..cfa7fb915b39 --- /dev/null +++ b/drivers/net/wireless/sd8797/mlan/mlan_11h.h @@ -0,0 +1,158 @@ +/** @file mlan_11h.h + * + * @brief This header file contains data structures and + * function declarations of 802.11h + * + * Copyright (C) 2008-2011, 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) + +/** + * 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); + +/** 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); + +/** 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); +#endif + +/** 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); + +/** Handler for RADAR_DETECTED */ +extern mlan_status wlan_11h_radar_detected_handling(mlan_adapter * pmadapter); + +/** 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); + +#endif /*_MLAN_11H_ */ diff --git a/drivers/net/wireless/sd8797/mlan/mlan_11n.c b/drivers/net/wireless/sd8797/mlan/mlan_11n.c new file mode 100644 index 000000000000..d55c2e7e4ab3 --- /dev/null +++ b/drivers/net/wireless/sd8797/mlan/mlan_11n.c @@ -0,0 +1,1937 @@ +/** @file mlan_11n.c + * + * @brief This file contains functions for 11n handling. + * + * Copyright (C) 2008-2011, 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; + + ENTER(); + + cfg = (mlan_ds_11n_cfg *) pioctl_req->pbuf; + 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) { + pmadapter->usr_dot_11n_dev_cap_bg = cfg->param.htcap_cfg.htcap; + PRINTM(MINFO, "Set: UsrDot11nCap for 2.4GHz 0x%x\n", + pmadapter->usr_dot_11n_dev_cap_bg); + } + if (cfg->param.htcap_cfg.misc_cfg == BAND_SELECT_A) { + pmadapter->usr_dot_11n_dev_cap_a = cfg->param.htcap_cfg.htcap; + PRINTM(MINFO, "Set: UsrDot11nCap for 5GHz 0x%x\n", + pmadapter->usr_dot_11n_dev_cap_a); + } + if (cfg->param.htcap_cfg.misc_cfg == BAND_SELECT_BOTH) { + pmadapter->usr_dot_11n_dev_cap_bg = cfg->param.htcap_cfg.htcap; + pmadapter->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 = pmadapter->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 = pmadapter->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 HT stream configurations + * + * @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_stream_cfg(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.stream_cfg = pmadapter->usr_dev_mcs_support; + } else if (pioctl_req->action == MLAN_ACT_SET) { + switch (cfg->param.stream_cfg) { + case HT_STREAM_MODE_2X2: + if (pmadapter->hw_dev_mcs_support == HT_STREAM_MODE_1X1) { + PRINTM(MERROR, "HW does not support this mode\n"); + ret = MLAN_STATUS_FAILURE; + } else + pmadapter->usr_dev_mcs_support = cfg->param.stream_cfg; + break; + case HT_STREAM_MODE_1X1: + pmadapter->usr_dev_mcs_support = cfg->param.stream_cfg; + break; + default: + PRINTM(MERROR, "Invalid stream mode\n"); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_FAILURE; + break; + } + } + + 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(); + + if (! + (ptx_tbl = + (TxBAStreamTbl *) util_peek_list(priv->adapter->pmoal_handle, + &priv->tx_ba_stream_tbl_ptr, + priv->adapter->callbacks. + moal_spin_lock, + priv->adapter->callbacks. + moal_spin_unlock))) { + 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; + } + /* 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->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 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 { + if (pmpriv->media_connected == MTRUE) { + PRINTM(MERROR, "Can not set aggr priority table in connected" + " state\n"); + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + 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]; + } + } + + LEAVE(); + return ret; +} + +/** + * @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 { + if (pmpriv->media_connected == MTRUE) { + PRINTM(MERROR, "Can not set aggr priority table in connected" + " state\n"); + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + 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]; + } + } + } + + LEAVE(); + return ret; +} + +/** + * @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]; + + 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(pmadapter->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(pmadapter->usr_dot_11n_dev_cap_bg) + || ISSUPP_CHANWIDTH40(pmadapter->usr_dot_11n_dev_cap_a) + ) + 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(); + + if (! + (ptx_tbl = + (TxBAStreamTbl *) util_peek_list(priv->adapter->pmoal_handle, + &priv->tx_ba_stream_tbl_ptr, MNULL, + MNULL))) { + 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(); + + if (! + (ptx_tbl = + (TxBAStreamTbl *) util_peek_list(priv->adapter->pmoal_handle, + &priv->tx_ba_stream_tbl_ptr, + priv->adapter->callbacks. + moal_spin_lock, + priv->adapter->callbacks. + moal_spin_unlock))) { + 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, + MrvlIETypes_HTCap_t * pht_cap, t_u8 bands) +{ + mlan_adapter *pmadapter = priv->adapter; + t_u32 usr_dot_11n_dev_cap; + + ENTER(); + + if (bands & BAND_A) + usr_dot_11n_dev_cap = pmadapter->usr_dot_11n_dev_cap_a; + else + usr_dot_11n_dev_cap = pmadapter->usr_dot_11n_dev_cap_bg; + + if (ISSUPP_CHANWIDTH40(usr_dot_11n_dev_cap)) + SETHT_SUPPCHANWIDTH(pht_cap->ht_cap.ht_cap_info); + else + RESETHT_SUPPCHANWIDTH(pht_cap->ht_cap.ht_cap_info); + + if (ISSUPP_GREENFIELD(usr_dot_11n_dev_cap)) + SETHT_GREENFIELD(pht_cap->ht_cap.ht_cap_info); + else + RESETHT_GREENFIELD(pht_cap->ht_cap.ht_cap_info); + + if (ISSUPP_SHORTGI20(usr_dot_11n_dev_cap)) + SETHT_SHORTGI20(pht_cap->ht_cap.ht_cap_info); + else + RESETHT_SHORTGI20(pht_cap->ht_cap.ht_cap_info); + + if (ISSUPP_SHORTGI40(usr_dot_11n_dev_cap)) + SETHT_SHORTGI40(pht_cap->ht_cap.ht_cap_info); + else + RESETHT_SHORTGI40(pht_cap->ht_cap.ht_cap_info); + + if (ISSUPP_RXSTBC(usr_dot_11n_dev_cap)) + SETHT_RXSTBC(pht_cap->ht_cap.ht_cap_info, 1); + else + RESETHT_RXSTBC(pht_cap->ht_cap.ht_cap_info); + + if (ISENABLED_40MHZ_INTOLARENT(usr_dot_11n_dev_cap)) + SETHT_40MHZ_INTOLARANT(pht_cap->ht_cap.ht_cap_info); + else + RESETHT_40MHZ_INTOLARANT(pht_cap->ht_cap.ht_cap_info); + + /* No user config for LDPC coding capability yet */ + if (ISSUPP_RXLDPC(pmadapter->hw_dot_11n_dev_cap)) + SETHT_LDPCCODINGCAP(pht_cap->ht_cap.ht_cap_info); + else + RESETHT_LDPCCODINGCAP(pht_cap->ht_cap.ht_cap_info); + + /* No user config for TX STBC yet */ + if (ISSUPP_TXSTBC(pmadapter->hw_dot_11n_dev_cap)) + SETHT_TXSTBC(pht_cap->ht_cap.ht_cap_info); + else + RESETHT_TXSTBC(pht_cap->ht_cap.ht_cap_info); + + /* No user config for Delayed BACK yet */ + if (GET_DELAYEDBACK(pmadapter->hw_dot_11n_dev_cap)) + SETHT_DELAYEDBACK(pht_cap->ht_cap.ht_cap_info); + else + RESETHT_DELAYEDBACK(pht_cap->ht_cap.ht_cap_info); + + /* Need change to support 8k AMSDU receive */ + RESETHT_MAXAMSDU(pht_cap->ht_cap.ht_cap_info); + + 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 + * + * @return N/A + */ +void +wlan_fill_ht_cap_tlv(mlan_private * priv, + MrvlIETypes_HTCap_t * pht_cap, t_u8 bands) +{ + 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 = pmadapter->usr_dot_11n_dev_cap_a; + else + usr_dot_11n_dev_cap = pmadapter->usr_dot_11n_dev_cap_bg; + + /* Fill HT cap info */ + wlan_fill_cap_info(priv, pht_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(pmadapter->usr_dev_mcs_support); + /* Set MCS for 1x1/2x2 */ + memset(pmadapter, (t_u8 *) pht_cap->ht_cap.supported_mcs_set, 0xff, + rx_mcs_supp); + /* Clear all the other values */ + 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 (ISSUPP_CHANWIDTH40(usr_dot_11n_dev_cap)) + 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; +} +#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 Delayed Block Ack streams = %d\n", + GET_DELAYEDBACK(cap)); + PRINTM(MINFO, "GET_HW_SPEC: Number of Immediate Block Ack streams = %d\n", + GET_IMMEDIATEBACK(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)); + + if ((ptx_ba_tbl = wlan_11n_get_txbastream_status(priv, + BA_STREAM_SETUP_INPROGRESS))) + { + 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_11n_create_txbastream_tbl(priv, pdel_ba->peer_mac_addr, tid, + BA_STREAM_SETUP_INPROGRESS); + if ((ptx_ba_tbl = wlan_11n_get_txbastream_status(priv, + BA_STREAM_SETUP_INPROGRESS))) + { + mlan_11n_delete_bastream_tbl(priv, ptx_ba_tbl->tid, + ptx_ba_tbl->ra, TYPE_DELBA_SENT, + MTRUE); + } + } + } + + 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; + + 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; + if (padd_ba_rsp->status_code == BA_RESULT_SUCCESS) { + if ((ptx_ba_tbl = wlan_11n_get_txbastream_tbl(priv, tid, + padd_ba_rsp-> + peer_mac_addr))) { + PRINTM(MCMND, + "ADDBA REQ: %02x:%02x:%02x:%02x:%02x:%02x tid=%d ssn=%d win_size=%d,amsdu=%d\n", + padd_ba_rsp->peer_mac_addr[0], padd_ba_rsp->peer_mac_addr[1], + padd_ba_rsp->peer_mac_addr[2], padd_ba_rsp->peer_mac_addr[3], + padd_ba_rsp->peer_mac_addr[4], padd_ba_rsp->peer_mac_addr[5], + 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; + } else { + PRINTM(MERROR, "BA stream not created\n"); + } + } else { + mlan_11n_delete_bastream_tbl(priv, tid, padd_ba_rsp->peer_mac_addr, + TYPE_DELBA_SENT, MTRUE); + 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 */ + priv->aggr_prio_tbl[tid].ampdu_ap = BA_STREAM_NOT_ALLOWED; + + } else { + /* reset packet threshold */ + ra_list = + wlan_wmm_get_ralist_node(priv, tid, padd_ba_rsp->peer_mac_addr); + 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 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); + 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 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; +} + +#ifdef STA_SUPPORT +/** + * @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; + 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 = pmadapter->usr_dot_11n_dev_cap_a; + else + usr_dot_11n_dev_cap = pmadapter->usr_dot_11n_dev_cap_bg; + + 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); + + 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); + } + + 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].radio_type = + 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)) + SET_SECONDARYCHAN(pchan_list->chan_scan_param[0].radio_type, + GET_SECONDARYCHAN(pbss_desc->pht_info->ht_info. + field2)); + + 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 *) pbss_desc->pext_cap + sizeof(IEEEtypes_Header_t), + pext_cap->header.len); + + 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); + } + + 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_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_STREAM_CFG: + status = wlan_11n_ioctl_stream_cfg(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(); + + pmadapter->callbacks.moal_spin_lock(pmadapter->pmoal_handle, + priv->tx_ba_stream_tbl_ptr.plock); + + 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: + pmadapter->callbacks.moal_spin_unlock(pmadapter->pmoal_handle, + priv->tx_ba_stream_tbl_ptr.plock); + 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(); + + while ((del_tbl_ptr = (TxBAStreamTbl *) + util_peek_list(priv->adapter->pmoal_handle, + &priv->tx_ba_stream_tbl_ptr, + priv->adapter->callbacks.moal_spin_lock, + priv->adapter->callbacks.moal_spin_unlock))) { + wlan_11n_delete_txbastream_tbl_entry(priv, del_tbl_ptr); + } + + util_init_list((pmlan_linked_list) & priv->tx_ba_stream_tbl_ptr); + + 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 + * + * @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) +{ + TxBAStreamTbl *ptx_tbl; + pmlan_adapter pmadapter = priv->adapter; + + ENTER(); + + if (!(ptx_tbl = (TxBAStreamTbl *) util_peek_list(pmadapter->pmoal_handle, + &priv-> + tx_ba_stream_tbl_ptr, + pmadapter->callbacks. + moal_spin_lock, + pmadapter->callbacks. + moal_spin_unlock))) { + 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)) { + LEAVE(); + return ptx_tbl; + } + + ptx_tbl = ptx_tbl->pnext; + } + + 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 *newNode = MNULL; + pmlan_adapter pmadapter = priv->adapter; + + ENTER(); + + if (!wlan_11n_get_txbastream_tbl(priv, tid, ra)) { + PRINTM(MDAT_D, "get_txbastream_tbl TID %d\n", tid); + DBG_HEXDUMP(MDAT_D, "RA", ra, MLAN_MAC_ADDR_LENGTH); + + pmadapter->callbacks.moal_malloc(pmadapter->pmoal_handle, + sizeof(TxBAStreamTbl), MLAN_MEM_DEF, + (t_u8 **) & newNode); + util_init_list((pmlan_linked_list) newNode); + + newNode->tid = tid; + newNode->ba_status = ba_status; + memcpy(pmadapter, newNode->ra, ra, MLAN_MAC_ADDR_LENGTH); + + util_enqueue_list_tail(pmadapter->pmoal_handle, + &priv->tx_ba_stream_tbl_ptr, + (pmlan_linked_list) newNode, + pmadapter->callbacks.moal_spin_lock, + pmadapter->callbacks.moal_spin_unlock); + } + + 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 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_SUCCESS or MLAN_STATUS_FAILURE + */ +int +wlan_send_delba(mlan_private * priv, 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); + + /* We don't wait for the response of this command */ + ret = wlan_prepare_cmd(priv, HostCmd_CMD_11N_DELBA, + HostCmd_ACT_GEN_SET, 0, MNULL, &delba); + + 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)); + + 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 *rxReorderTblPtr; + int count = 0; + ENTER(); + if (! + (rxReorderTblPtr = + (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))) { + LEAVE(); + return count; + } + while (rxReorderTblPtr != (RxReorderTbl *) & priv->rx_reorder_tbl_ptr) { + ptbl->tid = (t_u16) rxReorderTblPtr->tid; + memcpy(priv->adapter, ptbl->ta, rxReorderTblPtr->ta, + MLAN_MAC_ADDR_LENGTH); + ptbl->start_win = rxReorderTblPtr->start_win; + ptbl->win_size = rxReorderTblPtr->win_size; + ptbl->amsdu = rxReorderTblPtr->amsdu; + for (i = 0; i < rxReorderTblPtr->win_size; ++i) { + if (rxReorderTblPtr->rx_reorder_ptr[i]) + ptbl->buffer[i] = MTRUE; + else + ptbl->buffer[i] = MFALSE; + } + rxReorderTblPtr = rxReorderTblPtr->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; + + ENTER(); + + if (!(ptxtbl = (TxBAStreamTbl *) util_peek_list(priv->adapter->pmoal_handle, + &priv->tx_ba_stream_tbl_ptr, + priv->adapter->callbacks. + moal_spin_lock, + priv->adapter->callbacks. + moal_spin_unlock))) { + LEAVE(); + return count; + } + + 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 >= MLAN_MAX_TX_BASTREAM_SUPPORTED) + break; + } + + LEAVE(); + return count; +} + +/** + * @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(); + for (i = 0; i < MAX_NUM_TID; ++i) { + if ((ptx_tbl = wlan_11n_get_txbastream_tbl(priv, i, ra))) { + wlan_11n_delete_txbastream_tbl_entry(priv, ptx_tbl); + } + } + LEAVE(); + return; +} diff --git a/drivers/net/wireless/sd8797/mlan/mlan_11n.h b/drivers/net/wireless/sd8797/mlan/mlan_11n.h new file mode 100644 index 000000000000..dc3d98974f03 --- /dev/null +++ b/drivers/net/wireless/sd8797/mlan/mlan_11n.h @@ -0,0 +1,420 @@ +/** @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-2011, 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 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 +/** 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); +#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); +/** 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, 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); +/** 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); +/** 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); + +/** 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; + if ((sta_ptr = wlan_get_station_entry(priv, mac))) { + 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; + if ((sta_ptr = wlan_get_station_entry(priv, mac))) { + 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; + if ((sta_ptr = wlan_get_station_entry(priv, ptr->ra))) { + 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; + if ((sta_ptr = wlan_get_station_entry(priv, ra))) { + sta_ptr->ampdu_sta[tid] = BA_STREAM_NOT_ALLOWED; + } + return; +} + +/** + * @brief This function checks whether current BA stream is high priority or not + * + * @param priv A pointer to mlan_private + * @param tid TID + * + * @return MTRUE or MFALSE + */ +static INLINE t_u8 +wlan_is_cur_bastream_high_prio(mlan_private * priv, int tid) +{ + TxBAStreamTbl *ptx_tbl; + + ENTER(); + + if (! + (ptx_tbl = + (TxBAStreamTbl *) util_peek_list(priv->adapter->pmoal_handle, + &priv->tx_ba_stream_tbl_ptr, + priv->adapter->callbacks. + moal_spin_lock, + priv->adapter->callbacks. + moal_spin_unlock))) + return MFALSE; + + while (ptx_tbl != (TxBAStreamTbl *) & priv->tx_ba_stream_tbl_ptr) { + if (priv->aggr_prio_tbl[tid].ampdu_user > + priv->aggr_prio_tbl[ptx_tbl->tid].ampdu_user) { + LEAVE(); + return MTRUE; + } + + ptx_tbl = ptx_tbl->pnext; + } + + LEAVE(); + return MFALSE; +} + +/** + * @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; + + return ((priv->aggr_prio_tbl[tid].ampdu_ap != BA_STREAM_NOT_ALLOWED) + ? MTRUE : MFALSE); +} + +/** + * @brief This function checks whether AMSDU is allowed for BA stream + * + * @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 int INLINE +wlan_is_amsdu_in_ampdu_allowed(mlan_private * priv, raListTbl * ptr, int tid) +{ + TxBAStreamTbl *ptx_tbl; + ENTER(); + if ((ptx_tbl = wlan_11n_get_txbastream_tbl(priv, tid, ptr->ra))) { + LEAVE(); + return ptx_tbl->amsdu; + } + LEAVE(); + return 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) { + if ((sta_ptr = wlan_get_station_entry(priv, ptr->ra))) { + 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; + for (i = 0; i < priv->adapter->priv_num; i++) { + if ((pmpriv = priv->adapter->priv[i])) + bastream_num += + wlan_wmm_list_len(priv->adapter, + (pmlan_list_head) & pmpriv-> + tx_ba_stream_tbl_ptr); + } + return ((bastream_num < MLAN_MAX_TX_BASTREAM_SUPPORTED) ? 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(); + + if (! + (ptx_tbl = + (TxBAStreamTbl *) util_peek_list(priv->adapter->pmoal_handle, + &priv->tx_ba_stream_tbl_ptr, + priv->adapter->callbacks. + moal_spin_lock, + priv->adapter->callbacks. + moal_spin_unlock))) { + 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 BA stream is setup + * + * @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 int INLINE +wlan_is_bastream_setup(mlan_private * priv, raListTbl * ptr, int tid) +{ + TxBAStreamTbl *ptx_tbl; + + ENTER(); + + if ((ptx_tbl = wlan_11n_get_txbastream_tbl(priv, tid, ptr->ra))) { + LEAVE(); + return IS_BASTREAM_SETUP(ptx_tbl) ? MTRUE : MFALSE; + } + + LEAVE(); + return MFALSE; +} + +/** + * @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 */ + LEAVE(); + return ret; +} +#endif /* !_MLAN_11N_H_ */ diff --git a/drivers/net/wireless/sd8797/mlan/mlan_11n_aggr.c b/drivers/net/wireless/sd8797/mlan/mlan_11n_aggr.c new file mode 100644 index 000000000000..239be3d67cea --- /dev/null +++ b/drivers/net/wireless/sd8797/mlan/mlan_11n_aggr.c @@ -0,0 +1,508 @@ +/** @file mlan_11n_aggr.c + * + * @brief This file contains functions for 11n Aggregation. + * + * Copyright (C) 2008-2011, 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 (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; + + ENTER(); + while (total_pkt_len > 0) { + /* Length will be in network format, change it to host */ + pkt_len = mlan_ntohs((*(t_u16 *) (data + (2 * MLAN_MAC_ADDR_LENGTH)))); + 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; + int pad; + mlan_status ret = MLAN_STATUS_FAILURE; + RxPacketHdr_t *prx_pkt; + mlan_buffer *daggr_mbuf = MNULL; + mlan_adapter *pmadapter = priv->adapter; + t_u8 rfc1042_eth_hdr[MLAN_MAC_ADDR_LENGTH] = { 0xaa, 0xaa, 0x03, + 0x00, 0x00, 0x00 + }; + + ENTER(); + + data = (t_u8 *) (pmbuf->pbuf + pmbuf->data_offset); + total_pkt_len = pmbuf->data_len; + + /* Sanity test */ + if (total_pkt_len > MLAN_RX_DATA_BUF_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 > 0) { + 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); + 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, 0, MFALSE); + if (daggr_mbuf == MNULL) { + PRINTM(MERROR, "Error allocating daggr mlan_buffer\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + 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 */ + ret = + pmadapter->callbacks.moal_recv_packet(pmadapter->pmoal_handle, + daggr_mbuf); + + 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: + 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; + t_u32 sec, usec; + 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"); + + if ((pmbuf_src = + (pmlan_buffer) util_peek_list(pmadapter->pmoal_handle, + &pra_list->buf_head, MNULL, MNULL))) { + + if (!(pmbuf_aggr = wlan_alloc_mlan_buffer(pmadapter, + pmadapter->tx_buf_size, 0, + MTRUE))) { + 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; + + /* 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 + } 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); + } + + 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: + pmadapter->data_sent = MFALSE; + 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); + } + pmadapter->callbacks.moal_get_system_time(pmadapter->pmoal_handle, &sec, + &usec); + PRINTM_NETINTF(MDATA, priv); + PRINTM(MDATA, "%lu.%06lu : Data => FW\n", sec, usec); + + exit: + LEAVE(); + return (pkt_size + headroom); +} diff --git a/drivers/net/wireless/sd8797/mlan/mlan_11n_aggr.h b/drivers/net/wireless/sd8797/mlan/mlan_11n_aggr.h new file mode 100644 index 000000000000..ef8108a764c0 --- /dev/null +++ b/drivers/net/wireless/sd8797/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-2011, 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/sd8797/mlan/mlan_11n_rxreorder.c b/drivers/net/wireless/sd8797/mlan/mlan_11n_rxreorder.c new file mode 100644 index 000000000000..cabd78b9017f --- /dev/null +++ b/drivers/net/wireless/sd8797/mlan/mlan_11n_rxreorder.c @@ -0,0 +1,1135 @@ +/** @file mlan_11n_rxreorder.c + * + * @brief This file contains the handling of RxReordering in wlan + * driver. + * + * Copyright (C) 2008-2011, 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) +{ + ENTER(); + 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_TIMER_MS)); + + 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; + } + + 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); + priv->adapter->callbacks.moal_free_timer(pmadapter->pmoal_handle, + rx_reor_tbl_ptr->timer_context. + timer); + } + + 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); + + 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 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; + int startWin; + + ENTER(); + reorder_cnxt->timer_is_set = MFALSE; + wlan_11n_display_tbl_ptr(reorder_cnxt->priv->adapter, reorder_cnxt->ptr); + + if ((startWin = wlan_11n_find_last_seqnum(reorder_cnxt->ptr)) >= 0) { + PRINTM(MINFO, "Flush data %d\n", startWin); + wlan_11n_dispatch_pkt_until_start_win(reorder_cnxt->priv, + reorder_cnxt->ptr, + ((reorder_cnxt->ptr->start_win + + startWin + 1) & (MAX_TID_VALUE - + 1))); + } + + wlan_11n_display_tbl_ptr(reorder_cnxt->priv->adapter, reorder_cnxt->ptr); + 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 + */ + if ((rx_reor_tbl_ptr = wlan_11n_get_rxreorder_tbl(priv, tid, ta))) { + 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 %02x:%02x:%02x:%02x:" + "%02x:%02x, win_size %d\n", __FUNCTION__, + seq_num, tid, ta[0], ta[1], ta[2], ta[3], + ta[4], ta[5], 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; + if (queuing_ra_based(priv)) { + // TODO for adhoc + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) { + if ((sta_ptr = wlan_get_station_entry(priv, ta))) + 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 { + last_seq = priv->rx_seq[tid]; + } + if ((last_seq != DEFAULT_SEQ_NUM) && (last_seq >= new_node->start_win)) { + PRINTM(MDAT_D, "Update start_win: last_seq=%d, start_win=%d\n", + last_seq, new_node->start_win); + new_node->start_win = last_seq + 1; + } + new_node->win_size = win_size; + + 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\n", new_node); + 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(); + + if (! + (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))) { + 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); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @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->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 (priv->addba_reject[tid] +#ifdef STA_SUPPORT + || ((GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) + && priv->wps.session_enable) +#endif +#ifdef UAP_SUPPORT + || ((GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) + && (priv->adapter->pending_bridge_pkts > 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 || + (priv->aggr_prio_tbl[tid].amsdu == BA_STREAM_NOT_ALLOWED)) + /* 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; + 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); + + 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(); + + if (! + (rx_reor_tbl_ptr = + wlan_11n_get_rxreorder_tbl((mlan_private *) priv, tid, ta))) { + if (pkt_type != PKT_TYPE_BAR) + wlan_11n_dispatch_pkt(priv, payload); + + LEAVE(); + return ret; + + } else { + 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 "); + + 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 %02x:%02x:%02x:%02x:%02x:%02x\n", + tid, ta[0], ta[1], ta[2], ta[3], ta[4], ta[5]); + 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 ((start_win + TWOPOW11) > (MAX_TID_VALUE - 1)) { /* Wrap */ + if (seq_num >= ((start_win + (TWOPOW11)) & + (MAX_TID_VALUE - 1)) && (seq_num < start_win)) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + } else if ((seq_num < start_win) || + (seq_num > (start_win + (TWOPOW11)))) { + 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 < (TWOPOW11 - (MAX_TID_VALUE - 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; + + if ((ret = wlan_11n_dispatch_pkt_until_start_win(priv, + rx_reor_tbl_ptr, + start_win))) { + 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 + * + * @return N/A + */ +void +mlan_11n_delete_bastream_tbl(mlan_private * priv, int tid, + t_u8 * peer_mac, t_u8 type, int initiator) +{ + RxReorderTbl *rx_reor_tbl_ptr; + TxBAStreamTbl *ptxtbl; + t_u8 cleanup_rx_reorder_tbl; + + ENTER(); + + if (type == TYPE_DELBA_RECEIVE) + cleanup_rx_reorder_tbl = (initiator) ? MTRUE : MFALSE; + else + cleanup_rx_reorder_tbl = (initiator) ? MFALSE : MTRUE; + + PRINTM(MEVENT, "DELBA: %02x:%02x:%02x:%02x:%02x:%02x tid=%d," + "initiator=%d\n", peer_mac[0], + peer_mac[1], peer_mac[2], + peer_mac[3], peer_mac[4], peer_mac[5], tid, initiator); + + if (cleanup_rx_reorder_tbl) { + if (!(rx_reor_tbl_ptr = wlan_11n_get_rxreorder_tbl(priv, tid, + peer_mac))) { + PRINTM(MWARN, "TID, TA not found in table!\n"); + LEAVE(); + return; + } + wlan_11n_delete_rxreorder_tbl_entry(priv, rx_reor_tbl_ptr); + } else { + if (!(ptxtbl = wlan_11n_get_txbastream_tbl(priv, tid, peer_mac))) { + PRINTM(MWARN, "TID, RA not found in table!\n"); + LEAVE(); + return; + } + + wlan_11n_delete_txbastream_tbl_entry(priv, ptxtbl); + } + + 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: %02x:%02x:%02x:%02x:%02x:%02x tid=%d ssn=%d win_size=%d,amsdu=%d\n", + padd_ba_rsp->peer_mac_addr[0], padd_ba_rsp->peer_mac_addr[1], + padd_ba_rsp->peer_mac_addr[2], padd_ba_rsp->peer_mac_addr[3], + padd_ba_rsp->peer_mac_addr[4], padd_ba_rsp->peer_mac_addr[5], + 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); + + if ((rx_reor_tbl_ptr = + wlan_11n_get_rxreorder_tbl(priv, tid, + padd_ba_rsp->peer_mac_addr))) { + 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 && + (priv->aggr_prio_tbl[tid].amsdu != BA_STREAM_NOT_ALLOWED)) + rx_reor_tbl_ptr->amsdu = MTRUE; + else + rx_reor_tbl_ptr->amsdu = MFALSE; + } + } else { + PRINTM(MERROR, + "ADDBA RSP: Failed(%02x:%02x:%02x:%02x:%02x:%02x tid=%d)\n", + padd_ba_rsp->peer_mac_addr[0], padd_ba_rsp->peer_mac_addr[1], + padd_ba_rsp->peer_mac_addr[2], padd_ba_rsp->peer_mac_addr[3], + padd_ba_rsp->peer_mac_addr[4], padd_ba_rsp->peer_mac_addr[5], + tid); + if ((rx_reor_tbl_ptr = + wlan_11n_get_rxreorder_tbl(priv, tid, + padd_ba_rsp->peer_mac_addr))) { + 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, + "%02x:%02x:%02x:%02x:%02x:%02x tid=%d seq_num=%d bitmap_len=%d\n", + tlv_rxba->mac[0], tlv_rxba->mac[1], tlv_rxba->mac[2], + tlv_rxba->mac[3], tlv_rxba->mac[4], tlv_rxba->mac[5], + 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; + } + 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 will send a DELBA for each entry in the priv's + * rx reordering table + * + * @param priv A pointer to mlan_private + */ +t_void +wlan_send_delba_to_all_in_reorder_tbl(pmlan_private priv) +{ + RxReorderTbl *rx_reor_tbl_ptr; + + ENTER(); + + if (! + (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))) { + LEAVE(); + return; + } + + while (rx_reor_tbl_ptr != (RxReorderTbl *) & priv->rx_reorder_tbl_ptr) { + if (rx_reor_tbl_ptr->ba_status == BA_STREAM_SETUP_COMPLETE) { + rx_reor_tbl_ptr->ba_status = BA_STREAM_SETUP_INPROGRESS; + wlan_send_delba(priv, rx_reor_tbl_ptr->tid, rx_reor_tbl_ptr->ta, 0); + } + rx_reor_tbl_ptr = rx_reor_tbl_ptr->pnext; + } + LEAVE(); +} + +/** + * @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) { + if ((rx_reor_tbl_ptr = wlan_11n_get_rxreorder_tbl(priv, i, ta))) { + wlan_11n_delete_rxreorder_tbl_entry(priv, rx_reor_tbl_ptr); + } + } + LEAVE(); + return; +} diff --git a/drivers/net/wireless/sd8797/mlan/mlan_11n_rxreorder.h b/drivers/net/wireless/sd8797/mlan/mlan_11n_rxreorder.h new file mode 100644 index 000000000000..4ff15834c8a3 --- /dev/null +++ b/drivers/net/wireless/sd8797/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-2011, 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); +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); + +/** send delba for all entries in reorder_tbl */ +t_void wlan_send_delba_to_all_in_reorder_tbl(pmlan_private priv); + +/** 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/sd8797/mlan/mlan_cfp.c b/drivers/net/wireless/sd8797/mlan/mlan_cfp.c new file mode 100644 index 000000000000..742c9cd368bc --- /dev/null +++ b/drivers/net/wireless/sd8797/mlan/mlan_cfp.c @@ -0,0 +1,1148 @@ +/** + * @file mlan_cfp.c + * + * @brief This file contains WLAN client mode channel, frequency and power + * related code + * + * Copyright (C) 2009-2011, 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_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 + +/** + * 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}, + {2, 2417, WLAN_TX_PWR_US_DEFAULT}, + {3, 2422, WLAN_TX_PWR_US_DEFAULT}, + {4, 2427, WLAN_TX_PWR_US_DEFAULT}, + {5, 2432, WLAN_TX_PWR_US_DEFAULT}, + {6, 2437, WLAN_TX_PWR_US_DEFAULT}, + {7, 2442, WLAN_TX_PWR_US_DEFAULT}, + {8, 2447, WLAN_TX_PWR_US_DEFAULT}, + {9, 2452, WLAN_TX_PWR_US_DEFAULT}, + {10, 2457, WLAN_TX_PWR_US_DEFAULT}, + {11, 2462, WLAN_TX_PWR_US_DEFAULT} +}; + +/** Band: 'B/G', Region: Europe ETSI/China */ +static chan_freq_power_t channel_freq_power_EU_BG[] = { + {1, 2412, WLAN_TX_PWR_EMEA_DEFAULT}, + {2, 2417, WLAN_TX_PWR_EMEA_DEFAULT}, + {3, 2422, WLAN_TX_PWR_EMEA_DEFAULT}, + {4, 2427, WLAN_TX_PWR_EMEA_DEFAULT}, + {5, 2432, WLAN_TX_PWR_EMEA_DEFAULT}, + {6, 2437, WLAN_TX_PWR_EMEA_DEFAULT}, + {7, 2442, WLAN_TX_PWR_EMEA_DEFAULT}, + {8, 2447, WLAN_TX_PWR_EMEA_DEFAULT}, + {9, 2452, WLAN_TX_PWR_EMEA_DEFAULT}, + {10, 2457, WLAN_TX_PWR_EMEA_DEFAULT}, + {11, 2462, WLAN_TX_PWR_EMEA_DEFAULT}, + {12, 2467, WLAN_TX_PWR_EMEA_DEFAULT}, + {13, 2472, WLAN_TX_PWR_EMEA_DEFAULT} +}; + +/** Band: 'B/G', Region: France */ +static chan_freq_power_t channel_freq_power_FR_BG[] = { + {1, 2412, WLAN_TX_PWR_FR_100MW}, + {2, 2417, WLAN_TX_PWR_FR_100MW}, + {3, 2422, WLAN_TX_PWR_FR_100MW}, + {4, 2427, WLAN_TX_PWR_FR_100MW}, + {5, 2432, WLAN_TX_PWR_FR_100MW}, + {6, 2437, WLAN_TX_PWR_FR_100MW}, + {7, 2442, WLAN_TX_PWR_FR_100MW}, + {8, 2447, WLAN_TX_PWR_FR_100MW}, + {9, 2452, WLAN_TX_PWR_FR_100MW}, + {10, 2457, WLAN_TX_PWR_FR_10MW}, + {11, 2462, WLAN_TX_PWR_FR_10MW}, + {12, 2467, WLAN_TX_PWR_FR_10MW}, + {13, 2472, WLAN_TX_PWR_FR_10MW} +}; + +/** Band: 'B/G', Region: Japan */ +static chan_freq_power_t channel_freq_power_JPN41_BG[] = { + {1, 2412, WLAN_TX_PWR_JP_BG_DEFAULT}, + {2, 2417, WLAN_TX_PWR_JP_BG_DEFAULT}, + {3, 2422, WLAN_TX_PWR_JP_BG_DEFAULT}, + {4, 2427, WLAN_TX_PWR_JP_BG_DEFAULT}, + {5, 2432, WLAN_TX_PWR_JP_BG_DEFAULT}, + {6, 2437, WLAN_TX_PWR_JP_BG_DEFAULT}, + {7, 2442, WLAN_TX_PWR_JP_BG_DEFAULT}, + {8, 2447, WLAN_TX_PWR_JP_BG_DEFAULT}, + {9, 2452, WLAN_TX_PWR_JP_BG_DEFAULT}, + {10, 2457, WLAN_TX_PWR_JP_BG_DEFAULT}, + {11, 2462, WLAN_TX_PWR_JP_BG_DEFAULT}, + {12, 2467, WLAN_TX_PWR_JP_BG_DEFAULT}, + {13, 2472, WLAN_TX_PWR_JP_BG_DEFAULT} +}; + +/** Band: 'B/G', Region: Japan */ +static chan_freq_power_t channel_freq_power_JPN40_BG[] = { + {14, 2484, WLAN_TX_PWR_JP_BG_DEFAULT} +}; + +/** Band : 'B/G', Region: Special */ +static chan_freq_power_t channel_freq_power_SPECIAL_BG[] = { + {1, 2412, WLAN_TX_PWR_JP_BG_DEFAULT}, + {2, 2417, WLAN_TX_PWR_JP_BG_DEFAULT}, + {3, 2422, WLAN_TX_PWR_JP_BG_DEFAULT}, + {4, 2427, WLAN_TX_PWR_JP_BG_DEFAULT}, + {5, 2432, WLAN_TX_PWR_JP_BG_DEFAULT}, + {6, 2437, WLAN_TX_PWR_JP_BG_DEFAULT}, + {7, 2442, WLAN_TX_PWR_JP_BG_DEFAULT}, + {8, 2447, WLAN_TX_PWR_JP_BG_DEFAULT}, + {9, 2452, WLAN_TX_PWR_JP_BG_DEFAULT}, + {10, 2457, WLAN_TX_PWR_JP_BG_DEFAULT}, + {11, 2462, WLAN_TX_PWR_JP_BG_DEFAULT}, + {12, 2467, WLAN_TX_PWR_JP_BG_DEFAULT}, + {13, 2472, WLAN_TX_PWR_JP_BG_DEFAULT}, + {14, 2484, WLAN_TX_PWR_JP_BG_DEFAULT} +}; + +/** + * The 2.4GHz CFP tables + */ +static cfp_table_t cfp_table_BG[] = { + {0x10, /* US FCC */ + channel_freq_power_US_BG, + sizeof(channel_freq_power_US_BG) / sizeof(chan_freq_power_t), + } + , + {0x20, /* CANADA IC */ + channel_freq_power_US_BG, + sizeof(channel_freq_power_US_BG) / sizeof(chan_freq_power_t), + } + , + {0x30, /* EU */ + channel_freq_power_EU_BG, + sizeof(channel_freq_power_EU_BG) / sizeof(chan_freq_power_t), + } + , + {0x32, /* FRANCE */ + channel_freq_power_FR_BG, + sizeof(channel_freq_power_FR_BG) / sizeof(chan_freq_power_t), + } + , + {0x40, /* JAPAN */ + channel_freq_power_JPN40_BG, + sizeof(channel_freq_power_JPN40_BG) / sizeof(chan_freq_power_t), + } + , + {0x41, /* JAPAN */ + channel_freq_power_JPN41_BG, + sizeof(channel_freq_power_JPN41_BG) / sizeof(chan_freq_power_t), + } + , + {0x50, /* China */ + channel_freq_power_EU_BG, + sizeof(channel_freq_power_EU_BG) / sizeof(chan_freq_power_t), + } + , + {0xff, /* Special */ + channel_freq_power_SPECIAL_BG, + sizeof(channel_freq_power_SPECIAL_BG) / sizeof(chan_freq_power_t), + } + , +/* Add new region here */ +}; + +/** Number of the CFP tables for 2.4GHz */ +#define MLAN_CFP_TABLE_SIZE_BG (sizeof(cfp_table_BG)/sizeof(cfp_table_t)) + +/* Format { Channel, Frequency (MHz), MaxTxPower, DFS } */ +/** Band: 'A', Region: USA FCC, Spain, France */ +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} +}; + +/** Band: 'A', Region: Japan */ +static chan_freq_power_t channel_freq_power_JPN_A[] = { + {8, 5040, WLAN_TX_PWR_JP_A_DEFAULT, MFALSE}, + {12, 5060, WLAN_TX_PWR_JP_A_DEFAULT, MFALSE}, + {16, 5080, WLAN_TX_PWR_JP_A_DEFAULT, MFALSE}, + {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[] = { + {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} +}; + +/** + * The 5GHz CFP tables + */ +static cfp_table_t cfp_table_A[] = { + {0x10, /* US FCC */ + channel_freq_power_A, + sizeof(channel_freq_power_A) / sizeof(chan_freq_power_t), + } + , + {0x20, /* CANADA IC */ + channel_freq_power_CAN_A, + sizeof(channel_freq_power_CAN_A) / sizeof(chan_freq_power_t), + } + , + {0x30, /* EU */ + channel_freq_power_EU_A, + sizeof(channel_freq_power_EU_A) / sizeof(chan_freq_power_t), + } + , + {0x32, /* FRANCE */ + channel_freq_power_A, + sizeof(channel_freq_power_A) / sizeof(chan_freq_power_t), + } + , + {0x40, /* JAPAN */ + channel_freq_power_JPN_A, + sizeof(channel_freq_power_JPN_A) / sizeof(chan_freq_power_t), + } + , + {0x41, /* JAPAN */ + channel_freq_power_JPN_A, + sizeof(channel_freq_power_JPN_A) / sizeof(chan_freq_power_t), + } + , + {0x50, /* China */ + channel_freq_power_CN_A, + sizeof(channel_freq_power_CN_A) / sizeof(chan_freq_power_t), + } + , + {0xff, /* Special */ + channel_freq_power_JPN_A, + sizeof(channel_freq_power_JPN_A) / sizeof(chan_freq_power_t), + } + , +/* Add new region here */ +}; + +/** Number of the CFP tables for 5GHz */ +#define MLAN_CFP_TABLE_SIZE_A (sizeof(cfp_table_A)/sizeof(cfp_table_t)) + +/******************************************************** + Global Variables +********************************************************/ +/** + * The table to keep region code + */ +t_u16 region_code_index[MRVDRV_MAX_REGION_CODE] = + { 0x10, 0x20, 0x30, 0x32, 0x40, 0x41, 0x50, 0xff }; + +/** + * 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, 0 }; + +/** + * 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, 0 +}; + +/** + * The rates supported in A mode for ad-hoc + */ +t_u8 AdhocRates_A[A_SUPPORTED_RATES] = + { 0x8c, 0x12, 0x98, 0x24, 0xb0, 0x48, 0x60, 0x6c, 0 }; + +/** + * 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, 0 }; + +/** + * 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, 0 }; + +/** + * 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, 0 }; + +/** + * 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, 0 +}; + +/** + * 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 (band & (BAND_B | BAND_G | BAND_GN)) { + 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)) { + 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; + } + } + } + + PRINTM(MERROR, "Error Band[0x%x] or region[%#x]\n", band, region); + + LEAVE(); + return MNULL; +} + +/******************************************************** + Global Functions +********************************************************/ + +#ifdef STA_SUPPORT +#endif /* STA_SUPPORT */ + +/** + * @brief Use index to get the data rate + * + * @param pmadapter A pointer to mlan_adapter structure + * @param index The index of data rate + * @param ht_info ht info + * + * @return Data rate or 0 + */ + +t_u32 +wlan_index_to_data_rate(pmlan_adapter pmadapter, t_u8 index, t_u8 ht_info) +{ +#define MCS_NUM_SUPP 16 + t_u16 mcs_rate[4][MCS_NUM_SUPP] = + { {0x1b, 0x36, 0x51, 0x6c, 0xa2, 0xd8, 0xf3, 0x10e, 0x36, 0x6c, 0xa2, + 0xd8, 0x144, 0x1b0, 0x1e6, 0x21c} + , /* LG 40M */ + {0x1e, 0x3c, 0x5a, 0x78, 0xb4, 0xf0, 0x10e, 0x12c, 0x3c, 0x78, 0xb4, 0xf0, + 0x168, 0x1e0, 0x21c, 0x258} + , /* SG 40M */ + {0x0d, 0x1a, 0x27, 0x34, 0x4e, 0x68, 0x75, 0x82, 0x1a, 0x34, 0x4e, 0x68, + 0x9c, 0xd0, 0xea, 0x104} + , /* LG 20M */ + {0x0e, 0x1c, 0x2b, 0x39, 0x56, 0x73, 0x82, 0x90, 0x1c, 0x39, 0x56, 0x73, + 0xad, 0xe7, 0x104, 0x120} + }; /* SG 20M */ + + t_u32 rate = 0; + ENTER(); + + if (ht_info & MBIT(0)) { + if (index == MLAN_RATE_BITMAP_MCS0) { + if (ht_info & MBIT(2)) + rate = 0x0D; /* MCS 32 SGI rate */ + else + rate = 0x0C; /* MCS 32 LGI rate */ + } else if (index < MCS_NUM_SUPP) { + if (ht_info & MBIT(1)) { + if (ht_info & MBIT(2)) + rate = mcs_rate[1][index]; /* SGI, 40M */ + else + rate = mcs_rate[0][index]; /* LGI, 40M */ + } else { + if (ht_info & MBIT(2)) + rate = mcs_rate[3][index]; /* SGI, 20M */ + else + rate = mcs_rate[2][index]; /* LGI, 20M */ + } + } 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) + if ((ptr = wlan_memchr(pmadapter, WlanDataRates, (t_u8) rate, + sizeof(WlanDataRates)))) { + 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(MERROR, "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 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 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 BAND_B: + PRINTM(MINFO, "Band: Adhoc B\n"); + k = wlan_copy_rates(rates, k, AdhocRates_B, sizeof(AdhocRates_B)); + break; + case 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; +} + +/** + * @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; + chan_freq_power_t *cfp; + int cfp_no; + + ENTER(); + + 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; + 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; + } + 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].radar_detect; + break; + } + } + + done: + LEAVE(); + return required; +} diff --git a/drivers/net/wireless/sd8797/mlan/mlan_cmdevt.c b/drivers/net/wireless/sd8797/mlan/mlan_cmdevt.c new file mode 100644 index 000000000000..8b91ae2c43c2 --- /dev/null +++ b/drivers/net/wireless/sd8797/mlan/mlan_cmdevt.c @@ -0,0 +1,2927 @@ +/** + * @file mlan_cmdevt.c + * + * @brief This file contains the handling of CMD/EVENT in MLAN + * + * Copyright (C) 2009-2011, 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 +********************************************************/ + +/** + * @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 dara 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) { + while (*ptr && (wlan_isspace(*ptr) || *ptr == '\t')) { + ptr++; + } + + if (wlan_isxdigit(*ptr)) { + *dptr++ = wlan_atox(ptr); + ptr += 2; + } else { + ptr++; + } + } + LEAVE(); + return (dptr - dst); +} + +/** + * @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; + } + + if (util_peek_list(pmadapter->pmoal_handle, &pmadapter->cmd_free_q, + pmadapter->callbacks.moal_spin_lock, + pmadapter->callbacks.moal_spin_unlock)) { + pcmd_node = (cmd_ctrl_node *) util_dequeue_list(pmadapter->pmoal_handle, + &pmadapter->cmd_free_q, + pmadapter->callbacks. + moal_spin_lock, + pmadapter->callbacks. + moal_spin_unlock); + } else { + PRINTM(MERROR, "GET_CMD_NODE: cmd_ctrl_node is not available\n"); + pcmd_node = MNULL; + } + + 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; +} + +/** + * @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(); + + if (! + (pcmd_node = + (cmd_ctrl_node *) util_peek_list(pmadapter->pmoal_handle, + &pmadapter->cmd_pending_q, + pmadapter->callbacks.moal_spin_lock, + pmadapter->callbacks. + moal_spin_unlock))) { + LEAVE(); + return MNULL; + } + while (pcmd_node != (cmd_ctrl_node *) & pmadapter->cmd_pending_q) { + if (pcmd_node->pioctl_buf == pioctl_req) { + 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 sec, usec; + + 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_insert_cmd_to_free_q(pmadapter, pcmd_node); + 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)); + + wlan_request_cmd_lock(pmadapter); + pmadapter->curr_cmd = pcmd_node; + wlan_release_cmd_lock(pmadapter); + + cmd_code = wlan_le16_to_cpu(pcmd->command); + cmd_size = wlan_le16_to_cpu(pcmd->size); + + pcmd_node->cmdbuf->data_len = cmd_size; + + pmadapter->callbacks.moal_get_system_time(pmadapter->pmoal_handle, &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 (pioctl_buf) + pioctl_buf->status_code = MLAN_ERROR_CMD_DNLD_FAIL; + wlan_insert_cmd_to_free_q(pmadapter, pmadapter->curr_cmd); + + wlan_request_cmd_lock(pmadapter); + pmadapter->curr_cmd = MNULL; + wlan_release_cmd_lock(pmadapter); + + pmadapter->dbg.num_cmd_host_to_card_failure++; + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* 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)); + + /* Clear BSS_NO_BITS from HostCmd */ + cmd_code &= HostCmd_CMD_ID_MASK; + /* soft_reset command has no command response, we should return here */ + if (cmd_code == HostCmd_CMD_SOFT_RESET) { + PRINTM(MCMND, "DNLD_CMD: SoftReset\n"); + if (pioctl_buf) { + wlan_request_cmd_lock(pmadapter); + pmadapter->curr_cmd->pioctl_buf = MNULL; + wlan_release_cmd_lock(pmadapter); + pcb->moal_ioctl_complete(pmadapter->pmoal_handle, pioctl_buf, + MLAN_STATUS_SUCCESS); + } + goto done; + } + + /* Setup the timer after transmit command */ + pcb->moal_start_timer(pmadapter->pmoal_handle, pmadapter->pmlan_cmd_timer, + MFALSE, MRVDRV_TIMER_60S); + + 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 = 0; + 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); + + ENTER(); + + 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, + (wlan_get_priv(pmadapter, MLAN_BSS_ROLE_ANY))-> + bss_num, + (wlan_get_priv(pmadapter, MLAN_BSS_ROLE_ANY))-> + 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(wlan_get_priv(pmadapter, MLAN_BSS_ROLE_ANY)) == + MLAN_BSS_ROLE_UAP) + pmadapter->ps_state = PS_STATE_SLEEP_CFM; +#ifdef STA_SUPPORT + if (GET_BSS_ROLE(wlan_get_priv(pmadapter, MLAN_BSS_ROLE_ANY)) == + 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 = &priv->adapter->callbacks; + + ENTER(); + + 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++) { + if (!(pcmd_array[i].pmbuf = wlan_alloc_mlan_buffer(pmadapter, + MRVDRV_SIZE_OF_CMD_BUFFER, + 0, MTRUE))) { + PRINTM(MERROR, + "ALLOC_CMD_BUF: Failed to allocate command buffer\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + } + + for (i = 0; i < MRVDRV_NUM_OF_CMD_BUFFER; i++) { + wlan_insert_cmd_to_free_q(pmadapter, &pcmd_array[i]); + } + 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; + t_u32 in_ts_sec; + t_u32 in_ts_usec; + 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)); + } + } + /* 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); + + /* 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) + ) { + pmadapter->callbacks.moal_get_system_time(pmadapter->pmoal_handle, + &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); + + 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 = pmpriv->adapter; + cmd_ctrl_node *pcmd_node = MNULL; + HostCmd_DS_COMMAND *cmd_ptr = MNULL; + pmlan_ioctl_req pioctl_req = (mlan_ioctl_req *) pioctl_buf; + + ENTER(); + + /* 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"); + if (pioctl_req) + pioctl_req->status_code = MLAN_ERROR_NO_MEM; + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* 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; + wlan_insert_cmd_to_free_q(pmadapter, pcmd_node); + if (pioctl_req) + pioctl_req->status_code = MLAN_ERROR_CMD_DNLD_FAIL; + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* Send command */ +#ifdef STA_SUPPORT + if (cmd_no == HostCmd_CMD_802_11_SCAN + || cmd_no == HostCmd_CMD_802_11_SCAN_EXT) + 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 + 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, + pmadapter->callbacks.moal_spin_lock, + pmadapter->callbacks.moal_spin_unlock); + 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); + if (pcmd == MNULL) { + PRINTM(MERROR, "QUEUE_CMD: pcmd is MNULL\n"); + goto done; + } + + 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, + pmadapter->callbacks.moal_spin_lock, + pmadapter->callbacks.moal_spin_unlock); + } else { + util_enqueue_list_head(pmadapter->pmoal_handle, + &pmadapter->cmd_pending_q, + (pmlan_linked_list) pcmd_node, + pmadapter->callbacks.moal_spin_lock, + pmadapter->callbacks.moal_spin_unlock); + } + + 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, + pmadapter->callbacks.moal_spin_lock, + pmadapter->callbacks.moal_spin_unlock); + + 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, + pmadapter->callbacks.moal_spin_lock, + pmadapter->callbacks.moal_spin_unlock); + 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; + t_u32 sec, usec, 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; + wlan_release_cmd_lock(pmadapter); + PRINTM(MERROR, "CMD_RESP: 0x%x been canceled!\n", + wlan_le16_to_cpu(resp->command)); + wlan_insert_cmd_to_free_q(pmadapter, free_cmd); + if (pioctl_buf) + pioctl_buf->status_code = MLAN_ERROR_CMD_CANCEL; + 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; + + /* 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; + + pmadapter->callbacks.moal_get_system_time(pmadapter->pmoal_handle, &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_insert_cmd_to_free_q(pmadapter, pmadapter->curr_cmd); + wlan_request_cmd_lock(pmadapter); + 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) { + 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; + wlan_release_cmd_lock(pmadapter); + + 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 = 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); + } else { + wlan_release_cmd_lock(pmadapter); + } + + if ((pmadapter->hw_status == WlanHardwareStatusInitializing) && + (pmadapter->last_init_cmd == cmdresp_no)) { + i = pmpriv->bss_index + 1; + while (!(pmpriv_next = pmadapter->priv[i]) && i < pmadapter->priv_num) + 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); + } + } + + 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; + t_u32 sec, usec; + t_u8 i; + mlan_private *pmpriv = MNULL; + + ENTER(); + + pmadapter->cmd_timer_is_set = MFALSE; + pmadapter->num_cmd_timeout++; + pmadapter->dbg.num_cmd_timeout++; + if (!pmadapter->curr_cmd) { + PRINTM(MWARN, "CurCmd Empty\n"); + goto exit; + } + 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; + } + + if (pcmd_node) { + 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]; + pmadapter->callbacks.moal_get_system_time(pmadapter->pmoal_handle, &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); + } + + PRINTM(MERROR, "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_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); + } + if (pmadapter->hw_status == WlanHardwareStatusInitializing) + 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) +{ + mlan_callbacks *pcb = (pmlan_callbacks) & pmadapter->callbacks; + cmd_ctrl_node *pcmd_node = MNULL; + + ENTER(); + + while ((pcmd_node = + (cmd_ctrl_node *) util_peek_list(pmadapter->pmoal_handle, + &pmadapter->scan_pending_q, + pcb->moal_spin_lock, + pcb->moal_spin_unlock))) { + util_unlink_list(pmadapter->pmoal_handle, &pmadapter->scan_pending_q, + (pmlan_linked_list) pcmd_node, pcb->moal_spin_lock, + pcb->moal_spin_unlock); + pcmd_node->pioctl_buf = MNULL; + wlan_insert_cmd_to_free_q(pmadapter, pcmd_node); + } + wlan_request_cmd_lock(pmadapter); + pmadapter->scan_processing = MFALSE; + wlan_release_cmd_lock(pmadapter); + + LEAVE(); +} +#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; + + ENTER(); + /* Cancel current cmd */ + wlan_request_cmd_lock(pmadapter); + if ((pmadapter->curr_cmd) && (pmadapter->curr_cmd->pioctl_buf)) { + pioctl_buf = (mlan_ioctl_req *) pmadapter->curr_cmd->pioctl_buf; + pmadapter->curr_cmd->pioctl_buf = MNULL; + wlan_release_cmd_lock(pmadapter); + pioctl_buf->status_code = MLAN_ERROR_CMD_CANCEL; + pcb->moal_ioctl_complete(pmadapter->pmoal_handle, pioctl_buf, + MLAN_STATUS_FAILURE); + } else { + wlan_release_cmd_lock(pmadapter); + } + /* Cancel all pending command */ + while ((pcmd_node = + (cmd_ctrl_node *) util_peek_list(pmadapter->pmoal_handle, + &pmadapter->cmd_pending_q, + pcb->moal_spin_lock, + pcb->moal_spin_unlock))) { + util_unlink_list(pmadapter->pmoal_handle, &pmadapter->cmd_pending_q, + (pmlan_linked_list) pcmd_node, pcb->moal_spin_lock, + pcb->moal_spin_unlock); + 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); + } +#ifdef STA_SUPPORT + /* Cancel all pending scan command */ + wlan_flush_scan_queue(pmadapter); +#endif + LEAVE(); +} + +/** + * @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; + + 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); + 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; + } + + 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, + pmadapter->callbacks.moal_spin_lock, + pmadapter->callbacks.moal_spin_unlock); + pcmd_node->pioctl_buf = MNULL; + wlan_release_cmd_lock(pmadapter); + wlan_insert_cmd_to_free_q(pmadapter, pcmd_node); + wlan_request_cmd_lock(pmadapter); + } + wlan_release_cmd_lock(pmadapter); +#ifdef STA_SUPPORT + if (pioctl_req->req_id == MLAN_IOCTL_SCAN) { + /* IOCTL will be completed, avoid calling IOCTL complete again from + EVENT */ + if (pmadapter->pext_scan_ioctl_req == pioctl_req) + pmadapter->pext_scan_ioctl_req = MNULL; + /* Cancel all pending scan command */ + wlan_flush_scan_queue(pmadapter); + } +#endif + 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 (activated) { + if (priv->adapter->is_hs_configured) { + priv->adapter->hs_activated = 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(); +} + +/** + * @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_insert_cmd_to_free_q(pmadapter, pmadapter->curr_cmd); + wlan_request_cmd_lock(pmadapter); + 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_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_htinfo = resp->params.tx_rate.ht_info; + if (!pmpriv->is_data_rate_auto) { + pmpriv->data_rate = wlan_index_to_data_rate(pmadapter, + pmpriv->tx_rate, + pmpriv->tx_htinfo); + } + + 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_htinfo & MBIT(0)) + rate->param.rate_cfg.rate = + pmpriv->tx_rate + MLAN_RATE_INDEX_MCS0; + else + /* 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.rate_cfg.rate = + (pmpriv->tx_rate > + MLAN_RATE_INDEX_OFDM0) ? pmpriv->tx_rate - + 1 : pmpriv->tx_rate; + } else { + rate->param.rate_cfg.rate = + wlan_index_to_data_rate(pmadapter, pmpriv->tx_rate, + pmpriv->tx_htinfo); + } + } else if (rate->sub_command == MLAN_OID_GET_DATA_RATE) { + if (pmpriv->tx_htinfo & MBIT(0)) { + rate->param.data_rate.tx_data_rate = + pmpriv->tx_rate + MLAN_RATE_INDEX_MCS0; + if (pmpriv->tx_htinfo & MBIT(1)) + rate->param.data_rate.tx_ht_bw = MLAN_HT_BW40; + else + rate->param.data_rate.tx_ht_bw = MLAN_HT_BW20; + if (pmpriv->tx_htinfo & MBIT(2)) + rate->param.data_rate.tx_ht_gi = MLAN_HT_SGI; + else + rate->param.data_rate.tx_ht_gi = MLAN_HT_LGI; + } else + /* 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; + if (pmpriv->rxpd_htinfo & MBIT(0)) { + rate->param.data_rate.rx_data_rate = + pmpriv->rxpd_rate + MLAN_RATE_INDEX_MCS0; + if (pmpriv->rxpd_htinfo & MBIT(1)) + rate->param.data_rate.rx_ht_bw = MLAN_HT_BW40; + else + rate->param.data_rate.rx_ht_bw = MLAN_HT_BW20; + if (pmpriv->tx_htinfo & MBIT(2)) + rate->param.data_rate.rx_ht_gi = MLAN_HT_SGI; + else + rate->param.data_rate.rx_ht_gi = MLAN_HT_LGI; + } else + /* 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_multicast_list) + MLAN_SUB_COMMAND_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 = &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_cfg->cfg_index = 0; + + 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 < sizeof(rate_scope->ht_mcs_rate_bitmap) / sizeof(t_u16); + 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 < sizeof(rate_scope->ht_mcs_rate_bitmap) / sizeof(t_u16); + 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; + 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 = &resp->params.tx_rate_cfg; + + tlv_buf = (t_u8 *) ((t_u8 *) prate_cfg) + sizeof(HostCmd_DS_TX_RATE_CFG); + 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 < sizeof(prate_scope->ht_mcs_rate_bitmap) / sizeof(t_u16); + 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; + } 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 = 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 = index + MLAN_RATE_INDEX_MCS0; + } + 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_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 + + /* + * This should be issued in the very first to config + * SDIO_GPIO interrupt mode. + */ + if (wlan_set_sdio_gpio_int(pmpriv) != MLAN_STATUS_SUCCESS) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_FUNC_INIT, + HostCmd_ACT_GEN_SET, 0, MNULL, MNULL); + if (ret) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /** Cal data dnld cmd prepare */ + if ((pmadapter->pcal_data) && (pmadapter->cal_data_len > 0)) { + ret = + wlan_prepare_cmd(pmpriv, HostCmd_CMD_CFG_DATA, HostCmd_ACT_GEN_SET, + 0, MNULL, MNULL); + if (ret) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + pmadapter->pcal_data = MNULL; + pmadapter->cal_data_len = 0; + } + + /* + * Get HW spec + */ + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_GET_HW_SPEC, + HostCmd_ACT_GEN_GET, 0, MNULL, MNULL); + if (ret) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* 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; + } + } + + ret = MLAN_STATUS_PENDING; + done: + LEAVE(); + return ret; +} + +/** + * @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 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_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 cal_data_offset; + t_u8 *temp_pcmd = (t_u8 *) pcmd; + + ENTER(); + + cal_data_offset = S_DS_GEN + sizeof(HostCmd_DS_802_11_CFG_DATA); + if ((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 + cal_data_offset)); + } else { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + pcfg_data->action = cmd_action; + pcfg_data->type = 2; /* cal data type */ + pcfg_data->data_len = len; + + pcmd->command = HostCmd_CMD_CFG_DATA; + pcmd->size = pcfg_data->data_len + cal_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, "Cal data cmd resp failed\n"); + ret = MLAN_STATUS_FAILURE; + } + LEAVE(); + return ret; +} + +/** + * @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; + pmlan_ioctl_req pioctl_req = (mlan_ioctl_req *) pioctl_buf; + + ENTER(); + + pmadapter->fw_cap_info = wlan_le32_to_cpu(hw_spec->fw_cap_info); +#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 = hw_spec->fw_release_number; + pmadapter->number_of_antenna = wlan_le16_to_cpu(hw_spec->number_of_antenna); + + PRINTM(MINFO, "GET_HW_SPEC: fw_release_number- 0x%X\n", + wlan_le32_to_cpu(pmadapter->fw_release_number)); + PRINTM(MINFO, "GET_HW_SPEC: Permanent addr- %2x:%2x:%2x:%2x:%2x:%2x\n", + hw_spec->permanent_addr[0], hw_spec->permanent_addr[1], + hw_spec->permanent_addr[2], hw_spec->permanent_addr[3], + hw_spec->permanent_addr[4], hw_spec->permanent_addr[5]); + 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); + + pmadapter->hw_dot_11n_dev_cap = wlan_le32_to_cpu(hw_spec->dot_11n_dev_cap); + pmadapter->usr_dot_11n_dev_cap_bg = pmadapter->hw_dot_11n_dev_cap & + DEFAULT_11N_CAP_MASK_BG; + pmadapter->usr_dot_11n_dev_cap_a = pmadapter->hw_dot_11n_dev_cap & + DEFAULT_11N_CAP_MASK_A; + pmadapter->usr_dev_mcs_support = pmadapter->hw_dev_mcs_support = + hw_spec->dev_mcs_support; + wlan_show_dot11ndevcap(pmadapter, pmadapter->hw_dot_11n_dev_cap); + wlan_show_devmcssupport(pmadapter, pmadapter->hw_dev_mcs_support); + 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); + } + + 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 */ + + 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; +} + +#ifdef WIFI_DIRECT_SUPPORT +/** + * @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; +} + +/** + * @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; +} +#endif diff --git a/drivers/net/wireless/sd8797/mlan/mlan_decl.h b/drivers/net/wireless/sd8797/mlan/mlan_decl.h new file mode 100644 index 000000000000..d0ca7acae937 --- /dev/null +++ b/drivers/net/wireless/sd8797/mlan/mlan_decl.h @@ -0,0 +1,883 @@ +/** @file mlan_decl.h + * + * @brief This file declares the generic data structures and APIs. + * + * Copyright (C) 2008-2011, 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 "303" + +/** Re-define generic data types for MLAN/MOAL */ +/** Signed char (1-byte) */ +typedef 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) + +/** 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 0 + +/** DMA alignment */ +#define DMA_ALIGNMENT 64 +/** 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 2 +/** This is current limit on Maximum Rx AMPDU allowed */ +#define MLAN_MAX_RX_BASTREAM_SUPPORTED 16 + +/** Default Win size attached during ADDBA request */ +#define MLAN_AMPDU_DEF_TXWINSIZE 32 +/** Default Win size attached during ADDBA response */ +#define MLAN_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 12 +/** Rate index for MCS 7 */ +#define MLAN_RATE_INDEX_MCS7 19 +/** Rate index for MCS 9 */ +#define MLAN_RATE_INDEX_MCS9 21 +/** Rate index for MCS15 */ +#define MLAN_RATE_INDEX_MCS15 27 +/** Rate index for MCS 32 */ +#define MLAN_RATE_INDEX_MCS32 44 +/** Rate index for MCS 127 */ +#define MLAN_RATE_INDEX_MCS127 139 + +/** 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) + +/** 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) + +/** 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 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) + +#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 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; + +/** 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, + MLAN_ERROR_FW_CMDRESP, + MLAN_ERROR_DATA_TX_FAIL, + MLAN_ERROR_DATA_RX_FAIL, + /** Driver errors below (MSB=1) */ + MLAN_ERROR_PKT_SIZE_INVALID = 0x80000001, + MLAN_ERROR_PKT_TIMEOUT, + MLAN_ERROR_PKT_INVALID, + MLAN_ERROR_CMD_INVALID, + MLAN_ERROR_CMD_TIMEOUT, + MLAN_ERROR_CMD_DNLD_FAIL, + MLAN_ERROR_CMD_CANCEL, + MLAN_ERROR_CMD_RESP_FAIL, + MLAN_ERROR_CMD_ASSOC_FAIL, + MLAN_ERROR_CMD_SCAN_FAIL, + MLAN_ERROR_IOCTL_INVALID, + MLAN_ERROR_IOCTL_FAIL, + MLAN_ERROR_EVENT_UNKNOWN, + MLAN_ERROR_INVALID_PARAMETER, + MLAN_ERROR_NO_MEM, + /** 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; + +/** 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_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, + MLAN_EVENT_ID_FW_ADHOC_LINK_LOST, + MLAN_EVENT_ID_FW_DISCONNECTED, + MLAN_EVENT_ID_FW_MIC_ERR_UNI, + MLAN_EVENT_ID_FW_MIC_ERR_MUL, + MLAN_EVENT_ID_FW_BCN_RSSI_LOW, + MLAN_EVENT_ID_FW_BCN_RSSI_HIGH, + MLAN_EVENT_ID_FW_BCN_SNR_LOW, + MLAN_EVENT_ID_FW_BCN_SNR_HIGH, + MLAN_EVENT_ID_FW_MAX_FAIL, + MLAN_EVENT_ID_FW_DATA_RSSI_LOW, + MLAN_EVENT_ID_FW_DATA_RSSI_HIGH, + MLAN_EVENT_ID_FW_DATA_SNR_LOW, + MLAN_EVENT_ID_FW_DATA_SNR_HIGH, + MLAN_EVENT_ID_FW_LINK_QUALITY, + MLAN_EVENT_ID_FW_PORT_RELEASE, + MLAN_EVENT_ID_FW_PRE_BCN_LOST, + MLAN_EVENT_ID_FW_WMM_CONFIG_CHANGE, + MLAN_EVENT_ID_FW_HS_WAKEUP, + MLAN_EVENT_ID_FW_BG_SCAN, + MLAN_EVENT_ID_FW_WEP_ICV_ERR, + MLAN_EVENT_ID_FW_STOP_TX, + MLAN_EVENT_ID_FW_START_TX, + MLAN_EVENT_ID_FW_CHANNEL_SWITCH_ANN, + MLAN_EVENT_ID_FW_RADAR_DETECTED, + MLAN_EVENT_ID_FW_CHANNEL_REPORT_RDY, + MLAN_EVENT_ID_FW_BW_CHANGED, +#ifdef WIFI_DIRECT_SUPPORT + MLAN_EVENT_ID_FW_REMAIN_ON_CHAN_EXPIRED, +#endif +#ifdef UAP_SUPPORT + MLAN_EVENT_ID_UAP_FW_BSS_START, + MLAN_EVENT_ID_UAP_FW_BSS_ACTIVE, + MLAN_EVENT_ID_UAP_FW_BSS_IDLE, + MLAN_EVENT_ID_UAP_FW_STA_CONNECT, + MLAN_EVENT_ID_UAP_FW_STA_DISCONNECT, +#endif + + /* Event generated by MLAN driver (MSB=1) */ + MLAN_EVENT_ID_DRV_CONNECTED = 0x80000001, + MLAN_EVENT_ID_DRV_DEFER_HANDLING, + MLAN_EVENT_ID_DRV_HS_ACTIVATED, + MLAN_EVENT_ID_DRV_HS_DEACTIVATED, + MLAN_EVENT_ID_DRV_MGMT_FRAME, + MLAN_EVENT_ID_DRV_OBSS_SCAN_PARAM, + MLAN_EVENT_ID_DRV_PASSTHRU, + MLAN_EVENT_ID_DRV_SCAN_REPORT, + MLAN_EVENT_ID_DRV_MEAS_REPORT, + MLAN_EVENT_ID_DRV_REPORT_STRING, + MLAN_EVENT_ID_DRV_DBG_DUMP, +} 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; +} mlan_fw_image, *pmlan_fw_image; + +/** Custom data structure */ +typedef struct _mlan_init_param +{ + /** 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; + +/** 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[1]; +} mlan_event, *pmlan_event; + +/** 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; + +/** mlan_ioctl_req data structure */ +typedef struct _mlan_ioctl_req +{ + /** 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; + +/** 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; + + /** 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; +} mlan_buffer, *pmlan_buffer; + +/** 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; +} mlan_bss_attr, *pmlan_bss_attr; + +#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 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]; + /** Max mgmt IE TLV */ + tlvbuf_max_mgmt_ie max_mgmt_ie; +} MLAN_PACK_END mlan_ds_misc_custom_ie; + +#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_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_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 t_s8 * 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); +} mlan_callbacks, *pmlan_callbacks; + +/** Interrupt Mode SDIO */ +#define INT_MODE_SDIO 0 +/** Interrupt Mode GPIO */ +#define INT_MODE_GPIO 1 + +/** 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 +#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; +#if defined(STA_SUPPORT) + /** 802.11d configuration */ + t_u32 cfg_11d; +#endif +} 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); + +/** 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 t_void mlan_interrupt(IN t_void * pmlan_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/sd8797/mlan/mlan_fw.h b/drivers/net/wireless/sd8797/mlan/mlan_fw.h new file mode 100644 index 000000000000..4b15b43711b5 --- /dev/null +++ b/drivers/net/wireless/sd8797/mlan/mlan_fw.h @@ -0,0 +1,4630 @@ +/** @file mlan_fw.h + * + * @brief This file contains firmware specific defines. + * structures and declares global function prototypes used + * in MLAN module. + * + * Copyright (C) 2008-2011, 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 + +/** Ethernet header */ +typedef 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; + +} Eth803Hdr_t; + +/** RFC 1042 header */ +typedef 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; + +} Rfc1042Hdr_t; + +#ifdef PRAGMA_PACK +#pragma pack(push, 1) +#endif + +/** 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)) + +/** 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]; + +/** Default auto deep sleep mode */ +#define DEFAULT_AUTO_DS_MODE MTRUE +/** Default power save mode */ +#define DEFAULT_PS_MODE Wlan802_11PowerModePSP + +/** WEP Key index mask */ +#define HostCmd_WEP_KEY_INDEX_MASK 0x3fff + +/** 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, + /** Key type : AES */ + KEY_TYPE_ID_AES, + KEY_TYPE_ID_WAPI, +} KEY_TYPE_ID; + +/** 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; + +/** WPA AES key length */ +#define WPA_AES_KEY_LEN 16 +/** WPA TKIP key length */ +#define WPA_TKIP_KEY_LEN 32 + +/** 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 1000 + +/** 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 + +/** TLV type : Vendor Specific IE */ +#define TLV_TYPE_VENDOR_SPECIFIC_IE 0xdd + +/** 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 : 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: 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 : 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 type : RXBA_SYNC */ +#define TLV_TYPE_RXBA_SYNC (PROPRIETARY_TLV_BASE_ID + 0x99) // 0x0199 +/** 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 + +/** Test if 11n is enabled by checking the HTCap IE */ +#define IS_11N_ENABLED(priv) ((priv->adapter->config_bands & BAND_GN ||priv->adapter->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) +/** 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) +/** Default 11n capability mask for 5GHz */ +#define DEFAULT_11N_CAP_MASK_A (HWSPEC_CHANBW40_SUPP | HWSPEC_SHORTGI20_SUPP | \ + HWSPEC_SHORTGI40_SUPP | HWSPEC_RXSTBC_SUPP) +/** 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 : 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 : Delayed ACK */ +#define GET_DELAYEDBACK(Dot11nDevCap) (((Dot11nDevCap >> 20) & 0x03)) +/** HW_SPEC Dot11nDevCap : Immediate ACK */ +#define GET_IMMEDIATEBACK(Dot11nDevCap) (((Dot11nDevCap >> 18) & 0x03)) +/** 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)) +/** 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)) + +/** 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 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))) +/** RadioType : Set secondary channel */ +#define SET_SECONDARYCHAN(RadioType, SECCHAN) (RadioType |= (SECCHAN << 4)) + +/** 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 +/** HT bandwidth 20 MHz */ +#define HT_BW_20 0 +/** HT bandwidth 40 MHz */ +#define HT_BW_40 1 + +/** 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 + +/** 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 : 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 : 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 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 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 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 : 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 : 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 : 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 + +#endif /* UAP_SUPPORT */ + +/** Host Command ID: Tx data pause */ +#define HostCmd_CMD_CFG_TX_DATA_PAUSE 0x0103 + +#ifdef WIFI_DIRECT_SUPPORT +/** Host Command ID: WIFI_DIRECT_MODE_CONFIG */ +#define HOST_CMD_WIFI_DIRECT_MODE_CONFIG 0x00eb +/** Host Command ID: Remain On Channel */ +#define HostCmd_CMD_802_11_REMAIN_ON_CHANNEL 0x010d +#endif + +/** 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 + +/** 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 + +/* Radio type definitions for the channel TLV */ +/** Radio type BG */ +#define HostCmd_SCAN_RADIO_TYPE_BG 0 +/** Radio type A */ +#define HostCmd_SCAN_RADIO_TYPE_A 1 + +/** 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 20 +/** Size of command buffer */ +#define MRVDRV_SIZE_OF_CMD_BUFFER (2 * 1024) + +/** Maximum number of BSS Descriptors */ +#define MRVDRV_MAX_BSSID_LIST 64 + +/** Host command flag in command */ +#define CMD_F_HOSTCMD (1 << 0) +/** command cancel flag in command */ +#define CMD_F_CANCELED (1 << 1) + +/** 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 : 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 + +/** 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 +/** Remain on Channel expired event */ +#define EVENT_REMAIN_ON_CHANNEL_EXPIRED 0x0000005f +#endif + +/** 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 + +/** 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 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_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; + +/** 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 + +/** 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 + +/** 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 reserved1; +} 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; + /** Ht Info [Bit 0] RxRate format: LG=0, HT=1 + * [Bit 1] HT Bandwidth: BW20 = 0, BW40 = 1 + * [Bit 2] HT Guard Interval: LGI = 0, SGI = 1 */ + t_u8 ht_info; + /** Reserved */ + t_u8 reserved; +} MLAN_PACK_END RxPD, *PRxPD; + +#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 reserved1; + /** Reserved */ + t_u32 reserved; +} 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 number */ + t_u16 seq_num; + /** Packet Priority */ + t_u8 priority; + /** reserved */ + t_u8 reserved1; +} MLAN_PACK_END UapRxPD, *PUapRxPD; + +/** Fixed size of station association event */ +#define ASSOC_EVENT_FIX_SIZE 12 + +/** 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; + +/** 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_2_7:6; + /** 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; + /** Reserved */ + t_u8 reserved_2_7:6; +#endif +} MLAN_PACK_END ChanScanMode_t; + +/** secondary channel is below */ +#define SECOND_CHANNEL_BELOW 0x30 +/** secondary channel is above */ +#define SECOND_CHANNEL_ABOVE 0x10 +/** channel offset */ +enum +{ + SEC_CHAN_NONE = 0, + SEC_CHAN_ABOVE = 1, + SEC_CHAN_BELOW = 3 +}; +/** channel bandwidth */ +enum +{ + CHAN_BW_20MHZ = 0, + CHAN_BW_10MHZ, + CHAN_BW_40MHZ, +}; +/** ChanScanParamSet_t */ +typedef MLAN_PACK_START struct _ChanScanParamSet_t +{ + /** Channel scan parameter : Radio type */ + t_u8 radio_type; + /** 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 : Radio type */ + t_u8 radio_type; + /** 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_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_ActionFrame_t */ +typedef MLAN_PACK_START struct +{ + MrvlIEtypesHeader_t header; /**< Header */ + + t_u8 srcAddr[MLAN_MAC_ADDR_LENGTH]; + t_u8 dstAddr[MLAN_MAC_ADDR_LENGTH]; + + 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[1]; +} MLAN_PACK_END MrvlIEtypes_RsnParamSet_t; + +/** Key_param_set fixed length */ +#define KEYPARAMSET_FIXED_LEN 6 + +/** MrvlIEtype_KeyParamSet_t */ +typedef MLAN_PACK_START struct _MrvlIEtype_KeyParamSet_t +{ + /** Type ID */ + t_u16 type; + /** Length of Payload */ + t_u16 length; + /** Type of Key: WEP=0, TKIP=1, AES=2 */ + t_u16 key_type_id; + /** Key Control Info specific to a key_type_id */ + t_u16 key_info; + /** Length of key */ + t_u16 key_len; + /** Key material of size key_len */ + t_u8 key[50]; +} MLAN_PACK_END MrvlIEtype_KeyParamSet_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_KeyParamSet_t key_param_set; +} MLAN_PACK_END HostCmd_DS_802_11_KEY_MATERIAL; + +/** 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; + +/* 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 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; +} ps_param; + +/** Structure definition for the new auto deep sleep command */ +typedef struct __auto_ds_param +{ + /** Deep sleep inactivity timeout */ + t_u16 deep_sleep_timeout; +} auto_ds_param; + +/** Structure definition for sleep confirmation in the new ps command */ +typedef struct __sleep_confirm_param +{ + /** response control 0x00 - response not needed, 0x01 - response needed */ + t_u16 resp_ctrl; +} 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 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 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 */ +} 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 + +/** 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; + +/** 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; + +/** 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; +} MLAN_PACK_END HostCmd_DS_GET_HW_SPEC; + +/** 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 */ +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_u16 action; + /** Reserved field */ + t_u16 reserved; +} MLAN_PACK_END HostCmd_DS_MAC_CONTROL; + +/** 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]; +} 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; + /** Ht Info [Bit 0] RxRate format: LG=0, HT=1 + * [Bit 1] HT Bandwidth: BW20 = 0, BW40 = 1 + * [Bit 2] HT Guard Interval: LGI = 0, SGI = 1 */ + t_u8 ht_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; + +/** 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, +} 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]; +} MLAN_PACK_END MrvlRateScope_t; + +/** MrvlRateDropControl_t */ +typedef MLAN_PACK_START struct _MrvlRateDropControl_t +{ + /** Header Length */ + t_u16 length; + /** Rate Information */ + t_u32 rate_info[1]; +} MLAN_PACK_END MrvlRateDropControl_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; + /** Tx Rate Configuration Index */ + t_u16 cfg_index; + /* 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_u16 current_level; + /** Maximum power */ + t_u8 max_power; + /** Minimum power */ + t_u8 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_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 */ + t_u8 bandcfg; + /** channel */ + t_u8 channel; + /** remain time: Unit ms*/ + t_u32 remain_period; +} MLAN_PACK_END HostCmd_DS_REMAIN_ON_CHANNEL; + +/** 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; +#endif + +#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; + +/* + * 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 +{ + /** Reserved */ + t_u32 reserved; + /** 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; + */ +} MLAN_PACK_END HostCmd_DS_802_11_SCAN_EXT; + +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 */ + t_u8 band; + /** 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_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_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_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 + +/** Maximum number of AC QOS queues available in the driver/firmware */ +#define MAX_AC_QUEUES 4 + +/** 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; + +/** 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 */ + +/** 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; + +/** 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 */ + t_u16 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; + +/** HostCmd_CMD_802_11_RF_ANTENNA */ +typedef MLAN_PACK_START struct _HostCmd_DS_802_11_RF_ANTENNA +{ + /** Action for Tx antenna */ + t_u16 action_tx; + /** Tx antenna mode Bit0:1, Bit1:2, Bit0-1:1+2, 0xffff: diversity */ + t_u16 tx_antenna_mode; + /** Action for Rx antenna */ + t_u16 action_rx; + /** Rx antenna mode Bit0:1, Bit1:2, Bit0-1:1+2, 0xffff: diversity */ + t_u16 rx_antenna_mode; +} 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_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_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_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; + +/** 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 + +#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 ID : Management Frame */ +#define TLV_TYPE_UAP_MGMT_FRAME (PROPRIETARY_TLV_BASE_ID + 0x68) // 0x0168 +#ifdef UAP_SUPPORT +/**TLV type: AP mgmt IE passthru mask */ +#define TLV_TYPE_UAP_MGMT_IE_PASSTHRU_MASK (PROPRIETARY_TLV_BASE_ID + 0x70) // 0x0170 +#endif +/** TLV : 20/40 coex config */ +#define TLV_TYPE_2040_BSS_COEX_CONTROL (PROPRIETARY_TLV_BASE_ID + 0x98) // 0x0198 +/**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 + +#ifdef WIFI_DIRECT_SUPPORT +/** TLV type : AP PSK */ +#define TLV_TYPE_UAP_PSK (PROPRIETARY_TLV_BASE_ID + 0xa8) // 0x01a8 +#endif /* WIFI_DIRECT_SUPPORT */ + +/** 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; + +#ifdef UAP_SUPPORT +/** 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; +#endif + +/** 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; + +/** 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; + +/** setting for band_config - band=5GHZ */ +#define BAND_CONFIG_5GHZ 0x01 + +/** MrvlIEtypes_retry_limit_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_channel_band_t +{ + /** Header */ + MrvlIEtypesHeader_t header; + /** Band Configuration + * + * [7-6] Channel Selection Mode; 00 manual, 01 ACS + * [3-2] Channel Width; 00 20 MHz + * [1-0] Band Info; 00 2.4 GHz + */ + t_u8 band_config; + /** channel */ + t_u8 channel; +} MLAN_PACK_END MrvlIEtypes_channel_band_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; + +/** 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 */ + +/** + * @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 +{ + MrvlIEtypes_LocalPowerConstraint_t local_constraint; /**< Local constraint */ + MrvlIEtypes_PowerCapability_t power_cap; /**< Power Capability */ + +} 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 + t_u8 rsvd5_7:3; /**< Reserved */ + t_u8 duration_mandatory:1; /**< 11k: duration spec. for meas. is mandatory */ + t_u8 report:1; /**< 11h: en/disable report rcpt. of spec. type */ + t_u8 request:1; /**< 11h: en/disable requests of specified type */ + t_u8 enable:1; /**< 11h: enable report/request bits */ + t_u8 parallel:1; /**< 11k: series or parallel with previous meas */ +#else + t_u8 parallel:1; /**< 11k: series or parallel with previous meas */ + t_u8 enable:1; /**< 11h: enable report/request bits */ + t_u8 request:1; /**< 11h: en/disable requests of specified type */ + t_u8 report:1; /**< 11h: en/disable report rcpt. of spec. type */ + t_u8 duration_mandatory:1; /**< 11k: duration spec. for meas. is mandatory */ + t_u8 rsvd5_7:3; /**< Reserved */ +#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; + t_u8 chanWidth; + 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_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; + /** 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 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; + /** Enhanced power save command */ + HostCmd_DS_802_11_PS_MODE_ENH psmode_enh; + HostCmd_DS_802_11_HS_CFG_ENH opt_hs_cfg; + /** 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; + /** 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; + 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; + /** 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 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; + /** 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; + /** 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; + + /** 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; +#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_CMD_TX_DATA_PAUSE tx_data_pause; +#ifdef WIFI_DIRECT_SUPPORT + HostCmd_DS_REMAIN_ON_CHANNEL remain_on_chan; + HostCmd_DS_WIFI_DIRECT_MODE wifi_direct_mode; +#endif + } 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]; + /** 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/sd8797/mlan/mlan_ieee.h b/drivers/net/wireless/sd8797/mlan/mlan_ieee.h new file mode 100644 index 000000000000..923a38056800 --- /dev/null +++ b/drivers/net/wireless/sd8797/mlan/mlan_ieee.h @@ -0,0 +1,1318 @@ +/** @file mlan_ieee.h + * + * @brief This file contains IEEE information element related + * definitions used in MLAN and MOAL module. + * + * Copyright (C) 2008-2011, 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_ + +/** 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; + +/** Maximum size of IEEE Information Elements */ +#define IEEE_MAX_IE_SIZE 256 + +#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 + +/** 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, + +#ifdef STA_SUPPORT + COUNTRY_INFO = 7, +#endif /* STA_SUPPORT */ + + POWER_CONSTRAINT = 32, + POWER_CAPABILITY = 33, + TPC_REQUEST = 34, + TPC_REPORT = 35, + SUPPORTED_CHANNELS = 36, + CHANNEL_SWITCH_ANN = 37, + QUIET = 40, + IBSS_DFS = 41, + HT_CAPABILITY = 45, + HT_OPERATION = 61, + BSSCO_2040 = 72, + OVERLAPBSSSCANPARAM = 74, + EXT_CAPABILITY = 127, + + 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; + +/** Capability information mask */ +#define CAPINFO_MASK (~(MBIT(15) | MBIT(14) | \ + MBIT(12) | 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 rsvrd2:2; + t_u8 short_slot_time:1; + t_u8 rsrvd3:1; + t_u8 spectrum_mgmt:1; + t_u8 chan_agility:1; + t_u8 pbcc:1; + t_u8 short_preamble:1; + t_u8 privacy:1; + t_u8 cf_poll_rqst:1; + t_u8 cf_pollable:1; + t_u8 ibss:1; + t_u8 ess:1; +} 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; + +/** 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 + +/** 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; + +/** Maximum number of AC QOS queues available in the driver/firmware */ +#define MAX_AC_QUEUES 4 + +/** 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 +{ + + IEEEtypes_WMM_TSPEC_TS_Info_t TSInfo; + IEEEtypes_WMM_TSPEC_NomMSDUSize_t NomMSDUSize; + t_u16 MaximumMSDUSize; + t_u32 MinServiceInterval; + t_u32 MaxServiceInterval; + t_u32 InactivityInterval; + t_u32 SuspensionInterval; + t_u32 ServiceStartTime; + t_u32 MinimumDataRate; + t_u32 MeanDataRate; + t_u32 PeakDataRate; + t_u32 MaxBurstSize; + t_u32 DelayBound; + t_u32 MinPHYRate; + IEEEtypes_WMM_TSPEC_SBWA SurplusBWAllowance; + t_u16 MediumTime; +} MLAN_PACK_END IEEEtypes_WMM_TSPEC_Body_t; + +/** Data structure of WMM TSPEC all elements */ +typedef MLAN_PACK_START struct +{ + t_u8 ElementId; + t_u8 Len; + t_u8 OuiType[4]; /* 00:50:f2:02 */ + t_u8 OuiSubType; /* 01 */ + t_u8 Version; + + 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 +{ + + IEEEtypes_ActionCategory_e category; + IEEEtypes_WMM_Tspec_Action_e action; + t_u8 dialogToken; + +} MLAN_PACK_END IEEEtypes_WMM_Tspec_Action_Base_Tspec_t; + +/** WMM TSPEC AddTS request 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_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 +{ + IEEEtypes_WMM_Tspec_Action_Base_Tspec_t tspecAct; + t_u8 reasonCode; + IEEEtypes_WMM_TSPEC_t tspecIE; + +} MLAN_PACK_END IEEEtypes_Action_WMM_DelTs_t; + +/** union of WMM TSPEC structures */ +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; + +/** union of WMM TSPEC & Action category */ +typedef MLAN_PACK_START union +{ + IEEEtypes_ActionCategory_e category; + + 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 */ + +/** 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; + +/** Extended Capabilities Data */ +typedef struct MLAN_PACK_START _ExtCap_t +{ + /** Extended Capabilities value */ + t_u8 ext_cap_value; +} MLAN_PACK_END ExtCap_t, *pExtCap_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 + t_u8 rsvd5_7:3; /**< Reserved */ + t_u8 unmeasured:1; /**< Channel is unmeasured */ + t_u8 radar:1; /**< Radar detected on channel */ + t_u8 unidentified_sig:1; /**< Unidentified signal found on channel */ + t_u8 ofdm_preamble:1; /**< OFDM preamble detected on channel */ + t_u8 bss:1; /**< At least one valid MPDU received on channel */ +#else + t_u8 bss:1; /**< At least one valid MPDU received on channel */ + t_u8 ofdm_preamble:1; /**< OFDM preamble detected on channel */ + t_u8 unidentified_sig:1; /**< Unidentified signal found on channel */ + t_u8 radar:1; /**< Radar detected on channel */ + t_u8 unmeasured:1; /**< Channel is unmeasured */ + t_u8 rsvd5_7:3; /**< Reserved */ +#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; + +#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; + +/** + * 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 Reserved + */ + t_u8 reserved; + /** + * @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]; +} 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 +/** Maximum number of channels that can be sent in bg scan config */ +#define WLAN_BG_SCAN_CHAN_MAX 32 + +/** + * 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; + /** 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]; +} 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]; + + /** 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 + + /** 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/sd8797/mlan/mlan_init.c b/drivers/net/wireless/sd8797/mlan/mlan_init.c new file mode 100644 index 000000000000..e949502e7d69 --- /dev/null +++ b/drivers/net/wireless/sd8797/mlan/mlan_init.c @@ -0,0 +1,1038 @@ +/** @file mlan_init.c + * + * @brief This file contains the initialization for FW + * and HW. + * + * Copyright (C) 2008-2011, 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" + +/******************************************************** + Global Variables +********************************************************/ + +/******************************************************** + 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(); + + if ((status = + pmadapter->callbacks.moal_malloc(pmadapter->pmoal_handle, + sizeof(mlan_bssprio_node), + MLAN_MEM_DEF, + (t_u8 **) & pbssprio))) { + 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 buf_size; + BSSDescriptor_t *ptemp_scan_table = MNULL; +#endif + + ENTER(); + +#ifdef STA_SUPPORT + /* Allocate buffer to store the BSSID list */ + buf_size = sizeof(BSSDescriptor_t) * MRVDRV_MAX_BSSID_LIST; + 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; + ret = + pmadapter->callbacks.moal_malloc(pmadapter->pmoal_handle, + DEFAULT_SCAN_BEACON_BUFFER, + 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 = DEFAULT_SCAN_BEACON_BUFFER; +#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_TX_AGGR) || defined(SDIO_MULTI_PORT_RX_AGGR) + ret = wlan_alloc_sdio_mpa_buffers(pmadapter, SDIO_MP_TX_AGGR_DEF_BUF_SIZE, + SDIO_MP_RX_AGGR_DEF_BUF_SIZE); + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "Failed to allocate sdio mp-a buffers\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } +#endif + + pmadapter->psleep_cfm = + wlan_alloc_mlan_buffer(pmadapter, sizeof(opt_sleep_confirm_buffer), 0, + MTRUE); + + 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 < sizeof(priv->wep_key) / sizeof(priv->wep_key[0]); 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_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->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); +#if defined(UAP_SUPPORT) + priv->uap_bss_started = 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_htinfo = 0; + 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; + 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; + + 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->tx_bf_cap = 0; + priv->wmm_required = MTRUE; + priv->wmm_enabled = MFALSE; + priv->wmm_qosinfo = 0; +#ifdef STA_SUPPORT + priv->pcurr_bcn_buf = MNULL; + priv->curr_bcn_size = 0; +#endif /* STA_SUPPORT */ + + for (i = 0; i < MAX_NUM_TID; i++) + priv->addba_reject[i] = ADDBA_RSP_STATUS_ACCEPT; + priv->max_amsdu = 0; + + priv->scan_block = MFALSE; + + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) { + priv->port_ctrl_mode = MTRUE; + } else { + priv->port_ctrl_mode = MFALSE; + } + priv->port_open = MFALSE; + + if (!ret) + ret = wlan_add_bsspriotbl(priv); + + 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) +{ + int i; + opt_sleep_confirm_buffer *sleep_cfm_buf = MNULL; + + ENTER(); + + 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 = 1; + pmadapter->curr_wr_port = 1; + for (i = 0; i < MAX_NUM_TID; i++) { + pmadapter->tx_eligibility[i] = 1; + } + pmadapter->mp_data_port_mask = DATA_PORT_MASK; + +#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->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; + + /* Status variables */ + pmadapter->hw_status = WlanHardwareStatusInitializing; + + 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 + /* 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->ext_scan = 0; + 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; + pmadapter->multiple_dtim = MRVDRV_DEFAULT_MULTIPLE_DTIM; + + pmadapter->local_listen_interval = 0; /* default value in firmware + will be used */ +#endif /* STA_SUPPORT */ + + 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; + + memset(pmadapter, pmadapter->event_body, 0, sizeof(pmadapter->event_body)); + pmadapter->hw_dot_11n_dev_cap = 0; + pmadapter->hw_dev_mcs_support = 0; + pmadapter->usr_dot_11n_dev_cap_bg = 0; + pmadapter->usr_dot_11n_dev_cap_a = 0; + pmadapter->usr_dev_mcs_support = 0; +#ifdef STA_SUPPORT + pmadapter->chan_bandwidth = 0; + pmadapter->adhoc_11n_enabled = MFALSE; +#endif /* STA_SUPPORT */ + + /* Initialize 802.11d */ + wlan_11d_init(pmadapter); + + wlan_11h_init(pmadapter); + + wlan_wmm_init(pmadapter); + + 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)); + 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; + 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 */ + + LEAVE(); + return; +} + +/** + * @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_private priv = MNULL; + pmlan_callbacks pcb = &pmadapter->callbacks; + t_s32 i = 0; + t_u32 j = 0; + + 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->pmlan_cmd_lock) + != MLAN_STATUS_SUCCESS) { + ret = MLAN_STATUS_FAILURE; + goto error; + } + for (i = 0; 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 + } + } + + /* Initialize bypass_txq */ + util_init_list_head((t_void *) pmadapter->pmoal_handle, + &pmadapter->bypass_txq, MTRUE, + 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); + + for (i = 0; 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 = 0; 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); + } + } + + 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->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->bypass_txq, + pmadapter->callbacks.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); + + 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); + + 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; + pmlan_private priv = MNULL; + t_u8 i = 0; + + ENTER(); + + /* Initialize adapter structure */ + wlan_init_adapter(pmadapter); + + for (i = 0; i < pmadapter->priv_num; i++) { + if (pmadapter->priv[i]) { + priv = pmadapter->priv[i]; + + /* Initialize private structure */ + if ((ret = wlan_init_priv(priv))) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + } + } +#ifdef MFG_CMD_SUPPORT + if (pmadapter->mfg_mode != MTRUE) { +#endif + /* 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); + ret = priv->ops.init_cmd(priv, MTRUE); + if (ret == MLAN_STATUS_FAILURE) + goto done; +#ifdef MFG_CMD_SUPPORT + } +#endif + + if (util_peek_list(pmadapter->pmoal_handle, &pmadapter->cmd_pending_q, + pmadapter->callbacks.moal_spin_lock, + pmadapter->callbacks.moal_spin_unlock)) { + /* 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; + } +#ifdef STA_SUPPORT + PRINTM(MINFO, "Free ScanTable\n"); + if (pmadapter->pscan_table) { + pcb->moal_mfree(pmadapter->pmoal_handle, + (t_u8 *) pmadapter->pscan_table); + pmadapter->pscan_table = 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_TX_AGGR) || defined(SDIO_MULTI_PORT_RX_AGGR) + wlan_free_sdio_mpa_buffers(pmadapter); +#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 */ + + wlan_delete_station_list(pmpriv); + LEAVE(); +} + +/** + * @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; + + /* 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/sd8797/mlan/mlan_init.h b/drivers/net/wireless/sd8797/mlan/mlan_init.h new file mode 100644 index 000000000000..4496741a3cfb --- /dev/null +++ b/drivers/net/wireless/sd8797/mlan/mlan_init.h @@ -0,0 +1,88 @@ +/** @file mlan_init.h + * + * @brief This file defines the FW initialization data + * structures. + * + * Copyright (C) 2008-2011, 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 + +/** Firmware data transmit size */ +#define FW_DATA_XMIT_SIZE \ + sizeof(FWHeader) + DataLength + sizeof(t_u32) + +/** FWHeader */ +typedef 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; +} FWHeader; + +/** FWData */ +typedef struct _FWData +{ + /** FW data header */ + FWHeader fw_header; + /** FW data sequence number */ + t_u32 seq_num; + /** FW data buffer */ + t_u8 data[1]; +} FWData; + +/** FWSyncHeader */ +typedef struct _FWSyncHeader +{ + /** FW sync header command */ + t_u32 cmd; + /** FW sync header sequence number */ + t_u32 seq_num; +} 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/sd8797/mlan/mlan_ioctl.h b/drivers/net/wireless/sd8797/mlan/mlan_ioctl.h new file mode 100644 index 000000000000..b947ce09675b --- /dev/null +++ b/drivers/net/wireless/sd8797/mlan/mlan_ioctl.h @@ -0,0 +1,2918 @@ +/** @file mlan_ioctl.h + * + * @brief This file declares the IOCTL data structures and APIs. + * + * Copyright (C) 2008-2011, 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, + MLAN_OID_SCAN_SPECIFIC_SSID, + MLAN_OID_SCAN_USER_CONFIG, + MLAN_OID_SCAN_CONFIG, + MLAN_OID_SCAN_GET_CURRENT_BSS, + MLAN_OID_SCAN_CANCEL, + MLAN_OID_SCAN_TABLE_FLUSH, + MLAN_OID_SCAN_BGSCAN_CONFIG, + /* BSS Configuration Group */ + MLAN_IOCTL_BSS = 0x00020000, + MLAN_OID_BSS_START, + MLAN_OID_BSS_STOP, + MLAN_OID_BSS_MODE, + MLAN_OID_BSS_CHANNEL, + MLAN_OID_BSS_CHANNEL_LIST, + MLAN_OID_BSS_MAC_ADDR, + MLAN_OID_BSS_MULTICAST_LIST, + MLAN_OID_BSS_FIND_BSS, + MLAN_OID_IBSS_BCN_INTERVAL, + MLAN_OID_IBSS_ATIM_WINDOW, + MLAN_OID_IBSS_CHANNEL, +#ifdef UAP_SUPPORT + MLAN_OID_UAP_BSS_CONFIG, + MLAN_OID_UAP_DEAUTH_STA, + MLAN_OID_UAP_BSS_RESET, +#endif +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) + MLAN_OID_BSS_ROLE, +#endif +#ifdef WIFI_DIRECT_SUPPORT + MLAN_OID_WIFI_DIRECT_MODE, +#endif + + /* Radio Configuration Group */ + MLAN_IOCTL_RADIO_CFG = 0x00030000, + MLAN_OID_RADIO_CTRL, + MLAN_OID_BAND_CFG, + MLAN_OID_ANT_CFG, +#ifdef WIFI_DIRECT_SUPPORT + MLAN_OID_REMAIN_CHAN_CFG, +#endif + + /* SNMP MIB Group */ + MLAN_IOCTL_SNMP_MIB = 0x00040000, + MLAN_OID_SNMP_MIB_RTS_THRESHOLD, + MLAN_OID_SNMP_MIB_FRAG_THRESHOLD, + MLAN_OID_SNMP_MIB_RETRY_COUNT, +#if defined(UAP_SUPPORT) + MLAN_OID_SNMP_MIB_DOT11D, + MLAN_OID_SNMP_MIB_DOT11H, +#endif + + /* Status Information Group */ + MLAN_IOCTL_GET_INFO = 0x00050000, + MLAN_OID_GET_STATS, + MLAN_OID_GET_SIGNAL, + MLAN_OID_GET_FW_INFO, + MLAN_OID_GET_VER_EXT, + MLAN_OID_GET_BSS_INFO, + MLAN_OID_GET_DEBUG_INFO, +#ifdef UAP_SUPPORT + MLAN_OID_UAP_STA_LIST, +#endif + + /* Security Configuration Group */ + MLAN_IOCTL_SEC_CFG = 0x00060000, + MLAN_OID_SEC_CFG_AUTH_MODE, + MLAN_OID_SEC_CFG_ENCRYPT_MODE, + MLAN_OID_SEC_CFG_WPA_ENABLED, + MLAN_OID_SEC_CFG_ENCRYPT_KEY, + MLAN_OID_SEC_CFG_PASSPHRASE, + MLAN_OID_SEC_CFG_EWPA_ENABLED, + MLAN_OID_SEC_CFG_ESUPP_MODE, + MLAN_OID_SEC_CFG_WAPI_ENABLED, + MLAN_OID_SEC_CFG_PORT_CTRL_ENABLED, + + /* Rate Group */ + MLAN_IOCTL_RATE = 0x00070000, + MLAN_OID_RATE_CFG, + MLAN_OID_GET_DATA_RATE, + MLAN_OID_SUPPORTED_RATES, + + /* Power Configuration Group */ + MLAN_IOCTL_POWER_CFG = 0x00080000, + MLAN_OID_POWER_CFG, + MLAN_OID_POWER_CFG_EXT, + + /* Power Management Configuration Group */ + MLAN_IOCTL_PM_CFG = 0x00090000, + MLAN_OID_PM_CFG_IEEE_PS, + MLAN_OID_PM_CFG_HS_CFG, + MLAN_OID_PM_CFG_INACTIVITY_TO, + MLAN_OID_PM_CFG_DEEP_SLEEP, + MLAN_OID_PM_CFG_SLEEP_PD, + MLAN_OID_PM_CFG_PS_CFG, + MLAN_OID_PM_CFG_SLEEP_PARAMS, +#ifdef UAP_SUPPORT + MLAN_OID_PM_CFG_PS_MODE, +#endif /* UAP_SUPPORT */ + MLAN_OID_PM_INFO, + + /* WMM Configuration Group */ + MLAN_IOCTL_WMM_CFG = 0x000A0000, + MLAN_OID_WMM_CFG_ENABLE, + MLAN_OID_WMM_CFG_QOS, + MLAN_OID_WMM_CFG_ADDTS, + MLAN_OID_WMM_CFG_DELTS, + MLAN_OID_WMM_CFG_QUEUE_CONFIG, + MLAN_OID_WMM_CFG_QUEUE_STATS, + MLAN_OID_WMM_CFG_QUEUE_STATUS, + MLAN_OID_WMM_CFG_TS_STATUS, + + /* WPS Configuration Group */ + MLAN_IOCTL_WPS_CFG = 0x000B0000, + MLAN_OID_WPS_CFG_SESSION, + + /* 802.11n Configuration Group */ + MLAN_IOCTL_11N_CFG = 0x000C0000, + MLAN_OID_11N_CFG_TX, + MLAN_OID_11N_HTCAP_CFG, + MLAN_OID_11N_CFG_ADDBA_REJECT, + MLAN_OID_11N_CFG_AGGR_PRIO_TBL, + MLAN_OID_11N_CFG_ADDBA_PARAM, + MLAN_OID_11N_CFG_MAX_TX_BUF_SIZE, + MLAN_OID_11N_CFG_AMSDU_AGGR_CTRL, + MLAN_OID_11N_CFG_SUPPORTED_MCS_SET, + MLAN_OID_11N_CFG_TX_BF_CAP, + MLAN_OID_11N_CFG_TX_BF_CFG, + MLAN_OID_11N_CFG_STREAM_CFG, + + /* 802.11d Configuration Group */ + MLAN_IOCTL_11D_CFG = 0x000D0000, +#ifdef STA_SUPPORT + MLAN_OID_11D_CFG_ENABLE, + MLAN_OID_11D_CLR_CHAN_TABLE, +#endif /* STA_SUPPORT */ + MLAN_OID_11D_DOMAIN_INFO, + + /* Register Memory Access Group */ + MLAN_IOCTL_REG_MEM = 0x000E0000, + MLAN_OID_REG_RW, + MLAN_OID_EEPROM_RD, + MLAN_OID_MEM_RW, + + /* Multi-Radio Configuration Group */ + MLAN_IOCTL_MFR_CFG = 0x00100000, + + /* 802.11h Configuration Group */ + MLAN_IOCTL_11H_CFG = 0x00110000, + MLAN_OID_11H_CHANNEL_CHECK, + MLAN_OID_11H_LOCAL_POWER_CONSTRAINT, +#if defined(DFS_TESTING_SUPPORT) + MLAN_OID_11H_DFS_TESTING, +#endif + + /* Miscellaneous Configuration Group */ + MLAN_IOCTL_MISC_CFG = 0x00200000, + MLAN_OID_MISC_GEN_IE, + MLAN_OID_MISC_REGION, + MLAN_OID_MISC_WARM_RESET, +#if defined(SDIO_MULTI_PORT_TX_AGGR) || defined(SDIO_MULTI_PORT_RX_AGGR) + MLAN_OID_MISC_SDIO_MPA_CTRL, +#endif + MLAN_OID_MISC_HOST_CMD, + MLAN_OID_MISC_SYS_CLOCK, + MLAN_OID_MISC_SOFT_RESET, + MLAN_OID_MISC_WWS, + MLAN_OID_MISC_INIT_SHUTDOWN, + MLAN_OID_MISC_CUSTOM_IE, + MLAN_OID_MISC_TX_DATAPAUSE, + MLAN_OID_MISC_IP_ADDR, + MLAN_OID_MISC_MAC_CONTROL, + MLAN_OID_MISC_MEF_CFG, + MLAN_OID_MISC_THERMAL, + MLAN_OID_MISC_RX_MGMT_IND, + MLAN_OID_MISC_SUBSCRIBE_EVENT, +#ifdef DEBUG_LEVEL1 + MLAN_OID_MISC_DRVDBG, +#endif +}; + +/** 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 +}; + +/** 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; + +/** + * 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; +} mlan_scan_resp, *pmlan_scan_resp; + +/** 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; + /** Extended 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 + +/** max Wmm AC queues */ +#define MAX_AC_QUEUES 4 + +/** 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; + +/** 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; +} mlan_ssid_bssid; + +#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 + +/** 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 + +/** TKIP */ +#define CIPHER_TKIP 0x04 +/** AES CCMP */ +#define CIPHER_AES_CCMP 0x08 + +/** Valid cipher bitmap */ +#define VALID_CIPHER_BITMAP 0x0c + +/** Channel List Entry */ +typedef struct _channel_list +{ + /** Channel Number */ + t_u8 chan_number; + /** Band Config */ + t_u8 band_config_type; +} 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 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; + +/** 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; + +/** 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; + /** 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 */ + t_u8 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; + +/** 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; +#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; +#ifdef UAP_SUPPORT + /** BSS param for AP mode */ + mlan_uap_bss_param bss_config; + /** deauth param for MLAN_OID_UAP_DEAUTH_STA */ + mlan_deauth_param deauth_param; +#endif +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) + /** BSS role */ + t_u8 bss_role; +#endif +#ifdef WIFI_DIRECT_SUPPORT + t_u16 wfd_mode; +#endif + } param; +} mlan_ds_bss, *pmlan_ds_bss; + +/*-----------------------------------------------------------------*/ +/** 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, +}; + +/** NO secondary channel */ +#define NO_SEC_CHANNEL 0 +/** secondary channel is above primary channel */ +#define SEC_CHANNEL_ABOVE 1 +/** secondary channel is below primary channel */ +#define SEC_CHANNEL_BELOW 3 +/** Channel bandwidth */ +#define CHANNEL_BW_20MHZ 0 +#define CHANNEL_BW_40MHZ_ABOVE 1 +#define CHANNEL_BW_40MHZ_BELOW 3 + +/** 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 sec_chan_offset; + /** fw supported band */ + t_u32 fw_bands; +} mlan_ds_band_cfg; + +/** Type definition of mlan_ds_ant_cfg for MLAN_OID_ANT_CFG */ +typedef struct _mlan_ds_ant_cfg +{ + /** Tx antenna mode */ + t_u32 tx_antenna; + /** Rx antenna mode */ + t_u32 rx_antenna; +} mlan_ds_ant_cfg, *pmlan_ds_ant_cfg; + +#ifdef WIFI_DIRECT_SUPPORT +/** 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 */ + t_u8 bandcfg; + /** channel */ + t_u8 channel; + /** remain time: Unit ms*/ + t_u32 remain_period; +} mlan_ds_remain_chan, *pmlan_ds_remain_chan; +#endif + +/** 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 ant_cfg; + /** Antenna info for MLAN_OID_ANT_CFG */ + t_u32 antenna; +#ifdef WIFI_DIRECT_SUPPORT + /** remain on channel for MLAN_OID_REMAIN_CHAN_CFG */ + mlan_ds_remain_chan remain_chan; +#endif + } param; +} mlan_ds_radio_cfg, *pmlan_ds_radio_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; +#if defined(UAP_SUPPORT) + /** OID value for MLAN_OID_SNMP_MIB_DOT11D/H */ + t_u32 oid_value; +#endif + } 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 +}; + +/** 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]; +} mlan_ds_get_stats, *pmlan_ds_get_stats; + +/** 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 + +/** 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 + */ + 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; + +/** 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; + /** Device support for MIMO abstraction of MCSs */ + t_u8 hw_dev_mcs_support; + /** fw supported band */ + t_u8 fw_bands; +} 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; + +/** 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_u32 max_power_level; + /** Min power level in dBm */ + t_u32 min_power_level; + /** Adhoc state */ + t_u32 adhoc_state; + /** NF of last beacon */ + t_s32 bcn_nf_last; + /** wep status */ + t_u32 wep_status; + /** 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; + /** Listen Interval */ + t_u16 listen_interval; + /** Association Id */ + t_u16 assoc_id; + /** AP/Peer supported rates */ + t_u8 peer_supp_rates[MLAN_SUPPORTED_RATES]; +#endif /* STA_SUPPORT */ +} 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 5 + +/** 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]; + /** 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; + /** 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; + /** Corresponds to scan_processing member of mlan_adapter */ + t_u32 scan_processing; + /** 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 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; + + /** 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_debug_info, *pmlan_debug_info; + +#ifdef UAP_SUPPORT +/** Maximum number of clients supported by AP */ +#define MAX_NUM_CLIENTS 16 + +/** 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; +} 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; + /** 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 */ + mlan_debug_info debug_info; +#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_NETWORKEAP = 0x80, + MLAN_AUTH_MODE_AUTO = 0xFF, +}; + +/** 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 and rx */ +#define KEY_FLAG_SET_TX_KEY 0x00000008 +/** 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]; +} 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; + +/** 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; + +/** Type definition of mlan_ds_sec_cfg for MLAN_IOCTL_SEC_CFG */ +typedef struct _mlan_ds_sec_cfg +{ + /** Sub-command */ + t_u32 sub_command; + /** 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; + } 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 +}; + +/** 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 10 + +/** 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: valude */ + 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]; +} 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; +} 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_u32 power_level; +} mlan_power_cfg_t; + +/** max power table size */ +#define MAX_POWER_TABLE_SIZE 128 + +/** The HT BW40 bit in Tx rate index */ +#define TX_RATE_HT_BW40_BIT MBIT(7) + +/** Type definition of mlan_power_cfg_ext for MLAN_OID_POWER_CFG_EXT */ +typedef struct _mlan_power_cfg_ext +{ + /** Length of power_data */ + t_u32 len; + /** Buffer of power configuration data */ + t_u32 power_data[MAX_POWER_TABLE_SIZE]; +} 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 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 + +/** 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; +} mlan_ds_hs_cfg, *pmlan_ds_hs_cfg; + +/** 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 + +/** 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 + +/** 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_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; + /** 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; + } param; +} mlan_ds_pm_cfg, *pmlan_ds_pm_cfg; + +/*-----------------------------------------------------------------*/ +/** 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_u8 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; + /** 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 + +/** Supported stream modes */ +#define HT_STREAM_MODE_1X1 0x11 +#define HT_STREAM_MODE_2X2 0x22 + +/* 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 + +/* 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; + +/** 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; + /** HT stream configuration */ + t_u32 stream_cfg; + } 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; + +/*-----------------------------------------------------------------*/ +/** Register Memory Access Group */ +/*-----------------------------------------------------------------*/ +/** Enumeration for register type */ +enum _mlan_reg_type +{ + MLAN_REG_MAC = 1, + MLAN_REG_BBP, + MLAN_REG_RF, + MLAN_REG_CAU, +}; + +/** 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_u16 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; +#endif + +/** 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; +#endif + } 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; + +/** Enumeration for function init/shutdown */ +enum _mlan_func_cmd +{ + MLAN_FUNC_INIT = 1, + MLAN_FUNC_SHUTDOWN, +}; + +/** 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 - 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; + +/** 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) + +/** Type definition of mlan_ds_subscribe_evt for MLAN_OID_MISC_CFP_CODE */ +typedef struct _mlan_ds_subscribe_evt +{ + /** 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; + +/** 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; + /** Function init/shutdown for MLAN_OID_MISC_INIT_SHUTDOWN */ + t_u32 func_init_shutdown; + /** Custom IE for MLAN_OID_MISC_CUSTOM_IE */ + mlan_ds_misc_custom_ie cust_ie; + /** 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; + /** 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 + } param; +} mlan_ds_misc_cfg, *pmlan_ds_misc_cfg; + +#endif /* !_MLAN_IOCTL_H_ */ diff --git a/drivers/net/wireless/sd8797/mlan/mlan_join.c b/drivers/net/wireless/sd8797/mlan/mlan_join.c new file mode 100644 index 000000000000..c997a6f804ee --- /dev/null +++ b/drivers/net/wireless/sd8797/mlan/mlan_join.c @@ -0,0 +1,1853 @@ +/** @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-2011, 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" + +/******************************************************** + 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 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); + /* Get the STA supported rates */ + card_rates_size = wlan_get_active_data_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 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; + MrvlIEtypes_ChanListParamSet_t *pchan_tlv; + WLAN_802_11_RATES rates; + t_u32 rates_size; + t_u16 tmp_cap; + t_u8 *pos; + + 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) + pauth_tlv->auth_type = + wlan_cpu_to_le16((t_u16) pmpriv->sec_info.authentication_mode); + 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].radio_type = + wlan_band_to_radio_type((t_u8) pbss_desc->bss_band); + + PRINTM(MINFO, "Assoc: TLV Band = %d\n", + pchan_tlv->chan_scan_param[0].radio_type); + 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; + 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; + } + 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); + } 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); + } 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) + && (pmpriv->config_bands & BAND_GN || pmpriv->config_bands & BAND_AN)) + wlan_cmd_append_11n_tlv(pmpriv, pbss_desc, &pos); + + 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); + + 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); + } + + 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; + + ENTER(); + + passoc_rsp = (IEEEtypes_AssocRsp_t *) & resp->params; + passoc_rsp->status_code = wlan_le16_to_cpu(passoc_rsp->status_code); + + 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) { + pmpriv->adapter->dbg.num_cmd_assoc_failure++; + PRINTM(MERROR, "ASSOC_RESP: Association Failed, " + "status code = %d, error = 0x%x, a_id = 0x%x\n", + wlan_le16_to_cpu(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(MINFO, "ASSOC_RESP: %s\n", pbss_desc->ssid.ssid); + + /* 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; + + /* + * 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->rxpd_rate = 0; + pmpriv->rxpd_htinfo = 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 */ + 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); + + if (!pmpriv->sec_info.wpa_enabled + && !pmpriv->sec_info.wpa2_enabled + && !pmpriv->sec_info.ewpa_enabled && !pmpriv->sec_info.wapi_enabled) { + /* 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->scan_block = MTRUE; + + 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(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; + /* 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)); + 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++); + + 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].radio_type + = 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].radio_type |= + SECOND_CHANNEL_ABOVE; + pchan_tlv->chan_scan_param[0].radio_type |= CHAN_BW_40MHZ << 2; + } else if (pmadapter->chan_bandwidth == CHANNEL_BW_40MHZ_BELOW) { + pchan_tlv->chan_scan_param[0].radio_type |= + SECOND_CHANNEL_BELOW; + pchan_tlv->chan_scan_param[0].radio_type |= CHAN_BW_40MHZ << 2; + } + } + PRINTM(MINFO, "ADHOC_S_CMD: TLV Band = %d\n", + pchan_tlv->chan_scan_param[0].radio_type); + 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); + wlan_fill_ht_cap_tlv(pmpriv, pht_cap, pmpriv->curr_bss_params.band); + 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) { + pht_info->ht_info.field2 = pmadapter->chan_bandwidth; + SET_CHANWIDTH40(pht_info->ht_info.field2); + } + pht_info->ht_info.field3 = 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_u16 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)); + + memcpy(pmadapter, &padhoc_join->bss_descriptor.ss_param_set, + &pbss_desc->ss_param_set, sizeof(IEEEtypes_SsParamSet_t)); + + 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 = %02x-%02x-%02x-%02x-%02x-%02x, SSID = %s\n", + padhoc_join->bss_descriptor.bssid[0], + padhoc_join->bss_descriptor.bssid[1], + padhoc_join->bss_descriptor.bssid[2], + padhoc_join->bss_descriptor.bssid[3], + padhoc_join->bss_descriptor.bssid[4], + padhoc_join->bss_descriptor.bssid[5], + padhoc_join->bss_descriptor.ssid); + + for (i = 0; i < WLAN_SUPPORTED_RATES && pbss_desc->supported_rates[i]; i++); + 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].radio_type + = wlan_band_to_radio_type((t_u8) pbss_desc->bss_band); + + PRINTM(MINFO, "ADHOC_J_CMD: TLV Band = %d\n", + pchan_tlv->chan_scan_param[0].radio_type); + 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; + 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; + } + + 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) + ) + 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; + + ENTER(); + + if (command == HostCmd_CMD_802_11_AD_HOC_START) + result = padhoc_start_result->result; + else + result = padhoc_join_result->result; + + 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; + } + + PRINTM(MINFO, "ADHOC_RESP: Channel = %d\n", pmpriv->adhoc_channel); + PRINTM(MINFO, "ADHOC_RESP: BSSID = %02x:%02x:%02x:%02x:%02x:%02x\n", + pmpriv->curr_bss_params.bss_descriptor.mac_address[0], + pmpriv->curr_bss_params.bss_descriptor.mac_address[1], + pmpriv->curr_bss_params.bss_descriptor.mac_address[2], + pmpriv->curr_bss_params.bss_descriptor.mac_address[3], + pmpriv->curr_bss_params.bss_descriptor.mac_address[4], + pmpriv->curr_bss_params.bss_descriptor.mac_address[5]); + + 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 mac A pointer to mlan_802_11_mac_addr 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_802_11_mac_addr * mac) +{ + mlan_802_11_mac_addr mac_address; + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u8 zero_mac[] = { 0, 0, 0, 0, 0, 0 }; + + ENTER(); + + if (pmpriv->media_connected == MTRUE) { + if (pmpriv->bss_mode == MLAN_BSS_MODE_INFRA) { + if (mac) { + if (!memcmp(pmpriv->adapter, mac, zero_mac, sizeof(zero_mac))) + memcpy(pmpriv->adapter, (t_u8 *) & mac_address, + (t_u8 *) & pmpriv->curr_bss_params.bss_descriptor. + mac_address, MLAN_MAC_ADDR_LENGTH); + else { + memcpy(pmpriv->adapter, (t_u8 *) & mac_address, + (t_u8 *) mac, MLAN_MAC_ADDR_LENGTH); + } + } else { + memcpy(pmpriv->adapter, (t_u8 *) & mac_address, + (t_u8 *) & pmpriv->curr_bss_params.bss_descriptor. + mac_address, MLAN_MAC_ADDR_LENGTH); + } + + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_802_11_DEAUTHENTICATE, + HostCmd_ACT_GEN_SET, + 0, (t_void *) pioctl_req, &mac_address); + + 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 = HostCmd_SCAN_RADIO_TYPE_A; + break; + case BAND_B: + case BAND_G: + case BAND_B | BAND_G: + default: + ret_radio_type = HostCmd_SCAN_RADIO_TYPE_BG; + break; + } + + LEAVE(); + return ret_radio_type; +} diff --git a/drivers/net/wireless/sd8797/mlan/mlan_join.h b/drivers/net/wireless/sd8797/mlan/mlan_join.h new file mode 100644 index 000000000000..d7091f3a4b28 --- /dev/null +++ b/drivers/net/wireless/sd8797/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-2011, 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/sd8797/mlan/mlan_main.h b/drivers/net/wireless/sd8797/mlan/mlan_main.h new file mode 100644 index 000000000000..1a0a4e1d1871 --- /dev/null +++ b/drivers/net/wireless/sd8797/mlan/mlan_main.h @@ -0,0 +1,2693 @@ +/** @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-2011, 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 t_s8 * pformat, IN ...); +extern t_u32 drvdbg; + +#ifdef DEBUG_LEVEL2 +#define PRINTM_MINFO(msg...) do {if ((drvdbg & MINFO) && (print_callback)) \ + print_callback(MNULL, MINFO, msg);} while(0) +#define PRINTM_MWARN(msg...) do {if ((drvdbg & MWARN) && (print_callback)) \ + print_callback(MNULL, MWARN, msg);} while(0) +#define PRINTM_MENTRY(msg...) do {if ((drvdbg & MENTRY) && (print_callback)) \ + print_callback(MNULL, MENTRY, msg);} while(0) +#else +#define PRINTM_MINFO(msg...) do {} while (0) +#define PRINTM_MWARN(msg...) do {} while (0) +#define PRINTM_MENTRY(msg...) do {} while (0) +#endif /* DEBUG_LEVEL2 */ + +#define PRINTM_MFW_D(msg...) do {if ((drvdbg & MFW_D) && (print_callback)) \ + print_callback(MNULL, MFW_D, msg);} while(0) +#define PRINTM_MCMD_D(msg...) do {if ((drvdbg & MCMD_D) && (print_callback)) \ + print_callback(MNULL, MCMD_D, msg);} while(0) +#define PRINTM_MDAT_D(msg...) do {if ((drvdbg & MDAT_D) && (print_callback)) \ + print_callback(MNULL, MDAT_D, msg);} while(0) +#define PRINTM_MIF_D(msg...) do {if ((drvdbg & MIF_D) && (print_callback)) \ + print_callback(MNULL, MIF_D, msg);} while(0) + +#define PRINTM_MIOCTL(msg...) do {if ((drvdbg & MIOCTL) && (print_callback)) \ + print_callback(MNULL, MIOCTL, msg);} while(0) +#define PRINTM_MINTR(msg...) do {if ((drvdbg & MINTR) && (print_callback)) \ + print_callback(MNULL, MINTR, msg);} while(0) +#define PRINTM_MEVENT(msg...) do {if ((drvdbg & MEVENT) && (print_callback)) \ + print_callback(MNULL, MEVENT, msg);} while(0) +#define PRINTM_MCMND(msg...) do {if ((drvdbg & MCMND) && (print_callback)) \ + print_callback(MNULL, MCMND, msg);} while(0) +#define PRINTM_MDATA(msg...) do {if ((drvdbg & MDATA) && (print_callback)) \ + print_callback(MNULL, MDATA, msg);} while(0) +#define PRINTM_MERROR(msg...) do {if ((drvdbg & MERROR) && (print_callback)) \ + print_callback(MNULL, MERROR, msg);} while(0) +#define PRINTM_MFATAL(msg...) do {if ((drvdbg & MFATAL) && (print_callback)) \ + print_callback(MNULL, MFATAL, msg);} while(0) +#define PRINTM_MMSG(msg...) do {if ((drvdbg & MMSG) && (print_callback)) \ + print_callback(MNULL, MMSG, msg);} while(0) + +#define PRINTM(level,msg...) PRINTM_##level(msg) + +#ifdef DEBUG_LEVEL2 + +/** Hexdump for level-2 debugging */ +#define HEXDUMP(x,y,z) \ +do { \ + if ((drvdbg & (MHEX_DUMP | MINFO)) && (print_callback)) \ + print_callback(MNULL, MHEX_DUMP | MINFO, x, y, z); \ +} while (0) +#else + +/** Hexdump for debugging */ +#define HEXDUMP(x,y,z) do {} while (0) +#endif /* DEBUG_LEVEL2 */ + +/** Log debug message */ +#ifdef __GNUC__ +#define PRINTM_NETINTF(level, pmpriv) \ +do { \ + if ((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 ((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) + +#endif /* DEBUG_LEVEL1 */ + +/** Log entry point for debugging */ +#define ENTER() \ +do { \ + PRINTM(MENTRY, "Enter: %s\n", __FUNCTION__); \ +} while (0) + +/** Log exit point for debugging */ +#define LEAVE() \ +do { \ + PRINTM(MENTRY, "Leave: %s\n", __FUNCTION__); \ +} 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); \ + } + +/** 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); \ + } +#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) +#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", __FUNCTION__, __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 1024 + +#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 10 + +/** Maximum number of region codes */ +#define MRVDRV_MAX_REGION_CODE 8 + +/** Default region code */ +#define MRVDRV_DEFAULT_REGION_CODE 0x10 + +/** 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 16384 +/** 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 (10s) + */ +#define MRVDRV_MAX_TOTAL_SCAN_TIME (MRVDRV_TIMER_10S - 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 + +/** RF antenna selection */ +#define RF_ANTENNA_MASK(n) ((1<<(n))-1) +/** RF antenna auto select */ +#define RF_ANTENNA_AUTO 0xFFFF + +/** 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 + +/** Maximum numbfer of registers to read for multiple port */ +#define MAX_MP_REGS 64 +/** Maximum port */ +#define MAX_PORT 16 +/** Multi port aggregation packet limit */ +#define SDIO_MP_AGGR_DEF_PKT_LIMIT (8) + +#ifdef SDIO_MULTI_PORT_TX_AGGR +/** Multi port TX aggregation buffer size */ +#define SDIO_MP_TX_AGGR_DEF_BUF_SIZE (16384) /* 16K */ +#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 (16384) /* 16K */ +#endif /* SDIO_MULTI_PORT_RX_AGGR */ + +/** High threshold at which to start drop packets */ +#define RX_HIGH_THRESHOLD 1024 +/** Medium threshold at which to disable Rx BA */ +#define RX_MED_THRESHOLD 256 +/** Low threshold to allow Rx BA */ +#define RX_LOW_THRESHOLD 128 + +/** Debug command number */ +#define DBG_CMD_NUM 5 + +/** 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 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; +} wlan_dbg; + +/** Hardware status codes */ +typedef enum _WLAN_HARDWARE_STATUS +{ + WlanHardwareStatusReady, + 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 +/** 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; + +/** 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; + /** 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 + +/** 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]; + /** 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 + +/** 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; FALSE:radar detect not required*/ + t_bool radar_detect; + /** TRUE:channel unsupported; FALSE:supported */ + t_u8 unsupported; +} 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 +{ + /** 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; +} 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_cfg from MrvlIEtypes_channel_band_t */ + t_u8 band_config; + /** 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 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_u16 curr_pkt_filter; + /** Infrastructure mode */ + t_u32 bss_mode; + + /** Tx packet control */ + t_u32 pkt_tx_ctrl; + + /** Tx power level */ + t_u16 tx_power_level; + /** Maximum Tx power level */ + t_u8 max_tx_power_level; + /** Minimum Tx power level */ + t_u8 min_tx_power_level; + /** Tx rate */ + t_u8 tx_rate; + /** tx ht_info */ + t_u8 tx_htinfo; + /** rxpd_htinfo */ + t_u8 rxpd_htinfo; + /** max amsdu size */ + t_u16 max_amsdu; +#ifdef UAP_SUPPORT + /** UAP 11n flag */ + t_u8 is_11n_enabled; +#endif /* UAP_SUPPORT */ +#ifdef UAP_SUPPORT +#endif /* UAP_SUPPORT */ +#ifdef UAP_SUPPORT + /** packet forward control */ + t_u8 pkt_fwd; + /** dropped pkts */ + t_u32 num_drop_pkts; +#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; + + /** Current SSID/BSSID related parameters*/ + current_bss_params_t curr_bss_params; + + /** 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; +#if defined(UAP_SUPPORT) + /** Whether UAP interface has started */ + t_bool uap_bss_started; + /** state variable for UAP Get Info callback */ + wlan_uap_get_info_cb_t uap_state_chan_cb; +#endif + + /** 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; + /** AES key material */ + HostCmd_DS_802_11_KEY_MATERIAL aes_key; + /** 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; + + /** 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; + /** 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]; + /** Struct to store ADDBA parameters */ + add_ba_param_t add_ba_param; + /** 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; + + /** 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; + + /** Scan block flag */ + t_u8 scan_block; + /** IP address operation */ + t_u32 op_code; + /** IP address */ + t_u8 ip_addr[IPADDR_LEN]; +} mlan_private, *pmlan_private; + +/** BA stream status */ +typedef enum _baStatus_e +{ + BA_STREAM_NOT_SETUP = 0, + BA_STREAM_SETUP_INPROGRESS, + BA_STREAM_SETUP_COMPLETE +} baStatus_e; + +/** 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; + /** 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; +}; + +/** 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; +}; + +/** 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]; + /** 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; + /** wapi key on off flag */ + t_u8 wapi_key_on; + /** tx pause status */ + t_u8 tx_pause; +}; + +/** 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; + /** 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 */ + t_u8 uap_band_cfg; + /** BEACON*DTIM period (in msec; max of STA/UAP) */ + t_u16 max_bcn_dtim_ms; + /** 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_u16 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; + +#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_u16 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; +} 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_u16 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; +} mlan_init_para, *pmlan_init_para; + +/** Adapter data structure for MLAN */ +typedef struct _mlan_adapter +{ + /** MOAL handle structure */ + t_void *pmoal_handle; + /** 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; + /** 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; + /** IO port */ + t_u32 ioport; + + /** STATUS variables */ + WLAN_HARDWARE_STATUS hw_status; + /** PnP SUPPORT */ + t_u8 surprise_removed; + + /** Radio on flag */ + t_u16 radio_on; + + /** Firmware release number */ + t_u32 fw_release_number; + + /** Number of antenna used */ + t_u16 number_of_antenna; + + /** Firmware capability information */ + t_u32 fw_cap_info; + /** pint_lock for interrupt handling */ + t_void *pint_lock; + /** Interrupt status */ + t_u8 sdio_ireg; + /** 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; + /** 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; + /** Array to store data transfer eligibility based on tid (QoS-over-SDIO) */ + t_u8 tx_eligibility[MAX_NUM_TID]; + +#ifdef SDIO_MULTI_PORT_TX_AGGR + /** data structure for SDIO MPA TX */ + sdio_mpa_tx mpa_tx; +#endif /* SDIO_MULTI_PORT_TX_AGGR */ + +#ifdef SDIO_MULTI_PORT_RX_AGGR + /** data structure for SDIO MPA RX */ + sdio_mpa_rx mpa_rx; +#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; + + /** 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; + /** mlan_processing */ + t_u32 scan_processing; + + /** Region code */ + t_u16 region_code; + /** Region Channel data */ + region_chan_t region_channel[MAX_REGION_CHANNEL_NUM]; +#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; + /** 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; +#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; + + /** 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; + /** 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; + + /** 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; + + /** sleep_params_t */ + sleep_params_t sleep_params; + /** sleep_period_t (Enhanced Power Save) */ + sleep_period_t 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; + + /** 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; + + /** 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; + + /** 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; + /** 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 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; + +#ifdef MFG_CMD_SUPPORT + t_u32 mfg_mode; +#endif + /** Debug */ + wlan_dbg dbg; + + /** RX pending for forwarding packets */ + t_u16 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 */ + + /** Bypass TX queue */ + mlan_list_head bypass_txq; +#if defined(STA_SUPPORT) + /** warm-reset IOCTL request buffer pointer */ + pmlan_ioctl_req pwarm_reset_ioctl_req; +#endif + /** Extended SCAN IOCTL request buffer pointer */ + pmlan_ioctl_req pext_scan_ioctl_req; + /** Cal data pointer */ + t_u8 *pcal_data; + /** Cal data length */ + t_u32 cal_data_len; + +} 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_init_lock_list(IN pmlan_adapter pmadapter); +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); + +/** 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 * FunctionContext); +/** 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_set_ewpa_mode(mlan_private * priv, + mlan_ds_passphrase * psec_pp); +mlan_status wlan_find_bss(mlan_private * pmpriv, pmlan_ioctl_req pioctl_req); + +/** 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); +#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); + +/** 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(mlan_adapter * pmadapter); +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); + +/** 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_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); + +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_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); +#endif + +/** get pm info */ +mlan_status wlan_get_pm_info(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req); + +mlan_status wlan_radio_ioctl_radio_ctl(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req); + +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); + +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); + +/** send adapter specific init cmd to firmware */ +mlan_status wlan_adapter_init_cmd(IN pmlan_adapter pmadapter); + +#ifdef STA_SUPPORT +/** 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 const 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); + +/** Queue scan command handler */ +t_void wlan_queue_scan_cmd(IN mlan_private * pmpriv, + IN cmd_ctrl_node * pcmd_node); + +/** 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); + +/** 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); + +/** Disconnect */ +mlan_status wlan_disconnect(IN mlan_private * pmpriv, + IN mlan_ioctl_req * pioctl_req, + IN mlan_802_11_mac_addr * mac); + +/** 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); + +/** 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 */ +/** Convert index into data rate */ +t_u32 wlan_index_to_data_rate(pmlan_adapter pmadapter, t_u8 index, + t_u8 ht_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]; +/** 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); + +/* 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 + +/** 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 */ + +/** find specific ie */ +t_u8 *wlan_get_specific_ie(pmlan_private priv, t_u8 * ie_buf, t_u8 ie_len, + IEEEtypes_ElementId_e id); + +/** + * @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; + if ((sta_ptr = wlan_get_station_entry(priv, ra))) { + return sta_ptr->tx_pause; + } + return MFALSE; +} + +t_void wlan_updata_ralist_tx_pause(pmlan_private priv, t_u8 * mac, + t_u8 tx_pause); +sta_node *wlan_get_tx_pause_station_entry(mlan_private * priv); + +#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_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_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_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 + +/** + * @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 t_s8 * 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) + +/** Function to check if any command is pending in the queue */ +#define IS_COMMAND_PENDING(pmadapter) ((cmd_ctrl_node *)util_peek_list(pmadapter->pmoal_handle, \ + &pmadapter->cmd_pending_q,\ + pmadapter->callbacks.moal_spin_lock,\ + pmadapter->callbacks.moal_spin_unlock)) + +/** 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++) { + if ((pmpriv = pmadapter->priv[i])) { + 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++) { + if ((pmpriv = pmadapter->priv[i])) { + 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++) { + if ((pmpriv = pmadapter->priv[i])) { + 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++) { + if ((pmpriv = pmadapter->priv[i])) { + 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/sd8797/mlan/mlan_meas.c b/drivers/net/wireless/sd8797/mlan/mlan_meas.c new file mode 100644 index 000000000000..d9da9da20756 --- /dev/null +++ b/drivers/net/wireless/sd8797/mlan/mlan_meas.c @@ -0,0 +1,464 @@ +/** + * @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-2011, 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: %02x:%02x:%02x:%02x:%02x:%02x\n", + pmeas_req->mac_addr[0], + pmeas_req->mac_addr[1], + pmeas_req->mac_addr[2], + pmeas_req->mac_addr[3], + pmeas_req->mac_addr[4], pmeas_req->mac_addr[5]); + + 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) +{ + ENTER(); + + PRINTM(MINFO, "Meas: Rpt: ------------------------------\n"); + PRINTM(MINFO, "Meas: Rpt: mac_addr: %02x:%02x:%02x:%02x:%02x:%02x\n", + pmeas_rpt->mac_addr[0], + pmeas_rpt->mac_addr[1], + pmeas_rpt->mac_addr[2], + pmeas_rpt->mac_addr[3], + pmeas_rpt->mac_addr[4], pmeas_rpt->mac_addr[5]); + + 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 + + switch (pmeas_rpt->meas_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 = 0; + 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/sd8797/mlan/mlan_meas.h b/drivers/net/wireless/sd8797/mlan/mlan_meas.h new file mode 100644 index 000000000000..0eb51d3a3bb2 --- /dev/null +++ b/drivers/net/wireless/sd8797/mlan/mlan_meas.h @@ -0,0 +1,54 @@ +/** + * @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-2011, 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/sd8797/mlan/mlan_misc.c b/drivers/net/wireless/sd8797/mlan/mlan_misc.c new file mode 100644 index 000000000000..4054950f58f0 --- /dev/null +++ b/drivers/net/wireless/sd8797/mlan/mlan_misc.c @@ -0,0 +1,2153 @@ +/** + * @file mlan_misc.c + * + * @brief This file include miscellaneous functions for MLAN module + * + * Copyright (C) 2009-2011, 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 + +/******************************************************** + 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 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 < pmpriv->adapter->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(MERROR, + "IE with the same mask exists at index %d\n", + index); + *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 < pmpriv->adapter->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, ie_data->ie_length); + + for (index = 0; index < pmadapter->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[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; + 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 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; + t_u8 *ptid; + + ENTER(); + + info = (mlan_ds_get_info *) pioctl_req->pbuf; + + if (pioctl_req->action == MLAN_ACT_SET) { + pmadapter->max_tx_buf_size = + (t_u16) info->param.debug_info.max_tx_buf_size; + pmadapter->tx_buf_size = (t_u16) info->param.debug_info.tx_buf_size; + pmadapter->curr_tx_buf_size = + (t_u16) info->param.debug_info.curr_tx_buf_size; + pmadapter->ps_mode = info->param.debug_info.ps_mode; + pmadapter->ps_state = info->param.debug_info.ps_state; +#ifdef STA_SUPPORT + pmadapter->is_deep_sleep = info->param.debug_info.is_deep_sleep; +#endif /* STA_SUPPORT */ + pmadapter->pm_wakeup_card_req = + info->param.debug_info.pm_wakeup_card_req; + pmadapter->pm_wakeup_fw_try = info->param.debug_info.pm_wakeup_fw_try; + pmadapter->is_hs_configured = info->param.debug_info.is_hs_configured; + pmadapter->hs_activated = info->param.debug_info.hs_activated; + pmadapter->pps_uapsd_mode = info->param.debug_info.pps_uapsd_mode; + pmadapter->sleep_period.period = info->param.debug_info.sleep_pd; + pmpriv->wmm_qosinfo = info->param.debug_info.qos_cfg; + pmadapter->tx_lock_flag = info->param.debug_info.tx_lock_flag; + pmpriv->port_open = info->param.debug_info.port_open; + pmadapter->scan_processing = info->param.debug_info.scan_processing; + + pmadapter->dbg.num_cmd_host_to_card_failure = + info->param.debug_info.num_cmd_host_to_card_failure; + pmadapter->dbg.num_cmd_sleep_cfm_host_to_card_failure = + info->param.debug_info.num_cmd_sleep_cfm_host_to_card_failure; + pmadapter->dbg.num_tx_host_to_card_failure = + info->param.debug_info.num_tx_host_to_card_failure; + pmadapter->dbg.num_cmdevt_card_to_host_failure = + info->param.debug_info.num_cmdevt_card_to_host_failure; + pmadapter->dbg.num_rx_card_to_host_failure = + info->param.debug_info.num_rx_card_to_host_failure; + pmadapter->dbg.num_int_read_failure = + info->param.debug_info.num_int_read_failure; + pmadapter->dbg.last_int_status = info->param.debug_info.last_int_status; + pmadapter->dbg.num_event_deauth = + info->param.debug_info.num_event_deauth; + pmadapter->dbg.num_event_disassoc = + info->param.debug_info.num_event_disassoc; + pmadapter->dbg.num_event_link_lost = + info->param.debug_info.num_event_link_lost; + pmadapter->dbg.num_cmd_deauth = info->param.debug_info.num_cmd_deauth; + pmadapter->dbg.num_cmd_assoc_success = + info->param.debug_info.num_cmd_assoc_success; + pmadapter->dbg.num_cmd_assoc_failure = + info->param.debug_info.num_cmd_assoc_failure; + pmadapter->dbg.num_tx_timeout = info->param.debug_info.num_tx_timeout; + pmadapter->dbg.num_cmd_timeout = info->param.debug_info.num_cmd_timeout; + pmadapter->dbg.timeout_cmd_id = info->param.debug_info.timeout_cmd_id; + pmadapter->dbg.timeout_cmd_act = info->param.debug_info.timeout_cmd_act; + memcpy(pmadapter, pmadapter->dbg.last_cmd_id, + info->param.debug_info.last_cmd_id, + sizeof(pmadapter->dbg.last_cmd_id)); + memcpy(pmadapter, pmadapter->dbg.last_cmd_act, + info->param.debug_info.last_cmd_act, + sizeof(pmadapter->dbg.last_cmd_act)); + pmadapter->dbg.last_cmd_index = info->param.debug_info.last_cmd_index; + memcpy(pmadapter, pmadapter->dbg.last_cmd_resp_id, + info->param.debug_info.last_cmd_resp_id, + sizeof(pmadapter->dbg.last_cmd_resp_id)); + pmadapter->dbg.last_cmd_resp_index = + info->param.debug_info.last_cmd_resp_index; + memcpy(pmadapter, pmadapter->dbg.last_event, + info->param.debug_info.last_event, + sizeof(pmadapter->dbg.last_event)); + pmadapter->dbg.last_event_index = + info->param.debug_info.last_event_index; + + pmadapter->data_sent = info->param.debug_info.data_sent; + pmadapter->cmd_sent = info->param.debug_info.cmd_sent; + pmadapter->mp_rd_bitmap = info->param.debug_info.mp_rd_bitmap; + pmadapter->mp_wr_bitmap = info->param.debug_info.mp_wr_bitmap; + pmadapter->curr_rd_port = info->param.debug_info.curr_rd_port; + pmadapter->curr_wr_port = info->param.debug_info.curr_wr_port; + pmadapter->cmd_resp_received = info->param.debug_info.cmd_resp_received; +#ifdef UAP_SUPPORT + pmadapter->pending_bridge_pkts = info->param.debug_info.num_bridge_pkts; + pmpriv->num_drop_pkts = info->param.debug_info.num_drop_pkts; +#endif + } else { /* MLAN_ACT_GET */ + ptid = ac_to_tid[WMM_AC_BK]; + info->param.debug_info.wmm_ac_bk = + pmpriv->wmm.packets_out[ptid[0]] + pmpriv->wmm.packets_out[ptid[1]]; + ptid = ac_to_tid[WMM_AC_BE]; + info->param.debug_info.wmm_ac_be = + pmpriv->wmm.packets_out[ptid[0]] + pmpriv->wmm.packets_out[ptid[1]]; + ptid = ac_to_tid[WMM_AC_VI]; + info->param.debug_info.wmm_ac_vi = + pmpriv->wmm.packets_out[ptid[0]] + pmpriv->wmm.packets_out[ptid[1]]; + ptid = ac_to_tid[WMM_AC_VO]; + info->param.debug_info.wmm_ac_vo = + pmpriv->wmm.packets_out[ptid[0]] + pmpriv->wmm.packets_out[ptid[1]]; + info->param.debug_info.max_tx_buf_size = + (t_u32) pmadapter->max_tx_buf_size; + info->param.debug_info.tx_buf_size = (t_u32) pmadapter->tx_buf_size; + info->param.debug_info.curr_tx_buf_size = + (t_u32) pmadapter->curr_tx_buf_size; + info->param.debug_info.rx_tbl_num = + wlan_get_rxreorder_tbl(pmpriv, info->param.debug_info.rx_tbl); + info->param.debug_info.tx_tbl_num = + wlan_get_txbastream_tbl(pmpriv, info->param.debug_info.tx_tbl); + info->param.debug_info.ps_mode = pmadapter->ps_mode; + info->param.debug_info.ps_state = pmadapter->ps_state; +#ifdef STA_SUPPORT + info->param.debug_info.is_deep_sleep = pmadapter->is_deep_sleep; +#endif /* STA_SUPPORT */ + info->param.debug_info.pm_wakeup_card_req = + pmadapter->pm_wakeup_card_req; + info->param.debug_info.pm_wakeup_fw_try = pmadapter->pm_wakeup_fw_try; + info->param.debug_info.is_hs_configured = pmadapter->is_hs_configured; + info->param.debug_info.hs_activated = pmadapter->hs_activated; + info->param.debug_info.pps_uapsd_mode = pmadapter->pps_uapsd_mode; + info->param.debug_info.sleep_pd = pmadapter->sleep_period.period; + info->param.debug_info.qos_cfg = pmpriv->wmm_qosinfo; + info->param.debug_info.tx_lock_flag = pmadapter->tx_lock_flag; + info->param.debug_info.port_open = pmpriv->port_open; + info->param.debug_info.scan_processing = pmadapter->scan_processing; + + info->param.debug_info.num_cmd_host_to_card_failure + = pmadapter->dbg.num_cmd_host_to_card_failure; + info->param.debug_info.num_cmd_sleep_cfm_host_to_card_failure + = pmadapter->dbg.num_cmd_sleep_cfm_host_to_card_failure; + info->param.debug_info.num_tx_host_to_card_failure + = pmadapter->dbg.num_tx_host_to_card_failure; + info->param.debug_info.num_cmdevt_card_to_host_failure + = pmadapter->dbg.num_cmdevt_card_to_host_failure; + info->param.debug_info.num_rx_card_to_host_failure + = pmadapter->dbg.num_rx_card_to_host_failure; + info->param.debug_info.num_int_read_failure = + pmadapter->dbg.num_int_read_failure; + info->param.debug_info.last_int_status = pmadapter->dbg.last_int_status; + info->param.debug_info.num_event_deauth = + pmadapter->dbg.num_event_deauth; + info->param.debug_info.num_event_disassoc = + pmadapter->dbg.num_event_disassoc; + info->param.debug_info.num_event_link_lost = + pmadapter->dbg.num_event_link_lost; + info->param.debug_info.num_cmd_deauth = pmadapter->dbg.num_cmd_deauth; + info->param.debug_info.num_cmd_assoc_success = + pmadapter->dbg.num_cmd_assoc_success; + info->param.debug_info.num_cmd_assoc_failure = + pmadapter->dbg.num_cmd_assoc_failure; + info->param.debug_info.num_tx_timeout = pmadapter->dbg.num_tx_timeout; + info->param.debug_info.num_cmd_timeout = pmadapter->dbg.num_cmd_timeout; + info->param.debug_info.timeout_cmd_id = pmadapter->dbg.timeout_cmd_id; + info->param.debug_info.timeout_cmd_act = pmadapter->dbg.timeout_cmd_act; + memcpy(pmadapter, info->param.debug_info.last_cmd_id, + pmadapter->dbg.last_cmd_id, sizeof(pmadapter->dbg.last_cmd_id)); + memcpy(pmadapter, info->param.debug_info.last_cmd_act, + pmadapter->dbg.last_cmd_act, + sizeof(pmadapter->dbg.last_cmd_act)); + info->param.debug_info.last_cmd_index = pmadapter->dbg.last_cmd_index; + memcpy(pmadapter, info->param.debug_info.last_cmd_resp_id, + pmadapter->dbg.last_cmd_resp_id, + sizeof(pmadapter->dbg.last_cmd_resp_id)); + info->param.debug_info.last_cmd_resp_index = + pmadapter->dbg.last_cmd_resp_index; + memcpy(pmadapter, info->param.debug_info.last_event, + pmadapter->dbg.last_event, sizeof(pmadapter->dbg.last_event)); + info->param.debug_info.last_event_index = + pmadapter->dbg.last_event_index; + + info->param.debug_info.mp_rd_bitmap = pmadapter->mp_rd_bitmap; + info->param.debug_info.mp_wr_bitmap = pmadapter->mp_wr_bitmap; + info->param.debug_info.curr_rd_port = pmadapter->curr_rd_port; + info->param.debug_info.curr_wr_port = pmadapter->curr_wr_port; + info->param.debug_info.data_sent = pmadapter->data_sent; + info->param.debug_info.cmd_sent = pmadapter->cmd_sent; + info->param.debug_info.cmd_resp_received = pmadapter->cmd_resp_received; + info->param.debug_info.tx_pkts_queued = + util_scalar_read(pmadapter->pmoal_handle, + &pmpriv->wmm.tx_pkts_queued, MNULL, MNULL); +#ifdef UAP_SUPPORT + info->param.debug_info.num_bridge_pkts = pmadapter->pending_bridge_pkts; + info->param.debug_info.num_drop_pkts = pmpriv->num_drop_pkts; +#endif + } + + pioctl_req->data_read_written = + sizeof(mlan_debug_info) + MLAN_SUB_COMMAND_SIZE; + + 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; + pmlan_callbacks pcb = &pmadapter->callbacks; + + ENTER(); + PRINTM(MEVENT, "Wakeup device...\n"); + 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(); + + 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; + } + 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; + break; + default: + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + status = MLAN_STATUS_FAILURE; + break; + } + + LEAVE(); + return status; +} + +/** + * @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 = ALIGN_SZ(data_len, MLAN_SDIO_BLOCK_SIZE); + + /* head_room is not implemented for malloc mlan buffer */ + if (malloc_flag == MTRUE) { + 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; + } else { + /* 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; + } + 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_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; +} + +#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_SUCCESS --success, MLAN_STATUS_PENDING --pending + */ +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 { + /** 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); + + /* 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; + wlan_prepare_cmd(pmpriv, HostCmd_CMD_SET_BSS_MODE, + HostCmd_ACT_GEN_SET, 0, MNULL, &bss_mode); +#endif + pmpriv->ops.init_cmd(pmpriv, MFALSE); + + /* Issue dummy Get command to complete the ioctl */ + memset(pmadapter, &dummy, 0, sizeof(HostCmd_DS_VERSION_EXT)); + wlan_prepare_cmd(pmpriv, HostCmd_CMD_VERSION_EXT, HostCmd_ACT_GEN_GET, + 0, (t_void *) pioctl_req, (t_void *) & dummy); + ret = MLAN_STATUS_PENDING; + } + + 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, 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; + if (MLAN_CUSTOM_IE_AUTO_IDX_MASK == index) { /* Need to be + Autohandled */ + if (mask == MLAN_CUSTOM_IE_DELETE_MASK) { /* Automatic + Deletion */ + 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); + len = 0; + for (i = 0; i < pmadapter->max_mgmt_ie_index; 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; + } + 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); + 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].ie_length == + ie_data->ie_length) && + !memcmp(pmpriv->adapter, + pmpriv->mgmt_ie[index].ie_buffer, + ie_data->ie_buffer, + pmpriv->mgmt_ie[index].ie_length)) { + PRINTM(MIOCTL, + "same customer ie already configured!\n"); + goto done; + } + } + 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 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; + } + if (!(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))) { + LEAVE(); + return MNULL; + } + while (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"); + 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); + 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); + if ((sta_ptr = wlan_get_station_entry(priv, mac))) { + 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))) { + priv->adapter->callbacks.moal_mfree(priv->adapter->pmoal_handle, + (t_u8 *) sta_ptr); + } + LEAVE(); + return; +} + +/** + * @brief This function will return the pointer to station entry in station list + * table which in tx_pause state + * + * @param priv A pointer to mlan_private + * + * @return A pointer to structure sta_node + */ +sta_node * +wlan_get_tx_pause_station_entry(mlan_private * priv) +{ + sta_node *sta_ptr; + + ENTER(); + + if (!(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))) { + LEAVE(); + return MNULL; + } + while (sta_ptr != (sta_node *) & priv->sta_list) { + if (sta_ptr->tx_pause) { + LEAVE(); + return sta_ptr; + } + sta_ptr = sta_ptr->pnext; + } + LEAVE(); + return MNULL; +} + +/** + * @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 */ + 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 + * + * @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) +{ + 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; + + 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.\n", sub_type); + LEAVE(); + return ret; + } + switch (sub_type) { + case SUBTYPE_ASSOC_REQUEST: + case SUBTYPE_REASSOC_REQUEST: + case SUBTYPE_DISASSOC: + case SUBTYPE_DEAUTH: + case SUBTYPE_ACTION: + case SUBTYPE_AUTH: + case SUBTYPE_PROBE_RESP: + 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 %02x:%02x:%02x:%02x:%02x:%02x\n", + sub_type, pieee_pkt_hdr->addr1[0], pieee_pkt_hdr->addr1[1], + pieee_pkt_hdr->addr1[2], pieee_pkt_hdr->addr1[3], + pieee_pkt_hdr->addr1[4], pieee_pkt_hdr->addr1[5]); + 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; + 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); + wlan_recv_event(priv, MLAN_EVENT_ID_DRV_MGMT_FRAME, pevent); + + if (event_buf) + pcb->moal_mfree(pmadapter->pmoal_handle, event_buf); + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @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; + pmlan_callbacks pcb = &pmadapter->callbacks; + + ENTER(); + + pm_cfg = (mlan_ds_pm_cfg *) pioctl_req->pbuf; + pm_cfg->param.ps_info.is_suspend_allowed = MTRUE; + if (util_peek_list(pmadapter->pmoal_handle, &pmadapter->cmd_pending_q, + pcb->moal_spin_lock, pcb->moal_spin_unlock) + || 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, pcb->moal_spin_lock, + pcb->moal_spin_unlock), pmadapter->curr_cmd, + wlan_wmm_lists_empty(pmadapter), + wlan_bypass_tx_list_empty(pmadapter), pmadapter->sdio_ireg); + } + 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 *ant_cfg; + + ENTER(); + + radio_cfg = (mlan_ds_radio_cfg *) pioctl_req->pbuf; + ant_cfg = &radio_cfg->param.ant_cfg; + + if (pioctl_req->action == MLAN_ACT_SET) { + /* User input validation */ + if (!ant_cfg->tx_antenna || + ant_cfg->tx_antenna & ~RF_ANTENNA_MASK(pmadapter-> + number_of_antenna)) { + PRINTM(MERROR, "Invalid antenna setting\n"); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_FAILURE; + goto exit; + } + if (ant_cfg->rx_antenna) { + if (ant_cfg-> + rx_antenna & ~RF_ANTENNA_MASK(pmadapter->number_of_antenna)) { + PRINTM(MERROR, "Invalid antenna setting\n"); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_FAILURE; + goto exit; + } + } else + ant_cfg->rx_antenna = ant_cfg->tx_antenna; + 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_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 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 (pmadapter->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 (pmadapter->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 (pmadapter->config_bands & BAND_A) { + /* Return the lowest supported rate for A band */ + rate->param.rate_cfg.rate = SupportedRates_A[0] & 0x7f; + } else if (pmadapter->config_bands & BAND_G) { + /* Return the lowest supported rate for G band */ + rate->param.rate_cfg.rate = SupportedRates_G[0] & 0x7f; + } else if (pmadapter->config_bands & BAND_B) { + /* Return the lowest supported rate for B band */ + rate->param.rate_cfg.rate = SupportedRates_B[0] & 0x7f; + } else if (pmadapter->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", pmadapter->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; + /* Support all HT-MCSs rate */ + for (i = 0; i < NELEMENTS(pmpriv->bitmap_rates) - 3; i++) + bitmap_rates[i + 2] = 0xFFFF; + bitmap_rates[9] = 0x3FFF; + } else { + memset(pmadapter, rates, 0, sizeof(rates)); + wlan_get_active_data_rates(pmpriv, pmpriv->bss_mode, + (pmpriv->bss_mode == MLAN_BSS_MODE_INFRA) ? + pmadapter->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 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]; + int tx_mcs_supp = GET_TXMCSSUPP(pmadapter->usr_dev_mcs_support); + + ENTER(); + + ds_rate = (mlan_ds_rate *) pioctl_req->pbuf; + 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); + if ((rate_index > MLAN_RATE_INDEX_MCS7 && + rate_index <= MLAN_RATE_INDEX_MCS15) && (tx_mcs_supp < 2)) { + PRINTM(MERROR, + "HW don't support 2x2, rate_index=%d hw_mcs_supp=0x%x\n", + rate_index, pmadapter->usr_dev_mcs_support); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + LEAVE(); + return MLAN_STATUS_FAILURE; + } + memset(pmadapter, bitmap_rates, 0, sizeof(bitmap_rates)); + /* 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; + } + if ((rate_index >= MLAN_RATE_INDEX_MCS0) && + (rate_index <= MLAN_RATE_INDEX_MCS32)) { + rate_index -= MLAN_RATE_INDEX_MCS0; + 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, " + "IsRateAuto=%d, DataRate=%d\n", + 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_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 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 *ant_cfg = (mlan_ds_ant_cfg *) 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_tx = wlan_cpu_to_le16(HostCmd_ACT_SET_TX); + pantenna->tx_antenna_mode = + wlan_cpu_to_le16((t_u16) ant_cfg->tx_antenna); + pantenna->action_rx = wlan_cpu_to_le16(HostCmd_ACT_SET_RX); + pantenna->rx_antenna_mode = + wlan_cpu_to_le16((t_u16) ant_cfg->rx_antenna); + } else { + pantenna->action_tx = wlan_cpu_to_le16(HostCmd_ACT_GET_TX); + pantenna->action_rx = wlan_cpu_to_le16(HostCmd_ACT_GET_RX); + } + 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 tx_ant_mode = wlan_le16_to_cpu(pantenna->tx_antenna_mode); + t_u16 rx_ant_mode = wlan_le16_to_cpu(pantenna->rx_antenna_mode); + mlan_ds_radio_cfg *radio = MNULL; + + ENTER(); + + PRINTM(MINFO, "RF_ANT_RESP: Tx action = 0x%x, Tx Mode = 0x%04x" + " Rx action = 0x%x, Rx Mode = 0x%04x\n", + wlan_le16_to_cpu(pantenna->action_tx), tx_ant_mode, + wlan_le16_to_cpu(pantenna->action_rx), rx_ant_mode); + + if (pioctl_buf) { + radio = (mlan_ds_radio_cfg *) pioctl_buf->pbuf; + radio->param.ant_cfg.tx_antenna = tx_ant_mode; + radio->param.ant_cfg.rx_antenna = rx_ant_mode; + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +#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 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; +} +#endif diff --git a/drivers/net/wireless/sd8797/mlan/mlan_module.c b/drivers/net/wireless/sd8797/mlan/mlan_module.c new file mode 100644 index 000000000000..cd0bd0c5fb78 --- /dev/null +++ b/drivers/net/wireless/sd8797/mlan/mlan_module.c @@ -0,0 +1,47 @@ +/** @file mlan_module.c + * + * @brief This file declares the exported symbols from MLAN. + * + * Copyright (C) 2008-2011, 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_select_wmm_queue); +EXPORT_SYMBOL(mlan_interrupt); + +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/sd8797/mlan/mlan_scan.c b/drivers/net/wireless/sd8797/mlan/mlan_scan.c new file mode 100644 index 000000000000..6123365422c7 --- /dev/null +++ b/drivers/net/wireless/sd8797/mlan/mlan_scan.c @@ -0,0 +1,4155 @@ +/** @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-2011, 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" + +/******************************************************** + Local Constants +********************************************************/ + +/** The maximum number of channels the firmware can scan per command */ +#define MRVDRV_MAX_CHANNELS_PER_SPECIFIC_SCAN 14 + +/** + * 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_TKIP, + CIPHER_SUITE_CCMP, + CIPHER_SUITE_MAX +}; + +static t_u8 wpa_oui[CIPHER_SUITE_MAX][4] = { + {0x00, 0x50, 0xf2, 0x02}, /* TKIP */ + {0x00, 0x50, 0xf2, 0x04}, /* AES */ +}; + +static t_u8 rsn_oui[CIPHER_SUITE_MAX][4] = { + {0x00, 0x0f, 0xac, 0x02}, /* TKIP */ + {0x00, 0x0f, 0xac, 0x04}, /* AES */ +}; + +/** + * @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]; + if ((ret = search_oui_in_ie(pmadapter, ie_body, oui))) { + 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]; + if ((ret = search_oui_in_ie(pmadapter, ie_body, oui))) { + 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; + + if (scan_type == MLAN_SCAN_TYPE_ACTIVE + && wlan_11d_is_enabled(pmpriv)) { + scan_type = wlan_11d_get_scan_type(pmadapter, + pscan_region->band, + (t_u8) cfp->channel, + &pmadapter-> + parsed_region_chan); + } + + switch (pscan_region->band) { + case BAND_A: + pscan_chan_list[chan_idx].radio_type = + HostCmd_SCAN_RADIO_TYPE_A; + if (!wlan_11d_is_enabled(pmpriv)) { + /* 11D not available... play it safe 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: + default: + pscan_chan_list[chan_idx].radio_type = + HostCmd_SCAN_RADIO_TYPE_BG; + 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; + } 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_u32 tlv_idx; + t_u32 total_scan_time; + t_u32 done_early; + t_u32 cmd_no; + + 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; + } + + 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) { + + PRINTM(MINFO, "Scan: Chan(%3d), Radio(%d), Mode(%d,%d), Dur(%d)\n", + ptmp_chan_list->chan_number, + ptmp_chan_list->radio_type, + 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)); + + /* 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; + } + + /* 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; + } + } + + /* 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, pioctl_buf, pscan_cfg_out); + if (ret) + break; + } + + LEAVE(); + + 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 const 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; + 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; + + 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)); + + 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((t_s8 *) 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; + } + } + + /* + * 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 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) ? pmadapter-> + 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->adapter->config_bands & BAND_GN + || pmpriv->adapter->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->adapter->config_bands); + 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); + } + + wlan_add_wps_probe_request_ie(pmpriv, &ptlv_pos); + + /* + * 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)->radio_type = 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 == HostCmd_SCAN_RADIO_TYPE_A) { + 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 == HostCmd_SCAN_RADIO_TYPE_A) { + if (wlan_11h_radar_detect_required(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; + } 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; + } + } + + (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); + } + + /* 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; + 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 + * + * @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) +{ + 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-%02x:%02x:%02x:%02x:%02x:%02x\n", + pbss_entry->mac_address[0], pbss_entry->mac_address[1], + pbss_entry->mac_address[2], pbss_entry->mac_address[3], + pbss_entry->mac_address[4], pbss_entry->mac_address[5]); + + 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 (!pmadapter->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; + } + 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 HTCAP_IE", + (t_u8 *) pbss_entry->poverlap_bss_scan_param, + (*(pbss_entry->poverlap_bss_scan_param)).ieee_hdr.len + + sizeof(IEEEtypes_Header_t)); + 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->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->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->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): " + "%02x:%02x:%02x:%02x:%02x:%02x; sz=%03d, left=%03d\n", + beacon_idx, + pnew_beacon->mac_address[0], pnew_beacon->mac_address[1], + pnew_beacon->mac_address[2], pnew_beacon->mac_address[3], + pnew_beacon->mac_address[4], pnew_beacon->mac_address[5], + 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 Restore a beacon buffer of the current bss descriptor + * + * @param pmpriv A pointer to mlan_private structure + * + * @return N/A + */ +static t_void +wlan_restore_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; + + ENTER(); + + if (pmpriv->pcurr_bcn_buf && + ((pmadapter->pbcn_buf_end + pmpriv->curr_bcn_size) < + (pmadapter->bcn_buf + pmadapter->bcn_buf_size))) { + + pcb->moal_spin_lock(pmadapter->pmoal_handle, pmpriv->curr_bcn_buf_lock); + + /* restore the current beacon buffer */ + memcpy(pmadapter, pmadapter->pbcn_buf_end, pmpriv->pcurr_bcn_buf, + pmpriv->curr_bcn_size); + pcurr_bss->pbeacon_buf = pmadapter->pbcn_buf_end; + pcurr_bss->beacon_buf_size = pmpriv->curr_bcn_size; + pmadapter->pbcn_buf_end += 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->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); + } + + pcb->moal_spin_unlock(pmadapter->pmoal_handle, + pmpriv->curr_bcn_buf_lock); + + PRINTM(MINFO, "current beacon restored %d\n", pmpriv->curr_bcn_size); + } else { + PRINTM(MWARN, "curr_bcn_buf not saved or bcn_buf has no space\n"); + } + + LEAVE(); +} + +/** + * @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; + + 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.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)); + + wlan_save_curr_bcn(pmpriv); + pmadapter->callbacks.moal_spin_unlock(pmadapter->pmoal_handle, + pmpriv->curr_bcn_buf_lock); + } else { + wlan_restore_curr_bcn(pmpriv); + } + } + + for (i = 0; i < pmadapter->num_in_scan_table; i++) + PRINTM(MINFO, "Scan:(%02d) %02x:%02x:%02x:%02x:%02x:%02x, " + "RSSI[%03d], SSID[%s]\n", + i, + pmadapter->pscan_table[i].mac_address[0], + pmadapter->pscan_table[i].mac_address[1], + pmadapter->pscan_table[i].mac_address[2], + pmadapter->pscan_table[i].mac_address[3], + pmadapter->pscan_table[i].mac_address[4], + pmadapter->pscan_table[i].mac_address[5], + (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); + + LEAVE(); +} + +/** + * @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 + * + */ +static t_u8 +radio_type_to_band(t_u8 radio_type) +{ + t_u8 ret_band; + + switch (radio_type) { + case HostCmd_SCAN_RADIO_TYPE_A: + ret_band = BAND_A; + break; + case HostCmd_SCAN_RADIO_TYPE_BG: + default: + ret_band = BAND_G; + break; + } + + return ret_band; +} + +/** + * @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].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]; + pbss_desc->disable_11n = MFALSE; + + /* 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; + } + + 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)) { + 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; + 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 */ + 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) +{ + 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; + + 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 const 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; + + 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; + } + + 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 (util_peek_list + (pmadapter->pmoal_handle, &pmadapter->scan_pending_q, + pcb->moal_spin_lock, pcb->moal_spin_unlock)) { + pcmd_node = + (cmd_ctrl_node *) util_dequeue_list(pmadapter->pmoal_handle, + &pmadapter->scan_pending_q, + pcb->moal_spin_lock, + pcb->moal_spin_unlock); + pmadapter->pext_scan_ioctl_req = pioctl_req; + wlan_request_cmd_lock(pmadapter); + pmadapter->scan_processing = MTRUE; + wlan_release_cmd_lock(pmadapter); + wlan_insert_cmd_to_pending_q(pmadapter, pcmd_node, MTRUE); + } + } + 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 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; + mlan_ioctl_req *pioctl_req = (mlan_ioctl_req *) pioctl_buf; + 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; + 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; + + 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); + if (pioctl_req) + pioctl_req->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); + + 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); + + /* + * 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"); + if (pioctl_req) + pioctl_req->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) == + MLAN_STATUS_SUCCESS) { + PRINTM(MINFO, "SCAN_RESP: BSSID = %02x:%02x:%02x:%02x:%02x:%02x\n", + bss_new_entry->mac_address[0], bss_new_entry->mac_address[1], + bss_new_entry->mac_address[2], bss_new_entry->mac_address[3], + bss_new_entry->mac_address[4], + bss_new_entry->mac_address[5]); + /* + * 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 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)); + } + band = BAND_G; + if (pchan_band_tlv) { + pchan_band = &pchan_band_tlv->chan_band_param[idx]; + band = + radio_type_to_band(pchan_band-> + radio_type & (MBIT(0) | MBIT(1))); + } + + /* 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; + + /* 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 (!util_peek_list + (pmadapter->pmoal_handle, &pmadapter->scan_pending_q, + pcb->moal_spin_lock, pcb->moal_spin_unlock)) { + wlan_request_cmd_lock(pmadapter); + pmadapter->scan_processing = MFALSE; + wlan_release_cmd_lock(pmadapter); + /* + * Process the resulting scan table: + * - Remove any bad ssids + * - Update our current BSS information from scan data + */ + wlan_scan_process_results(pmpriv); + + /* 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_buf, + MLAN_STATUS_SUCCESS); + } + 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) { + /* Flush all pending scan commands */ + wlan_flush_scan_queue(pmadapter); + /* 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_buf, + MLAN_STATUS_FAILURE); + } + } 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, + pcb->moal_spin_lock, + pcb->moal_spin_unlock); + + wlan_insert_cmd_to_pending_q(pmadapter, pcmd_node, MTRUE); + } + } + + done: + if (bss_new_entry) + pcb->moal_mfree(pmadapter->pmoal_handle, (t_u8 *) bss_new_entry); + + 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(); + + 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); + + pcmd->command = wlan_cpu_to_le16(HostCmd_CMD_802_11_SCAN_EXT); + + /* Size is equal to the sizeof(fixed portions) + the TLV len + header */ + pcmd->size = wlan_cpu_to_le16((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) +{ + ENTER(); + + PRINTM(MINFO, "EXT scan returns successfully\n"); + + 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; + + 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) == + MLAN_STATUS_SUCCESS) { + PRINTM(MINFO, "EXT_SCAN: BSSID = %02x:%02x:%02x:%02x:%02x:%02x\n", + bss_new_entry->mac_address[0], bss_new_entry->mac_address[1], + bss_new_entry->mac_address[2], bss_new_entry->mac_address[3], + bss_new_entry->mac_address[4], + bss_new_entry->mac_address[5]); + /* + * 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 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++; + } + } + + 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->band); + } + /* + * 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; + } + /* 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; + + /* 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_cpu_to_le16(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) { + pioctl_req = pmadapter->pext_scan_ioctl_req; + if (!util_peek_list(pmadapter->pmoal_handle, + &pmadapter->scan_pending_q, pcb->moal_spin_lock, + pcb->moal_spin_unlock)) { + pmadapter->pext_scan_ioctl_req = MNULL; + wlan_request_cmd_lock(pmadapter); + pmadapter->scan_processing = MFALSE; + wlan_release_cmd_lock(pmadapter); + /* + * Process the resulting scan table: + * - Remove any bad ssids + * - Update our current BSS information from scan data + */ + wlan_scan_process_results(pmpriv); + + /* 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_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) { + /* Flush all pending scan commands */ + wlan_flush_scan_queue(pmadapter); + /* 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); + } + } 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, + pcb->moal_spin_lock, + pcb->moal_spin_unlock); + wlan_insert_cmd_to_pending_q(pmadapter, pcmd_node, MTRUE); + } + } + } + + 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; + if (scan_type == MLAN_SCAN_TYPE_ACTIVE + && wlan_11d_is_enabled(pmpriv)) { + scan_type = wlan_11d_get_scan_type(pmadapter, + pscan_region->band, + (t_u8) cfp->channel, + &pmadapter-> + parsed_region_chan); + } + switch (pscan_region->band) { + case BAND_A: + tlv_chan_list->chan_scan_param[chan_idx].radio_type = + HostCmd_SCAN_RADIO_TYPE_A; + if (!wlan_11d_is_enabled(pmpriv)) { + /* 11D not available... play it safe 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: + default: + tlv_chan_list->chan_scan_param[chan_idx].radio_type = + HostCmd_SCAN_RADIO_TYPE_BG; + 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; + 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->chan_per_scan) + bg_scan->chan_per_scan = bg_scan_in->chan_per_scan; + else + bg_scan->chan_per_scan = MRVDRV_MAX_CHANNELS_PER_SPECIFIC_SCAN; + 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); + } + 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((t_s8 *) 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 == HostCmd_SCAN_RADIO_TYPE_A) { + if (wlan_11h_radar_detect_required + (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].radio_type = + 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; + } + 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 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(pmadapter->config_bands, + pmadapter->pscan_table[i].bss_band)) + || + (wlan_find_cfp_by_band_and_channel + (pmadapter, (t_u8) pmadapter->pscan_table[i].bss_band, + (t_u16) pmadapter->pscan_table[i].channel) == MNULL)) { + 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(pmadapter->config_bands, + pmadapter->pscan_table[i].bss_band)) + || + (wlan_find_cfp_by_band_and_channel + (pmadapter, (t_u8) pmadapter->pscan_table[i].bss_band, + (t_u16) pmadapter->pscan_table[i].channel) == MNULL)) { + 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 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 + */ +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; + util_enqueue_list_tail(pmadapter->pmoal_handle, &pmadapter->scan_pending_q, + (pmlan_linked_list) pcmd_node, + pmadapter->callbacks.moal_spin_lock, + pmadapter->callbacks.moal_spin_unlock); + + done: + LEAVE(); +} + +/** + * @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; + } + + if (!preq_ssid_bssid->ssid.ssid_len) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + PRINTM(MINFO, "Best network found = [%s], " + "[%02x:%02x:%02x:%02x:%02x:%02x]\n", + preq_ssid_bssid->ssid.ssid, + preq_ssid_bssid->bssid[0], preq_ssid_bssid->bssid[1], + preq_ssid_bssid->bssid[2], preq_ssid_bssid->bssid[3], + preq_ssid_bssid->bssid[4], preq_ssid_bssid->bssid[5]); + + 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(); + /* 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); + } + } + } + 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/sd8797/mlan/mlan_sdio.c b/drivers/net/wireless/sd8797/mlan/mlan_sdio.c new file mode 100644 index 000000000000..4812797c6361 --- /dev/null +++ b/drivers/net/wireless/sd8797/mlan/mlan_sdio.c @@ -0,0 +1,1661 @@ +/** @file mlan_sdio.c + * + * @brief This file contains SDIO specific code + * + * Copyright (C) 2008-2011, 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; + + ENTER(); + pmadapter->ioport = 0; + + /* Read the IO port */ + if (MLAN_STATUS_SUCCESS == + pcb->moal_read_reg(pmadapter->pmoal_handle, IO_PORT_0_REG, ®)) + pmadapter->ioport |= (reg & 0xff); + else { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + if (MLAN_STATUS_SUCCESS == + pcb->moal_read_reg(pmadapter->pmoal_handle, IO_PORT_1_REG, ®)) + pmadapter->ioport |= ((reg & 0xff) << 8); + else { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + if (MLAN_STATUS_SUCCESS == + pcb->moal_read_reg(pmadapter->pmoal_handle, IO_PORT_2_REG, ®)) + pmadapter->ioport |= ((reg & 0xff) << 16); + else { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + PRINTM(MINFO, "SDIO FUNC1 IO port: 0x%x\n", pmadapter->ioport); + + /* 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, 0x04)) { + 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; + + ENTER(); + + PRINTM(MIF_D, "wlan_get_rd_port: mp_rd_bitmap=0x%08x\n", rd_bitmap); + + if (!(rd_bitmap & (CTRL_PORT_MASK | DATA_PORT_MASK))) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + if (pmadapter->mp_rd_bitmap & CTRL_PORT_MASK) { + pmadapter->mp_rd_bitmap &= (t_u32) (~CTRL_PORT_MASK); + *pport = CTRL_PORT; + PRINTM(MIF_D, "wlan_get_rd_port: port=%d mp_rd_bitmap=0x%08x\n", *pport, + pmadapter->mp_rd_bitmap); + } else { + 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_PORT) + /* port 0 is reserved for cmd port */ + pmadapter->curr_rd_port = 1; + } 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)) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + 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 = 1; + } else { + pmadapter->data_sent = MTRUE; + LEAVE(); + return MLAN_STATUS_RESOURCE; + } + + if (*pport == CTRL_PORT) { + PRINTM(MERROR, + "Invalid data port=%d cur port=%d mp_wr_bitmap=0x%08x -> 0x%08x\n", + *pport, pmadapter->curr_wr_port, wr_bitmap, + pmadapter->mp_wr_bitmap); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + 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; + + ENTER(); + + if (!pmbuf) { + PRINTM(MWARN, "pmbuf is NULL!\n"); + ret = MLAN_STATUS_FAILURE; + goto exit; + } + + ret = pcb->moal_read_data_sync(pmadapter->pmoal_handle, pmbuf, ioport, 0); + + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "card_to_host, read iomem failed: %d\n", ret); + pmbuf->status_code = MLAN_ERROR_DATA_RX_FAIL; + ret = MLAN_STATUS_FAILURE; + goto exit; + } + *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 pmfw A pointer to firmware image + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status +wlan_prog_fw_w_helper(IN pmlan_adapter pmadapter, IN pmlan_fw_image pmfw) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_callbacks pcb = &pmadapter->callbacks; + t_u8 *firmware = pmfw->pfw_buf; + t_u32 firmwarelen = pmfw->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; + + 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++) { + if ((ret = pcb->moal_read_reg(pmadapter->pmoal_handle, + READ_BASE_0_REG, + &base0)) != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, + "Dev BASE0 register read failed:" + " base0=0x%04X(%d). Terminating download\n", base0, + base0); + goto done; + } + if ((ret = pcb->moal_read_reg(pmadapter->pmoal_handle, + READ_BASE_1_REG, + &base1)) != 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; + } + + 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:", mbuf.pbuf, 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, + 0x04) != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "write CFG reg failed\n"); + } + ret = MLAN_STATUS_FAILURE; + goto done; + } + + offset += txlen; + } while (MTRUE); + + PRINTM(MINFO, "\nFW download over, size %d bytes\n", 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 + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_decode_rx_packet(mlan_adapter * pmadapter, mlan_buffer * pmbuf, + t_u32 upld_typ) +{ + t_u8 *cmdBuf; + t_u32 event; + + ENTER(); + + switch (upld_typ) { + 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; + 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) { + cmdBuf = 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, cmdBuf, + 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; + } + 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 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; + pmlan_callbacks pcb = &pmadapter->callbacks; + t_s32 f_do_rx_aggr = 0; + t_s32 f_do_rx_cur = 0; + t_s32 f_aggr_cur = 0; + 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; + + ENTER(); + + if (port == CTRL_PORT) { + /* Read the command response or event without aggr */ + PRINTM(MINFO, "card_2_host_mp_aggr: No aggr for control port\n"); + + f_do_rx_cur = 1; + goto rx_curr_single; + } + + 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 & (~((t_u32) CTRL_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_do_rx_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 */ + PRINTM(MINFO, "do_rx_aggr: num of packets: %d\n", + pmadapter->mpa_rx.pkt_cnt); + + memset(pmadapter, &mbuf_aggr, 0, sizeof(mlan_buffer)); + + mbuf_aggr.pbuf = (t_u8 *) pmadapter->mpa_rx.buf; + mbuf_aggr.data_len = pmadapter->mpa_rx.buf_len; + cmd53_port = (pmadapter->ioport | SDIO_MPA_ADDR_BASE | + (pmadapter->mpa_rx.ports << 4)) + + pmadapter->mpa_rx.start_port; + if (MLAN_STATUS_SUCCESS != + pcb->moal_read_data_sync(pmadapter->pmoal_handle, &mbuf_aggr, + cmd53_port, 0)) { + pmbuf->status_code = MLAN_ERROR_DATA_RX_FAIL; + ret = MLAN_STATUS_FAILURE; + goto done; + } + + 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_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); + } 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]; + } + MP_RX_AGGR_BUF_RESET(pmadapter); + } + + 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 ((port == CTRL_PORT) && ((pkt_type != MLAN_TYPE_EVENT) && + (pkt_type != MLAN_TYPE_CMD))) { + PRINTM(MERROR, "Wrong pkt from CTRL 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; + } + wlan_decode_rx_packet(pmadapter, pmbuf, pkt_type); + } + + 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 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_u32 cmd53_port = 0; + mlan_buffer mbuf_aggr; + + 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_PORT_LIMIT_REACHED(pmadapter) && + 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 (MP_TX_AGGR_PORT_LIMIT_REACHED(pmadapter) || + !(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; + } + } + + if (f_precopy_cur_buf) { + PRINTM(MINFO, "host_2_card_mp_aggr: Precopy current buffer\n"); + 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) { + 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)); + + mbuf_aggr.pbuf = (t_u8 *) pmadapter->mpa_tx.buf; + mbuf_aggr.data_len = pmadapter->mpa_tx.buf_len; + cmd53_port = (pmadapter->ioport | SDIO_MPA_ADDR_BASE | + (pmadapter->mpa_tx.ports << 4)) + + pmadapter->mpa_tx.start_port; + ret = wlan_write_data_sync(pmadapter, &mbuf_aggr, cmd53_port); + MP_TX_AGGR_BUF_RESET(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 (f_postcopy_cur_buf) { + PRINTM(MINFO, "host_2_card_mp_aggr: Postcopy current buffer\n"); + MP_TX_AGGR_BUF_PUT(pmadapter, mbuf, port); + } + + 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; + + 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; + t_u32 tries; + + ENTER(); + + /* Wait for firmware initialization event */ + for (tries = 0; tries < pollnum; tries++) { + if (MLAN_STATUS_SUCCESS != + (ret = wlan_sdio_read_fw_status(pmadapter, &firmwarestat))) + continue; + if (firmwarestat == FIRMWARE_READY) { + ret = MLAN_STATUS_SUCCESS; + break; + } else { + wlan_mdelay(pmadapter, 100); + ret = MLAN_STATUS_FAILURE; + } + } + + if (ret != MLAN_STATUS_SUCCESS) + 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); + 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 N/A + */ +t_void +wlan_interrupt(pmlan_adapter pmadapter) +{ + pmlan_callbacks pcb = &pmadapter->callbacks; + mlan_buffer mbuf; + t_u32 sdio_ireg = 0; + + 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); + 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); + } else { + PRINTM(MMSG, "wlan_interrupt: sdio_ireg = 0x%x\n", sdio_ireg); + } + done: + LEAVE(); +} + +/** + * @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; + + ENTER(); + ret = wlan_sdio_enable_host_int_mask(pmadapter, HIM_ENABLE); + LEAVE(); + return ret; +} + +/** + * @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_u32 ps_state = pmadapter->ps_state; + t_u16 rx_len; +#if !defined(SDIO_MULTI_PORT_RX_AGGR) + t_u32 upld_typ = 0; +#endif + t_u32 cr = 0; + + 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; + + if (sdio_ireg & DN_LD_HOST_INT_STATUS) { + 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; + PRINTM(MINTR, "DNLD: wr_bitmap=0x%08x\n", pmadapter->mp_wr_bitmap); + if (pmadapter->data_sent && + (pmadapter->mp_wr_bitmap & pmadapter->mp_data_port_mask)) { + PRINTM(MINFO, " <--- Tx DONE Interrupt --->\n"); + pmadapter->data_sent = MFALSE; + } + } + + /* As firmware will not generate download ready interrupt if the port + updated is command port only, cmd_sent should be done for any SDIO + interrupt. */ + if (pmadapter->cmd_sent == MTRUE) { + /* Check if firmware has attach buffer at command port and update just + that in wr_bit_map. */ + pmadapter->mp_wr_bitmap |= + (t_u32) pmadapter->mp_regs[WR_BITMAP_L] & CTRL_PORT_MASK; + if (pmadapter->mp_wr_bitmap & CTRL_PORT_MASK) + pmadapter->cmd_sent = MFALSE; + } + + PRINTM(MINFO, "cmd_sent=%d, data_sent=%d\n", pmadapter->cmd_sent, + pmadapter->data_sent); + + 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; + 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 (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); + if (port == CTRL_PORT) + pmbuf = wlan_alloc_mlan_buffer(pmadapter, rx_len, 0, MTRUE); + else + pmbuf = + wlan_alloc_mlan_buffer(pmadapter, rx_len, + MLAN_RX_HEADER_LEN, MFALSE); + if (pmbuf == MNULL) { + PRINTM(MERROR, "Failed to allocate 'mlan_buffer'\n"); + 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 */ + + if (port == CTRL_PORT) + pmadapter->dbg.num_cmdevt_card_to_host_failure++; + else + 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); +#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: %d\n", ret); + pmbuf->status_code = MLAN_ERROR_PKT_INVALID; + 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; + /* clear CTRL PORT */ + pmadapter->mp_wr_bitmap &= (t_u32) (~(1 << CTRL_PORT)); + 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 + CTRL_PORT; + ret = wlan_write_data_sync(pmadapter, pmbuf, cmd53_port); + } + + if (ret != MLAN_STATUS_SUCCESS) { + 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; +} + +#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; + + ENTER(); + +#ifdef SDIO_MULTI_PORT_TX_AGGR + 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); + pmadapter->mpa_tx.buf_size = mpa_tx_buf_size; +#endif /* SDIO_MULTI_PORT_TX_AGGR */ + +#ifdef SDIO_MULTI_PORT_RX_AGGR + 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); + 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; +} +#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 = priv->adapter; + HostCmd_DS_SDIO_GPIO_INT_CONFIG sdio_int_cfg; + + ENTER(); + + if (pmadapter->int_mode == INT_MODE_GPIO) { + 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; +} diff --git a/drivers/net/wireless/sd8797/mlan/mlan_sdio.h b/drivers/net/wireless/sd8797/mlan/mlan_sdio.h new file mode 100644 index 000000000000..f77397d66eb4 --- /dev/null +++ b/drivers/net/wireless/sd8797/mlan/mlan_sdio.h @@ -0,0 +1,307 @@ +/** @file mlan_sdio.h + * + * @brief This file contains definitions for SDIO interface. + * driver. + * + * Copyright (C) 2008-2011, 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 */ +#define BLOCK_MODE 1 +/** Fixed address mode */ +#define FIXED_ADDRESS 0 + +/* 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 0x01 +/** Host Control Registers : Upload host interrupt RSR */ +#define UP_LD_HOST_INT_RSR (0x1U) +#define HOST_INT_RSR_MASK 0x3F + +/** Host Control Registers : Host interrupt mask */ +#define HOST_INT_MASK_REG 0x02 +/** Host Control Registers : Upload host interrupt mask */ +#define UP_LD_HOST_INT_MASK (0x1U) +/** Host Control Registers : Download host interrupt mask */ +#define DN_LD_HOST_INT_MASK (0x2U) +/** Enable Host interrupt mask */ +#define HIM_ENABLE (UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK) +/** Disable Host interrupt mask */ +#define HIM_DISABLE 0xff + +/** Host Control Registers : Host interrupt status */ +#define HOST_INT_STATUS_REG 0x03 +/** Host Control Registers : Upload host interrupt status */ +#define UP_LD_HOST_INT_STATUS (0x1U) +/** Host Control Registers : Download host interrupt status */ +#define DN_LD_HOST_INT_STATUS (0x2U) + +/** Port for registers */ +#define REG_PORT 0 +/** LSB of read bitmap */ +#define RD_BITMAP_L 0x04 +/** MSB of read bitmap */ +#define RD_BITMAP_U 0x05 +/** LSB of write bitmap */ +#define WR_BITMAP_L 0x06 +/** MSB of write bitmap */ +#define WR_BITMAP_U 0x07 +/** LSB of read length for port 0 */ +#define RD_LEN_P0_L 0x08 +/** MSB of read length for port 0 */ +#define RD_LEN_P0_U 0x09 +/** Ctrl port */ +#define CTRL_PORT 0 +/** Ctrl port mask */ +#define CTRL_PORT_MASK 0x0001 +/** Data port mask */ +#define DATA_PORT_MASK 0xfffe +/** Misc. Config Register : Auto Re-enable interrupts */ +#define AUTO_RE_ENABLE_INT MBIT(4) + +/** Host Control Registers : Host transfer status */ +#define HOST_RESTART_REG 0x28 +/** 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 0x30 +/** Card Control Registers : Card I/O ready */ +#define CARD_IO_READY (0x1U << 3) +/** Card Control Registers : CIS card ready */ +#define CIS_CARD_RDY (0x1U << 2) +/** Card Control Registers : Upload card ready */ +#define UP_LD_CARD_RDY (0x1U << 1) +/** Card Control Registers : Download card ready */ +#define DN_LD_CARD_RDY (0x1U << 0) + +/** Card Control Registers : Host interrupt mask register */ +#define HOST_INTERRUPT_MASK_REG 0x34 +/** Card Control Registers : Host power interrupt mask */ +#define HOST_POWER_INT_MASK (0x1U << 3) +/** Card Control Registers : Abort card interrupt mask */ +#define ABORT_CARD_INT_MASK (0x1U << 2) +/** Card Control Registers : Upload card interrupt mask */ +#define UP_LD_CARD_INT_MASK (0x1U << 1) +/** Card Control Registers : Download card interrupt mask */ +#define DN_LD_CARD_INT_MASK (0x1U << 0) + +/** Card Control Registers : Card interrupt status register */ +#define CARD_INTERRUPT_STATUS_REG 0x38 +/** Card Control Registers : Power up interrupt */ +#define POWER_UP_INT (0x1U << 4) +/** Card Control Registers : Power down interrupt */ +#define POWER_DOWN_INT (0x1U << 3) + +/** Card Control Registers : Card interrupt RSR register */ +#define CARD_INTERRUPT_RSR_REG 0x3c +/** Card Control Registers : Power up RSR */ +#define POWER_UP_RSR (0x1U << 4) +/** Card Control Registers : Power down RSR */ +#define POWER_DOWN_RSR (0x1U << 3) + +/** Card Control Registers : SQ Read base address 0 register */ +#define READ_BASE_0_REG 0x40 +/** Card Control Registers : SQ Read base address 1 register */ +#define READ_BASE_1_REG 0x41 + +/** Card Control Registers : Card revision register */ +#define CARD_REVISION_REG 0x5c + +/** Firmware status 0 register (SCRATCH0_0) */ +#define CARD_FW_STATUS0_REG 0x60 +/** Firmware status 1 register (SCRATCH0_1) */ +#define CARD_FW_STATUS1_REG 0x61 +/** Rx length register (SCRATCH0_2) */ +#define CARD_RX_LEN_REG 0x62 +/** Rx unit register (SCRATCH0_3) */ +#define CARD_RX_UNIT_REG 0x63 + +/** Card Control Registers : Card OCR 0 register */ +#define CARD_OCR_0_REG 0x68 +/** Card Control Registers : Card OCR 1 register */ +#define CARD_OCR_1_REG 0x69 +/** Card Control Registers : Card OCR 3 register */ +#define CARD_OCR_3_REG 0x6A +/** Card Control Registers : Card config register */ +#define CARD_CONFIG_REG 0x6B +/** Card Control Registers : Miscellaneous Configuration Register */ +#define CARD_MISC_CFG_REG 0x6C + +/** Card Control Registers : Debug 0 register */ +#define DEBUG_0_REG 0x70 +/** Card Control Registers : SD test BUS 0 */ +#define SD_TESTBUS0 (0x1U) +/** Card Control Registers : Debug 1 register */ +#define DEBUG_1_REG 0x71 +/** Card Control Registers : SD test BUS 1 */ +#define SD_TESTBUS1 (0x1U) +/** Card Control Registers : Debug 2 register */ +#define DEBUG_2_REG 0x72 +/** Card Control Registers : SD test BUS 2 */ +#define SD_TESTBUS2 (0x1U) +/** Card Control Registers : Debug 3 register */ +#define DEBUG_3_REG 0x73 +/** Card Control Registers : SD test BUS 3 */ +#define SD_TESTBUS3 (0x1U) + +/** Host Control Registers : I/O port 0 */ +#define IO_PORT_0_REG 0x78 +/** Host Control Registers : I/O port 1 */ +#define IO_PORT_1_REG 0x79 +/** Host Control Registers : I/O port 2 */ +#define IO_PORT_2_REG 0x7A + +/** 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; \ + if(!a->mpa_tx.pkt_cnt){ \ + a->mpa_tx.start_port = port; \ + } \ + if(a->mpa_tx.start_port<=port){ \ + a->mpa_tx.ports |= (1<<(a->mpa_tx.pkt_cnt)); \ + }else{ \ + a->mpa_tx.ports |= (1<<(a->mpa_tx.pkt_cnt+1+(MAX_PORT - a->mp_end_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) ((a->curr_wr_port < \ + a->mpa_tx.start_port) && (((MAX_PORT - \ + a->mpa_tx.start_port) + a->curr_wr_port) >= \ + SDIO_MP_AGGR_DEF_PKT_LIMIT)) + +/** Reset SDIO Tx aggregation buffer parameters */ +#define MP_TX_AGGR_BUF_RESET(a) do{ \ + 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 ? */ +#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) >= \ + SDIO_MP_AGGR_DEF_PKT_LIMIT)) + +/** 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; \ + } \ + if(a->mpa_rx.start_port<=port){ \ + a->mpa_rx.ports |= (1<<(a->mpa_rx.pkt_cnt)); \ + }else{ \ + a->mpa_rx.ports |= (1<<(a->mpa_rx.pkt_cnt+1)); \ + } \ + 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); +/** Firmware status check */ +mlan_status wlan_check_fw_status(mlan_adapter * pmadapter, t_u32 pollnum); +/** Read interrupt status */ +t_void 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); +#endif /* _MLAN_SDIO_H */ diff --git a/drivers/net/wireless/sd8797/mlan/mlan_shim.c b/drivers/net/wireless/sd8797/mlan/mlan_shim.c new file mode 100644 index 000000000000..c17b8e21c482 --- /dev/null +++ b/drivers/net/wireless/sd8797/mlan/mlan_shim.c @@ -0,0 +1,939 @@ +/** @file mlan_shim.c + * + * @brief This file contains APIs to MOAL module. + * + * Copyright (C) 2008-2011, 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" + +/******************************************************** + 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 t_s8 * pformat, IN ...) = MNULL; +/** Global driver debug mit masks */ +t_u32 drvdbg = DEFAULT_DEBUG_MASK; +#endif + +/******************************************************** + Local Functions +*******************************************************/ + +/******************************************************** + 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; + + MASSERT(pmdevice); + MASSERT(ppmlan_adapter); + MASSERT(pmdevice->callbacks.moal_print); +#ifdef DEBUG_LEVEL1 + print_callback = pmdevice->callbacks.moal_print; +#endif + assert_callback = pmdevice->callbacks.moal_assert; + + ENTER(); + + MASSERT(pmdevice->callbacks.moal_malloc); + MASSERT(pmdevice->callbacks.moal_memset); + MASSERT(pmdevice->callbacks.moal_memmove); + + /* Allocate memory for adapter structure */ + if ((pmdevice->callbacks. + moal_malloc(pmdevice->pmoal_handle, sizeof(mlan_adapter), MLAN_MEM_DEF, + (t_u8 **) & pmadapter) != MLAN_STATUS_SUCCESS) + || !pmadapter) { + ret = MLAN_STATUS_FAILURE; + goto exit_register; + } + + pmdevice->callbacks.moal_memset(pmadapter, 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_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); + + /* 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 .. */ + if (MLAN_STATUS_SUCCESS != (ret = wlan_sdio_probe(pmadapter))) { + ret = MLAN_STATUS_FAILURE; + goto error; + } +#ifdef DEBUG_LEVEL1 + drvdbg = pmdevice->drvdbg; +#endif + +#ifdef MFG_CMD_SUPPORT + pmadapter->init_para.mfg_mode = pmdevice->mfg_mode; +#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->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->priv_num = 0; + for (i = 0; i < MLAN_MAX_BSS_NUM; i++) { + pmadapter->priv[i] = MNULL; + if (pmdevice->bss_attr[i].active == MTRUE) { + /* For valid bss_attr, allocate memory for private structure */ + if ((pcb-> + moal_malloc(pmadapter->pmoal_handle, sizeof(mlan_private), + MLAN_MEM_DEF, + (t_u8 **) & pmadapter->priv[i]) != + 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) pmdevice->bss_attr[i].bss_type; + pmadapter->priv[i]->frame_type = + (t_u8) pmdevice->bss_attr[i].frame_type; + pmadapter->priv[i]->bss_priority = + (t_u8) pmdevice->bss_attr[i].bss_priority; + if (pmdevice->bss_attr[i].bss_type == MLAN_BSS_TYPE_STA) + pmadapter->priv[i]->bss_role = MLAN_BSS_ROLE_STA; + else if (pmdevice->bss_attr[i].bss_type == MLAN_BSS_TYPE_UAP) + pmadapter->priv[i]->bss_role = MLAN_BSS_ROLE_UAP; +#ifdef WIFI_DIRECT_SUPPORT + else if (pmdevice->bss_attr[i].bss_type == MLAN_BSS_TYPE_WIFIDIRECT) + pmadapter->priv[i]->bss_role = MLAN_BSS_ROLE_STA; +#endif + /* Save bss_index and bss_num */ + pmadapter->priv[i]->bss_index = i; + pmadapter->priv[i]->bss_num = (t_u8) pmdevice->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)); + } + } + } + } + + /* Initialize lock variables */ + if (wlan_init_lock_list(pmadapter) != 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]) + pcb->moal_mfree(pmadapter->pmoal_handle, + (t_u8 *) pmadapter->priv[i]); + } + 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]) { + pcb->moal_mfree(pmadapter->pmoal_handle, + (t_u8 *) pmadapter->priv[i]); + } + } + + /* Free mlan_adapter */ + 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); + + /* 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) { + 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 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 = WlanHardwareStatusInitializing; + + /* 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_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"); + + /* 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; + + if (pcb->moal_spin_lock(pmadapter->pmoal_handle, pmadapter->pmlan_lock) + != MLAN_STATUS_SUCCESS) { + ret = MLAN_STATUS_FAILURE; + goto exit_shutdown_fw; + } + + if (pcb->moal_spin_unlock(pmadapter->pmoal_handle, pmadapter->pmlan_lock) + != MLAN_STATUS_SUCCESS) { + ret = MLAN_STATUS_FAILURE; + goto exit_shutdown_fw; + } + + /* Notify completion */ + ret = wlan_shutdown_fw_complete(pmadapter); + + exit_shutdown_fw: + 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) { + pcb->moal_spin_unlock(pmadapter->pmoal_handle, + pmadapter->pmain_proc_lock); + goto exit_main_proc; + } else { + pmadapter->mlan_processing = MTRUE; + 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; + + /* 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); + } + + /* Need to wake up the card ? */ + if ((pmadapter->ps_state == PS_STATE_SLEEP) && + (pmadapter->pm_wakeup_card_req && + !pmadapter->pm_wakeup_fw_try) && + (util_peek_list + (pmadapter->pmoal_handle, &pmadapter->cmd_pending_q, + pcb->moal_spin_lock, pcb->moal_spin_unlock) + || !wlan_bypass_tx_list_empty(pmadapter) + || !wlan_wmm_lists_empty(pmadapter) + )) { + pmadapter->pm_wakeup_fw_try = MTRUE; + wlan_pm_wakeup_card(pmadapter); + 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; + if (pmadapter->ps_state != PS_STATE_AWAKE || + (pmadapter->tx_lock_flag == MTRUE)) + break; + + if (pmadapter->scan_processing || pmadapter->data_sent + || (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 || + (!util_peek_list + (pmadapter->pmoal_handle, &pmadapter->cmd_pending_q, + pcb->moal_spin_lock, pcb->moal_spin_unlock))) { + 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); + } + } + + /* 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) { + if (wlan_exec_next_cmd(pmadapter) == MLAN_STATUS_FAILURE) { + ret = MLAN_STATUS_FAILURE; + break; + } + } + + if (!pmadapter->scan_processing && !pmadapter->data_sent && + !wlan_11h_radar_detected_tx_blocked(pmadapter) && + !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->scan_processing && !pmadapter->data_sent && + !wlan_wmm_lists_empty(pmadapter) + && !wlan_11h_radar_detected_tx_blocked(pmadapter) + ) { + 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 && !IS_COMMAND_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); + + if ((pmadapter->sdio_ireg) || IS_CARD_RX_RCVD(pmadapter)) { + goto process_start; + } + pcb->moal_spin_lock(pmadapter->pmoal_handle, pmadapter->pmain_proc_lock); + 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; + + ENTER(); + MASSERT(pmlan_adapter && pmbuf); + + MASSERT(pmbuf->bss_index < pmadapter->priv_num); + pmbuf->flags = MLAN_BUF_FLAG_MOAL_TX_BUF; + + if (((pmadapter->priv[pmbuf->bss_index]->port_ctrl_mode == MTRUE) && + ((mlan_ntohs(*(t_u16 *) & pmbuf->pbuf[pmbuf->data_offset + + MLAN_ETHER_PKT_TYPE_OFFSET]) == + MLAN_ETHER_PKT_TYPE_EAPOL) + || + (mlan_ntohs + (*(t_u16 *) & pmbuf-> + pbuf[pmbuf->data_offset + MLAN_ETHER_PKT_TYPE_OFFSET]) == + MLAN_ETHER_PKT_TYPE_WAPI) + )) + || (pmbuf->buf_type == MLAN_BUF_TYPE_RAW_DATA) + + ) { + 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(MERROR, "MLAN IOCTL information buffer is NULL\n"); + ret = MLAN_STATUS_FAILURE; + 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 + */ +t_void +mlan_interrupt(IN t_void * adapter) +{ + mlan_adapter *pmadapter = (mlan_adapter *) adapter; + + ENTER(); + if (!pmadapter->pps_uapsd_mode && pmadapter->ps_state == PS_STATE_SLEEP) { + pmadapter->pm_wakeup_fw_try = MFALSE; + pmadapter->ps_state = PS_STATE_AWAKE; + } + wlan_interrupt(pmadapter); + LEAVE(); +} diff --git a/drivers/net/wireless/sd8797/mlan/mlan_sta_cmd.c b/drivers/net/wireless/sd8797/mlan/mlan_sta_cmd.c new file mode 100644 index 000000000000..c3a3b956aa61 --- /dev/null +++ b/drivers/net/wireless/sd8797/mlan/mlan_sta_cmd.c @@ -0,0 +1,1888 @@ +/** @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-2011, 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 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 + */ +static 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_u16 action = *((t_u16 *) 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_le16(action); + + 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 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; + 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_u16 *) pdata_buf)); + break; + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @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; + + 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 = + (t_u16) wlan_cpu_to_le16(pmadapter->arp_filter_size + + sizeof(HostCmd_DS_802_11_HS_CFG_ENH) + + S_DS_GEN); + } else + cmd->size = + wlan_cpu_to_le16(S_DS_GEN + sizeof(HostCmd_DS_802_11_HS_CFG_ENH)); + + if (hs_activate) { + 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; + PRINTM(MCMND, "HS_CFG_CMD: condition:0x%x gpio:0x%x gap:0x%x\n", + phs_cfg->params.hs_config.conditions, + phs_cfg->params.hs_config.gpio, phs_cfg->params.hs_config.gap); + } + + 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. + * + * @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_802_11_deauthenticate(IN pmlan_private pmpriv, + 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(HostCmd_CMD_802_11_DEAUTHENTICATE); + cmd->size = + wlan_cpu_to_le16(sizeof(HostCmd_DS_802_11_DEAUTHENTICATE) + S_DS_GEN); + + /* Set AP MAC address */ + memcpy(pmpriv->adapter, pdeauth->mac_addr, (t_u8 *) pdata_buf, + MLAN_MAC_ADDR_LENGTH); + + PRINTM(MCMND, "Deauth: %02x:%02x:%02x:%02x:%02x:%02x\n", + pdeauth->mac_addr[0], pdeauth->mac_addr[1], pdeauth->mac_addr[2], + pdeauth->mac_addr[3], pdeauth->mac_addr[4], pdeauth->mac_addr[5]); + + 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 { +/** Reason code 3 = Station is leaving */ +#define REASON_CODE_STA_LEAVING 3 + pdeauth->reason_code = wlan_cpu_to_le16(REASON_CODE_STA_LEAVING); + } + + 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; +} + +/** Length of WEP 40 bit key */ +#define WEP_40_BIT_LEN 5 +/** Length of WEP 104 bit key */ +#define WEP_104_BIT_LEN 13 + +/** + * @brief This function sets WEP key(s) to key_param_set TLV(s). + * + * @param priv A pointer to mlan_private structure + * @param key_param_set A pointer to MrvlIEtype_KeyParamSet_t structure + * @param key_param_len A pointer to the length variable + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status +wlan_set_keyparamset_wep(mlan_private * priv, + MrvlIEtype_KeyParamSet_t * key_param_set, + t_u16 * key_param_len) +{ + int cur_key_param_len = 0; + t_u8 i; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + /* Multi-key_param_set TLV is supported */ + for (i = 0; i < MRVL_NUM_WEP_KEY; i++) { + if ((priv->wep_key[i].key_length == WEP_40_BIT_LEN) || + (priv->wep_key[i].key_length == WEP_104_BIT_LEN)) { + key_param_set->type = wlan_cpu_to_le16(TLV_TYPE_KEY_MATERIAL); +/** Key_param_set WEP fixed length */ +#define KEYPARAMSET_WEP_FIXED_LEN 8 + key_param_set->length = + wlan_cpu_to_le16((t_u16) + (priv->wep_key[i].key_length + + KEYPARAMSET_WEP_FIXED_LEN)); + key_param_set->key_type_id = wlan_cpu_to_le16(KEY_TYPE_ID_WEP); + key_param_set->key_info = wlan_cpu_to_le16 + (KEY_INFO_WEP_ENABLED | KEY_INFO_WEP_UNICAST | + KEY_INFO_WEP_MCAST); + key_param_set->key_len = + (t_u16) wlan_cpu_to_le16(priv->wep_key[i].key_length); + /* Set WEP key index */ + key_param_set->key[0] = i; + /* Set default Tx key flag */ + if (i == (priv->wep_key_curr_index & HostCmd_WEP_KEY_INDEX_MASK)) + key_param_set->key[1] = 1; + else + key_param_set->key[1] = 0; + memmove(priv->adapter, &key_param_set->key[2], + priv->wep_key[i].key_material, priv->wep_key[i].key_length); + + cur_key_param_len = priv->wep_key[i].key_length + + KEYPARAMSET_WEP_FIXED_LEN + sizeof(MrvlIEtypesHeader_t); + *key_param_len += (t_u16) cur_key_param_len; + key_param_set = + (MrvlIEtype_KeyParamSet_t *) ((t_u8 *) key_param_set + + cur_key_param_len); + } else if (!priv->wep_key[i].key_length) { + continue; + } else { + PRINTM(MERROR, "key%d Length = %d is incorrect\n", (i + 1), + priv->wep_key[i].key_length); + ret = MLAN_STATUS_FAILURE; + goto done; + } + } + + done: + LEAVE(); + return ret; +} + +/** + * @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; + t_u16 key_param_len = 0; + mlan_status ret = MLAN_STATUS_SUCCESS; + const t_u8 bc_mac[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + + ENTER(); + + 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; + } + + if (!pkey) { + memset(pmpriv->adapter, &pkey_material->key_param_set, 0, + (MRVL_NUM_WEP_KEY * sizeof(MrvlIEtype_KeyParamSet_t))); + ret = + wlan_set_keyparamset_wep(pmpriv, &pkey_material->key_param_set, + &key_param_len); + cmd->size = + wlan_cpu_to_le16(key_param_len + sizeof(pkey_material->action) + + S_DS_GEN); + goto done; + } else + memset(pmpriv->adapter, &pkey_material->key_param_set, 0, + sizeof(MrvlIEtype_KeyParamSet_t)); + if (pkey->is_wapi_key) { + PRINTM(MINFO, "Set WAPI Key\n"); + pkey_material->key_param_set.key_type_id = + wlan_cpu_to_le16(KEY_TYPE_ID_WAPI); + if (cmd_oid == KEY_INFO_ENABLED) + pkey_material->key_param_set.key_info = + wlan_cpu_to_le16(KEY_INFO_WAPI_ENABLED); + else + pkey_material->key_param_set.key_info = + !(wlan_cpu_to_le16(KEY_INFO_WAPI_ENABLED)); + + pkey_material->key_param_set.key[0] = (t_u8) pkey->key_index; + if (!pmpriv->sec_info.wapi_key_on) + pkey_material->key_param_set.key[1] = 1; + else + pkey_material->key_param_set.key[1] = 0; /* set 0 when re-key */ + + if (0 != memcmp(pmpriv->adapter, pkey->mac_addr, bc_mac, sizeof(bc_mac))) /* WAPI + pairwise + key: + unicast + */ + pkey_material->key_param_set.key_info |= + wlan_cpu_to_le16(KEY_INFO_WAPI_UNICAST); + else { /* WAPI group key: multicast */ + pkey_material->key_param_set.key_info |= + wlan_cpu_to_le16(KEY_INFO_WAPI_MCAST); + pmpriv->sec_info.wapi_key_on = MTRUE; + } + + pkey_material->key_param_set.type = + wlan_cpu_to_le16(TLV_TYPE_KEY_MATERIAL); + pkey_material->key_param_set.key_len = wlan_cpu_to_le16(WAPI_KEY_LEN); + memcpy(pmpriv->adapter, &pkey_material->key_param_set.key[2], + pkey->key_material, pkey->key_len); + memcpy(pmpriv->adapter, + &pkey_material->key_param_set.key[2 + pkey->key_len], pkey->pn, + PN_SIZE); + pkey_material->key_param_set.length = + wlan_cpu_to_le16(WAPI_KEY_LEN + KEYPARAMSET_FIXED_LEN); + + key_param_len = + (WAPI_KEY_LEN + KEYPARAMSET_FIXED_LEN) + + sizeof(MrvlIEtypesHeader_t); + cmd->size = + wlan_cpu_to_le16(key_param_len + sizeof(pkey_material->action) + + S_DS_GEN); + goto done; + } + if (pkey->key_len == WPA_AES_KEY_LEN) { + PRINTM(MCMND, "WPA_AES\n"); + pkey_material->key_param_set.key_type_id = + wlan_cpu_to_le16(KEY_TYPE_ID_AES); + if (cmd_oid == KEY_INFO_ENABLED) + pkey_material->key_param_set.key_info = + wlan_cpu_to_le16(KEY_INFO_AES_ENABLED); + else + pkey_material->key_param_set.key_info = + !(wlan_cpu_to_le16(KEY_INFO_AES_ENABLED)); + + if (pkey->key_index & MLAN_KEY_INDEX_UNICAST) /* AES pairwise key: + unicast */ + pkey_material->key_param_set.key_info |= + wlan_cpu_to_le16(KEY_INFO_AES_UNICAST); + else /* AES group key: multicast */ + pkey_material->key_param_set.key_info |= + wlan_cpu_to_le16(KEY_INFO_AES_MCAST); + } else if (pkey->key_len == WPA_TKIP_KEY_LEN) { + PRINTM(MCMND, "WPA_TKIP\n"); + pkey_material->key_param_set.key_type_id = + wlan_cpu_to_le16(KEY_TYPE_ID_TKIP); + pkey_material->key_param_set.key_info = + wlan_cpu_to_le16(KEY_INFO_TKIP_ENABLED); + + if (pkey->key_index & MLAN_KEY_INDEX_UNICAST) /* TKIP pairwise key: + unicast */ + pkey_material->key_param_set.key_info |= + wlan_cpu_to_le16(KEY_INFO_TKIP_UNICAST); + else /* TKIP group key: multicast */ + pkey_material->key_param_set.key_info |= + wlan_cpu_to_le16(KEY_INFO_TKIP_MCAST); + } + + if (pkey_material->key_param_set.key_type_id) { + pkey_material->key_param_set.type = + wlan_cpu_to_le16(TLV_TYPE_KEY_MATERIAL); + pkey_material->key_param_set.key_len = + wlan_cpu_to_le16((t_u16) pkey->key_len); + memcpy(pmpriv->adapter, pkey_material->key_param_set.key, + pkey->key_material, pkey->key_len); + pkey_material->key_param_set.length = + wlan_cpu_to_le16((t_u16) pkey->key_len + KEYPARAMSET_FIXED_LEN); + + key_param_len = + (t_u16) (pkey->key_len + KEYPARAMSET_FIXED_LEN) + + sizeof(MrvlIEtypesHeader_t); + + cmd->size = + wlan_cpu_to_le16(key_param_len + sizeof(pkey_material->action) + + S_DS_GEN); + } + done: + LEAVE(); + return ret; +} + +/** + * @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 + */ +static 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_passphrase *psk = (mlan_ds_passphrase *) pdata_buf; + t_u8 zero_mac[] = { 0, 0, 0, 0, 0, 0 }; + + ENTER(); + /* + * 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->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, (char *) pssid_tlv->ssid, 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); + } + if (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); + } + 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); + } + if (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); + } + if ((cmd_action == HostCmd_ACT_GEN_SET) && + ((pssid_tlv || pbssid_tlv) && (!ppmk_tlv && !ppassphrase_tlv))) { + 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 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 + * + * @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) +{ + HostCmd_DS_802_11_SUPPLICANT_PROFILE *sup_profile = + &cmd->params.esupplicant_profile; + + 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); + 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 = HostCmd_SCAN_RADIO_TYPE_A; + SET_SECONDARYCHAN(prf_chan->rf_type, pmpriv->adapter->chan_bandwidth); + prf_chan->rf_type = wlan_cpu_to_le16(prf_chan->rf_type); + 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 * 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 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 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 + */ +static 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_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 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 + */ +static 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 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; +#define HostCmd_ACT_BITWISE_SET 0x02 + evt->action = wlan_cpu_to_le16(HostCmd_ACT_BITWISE_SET); + 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 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; +} + +/******************************************************** + 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_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(); + + /* Prepare command */ + switch (cmd_no) { + case HostCmd_CMD_GET_HW_SPEC: + ret = wlan_cmd_get_hw_spec(pmpriv, cmd_ptr); + break; + case HostCmd_CMD_CFG_DATA: + ret = wlan_cmd_cfg_data(pmpriv, cmd_ptr, cmd_action, 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_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_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: + ret = wlan_cmd_802_11_deauthenticate(pmpriv, 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_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(t_u32) + 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_SUPPLICANT_PMK: + ret = wlan_cmd_802_11_supplicant_pmk(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); + 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_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_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_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_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_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_MEASUREMENT_REQUEST: + case HostCmd_CMD_MEASUREMENT_REPORT: + ret = wlan_meas_cmd_process(pmpriv, cmd_ptr, pdata_buf); + break; +#ifdef WIFI_DIRECT_SUPPORT + case HostCmd_CMD_802_11_REMAIN_ON_CHANNEL: + ret = + wlan_cmd_remain_on_channel(pmpriv, cmd_ptr, cmd_action, pdata_buf); + break; + 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; + 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 (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; + } + + /* 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/sd8797/mlan/mlan_sta_cmdresp.c b/drivers/net/wireless/sd8797/mlan/mlan_sta_cmdresp.c new file mode 100644 index 000000000000..19ff2ef7fdf2 --- /dev/null +++ b/drivers/net/wireless/sd8797/mlan/mlan_sta_cmdresp.c @@ -0,0 +1,1640 @@ +/** @file mlan_sta_cmdresp.c + * + * @brief This file contains the handling of command + * responses generated by firmware. + * + * Copyright (C) 2008-2011, 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 + * + * @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 void +wlan_process_cmdresp_error(mlan_private * pmpriv, HostCmd_DS_COMMAND * resp, + mlan_ioctl_req * pioctl_buf) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + + ENTER(); + + 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); + wlan_recv_event(pmpriv, MLAN_EVENT_ID_DRV_SCAN_REPORT, MNULL); + break; + + case HostCmd_CMD_MAC_CONTROL: + break; + + default: + break; + } + /* + * Handling errors here + */ + wlan_insert_cmd_to_free_q(pmadapter, pmadapter->curr_cmd); + + wlan_request_cmd_lock(pmadapter); + pmadapter->curr_cmd = MNULL; + wlan_release_cmd_lock(pmadapter); + + LEAVE(); + return; +} + +/** + * @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; + + 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); + + /* 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 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 + */ +static 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 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 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; + + 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; + + 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]); + /* Indicate ioctl complete */ + pioctl_buf->data_read_written = sizeof(mlan_ds_get_info); + } + 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) +{ + int length = -1, 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 = (t_u8) min_power; + pmpriv->max_tx_power_level = (t_u8) 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; + t_u32 data[5]; + + 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_u16) 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_u16) 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.len = 0; + while (ppg_tlv->length) { + data[0] = pg->first_rate_code; + data[1] = pg->last_rate_code; + if (pg->modulation_class == MOD_CLASS_OFDM) { + data[0] += MLAN_RATE_INDEX_OFDM0; + data[1] += MLAN_RATE_INDEX_OFDM0; + } else if (pg->modulation_class == MOD_CLASS_HT) { + data[0] += MLAN_RATE_INDEX_MCS0; + data[1] += MLAN_RATE_INDEX_MCS0; + if (pg->ht_bandwidth == HT_BW_40) { + data[0] |= TX_RATE_HT_BW40_BIT; + data[1] |= TX_RATE_HT_BW40_BIT; + } + } + data[2] = pg->power_min; + data[3] = pg->power_max; + data[4] = pg->power_step; + memcpy(pmpriv->adapter, + (t_u8 *) (&power->param.power_ext. + power_data[power->param.power_ext.len]), + (t_u8 *) data, sizeof(data)); + power->param.power_ext.len += 5; + pg++; + ppg_tlv->length -= sizeof(Power_Group_t); + } + 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->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: %02x:%02x:%02x:%02x:%02x:%02x\n", + pmpriv->curr_addr[0], pmpriv->curr_addr[1], pmpriv->curr_addr[2], + pmpriv->curr_addr[3], pmpriv->curr_addr[4], pmpriv->curr_addr[5]); + 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; + + 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; + } + pmpriv->scan_block = MFALSE; + } + } + + 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 + */ +static 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; + + } + } + if (((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); + } + } else if (action == HostCmd_ACT_GEN_SET) { + PRINTM(MINFO, "Esupp PMK set: enable ewpa query\n"); + pmpriv->ewpa_query = MTRUE; + } + } + + LEAVE(); + return ret; +} + +/** + * @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 %02x:%02x:%02x:%02x:%02x:%02x\n", + pibss_coal_resp->bssid[0], pibss_coal_resp->bssid[1], + pibss_coal_resp->bssid[2], pibss_coal_resp->bssid[3], + pibss_coal_resp->bssid[4], pibss_coal_resp->bssid[5]); + + /* 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)) + 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; + max_mgmt_ie = + (tlvbuf_max_mgmt_ie *) ((t_u8 *) cust_ie + cust_ie->len + + sizeof(MrvlIEtypesHeader_t)); + 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))); + 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 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 reg_access + * + * @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 + */ +static 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_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 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 + */ +static 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 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 + * 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; +} + +/******************************************************** + 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) + ) { + wlan_process_cmdresp_error(pmpriv, resp, pioctl_buf); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + /* 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_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_scan(pmpriv, resp, pioctl_buf); + wlan_recv_event(pmpriv, MLAN_EVENT_ID_FW_BG_SCAN, 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_ASSOCIATE: + ret = wlan_ret_802_11_associate(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_802_11_DEAUTHENTICATE: + 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: + 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_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_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_11D_DOMAIN_INFO: + ret = wlan_ret_802_11d_domain_info(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: + 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 = 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_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_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_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; +#ifdef WIFI_DIRECT_SUPPORT + case HostCmd_CMD_802_11_REMAIN_ON_CHANNEL: + ret = wlan_ret_remain_on_channel(pmpriv, resp, pioctl_buf); + break; + 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; + default: + PRINTM(MERROR, "CMD_RESP: Unknown command response %#x\n", + resp->command); + break; + } + + LEAVE(); + return ret; +} diff --git a/drivers/net/wireless/sd8797/mlan/mlan_sta_event.c b/drivers/net/wireless/sd8797/mlan/mlan_sta_event.c new file mode 100644 index 000000000000..232496681dae --- /dev/null +++ b/drivers/net/wireless/sd8797/mlan/mlan_sta_event.c @@ -0,0 +1,595 @@ +/** @file mlan_sta_event.c + * + * @brief This file contains MLAN event handling. + * + * Copyright (C) 2008-2011, 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" + +/******************************************************** + Global Variables +********************************************************/ + +/******************************************************** + Local Functions +********************************************************/ + +/** + * @brief This function handles link lost, deauth and + * disassoc events. + * + * @param priv 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(); +} + +/******************************************************** + 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(); + + if (priv->media_connected != MTRUE) { + LEAVE(); + return; + } + + PRINTM(MINFO, "Handles disconnect event.\n"); + + if (drv_disconnect) { + priv->media_connected = 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; + } + priv->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_htinfo = 0; + priv->max_amsdu = 0; + + priv->sec_info.ewpa_enabled = MFALSE; + priv->sec_info.wpa_enabled = MFALSE; + priv->sec_info.wpa2_enabled = MFALSE; + priv->wpa_ie_len = 0; + + 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)); + } + pmadapter->tx_lock_flag = MFALSE; + pmadapter->pps_uapsd_mode = MFALSE; + pmadapter->delay_null_pkt = MFALSE; + + 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 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; + pmlan_callbacks pcb = &pmadapter->callbacks; + mlan_event *pevent = (mlan_event *) event_buf; + + ENTER(); + + 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: + PRINTM(MEVENT, "EVENT: Deauthenticated\n"); + pmadapter->dbg.num_event_deauth++; + wlan_handle_disconnect_event(pmpriv); + + break; + + case EVENT_DISASSOCIATED: + PRINTM(MEVENT, "EVENT: Disassociated\n"); + pmadapter->dbg.num_event_disassoc++; + wlan_handle_disconnect_event(pmpriv); + break; + + case EVENT_LINK_LOST: + PRINTM(MEVENT, "EVENT: Link lost\n"); + 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_BG_SCAN_REPORT: + PRINTM(MEVENT, "EVENT: BGS_REPORT\n"); + /* 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; + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_802_11_BG_SCAN_QUERY, + HostCmd_ACT_GEN_GET, 0, MNULL, 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; + } + pmpriv->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); + /* To be handled for 'chanswann' private command */ + 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); + 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); + } 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 (%#x)\n", eventcause); + ret = wlan_handle_event_ext_scan_report(priv, pmbuf); + break; + case EVENT_MEAS_REPORT_RDY: + PRINTM(MEVENT, "EVENT: Measurement Report Ready (%#x)\n", eventcause); + 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"); + wlan_recv_event(pmpriv, MLAN_EVENT_ID_FW_BCN_RSSI_LOW, MNULL); + 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"); + wlan_recv_event(pmpriv, MLAN_EVENT_ID_FW_BCN_RSSI_HIGH, MNULL); + 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"); + wlan_prepare_cmd(pmpriv, HostCmd_CMD_11N_ADDBA_RSP, + HostCmd_ACT_GEN_SET, 0, MNULL, pmadapter->event_body); + break; + case EVENT_DELBA: + PRINTM(MEVENT, "EVENT: DELBA Request\n"); + wlan_11n_delete_bastream(pmpriv, pmadapter->event_body); + break; + case EVENT_BA_STREAM_TIMEOUT: + PRINTM(MEVENT, "EVENT: BA Stream timeout\n"); + wlan_11n_ba_stream_timeout(pmpriv, + (HostCmd_DS_11N_BATIMEOUT *) pmadapter-> + event_body); + 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); + wlan_recv_event(pmpriv, MLAN_EVENT_ID_FW_BW_CHANGED, pevent); + break; + +#ifdef WIFI_DIRECT_SUPPORT + case EVENT_WIFIDIRECT_GENERIC_EVENT: + case EVENT_WIFIDIRECT_SERVICE_DISCOVERY: + 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_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_FW_REMAIN_ON_CHAN_EXPIRED, MNULL); + break; +#endif /* WIFI_DIRECT_SUPPORT */ + + 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/sd8797/mlan/mlan_sta_ioctl.c b/drivers/net/wireless/sd8797/mlan/mlan_sta_ioctl.c new file mode 100644 index 000000000000..f5e2edc56b09 --- /dev/null +++ b/drivers/net/wireless/sd8797/mlan/mlan_sta_ioctl.c @@ -0,0 +1,5257 @@ +/** @file mlan_sta_ioctl.c + * + * @brief This file contains the functions for station ioctl. + * + * Copyright (C) 2008-2011, 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" + +/******************************************************** + 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) +{ + mlan_ds_encrypt_key encrypt_key; + + ENTER(); + + if (pmpriv->aes_key.key_param_set.key_len != WPA_AES_KEY_LEN) { + LEAVE(); + return; + } + + memset(pmpriv->adapter, &encrypt_key, 0, sizeof(mlan_ds_encrypt_key)); + encrypt_key.key_len = WPA_AES_KEY_LEN; + encrypt_key.key_index = MLAN_KEY_INDEX_UNICAST; + memcpy(pmpriv->adapter, encrypt_key.key_material, + pmpriv->aes_key.key_param_set.key, encrypt_key.key_len); + wlan_prepare_cmd(pmpriv, HostCmd_CMD_802_11_KEY_MATERIAL, + HostCmd_ACT_GEN_SET, KEY_INFO_ENABLED, MNULL, + &encrypt_key); + encrypt_key.key_index &= ~MLAN_KEY_INDEX_UNICAST; + wlan_prepare_cmd(pmpriv, HostCmd_CMD_802_11_KEY_MATERIAL, + HostCmd_ACT_GEN_SET, + KEY_INFO_ENABLED, MNULL, &encrypt_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 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 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; + + /* 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; + 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)); + + /* 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))); + + 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_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.hw_dev_mcs_support = + pmadapter->hw_dev_mcs_support; + 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; + } + + /* 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 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.sec_chan_offset; + /* + * 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; + } + if ((adhoc_band & BAND_GN) + || (adhoc_band & BAND_AN) + ) { + pmadapter->adhoc_11n_enabled = MTRUE; + } else { + pmadapter->adhoc_11n_enabled = MFALSE; + } + } else { + radio_cfg->param.band_cfg.config_bands = pmpriv->config_bands; /* Infra + Bands + */ + radio_cfg->param.band_cfg.adhoc_start_band = pmadapter->adhoc_start_band; /* Adhoc + Band + */ + radio_cfg->param.band_cfg.adhoc_channel = pmpriv->adhoc_channel; /* Adhoc + Channel + */ + radio_cfg->param.band_cfg.fw_bands = pmadapter->fw_bands; /* FW + support + Bands + */ + PRINTM(MINFO, "Global config band = %d\n", pmadapter->config_bands); + radio_cfg->param.band_cfg.sec_chan_offset = pmadapter->chan_bandwidth; /* adhoc + channel + 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; +#ifdef WIFI_DIRECT_SUPPORT + case MLAN_OID_REMAIN_CHAN_CFG: + status = wlan_radio_ioctl_remain_chan_cfg(pmadapter, pioctl_req); + break; +#endif + 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; + mlan_status ret = MLAN_STATUS_SUCCESS; + ENTER(); + bss = (mlan_ds_bss *) pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_GET) { + pioctl_req->data_read_written = + MLAN_MAC_ADDR_LENGTH + MLAN_SUB_COMMAND_SIZE; + memcpy(pmadapter, &bss->param.mac_addr, pmpriv->curr_addr, + MLAN_MAC_ADDR_LENGTH); + ret = MLAN_STATUS_SUCCESS; + goto exit; + } + + 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, + HostCmd_ACT_GEN_SET, + 0, (t_void *) pioctl_req, MNULL); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + exit: + 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); + } + } + } + } + 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 = pmadapter->priv[pioctl_req->bss_index]; + 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; + } + 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(); + + /* 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->port_open = MFALSE; + } + pmpriv->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) { + PRINTM(MINFO, "SSID found in scan list ... associating...\n"); + + /* Clear any past association response stored for application + retrieval */ + pmpriv->assoc_rsp_size = 0; + 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, %02x:%02x:%02x:%02x:%02x:%02x, idx=%d\n", + bss->param.ssid_bssid.ssid.ssid, + bss->param.ssid_bssid.bssid[0], + bss->param.ssid_bssid.bssid[1], + bss->param.ssid_bssid.bssid[2], + bss->param.ssid_bssid.bssid[3], + bss->param.ssid_bssid.bssid[4], + bss->param.ssid_bssid.bssid[5], + (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"); + 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); + 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.bssid); + + 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 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 embe + * + * @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_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_pp, 0, sizeof(mlan_ds_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, &sec_pp.ssid, &pbss_desc->ssid, + sizeof(mlan_802_11_ssid)); + } else + memcpy(pmadapter, &sec_pp.bssid, &ssid_bssid->bssid, + MLAN_MAC_ADDR_LENGTH); + } else { + memcpy(pmadapter, &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, &sec_pp); + + 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; + + 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; + } + } + ret = wlan_find_bss(pmpriv, pioctl_req); + + 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: + status = wlan_bss_ioctl_bss_role(pmadapter, pioctl_req); + break; +#endif +#ifdef WIFI_DIRECT_SUPPORT + case MLAN_OID_WIFI_DIRECT_MODE: + status = wlan_bss_ioctl_wifi_direct_mode(pmadapter, pioctl_req); + break; +#endif + 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) ? + pmadapter->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 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 + */ +static 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 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_u16 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_u16) 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 Get modulation class from rate index + * + * @param pmadapter A pointer to mlan_adapter structure + * @param rate_index Rate index + * + * @return 0 fail, otherwise return modulation class + */ +static int +wlan_get_modulation_class(pmlan_adapter pmadapter, int rate_index) +{ + int tx_mcs_supp = GET_TXMCSSUPP(pmadapter->usr_dev_mcs_support); + ENTER(); + if (rate_index >= MLAN_RATE_INDEX_HRDSSS0 && + rate_index <= MLAN_RATE_INDEX_HRDSSS3) { + LEAVE(); + return MOD_CLASS_HR_DSSS; + } else if (rate_index >= MLAN_RATE_INDEX_OFDM0 && + rate_index <= MLAN_RATE_INDEX_OFDM7) { + LEAVE(); + return MOD_CLASS_OFDM; + } else if (rate_index >= MLAN_RATE_INDEX_MCS0 && + rate_index <= MLAN_RATE_INDEX_MCS127) { + if ((rate_index > MLAN_RATE_INDEX_MCS7 && + rate_index <= MLAN_RATE_INDEX_MCS15) && (tx_mcs_supp < 2)) { + PRINTM(MERROR, + "HW don't support 2x2, rate_index=%d hw_mcs_supp=0x%x\n", + rate_index, pmadapter->usr_dev_mcs_support); + LEAVE(); + return 0; + } + LEAVE(); + return MOD_CLASS_HT; + } + PRINTM(MERROR, "Invalid rate index = %d supplied!\n", rate_index); + + LEAVE(); + return 0; +} + +/** + * @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; + int mod_class; + t_u32 data[4]; + t_u8 ht_bw; + + 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; + memcpy(pmadapter, (t_u8 *) & data, + (t_u8 *) power->param.power_ext.power_data, sizeof(data)); + switch (power->param.power_ext.len) { + case 1: + if (data[0] == 0xFF) + txp_cfg->mode = 0; + else { + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_FAILURE; + } + break; + case 2: + case 4: + ht_bw = (data[0] & TX_RATE_HT_BW40_BIT) ? HT_BW_40 : HT_BW_20; + data[0] &= ~TX_RATE_HT_BW40_BIT; + if (!(mod_class = wlan_get_modulation_class(pmadapter, data[0]))) { + pioctl_req->status_code = MLAN_ERROR_CMD_RESP_FAIL; + ret = MLAN_STATUS_FAILURE; + break; + } + if (ht_bw && mod_class != MOD_CLASS_HT) { + pioctl_req->status_code = MLAN_ERROR_CMD_RESP_FAIL; + ret = MLAN_STATUS_FAILURE; + break; + } + 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->modulation_class = (t_u8) mod_class; + pg->first_rate_code = (t_u8) data[0]; + pg->last_rate_code = (t_u8) data[0]; + if (mod_class == MOD_CLASS_OFDM) { + pg->first_rate_code = (t_u8) (data[0] - MLAN_RATE_INDEX_OFDM0); + pg->last_rate_code = (t_u8) (data[0] - MLAN_RATE_INDEX_OFDM0); + } else if (mod_class == MOD_CLASS_HT) { + pg->first_rate_code = (t_u8) (data[0] - MLAN_RATE_INDEX_MCS0); + pg->last_rate_code = (t_u8) (data[0] - MLAN_RATE_INDEX_MCS0); + pg->ht_bandwidth = ht_bw; + } + pg->power_min = (t_s8) data[1]; + pg->power_max = (t_s8) data[1]; + if (power->param.power_ext.len == 4) { + pg->power_max = (t_s8) data[2]; + pg->power_step = (t_s8) data[3]; + } + break; + default: + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_FAILURE; + break; + } + 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 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 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: + 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_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; + default: + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + status = MLAN_STATUS_FAILURE; + break; + } + LEAVE(); + return status; +} + +/** + * @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; + } + + 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 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 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; +} + +/** + * @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 + */ +static 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) { + 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_CONFIG: + status = wlan_wmm_ioctl_queue_config(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; + 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(pmadapter); + } + } + } + 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; + if (pmpriv->sec_info.authentication_mode == MLAN_AUTH_MODE_NETWORKEAP) + wlan_set_wpa_ie_helper(pmpriv, MNULL, 0); + } + 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 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_param_set.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; + + 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; + } else + 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)); + } + } else { + if (sec->param.encrypt_key.key_len) { + 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) { + 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 (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; + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_802_11_KEY_MATERIAL, + HostCmd_ACT_GEN_SET, + 0, (t_void *) pioctl_req, MNULL); + } else { + if (pwep_key->key_length) { + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_802_11_KEY_MATERIAL, + HostCmd_ACT_GEN_SET, 0, MNULL, MNULL); + 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; + t_u8 broadcast_mac_addr[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + t_u8 remove_key = MFALSE; + + 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 ((pmpriv->bss_mode == MLAN_BSS_MODE_IBSS) && + pmpriv->sec_info.wpa_enabled) { + /* + * IBSS/WPA-None uses only one key (Group) for both receiving and + * sending unicast and multicast packets. + */ + /* Send the key as PTK to firmware */ + sec->param.encrypt_key.key_index = MLAN_KEY_INDEX_UNICAST; + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_802_11_KEY_MATERIAL, + HostCmd_ACT_GEN_SET, + KEY_INFO_ENABLED, + MNULL, &sec->param.encrypt_key); + if (ret) + goto exit; + + /* Send the key as GTK to firmware */ + sec->param.encrypt_key.key_index = ~MLAN_KEY_INDEX_UNICAST; + } + + if (sec->param.encrypt_key.key_len == WPA_AES_KEY_LEN) { + /** back up adhoc AES key */ + memset(pmpriv->adapter, pmpriv->aes_key.key_param_set.key, 0, + sizeof(pmpriv->aes_key.key_param_set.key)); + pmpriv->aes_key.key_param_set.key_len = sec->param.encrypt_key.key_len; + memcpy(pmpriv->adapter, pmpriv->aes_key.key_param_set.key, + sec->param.encrypt_key.key_material, + pmpriv->aes_key.key_param_set.key_len); + } + + /** 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; + remove_key = MFALSE; + } else { + PRINTM(MINFO, "Adhoc AES Disabled.\n"); + pmpriv->adhoc_aes_enabled = MFALSE; + /** clear adhoc AES key */ + remove_key = MTRUE; + pmpriv->aes_key.key_param_set.key_len = 0; + } + } + + if (memcmp + (pmadapter, sec->param.encrypt_key.mac_addr, broadcast_mac_addr, + MLAN_MAC_ADDR_LENGTH)) + sec->param.encrypt_key.key_index = MLAN_KEY_INDEX_UNICAST; + + if (remove_key == MTRUE) { + /* Send request to firmware */ + 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); + } else { + /* Send request to firmware */ + 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; + + 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_param_set.key_len == WPA_AES_KEY_LEN)) { + HEXDUMP("Get ADHOCAES Key", pmpriv->aes_key.key_param_set.key, + WPA_AES_KEY_LEN); + memcpy(pmadapter, sec->param.encrypt_key.key_material, + pmpriv->aes_key.key_param_set.key, 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_param_set.key, MIN(MLAN_MAX_KEY_LENGTH, + pmpriv->aes_key. + key_param_set. + key_len)); + sec->param.encrypt_key.key_len = + MIN(MLAN_MAX_KEY_LENGTH, pmpriv->aes_key.key_param_set.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 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 + */ +static 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; + BSSDescriptor_t *pbss_desc; + int i = 0; + + ENTER(); + + sec = (mlan_ds_sec_cfg *) pioctl_req->pbuf; + 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 (sec->param.passphrase.psk_type == MLAN_PSK_QUERY) { + if (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 + 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, &sec->param.passphrase); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @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]; + + ENTER(); + + if (pmpriv->media_connected != MTRUE) { + 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_SUPPLICANT_PROFILE, + HostCmd_ACT_GEN_GET_CURRENT, + 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); + } else if (!ie_data_ptr) { + /* MNULL check */ + ret = MLAN_STATUS_FAILURE; + } else { + + pvendor_ie = (IEEEtypes_VendorHeader_t *) ie_data_ptr; + /* 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 { + 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 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 + */ +static 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; + 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 *) & reg_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 + */ +static 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 *) & reg_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 + */ +static 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 *) & reg_mem->param.mem_rw); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @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; + + 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; + } + + 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 */ + ret = wlan_11h_issue_radar_detect(pmpriv, pioctl_req, + pmpriv->adhoc_channel); + 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 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 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 + */ +static 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 { + 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; + } + 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 Perform warm reset + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING + */ +static 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]; + t_s32 i = 0; + + ENTER(); + + /** 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]); + } + + /* Initialize adapter structure */ + wlan_init_adapter(pmadapter); + + /* Initialize private structures */ + for (i = 0; i < pmadapter->priv_num; i++) { + if (pmadapter->priv[i]) { + wlan_init_priv(pmadapter->priv[i]); + } + } + + /* Restart the firmware */ + wlan_prepare_cmd(pmpriv, HostCmd_CMD_FUNC_SHUTDOWN, + HostCmd_ACT_GEN_SET, 0, MNULL, MNULL); + + /* 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); + pmadapter->pwarm_reset_ioctl_req = pioctl_req; + pmpriv->ops.init_cmd(pmpriv, MTRUE); + + LEAVE(); + return MLAN_STATUS_PENDING; +} + +#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 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 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 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 39 +/** + * @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 + * + * @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) +{ + 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, 0x30, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x01, 0xff, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x41, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, + 0x06, 0x02, 0x02, 0x14, 0x00, 0x00, 0x00, 0x01, + 0x41, 0x44, 0x01, 0x00, 0x00, 0x00, 0x01, 0xc0, + 0xa8, 0x00, 0x68, 0x04, 0x02, 0x2e, 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; + memcpy(pmpriv->adapter, filter, fltr_buf, sizeof(fltr_buf)); + memcpy(pmpriv->adapter, &filter[FLTR_BUF_IP_OFFSET], &ipv4_addr, + sizeof(ipv4_addr)); + buf_len += sizeof(fltr_buf); + } + 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; + + 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 */ + memcpy(pmadapter, &ipv4_addr, misc->param.ipaddr_cfg.ip_addr[0], + sizeof(t_u32)); + + if (misc->param.ipaddr_cfg.op_code != MLAN_IPADDR_OP_IP_REMOVE && + !ipv4_addr) { + 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); + 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); + else if (pmpriv->op_code & MLAN_IPADDR_OP_AUTO_ARP_RESP) + ret = wlan_ipaddr_auto_arp_resp(pmadapter, pioctl_req, 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 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->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; + } + if ((pioctl_req == MNULL) || (pioctl_req->pbuf == MNULL)) { + PRINTM(MERROR, "Request buffer not found!\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + 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: + status = wlan_misc_ioctl_warm_reset(pmadapter, pioctl_req); + 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_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_CUSTOM_IE: + status = wlan_misc_ioctl_custom_ie_list(pmadapter, pioctl_req, MTRUE); + 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_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; + 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) + 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 (pmpriv->scan_block && pioctl_req->action == MLAN_ACT_SET) { + PRINTM(MINFO, "Scan is blocked during association...\n"); + wlan_recv_event(pmpriv, MLAN_EVENT_ID_DRV_SCAN_REPORT, MNULL); + LEAVE(); + return status; + } + 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: + wlan_flush_scan_queue(pmadapter); + 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 { + 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; + } + } + + 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; + + 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(MERROR, "Can not find bssid %02x:%02x:%02x:%02x:%02x:%02x\n", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + 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)); + /* 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(MERROR, "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); + /* 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); + } + + 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; + default: + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + status = MLAN_STATUS_FAILURE; + break; + } + LEAVE(); + return status; +} diff --git a/drivers/net/wireless/sd8797/mlan/mlan_sta_rx.c b/drivers/net/wireless/sd8797/mlan/mlan_sta_rx.c new file mode 100644 index 000000000000..eb1bd845a587 --- /dev/null +++ b/drivers/net/wireless/sd8797/mlan/mlan_sta_rx.c @@ -0,0 +1,287 @@ +/** @file mlan_sta_rx.c + * + * @brief This file contains the handling of RX in MLAN + * module. + * + * Copyright (C) 2008-2011, 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" + +/******************************************************** + 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 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 }; + + 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 dbgType; + dbgType = *(t_u8 *) & prx_pkt->eth803_hdr; + if (dbgType == DBG_TYPE_SMALL) { + PRINTM(MFW_D, "\n"); + DBG_HEXDUMP(MFW_D, "FWDBG", + (t_s8 *) ((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)); + + /* 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; + + priv->rxpd_htinfo = prx_pd->ht_info; + pmadapter->callbacks.moal_get_system_time(pmadapter->pmoal_handle, + &pmbuf->out_ts_sec, + &pmbuf->out_ts_usec); + 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) { + 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; + ENTER(); + + prx_pd = (RxPD *) (pmbuf->pbuf + pmbuf->data_offset); + /* Endian conversion */ + endian_convert_RxPD(prx_pd); + rx_pkt_type = prx_pd->rx_pkt_type; + prx_pkt = (RxPacketHdr_t *) ((t_u8 *) prx_pd + 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 */ + 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)); + 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) || + 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)) { + memcpy(pmadapter, ta, prx_pkt->eth803_hdr.src_addr, + MLAN_MAC_ADDR_LENGTH); + } 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); + } + + /* Reorder and send to OS */ + if ((ret = mlan_11n_rxreorder_pkt(priv, prx_pd->seq_num, + prx_pd->priority, ta, + (t_u8) prx_pd->rx_pkt_type, + (void *) pmbuf)) || + (rx_pkt_type == PKT_TYPE_BAR) + ) { + wlan_free_mlan_buffer(pmadapter, pmbuf); + } + + done: + + LEAVE(); + return (ret); +} diff --git a/drivers/net/wireless/sd8797/mlan/mlan_sta_tx.c b/drivers/net/wireless/sd8797/mlan/mlan_sta_tx.c new file mode 100644 index 000000000000..f509468d586f --- /dev/null +++ b/drivers/net/wireless/sd8797/mlan/mlan_sta_tx.c @@ -0,0 +1,276 @@ +/** @file mlan_sta_tx.c + * + * @brief This file contains the handling of data packet + * transmission in MLAN module. + * + * Copyright (C) 2008-2011, 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) + INTF_HEADER_LEN + DMA_ALIGNMENT)) { + PRINTM(MERROR, "not enough space for TxPD: %d\n", 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(TxPD) - INTF_HEADER_LEN; + head_ptr = (t_u8 *) ((t_ptr) head_ptr & ~((t_ptr) (DMA_ALIGNMENT - 1))); + + plocal_tx_pd = (TxPD *) (head_ptr + INTF_HEADER_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; + } + } + + /* 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; + } + + 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 = priv->adapter; + 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; + t_u32 sec, usec; + + ENTER(); + + 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, MTRUE); + 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; + ptr = pmbuf->pbuf + pmbuf->data_offset; + pmbuf->data_len = sizeof(TxPD) + INTF_HEADER_LEN; + ptx_pd = (TxPD *) (ptr + INTF_HEADER_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: + pmadapter->data_sent = MTRUE; + /* Fall through FAILURE handling */ + case MLAN_STATUS_FAILURE: + 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: + break; + default: + break; + } + + pmadapter->callbacks.moal_get_system_time(pmadapter->pmoal_handle, &sec, + &usec); + PRINTM(MDATA, "%lu.%06lu : Null data => FW\n", sec, usec); + DBG_HEXDUMP(MDAT_D, "Null data", ptr, sizeof(TxPD) + INTF_HEADER_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 + && !IS_COMMAND_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/sd8797/mlan/mlan_txrx.c b/drivers/net/wireless/sd8797/mlan/mlan_txrx.c new file mode 100644 index 000000000000..f243fc191800 --- /dev/null +++ b/drivers/net/wireless/sd8797/mlan/mlan_txrx.c @@ -0,0 +1,374 @@ +/** + * @file mlan_txrx.c + * + * @brief This file contains the handling of TX/RX in MLAN + * + * Copyright (C) 2009-2011, 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 = wlan_get_priv(pmadapter, MLAN_BSS_ROLE_ANY); + RxPD *prx_pd; + t_u32 sec, usec; + + 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); + pmbuf->bss_index = priv->bss_index; + pmadapter->callbacks.moal_get_system_time(pmadapter->pmoal_handle, &sec, + &usec); + PRINTM_NETINTF(MDATA, priv); + PRINTM(MDATA, "%lu.%06lu : Data <= FW\n", sec, usec); + ret = priv->ops.process_rx_packet(pmadapter, pmbuf); + + 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; + t_u32 sec, usec; +#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 + INTF_HEADER_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: + pmadapter->data_sent = MFALSE; + DBG_HEXDUMP(MDAT_D, "Tx", head_ptr + INTF_HEADER_LEN, + MIN(pmbuf->data_len + sizeof(TxPD), MAX_DATA_DUMP_LEN)); + break; + case MLAN_STATUS_SUCCESS: + DBG_HEXDUMP(MDAT_D, "Tx", head_ptr + INTF_HEADER_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)) { + pmadapter->callbacks.moal_get_system_time(pmadapter->pmoal_handle, &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); + + 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 { + if (pmbuf->flags & MLAN_BUF_FLAG_BRIDGE_BUF) { + pmadapter->pending_bridge_pkts--; + } + /* 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_private pmp; + pmlan_callbacks pcb; + pmlan_buffer pmbuf_parent; + + ENTER(); + + MASSERT(pmadapter && pmbuf); + pcb = &pmadapter->callbacks; + MASSERT(pmbuf->bss_index < pmadapter->priv_num); + + pmp = pmadapter->priv[pmbuf->bss_index]; + + if (pmbuf->pparent) { + pmbuf_parent = pmbuf->pparent; + + pmadapter->callbacks.moal_spin_lock(pmadapter->pmoal_handle, + pmp->rx_pkt_lock); + --pmbuf_parent->use_count; + if (!pmbuf_parent->use_count) { + pmadapter->callbacks.moal_spin_unlock(pmadapter->pmoal_handle, + pmp->rx_pkt_lock); + wlan_free_mlan_buffer(pmadapter, pmbuf_parent); + } else { + pmadapter->callbacks.moal_spin_unlock(pmadapter->pmoal_handle, + pmp->rx_pkt_lock); + } + 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) +{ + ENTER(); + + if (pmbuf->buf_type != MLAN_BUF_TYPE_RAW_DATA) { + pmbuf->buf_type = MLAN_BUF_TYPE_DATA; + } + + util_enqueue_list_tail(pmadapter->pmoal_handle, &pmadapter->bypass_txq, + (pmlan_linked_list) pmbuf, + pmadapter->callbacks.moal_spin_lock, + pmadapter->callbacks.moal_spin_unlock); + 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) +{ + t_u8 q_empty = MTRUE; + pmlan_callbacks pcb = &pmadapter->callbacks; + ENTER(); + + q_empty = (util_peek_list(pmadapter->pmoal_handle, &pmadapter->bypass_txq, + pcb->moal_spin_lock, + pcb->moal_spin_unlock)) ? MFALSE : MTRUE; + + LEAVE(); + return q_empty; +} + +/** + * @brief Clean up the By-pass TX queue + * + * @param pmadapter Pointer to the mlan_adapter driver data struct + * + * @return N/A + */ +t_void +wlan_cleanup_bypass_txq(mlan_adapter * pmadapter) +{ + pmlan_buffer pmbuf; + ENTER(); + + pmadapter->callbacks.moal_spin_lock(pmadapter->pmoal_handle, + pmadapter->bypass_txq.plock); + + while ((pmbuf = + (pmlan_buffer) util_peek_list(pmadapter->pmoal_handle, + &pmadapter->bypass_txq, MNULL, + MNULL))) { + util_unlink_list(pmadapter->pmoal_handle, &pmadapter->bypass_txq, + (pmlan_linked_list) pmbuf, MNULL, MNULL); + wlan_write_data_complete(pmadapter, pmbuf, MLAN_STATUS_FAILURE); + } + + pmadapter->callbacks.moal_spin_unlock(pmadapter->pmoal_handle, + pmadapter->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; + + ENTER(); + + if ((pmbuf = (pmlan_buffer) util_dequeue_list(pmadapter->pmoal_handle, + &pmadapter->bypass_txq, + pmadapter->callbacks. + moal_spin_lock, + pmadapter->callbacks. + moal_spin_unlock))) { + 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, + &pmadapter->bypass_txq, + (pmlan_linked_list) pmbuf, + pmadapter->callbacks.moal_spin_lock, + pmadapter->callbacks.moal_spin_unlock); + } + } else { + PRINTM(MINFO, "Nothing to send\n"); + } + + LEAVE(); +} diff --git a/drivers/net/wireless/sd8797/mlan/mlan_uap.h b/drivers/net/wireless/sd8797/mlan/mlan_uap.h new file mode 100644 index 000000000000..c2d2800f1a2d --- /dev/null +++ b/drivers/net/wireless/sd8797/mlan/mlan_uap.h @@ -0,0 +1,104 @@ +/** @file mlan_uap.h + * + * @brief This file contains related macros, enum, and struct + * of uap functionalities + * + * Copyright (C) 2009-2011, 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); \ + } + +/** 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); \ + } +#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 */ + +/** Band config 5GHz */ +#define UAP_BAND_CONFIG_5GHZ 0x01 + +/** 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 + +mlan_status wlan_uap_get_channel(IN pmlan_private pmpriv); + +mlan_status wlan_uap_set_channel(IN pmlan_private pmpriv, + IN t_u8 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/sd8797/mlan/mlan_uap_cmdevent.c b/drivers/net/wireless/sd8797/mlan/mlan_uap_cmdevent.c new file mode 100644 index 000000000000..8c041c8159c2 --- /dev/null +++ b/drivers/net/wireless/sd8797/mlan/mlan_uap_cmdevent.c @@ -0,0 +1,3021 @@ +/** @file mlan_uap_cmdevent.c + * + * @brief This file contains the handling of AP mode command and event + * + * Copyright (C) 2009-2011, 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" + +/******************************************************** + 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 void +uap_process_cmdresp_error(mlan_private * pmpriv, HostCmd_DS_COMMAND * resp, + mlan_ioctl_req * pioctl_buf) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + + ENTER(); + + 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 + */ + wlan_insert_cmd_to_free_q(pmadapter, pmadapter->curr_cmd); + + wlan_request_cmd_lock(pmadapter); + pmadapter->curr_cmd = MNULL; + wlan_release_cmd_lock(pmadapter); + + 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) +{ + HostCmd_DS_802_11_HS_CFG_ENH *phs_cfg = &cmd->params.opt_hs_cfg; + + 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; + PRINTM(MCMND, "HS_CFG_CMD: condition:0x%x gpio:0x%x gap:0x%x\n", + phs_cfg->params.hs_config.conditions, + phs_cfg->params.hs_config.gpio, phs_cfg->params.hs_config.gap); + } + 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; + + 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: %02x:%02x:%02x:%02x:%02x:%02x pause=%d, pkts=%d\n", + tx_pause_tlv->peermac[0], tx_pause_tlv->peermac[1], + tx_pause_tlv->peermac[2], tx_pause_tlv->peermac[3], + tx_pause_tlv->peermac[4], tx_pause_tlv->peermac[5], + tx_pause_tlv->tx_pause, tx_pause_tlv->pkt_cnt); + if ((sta_ptr = wlan_get_station_entry(priv, tx_pause_tlv->peermac))) { + if (sta_ptr->tx_pause != tx_pause_tlv->tx_pause) { + sta_ptr->tx_pause = tx_pause_tlv->tx_pause; + wlan_updata_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.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.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 <= MTRUE) { + 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); + } + + 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); + } + + 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.band_cfg & BAND_CONFIG_ACS_MODE) == + BAND_CONFIG_MANUAL) && (bss->param.bss_config.channel > 0) && + (bss->param.bss_config.channel <= MLAN_MAX_CHANNEL)) || + (bss->param.bss_config.band_cfg & BAND_CONFIG_ACS_MODE)) { + 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->band_config = bss->param.bss_config.band_cfg; + 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->radio_type = + bss->param.bss_config.chan_list[i].band_config_type; + 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); + } + + 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); + } + } 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].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); + } + + 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 *chan_band_tlv = MNULL, *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; + mlan_ds_misc_custom_ie *cust_ie = MNULL; + mlan_ds_misc_cfg *misc = MNULL; + MrvlIEtypesHeader_t *ie_header = + (MrvlIEtypesHeader_t *) sys_config->tlv_buffer; + 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 (*(t_u16 *) pdata_buf) { + 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->band_config = pdat_tlv_cb->band_config; + 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; + 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_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_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.band_cfg = tlv_chan_band->band_config; + bss->param.bss_config.channel = tlv_chan_band->channel; + pmpriv->uap_state_chan_cb.band_config = tlv_chan_band->band_config; + 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].band_config_type = + pscan_chan->radio_type; + 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 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].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)); + } + + 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(); + + pmpriv->uap_state_chan_cb.band_config = 0; + 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_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_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; + max_mgmt_ie = + (tlvbuf_max_mgmt_ie *) (sys_config->tlv_buffer + cust_ie->len + + sizeof(MrvlIEtypesHeader_t)); + if ((pioctl_buf->action == MLAN_ACT_GET) && + (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)))); + 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.band_config = tlv_cb->band_config; + 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)) + 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); + 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; + 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 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; + t_u8 *psnmp_oid = MNULL; + t_u32 data; + t_u16 tlv_buf_left = 0; + t_u16 tlv_type = 0; + + ENTER(); + if (psnmp_mib->query_type == HostCmd_ACT_GEN_GET) { + if (!pioctl_buf) { + 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 (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 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; + MrvlIEtypes_MacAddr_t *tlv = MNULL; + const t_u8 bc_mac[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + t_u16 key_param_len = 0; + mlan_status ret = MLAN_STATUS_SUCCESS; + sta_node *sta_ptr = MNULL; + + ENTER(); + + 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_KeyParamSet_t)); + if (pkey->is_wapi_key) { + PRINTM(MINFO, "Set WAPI Key\n"); + pkey_material->key_param_set.key_type_id = + wlan_cpu_to_le16(KEY_TYPE_ID_WAPI); + if (cmd_oid == KEY_INFO_ENABLED) + pkey_material->key_param_set.key_info = + wlan_cpu_to_le16(KEY_INFO_WAPI_ENABLED); + else + pkey_material->key_param_set.key_info = + !(wlan_cpu_to_le16(KEY_INFO_WAPI_ENABLED)); + + pkey_material->key_param_set.key[0] = pkey->key_index; + if (!pmpriv->sec_info.wapi_key_on) + pkey_material->key_param_set.key[1] = 1; + else + pkey_material->key_param_set.key[1] = 0; /* set 0 when re-key */ + + if (0 != memcmp(pmpriv->adapter, pkey->mac_addr, bc_mac, sizeof(bc_mac))) { /* WAPI + pairwise + key: + unicast + */ + pkey_material->key_param_set.key_info |= + wlan_cpu_to_le16(KEY_INFO_WAPI_UNICAST); + if ((sta_ptr = wlan_add_station_entry(pmpriv, pkey->mac_addr))) { + PRINTM(MCMND, "station: wapi_key_on\n"); + sta_ptr->wapi_key_on = MTRUE; + } + } else { /* WAPI group key: multicast */ + pkey_material->key_param_set.key_info |= + wlan_cpu_to_le16(KEY_INFO_WAPI_MCAST); + pmpriv->sec_info.wapi_key_on = MTRUE; + } + pkey_material->key_param_set.type = + wlan_cpu_to_le16(TLV_TYPE_KEY_MATERIAL); + pkey_material->key_param_set.key_len = wlan_cpu_to_le16(WAPI_KEY_LEN); + memcpy(pmpriv->adapter, &pkey_material->key_param_set.key[2], + pkey->key_material, pkey->key_len); + pkey_material->key_param_set.length = + wlan_cpu_to_le16(WAPI_KEY_LEN + KEYPARAMSET_FIXED_LEN); + + key_param_len = + (WAPI_KEY_LEN + KEYPARAMSET_FIXED_LEN) + + sizeof(MrvlIEtypesHeader_t); + tlv = + (MrvlIEtypes_MacAddr_t *) ((t_u8 *) & pkey_material->key_param_set + + key_param_len); + tlv->header.type = wlan_cpu_to_le16(TLV_TYPE_STA_MAC_ADDRESS); + tlv->header.len = wlan_cpu_to_le16(MLAN_MAC_ADDR_LENGTH); + memcpy(pmpriv->adapter, tlv->mac, pkey->mac_addr, MLAN_MAC_ADDR_LENGTH); + cmd->size = + wlan_cpu_to_le16(key_param_len + sizeof(pkey_material->action) + + S_DS_GEN + sizeof(MrvlIEtypes_MacAddr_t)); + goto done; + } + if (pkey->key_len == WPA_AES_KEY_LEN) { + PRINTM(MCMND, "WPA_AES\n"); + pkey_material->key_param_set.key_type_id = + wlan_cpu_to_le16(KEY_TYPE_ID_AES); + if (cmd_oid == KEY_INFO_ENABLED) + pkey_material->key_param_set.key_info = + wlan_cpu_to_le16(KEY_INFO_AES_ENABLED); + else + pkey_material->key_param_set.key_info = + !(wlan_cpu_to_le16(KEY_INFO_AES_ENABLED)); + + if (memcmp(pmpriv->adapter, pkey->mac_addr, bc_mac, sizeof(bc_mac))) /* AES + pairwise + key: + unicast + */ + pkey_material->key_param_set.key_info |= + wlan_cpu_to_le16(KEY_INFO_AES_UNICAST); + else /* AES group key: multicast */ + pkey_material->key_param_set.key_info |= + wlan_cpu_to_le16(KEY_INFO_AES_MCAST); + } else if (pkey->key_len == WPA_TKIP_KEY_LEN) { + PRINTM(MCMND, "WPA_TKIP\n"); + pkey_material->key_param_set.key_type_id = + wlan_cpu_to_le16(KEY_TYPE_ID_TKIP); + pkey_material->key_param_set.key_info = + wlan_cpu_to_le16(KEY_INFO_TKIP_ENABLED); + + if (memcmp(pmpriv->adapter, pkey->mac_addr, bc_mac, sizeof(bc_mac))) /* TKIP + pairwise + key: + unicast + */ + pkey_material->key_param_set.key_info |= + wlan_cpu_to_le16(KEY_INFO_TKIP_UNICAST); + else /* TKIP group key: multicast */ + pkey_material->key_param_set.key_info |= + wlan_cpu_to_le16(KEY_INFO_TKIP_MCAST); + } + + if (pkey_material->key_param_set.key_type_id) { + pkey_material->key_param_set.type = + wlan_cpu_to_le16(TLV_TYPE_KEY_MATERIAL); + pkey_material->key_param_set.key_len = wlan_cpu_to_le16(pkey->key_len); + memcpy(pmpriv->adapter, pkey_material->key_param_set.key, + pkey->key_material, pkey->key_len); + pkey_material->key_param_set.length = + wlan_cpu_to_le16((t_u16) pkey->key_len + KEYPARAMSET_FIXED_LEN); + key_param_len = + (pkey->key_len + KEYPARAMSET_FIXED_LEN) + + sizeof(MrvlIEtypesHeader_t); + + tlv = + (MrvlIEtypes_MacAddr_t *) ((t_u8 *) & pkey_material->key_param_set + + key_param_len); + tlv->header.type = wlan_cpu_to_le16(TLV_TYPE_STA_MAC_ADDRESS); + tlv->header.len = wlan_cpu_to_le16(MLAN_MAC_ADDR_LENGTH); + memcpy(pmpriv->adapter, tlv->mac, pkey->mac_addr, MLAN_MAC_ADDR_LENGTH); + cmd->size = + wlan_cpu_to_le16(key_param_len + sizeof(pkey_material->action) + + S_DS_GEN + sizeof(MrvlIEtypes_MacAddr_t)); + } + 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; + + 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; + tlv++; + } + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @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 + */ +static 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; + 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_UAP_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; + 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; + 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; + } else { + PRINTM(MCMND, "STA doesn't support 11n\n"); + } + break; + } + } + tlv_buf_left -= (sizeof(MrvlIEtypesHeader_t) + tlv_len); + tlv = + (MrvlIEtypesHeader_t *) ((t_u8 *) tlv + tlv_len + + sizeof(MrvlIEtypesHeader_t)); + } + LEAVE(); + + return; +} + +/** 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 *pWmmParamIe = MNULL; + 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; + pWmmParamIe = (IEEEtypes_WmmParameter_t *) ((t_u8 *) tlv + 2); + pWmmParamIe->vend_hdr.len = (t_u8) tlv_len; + pWmmParamIe->vend_hdr.element_id = WMM_IE; + wlan_wmm_setup_queue_priorities(priv, pWmmParamIe); + } + } + 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); + } + 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); + } + + 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_UAP_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; +} + +/******************************************************** + 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); + 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, 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_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_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_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_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_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(t_u32) + 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; +#ifdef WIFI_DIRECT_SUPPORT + case HostCmd_CMD_802_11_REMAIN_ON_CHANNEL: + ret = + wlan_cmd_remain_on_channel(pmpriv, cmd_ptr, cmd_action, pdata_buf); + break; + 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_RF_ANTENNA: + ret = + wlan_cmd_802_11_rf_antenna(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; + + ENTER(); + + /* If the command is not successful, cleanup and return failure */ + if (resp->result != HostCmd_RESULT_OK) { + uap_process_cmdresp_error(pmpriv, resp, pioctl_buf); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + /* Command successful, handle response */ + switch (cmdresp_no) { + case HOST_CMD_APCMD_BSS_STOP: + pmpriv->uap_bss_started = MFALSE; + 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); + break; + case HOST_CMD_APCMD_BSS_START: + if (pmpriv->adapter->state_rdh.stage == RDH_RESTART_INTFS) + wlan_11h_radar_detected_callback((t_void *) pmpriv); + break; + case HOST_CMD_APCMD_SYS_RESET: + ret = wlan_uap_ret_sys_reset(pmpriv, resp, pioctl_buf); + + pmpriv->uap_bss_started = MFALSE; + wlan_11h_check_update_radar_det_state(pmpriv); + 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_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_CFG_DATA: + ret = wlan_ret_cfg_data(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_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_RECONFIGURE_TX_BUFF: + 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 = 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_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; +#ifdef WIFI_DIRECT_SUPPORT + case HostCmd_CMD_802_11_REMAIN_ON_CHANNEL: + ret = wlan_ret_remain_on_channel(pmpriv, resp, pioctl_buf); + break; + case HOST_CMD_WIFI_DIRECT_MODE_CONFIG: + ret = wlan_ret_wifi_direct_mode(pmpriv, resp, pioctl_buf); + break; +#endif + case HostCmd_CMD_802_11_RF_ANTENNA: + ret = wlan_ret_802_11_rf_antenna(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; + + ENTER(); + + /* 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); + break; + case EVENT_MICRO_AP_BSS_ACTIVE: + PRINTM(MEVENT, "EVENT: MICRO_AP_BSS_ACTIVE\n"); + pmpriv->media_connected = 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_delete_station_list(pmpriv); + 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: + PRINTM(MEVENT, "EVENT: MICRO_AP_STA_ASSOC\n"); + 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); + if (pmpriv->is_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->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); + pevent->event_id = MLAN_EVENT_ID_DRV_PASSTHRU; + break; + case EVENT_MICRO_AP_STA_DEAUTH: + PRINTM(MEVENT, "EVENT: MICRO_AP_STA_DEAUTH\n"); + 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); + if (pmpriv->is_11n_enabled) { + wlan_cleanup_reorder_tbl(pmpriv, sta_addr); + pmadapter->callbacks.moal_spin_lock(pmadapter->pmoal_handle, + pmpriv->wmm.ra_list_spinlock); + wlan_11n_cleanup_txbastream_tbl(pmpriv, sta_addr); + pmadapter->callbacks.moal_spin_unlock(pmadapter->pmoal_handle, + pmpriv->wmm.ra_list_spinlock); + } + 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"); + wlan_prepare_cmd(pmpriv, HostCmd_CMD_11N_ADDBA_RSP, + HostCmd_ACT_GEN_SET, 0, MNULL, pmadapter->event_body); + break; + case EVENT_DELBA: + PRINTM(MEVENT, "EVENT: DELBA Request\n"); + wlan_11n_delete_bastream(pmpriv, pmadapter->event_body); + break; + case EVENT_BA_STREAM_TIMEOUT: + PRINTM(MEVENT, "EVENT: BA Stream timeout\n"); + wlan_11n_ba_stream_timeout(pmpriv, + (HostCmd_DS_11N_BATIMEOUT *) pmadapter-> + event_body); + 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 (pmadapter->state_rdh.stage == RDH_OFF) { + pmadapter->state_rdh.stage = RDH_CHK_INTFS; + wlan_11h_radar_detected_handling(pmadapter); + } 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"); + 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); + + /* 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; +#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); + pevent->event_id = MLAN_EVENT_ID_FW_REMAIN_ON_CHAN_EXPIRED; + break; +#endif + 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 (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/sd8797/mlan/mlan_uap_ioctl.c b/drivers/net/wireless/sd8797/mlan/mlan_uap_ioctl.c new file mode 100644 index 000000000000..be78d72de25c --- /dev/null +++ b/drivers/net/wireless/sd8797/mlan/mlan_uap_ioctl.c @@ -0,0 +1,1393 @@ +/** @file mlan_uap_ioctl.c + * + * @brief This file contains the handling of AP mode ioctls + * + * Copyright (C) 2009-2011, 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; +} + +/** + * @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; + + 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->band_config & BAND_CONFIG_5GHZ) && + wlan_11h_radar_detect_required(pmpriv, puap_state_chan_cb->channel)) { + /* 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; + } + + /* 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) { + ret = wlan_uap_set_channel(pmpriv, + pmpriv->uap_state_chan_cb. + band_config, + puap_state_chan_cb->channel); + if (ret == MLAN_STATUS_SUCCESS) { + PRINTM(MMSG, "Radar found on channel %d," + "switch to new channel %d.\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 { + 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); + } + } + + /* 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]; + + ENTER(); + + /* 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; + + ret = wlan_uap_get_channel(pmpriv); + 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_AMPDU_DEF_TXWINSIZE; + pmpriv->add_ba_param.rx_win_size = MLAN_AMPDU_DEF_RXWINSIZE; + 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 = BA_STREAM_NOT_ALLOWED; + pmpriv->addba_reject[i] = ADDBA_RSP_STATUS_ACCEPT; + } + + /* 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 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 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_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.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; + + ENTER(); + /* clear callback now that we're here */ + puap_state_chan_cb->get_chan_callback = MNULL; + + cfg11d = (mlan_ds_11d_cfg *) puap_state_chan_cb->pioctl_req_curr->pbuf; + band = + (puap_state_chan_cb->band_config & BAND_CONFIG_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; + + 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; + /* 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->band_config & BAND_CONFIG_5GHZ) && + 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); + 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_FAILURE); + } + + 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]; + + 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; + } + + /* 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; + + 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->band_config & BAND_CONFIG_5GHZ) && + 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 t_u8 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.band_config = 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 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_snmp_mib *snmp = MNULL; + mlan_ds_11d_cfg *cfg11d = MNULL; + mlan_ds_11h_cfg *cfg11h = MNULL; + mlan_ds_radio_cfg *radiocfg = MNULL; + mlan_ds_rate *rate = MNULL; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + + 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) + status = wlan_bss_ioctl_bss_role(pmadapter, pioctl_req); +#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 + break; + 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_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.hw_dev_mcs_support = + pmadapter->hw_dev_mcs_support; + } + 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_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 + 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_INFO) { + status = wlan_get_pm_info(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_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); +#endif + 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); +#ifdef WIFI_DIRECT_SUPPORT + if (radiocfg->sub_command == MLAN_OID_REMAIN_CHAN_CFG) + status = wlan_radio_ioctl_remain_chan_cfg(pmadapter, pioctl_req); +#endif + if (radiocfg->sub_command == MLAN_OID_ANT_CFG) + status = wlan_radio_ioctl_ant_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); + break; + default: + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + break; + } + LEAVE(); + return status; +} diff --git a/drivers/net/wireless/sd8797/mlan/mlan_uap_txrx.c b/drivers/net/wireless/sd8797/mlan/mlan_uap_txrx.c new file mode 100644 index 000000000000..1dad10bce62a --- /dev/null +++ b/drivers/net/wireless/sd8797/mlan/mlan_uap_txrx.c @@ -0,0 +1,556 @@ +/** @file mlan_uap_txrx.c + * + * @brief This file contains AP mode transmit and receive functions + * + * Copyright (C) 2009-2011, 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" + +/******************************************************** + 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; + 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(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) { + if ((pmpriv = pmadapter->priv[j])) { + 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; + 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) + INTF_HEADER_LEN + + DMA_ALIGNMENT)) { + PRINTM(MERROR, "not enough space for UapTxPD: len=%d, offset=%d\n", + pmbuf->data_len, pmbuf->data_offset); + 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) - INTF_HEADER_LEN; + head_ptr = (t_u8 *) ((t_ptr) head_ptr & ~((t_ptr) (DMA_ALIGNMENT - 1))); + + plocal_tx_pd = (UapTxPD *) (head_ptr + INTF_HEADER_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]; + + /* 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 (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; + } + 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; + + ENTER(); + + prx_pd = (UapRxPD *) (pmbuf->pbuf + pmbuf->data_offset); + /* Endian conversion */ + uap_endian_convert_RxPD(prx_pd); + 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)); + wlan_free_mlan_buffer(pmadapter, pmbuf); + goto done; + } + + 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)) { + if ((sta_ptr = wlan_get_station_entry(priv, ta))) + 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 */ + if ((ret = mlan_11n_rxreorder_pkt(priv, prx_pd->seq_num, + prx_pd->priority, ta, + (t_u8) prx_pd->rx_pkt_type, + (void *) pmbuf)) + || (rx_pkt_type == PKT_TYPE_BAR)) { + wlan_free_mlan_buffer(pmadapter, pmbuf); + } + done: + if (priv->is_11n_enabled && + (pmadapter->pending_bridge_pkts >= RX_MED_THRESHOLD)) + wlan_send_delba_to_all_in_reorder_tbl(priv); + 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 %02x:%02x:%02x:%02x:%02x:%02x\n", + prx_pkt->eth803_hdr.dest_addr[0], prx_pkt->eth803_hdr.dest_addr[1], + prx_pkt->eth803_hdr.dest_addr[2], prx_pkt->eth803_hdr.dest_addr[3], + prx_pkt->eth803_hdr.dest_addr[4], prx_pkt->eth803_hdr.dest_addr[5]); + + /* 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 */ + if ((newbuf = + wlan_alloc_mlan_buffer(pmadapter, MLAN_TX_DATA_BUF_SIZE_2K, 0, + MTRUE))) { + 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) + INTF_HEADER_LEN + DMA_ALIGNMENT); + pmadapter->pending_bridge_pkts++; + 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 (pmadapter->pending_bridge_pkts > RX_HIGH_THRESHOLD) + wlan_drop_tx_pkts(priv); + } + } + } else { + if ((!(priv->pkt_fwd & PKT_FWD_INTRA_UCAST)) && + (wlan_get_station_entry(priv, prx_pkt->eth803_hdr.dest_addr))) { + /* Intra BSS packet */ + if ((newbuf = + wlan_alloc_mlan_buffer(pmadapter, MLAN_TX_DATA_BUF_SIZE_2K, 0, + MTRUE))) { + 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) + INTF_HEADER_LEN + DMA_ALIGNMENT); + pmadapter->pending_bridge_pkts++; + 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 (pmadapter->pending_bridge_pkts > RX_HIGH_THRESHOLD) + wlan_drop_tx_pkts(priv); + } + 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 %02x:%02x:%02x:%02x:%02x:%02x\n", + prx_pkt->eth803_hdr.dest_addr[0], + prx_pkt->eth803_hdr.dest_addr[1], + prx_pkt->eth803_hdr.dest_addr[2], + prx_pkt->eth803_hdr.dest_addr[3], + prx_pkt->eth803_hdr.dest_addr[4], + prx_pkt->eth803_hdr.dest_addr[5]); + 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 %02x:%02x:%02x:%02x:%02x:%02x\n", + prx_pkt->eth803_hdr.dest_addr[0], prx_pkt->eth803_hdr.dest_addr[1], + prx_pkt->eth803_hdr.dest_addr[2], prx_pkt->eth803_hdr.dest_addr[3], + prx_pkt->eth803_hdr.dest_addr[4], prx_pkt->eth803_hdr.dest_addr[5]); + + /* 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 */ + if ((newbuf = + wlan_alloc_mlan_buffer(pmadapter, MLAN_TX_DATA_BUF_SIZE_2K, 0, + MTRUE))) { + 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) + INTF_HEADER_LEN + DMA_ALIGNMENT); + pmadapter->pending_bridge_pkts++; + 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 (pmadapter->pending_bridge_pkts > RX_HIGH_THRESHOLD) + wlan_drop_tx_pkts(priv); + } + } + } 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; + pmadapter->pending_bridge_pkts++; + wlan_wmm_add_buf_txqueue(pmadapter, pmbuf); + if (pmadapter->pending_bridge_pkts > RX_HIGH_THRESHOLD) + wlan_drop_tx_pkts(priv); + goto done; + } else if (MLAN_STATUS_FAILURE == + wlan_check_unicast_packet(priv, + prx_pkt->eth803_hdr.dest_addr)) { + PRINTM(MDATA, "Drop Pkts: Rx dest %02x:%02x:%02x:%02x:%02x:%02x\n", + prx_pkt->eth803_hdr.dest_addr[0], + prx_pkt->eth803_hdr.dest_addr[1], + prx_pkt->eth803_hdr.dest_addr[2], + prx_pkt->eth803_hdr.dest_addr[3], + prx_pkt->eth803_hdr.dest_addr[4], + prx_pkt->eth803_hdr.dest_addr[5]); + 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(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); + } + done: + LEAVE(); + return ret; +} diff --git a/drivers/net/wireless/sd8797/mlan/mlan_util.h b/drivers/net/wireless/sd8797/mlan/mlan_util.h new file mode 100644 index 000000000000..c5231f6b775e --- /dev/null +++ b/drivers/net/wireless/sd8797/mlan/mlan_util.h @@ -0,0 +1,526 @@ +/** @file mlan_util.h + * + * @brief This file contains wrappers for linked-list, + * spinlock and timer defines. + * + * Copyright (C) 2008-2011, 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; +} + +#endif /* !_MLAN_UTIL_H_ */ diff --git a/drivers/net/wireless/sd8797/mlan/mlan_wmm.c b/drivers/net/wireless/sd8797/mlan/mlan_wmm.c new file mode 100644 index 000000000000..9c9f90be295d --- /dev/null +++ b/drivers/net/wireless/sd8797/mlan/mlan_wmm.c @@ -0,0 +1,2534 @@ +/** @file mlan_wmm.c + * + * @brief This file contains functions for WMM. + * + * Copyright (C) 2008-2011, 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 +}; + +/** + * 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->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 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; + } + 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 typeStr Type of host event + * @param srcAddr 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 *typeStr, + t_u8 * srcAddr, t_u8 tid, t_u8 up, t_u8 status) +{ + t_u8 event_buf[100]; + mlan_event *pevent; + t_u8 *pOutBuf; + + 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; + pOutBuf = pevent->event_buf; + + memcpy(priv->adapter, pOutBuf, (t_u8 *) "TSPEC:", 6); + pOutBuf += 6; + + memcpy(priv->adapter, pOutBuf, (t_u8 *) typeStr, wlan_strlen(typeStr)); + pOutBuf += wlan_strlen(typeStr); + + *pOutBuf++ = ':'; + *pOutBuf++ = '['; + + if (status >= 100) { + *pOutBuf++ = (status / 100) + '0'; + status = (status % 100); + } + + if (status >= 10) { + *pOutBuf++ = (status / 10) + '0'; + status = (status % 10); + } + + *pOutBuf++ = status + '0'; + + memcpy(priv->adapter, pOutBuf, (t_u8 *) "]:TID", 5); + pOutBuf += 5; + *pOutBuf++ = tid + '0'; + + memcpy(priv->adapter, pOutBuf, (t_u8 *) ":UP", 3); + pOutBuf += 3; + *pOutBuf++ = up + '0'; + + *pOutBuf = '\0'; + + pevent->bss_index = priv->bss_index; + pevent->event_id = MLAN_EVENT_ID_DRV_REPORT_STRING; + pevent->event_len = wlan_strlen((const t_s8 *) (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, + pmadapter->callbacks.moal_spin_lock, + pmadapter->callbacks.moal_spin_unlock))) + 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; + } + + 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]) + 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; + } + + if ((ptr = ptr->pnext) == (raListTbl *) & tid_ptr->ra_list) + ptr = ptr->pnext; + } while (ptr != head); + } + + /* 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: + if ((bssprio_node = bssprio_node->pnext) == (mlan_bssprio_node *) + & pmadapter->bssprio_tbl[j].bssprio_head) + bssprio_node = bssprio_node->pnext; + } 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 maxBufSize Maximum buffer size + * + * @return Packet count + */ +static int +wlan_num_pkts_in_txq(mlan_private * priv, raListTbl * ptr, int maxBufSize) +{ + 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 < maxBufSize) + ++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(); + + if ((pmbuf = (pmlan_buffer) util_dequeue_list(pmadapter->pmoal_handle, + &ptr->buf_head, + MNULL, MNULL))) { + 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; + + if ((pmbuf = (pmlan_buffer) util_peek_list(priv->adapter->pmoal_handle, + &ptr->buf_head, MNULL, MNULL)) + && (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; + mlan_tx_param tx_param; + pmlan_buffer pmbuf; + pmlan_adapter pmadapter = priv->adapter; + mlan_status ret = MLAN_STATUS_FAILURE; + + if ((pmbuf = (pmlan_buffer) util_dequeue_list(pmadapter->pmoal_handle, + &ptr->buf_head, + MNULL, MNULL))) { + 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: + pmadapter->data_sent = MFALSE; + 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; + t_u8 avail_ports = 0; + int i; + + ENTER(); + + if (!(ptr = wlan_wmm_get_highest_priolist_ptr(pmadapter, &priv, &ptrindex))) { + 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); + + for (i = 1; i < pmadapter->mp_end_port; i++) { + if ((pmadapter->mp_wr_bitmap >> i) & 1) + avail_ports++; + } + + PRINTM(MINFO, + "mp_wr_bitmap=0x%08x avail_ports=%d tid=%d tx_eligibility[%d]=%d\n", + pmadapter->mp_wr_bitmap, avail_ports, + tid, tid, pmadapter->tx_eligibility[tid]); + + if (avail_ports < pmadapter->tx_eligibility[tid]) { + pmadapter->callbacks.moal_spin_unlock(pmadapter->pmoal_handle, + priv->wmm.ra_list_spinlock); + pmadapter->data_sent = MTRUE; + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + if (wlan_is_ptr_processed(priv, ptr)) { + wlan_send_processed_packet(priv, ptr, ptrindex); + LEAVE(); + return MLAN_STATUS_SUCCESS; + } + + if (!ptr->is_11n_enabled || wlan_is_bastream_setup(priv, ptr, tid) +#ifdef STA_SUPPORT + || ((priv->sec_info.ewpa_enabled == MFALSE) && + ((priv->sec_info.wpa_enabled + || priv->sec_info.wpa2_enabled) && + priv->wpa_is_gtk_set == MFALSE)) + || priv->wps.session_enable +#endif /* STA_SUPPORT */ + ) { + if (ptr->is_11n_enabled && wlan_is_bastream_setup(priv, ptr, tid) + && wlan_is_amsdu_in_ampdu_allowed(priv, ptr, tid) + && 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, INTF_HEADER_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); + 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); + wlan_11n_create_txbastream_tbl(priv, + ptr->ra, tid, + BA_STREAM_SETUP_INPROGRESS); + wlan_send_delba(priv, 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, INTF_HEADER_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_updata_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) { + pkt_cnt += ra_list->total_pkts; + ra_list->tx_pause = tx_pause; + } + } + 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 +#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(); + + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) { + wlan_cleanup_bypass_txq(pmadapter); + } + + wlan_11n_cleanup_reorder_tbl(priv); + + pmadapter->callbacks.moal_spin_lock(pmadapter->pmoal_handle, + priv->wmm.ra_list_spinlock); + + wlan_wmm_cleanup_queues(priv); + wlan_11n_deleteall_txbastream_tbl(priv); +#ifdef SDIO_MULTI_PORT_TX_AGGR + MP_TX_AGGR_BUF_RESET(priv->adapter); +#endif +#ifdef SDIO_MULTI_PORT_RX_AGGR + MP_RX_AGGR_BUF_RESET(priv->adapter); +#endif + 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 + 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; + } + + 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; + + 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; + 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_11n_enabled = IS_11N_ENABLED(priv); + if (ra_list->is_11n_enabled) + ra_list->max_amsdu = priv->max_amsdu; + } + + 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 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) { + if ((priv = pmadapter->priv[j])) { + for (i = 0; i < MAX_NUM_TID; ++i) { + priv->aggr_prio_tbl[i].amsdu = BA_STREAM_NOT_ALLOWED; +#ifdef WIFI_DIRECT_SUPPORT + if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT) + priv->aggr_prio_tbl[i].ampdu_ap = + priv->aggr_prio_tbl[i].ampdu_user = + BA_STREAM_NOT_ALLOWED; + else +#endif + priv->aggr_prio_tbl[i].ampdu_ap = + priv->aggr_prio_tbl[i].ampdu_user = tos_to_tid_inv[i]; + priv->wmm.pkts_queued[i] = 0; + priv->wmm.tid_tbl_ptr[i].ra_list_curr = MNULL; + } + + priv->aggr_prio_tbl[6].ampdu_ap + = priv->aggr_prio_tbl[6].ampdu_user = BA_STREAM_NOT_ALLOWED; + + priv->aggr_prio_tbl[7].ampdu_ap + = priv->aggr_prio_tbl[7].ampdu_user = BA_STREAM_NOT_ALLOWED; + + priv->add_ba_param.timeout = MLAN_DEFAULT_BLOCK_ACK_TIMEOUT; + priv->add_ba_param.tx_win_size = MLAN_AMPDU_DEF_TXWINSIZE; + priv->add_ba_param.rx_win_size = MLAN_AMPDU_DEF_RXWINSIZE; + 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) { + if ((priv = pmadapter->priv[j])) { + 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 (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); + PRINTM(MINFO, + "ralist_update: %p, %d, %02x:%02x:%02x:%02x:%02x:%02x-->" + "%02x:%02x:%02x:%02x:%02x:%02x\n", ra_list, + ra_list->is_11n_enabled, ra_list->ra[0], ra_list->ra[1], + ra_list->ra[2], ra_list->ra[3], ra_list->ra[4], + ra_list->ra[5], new_ra[0], new_ra[1], new_ra[2], new_ra[3], + new_ra[4], new_ra[5]); + + 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; + + ENTER(); + + pmbuf->buf_type = MLAN_BUF_TYPE_DATA; + if (!priv->media_connected) { + PRINTM(MWARN, "Drop packet in disconnect state\n"); + 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)) { + 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)); + ra_list = wlan_wmm_get_queue_raptr(priv, tid_down, ra); + } + + if (!ra_list) { + PRINTM(MWARN, "Drop packet, ra_list=%p, " + "media_connected=%d\n", 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(MDATA, "Adding pkt to ra_list %p %p priority=%d, tid_down=%d\n", + ra_list, pmbuf, pmbuf->priority, tid_down); + 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) { + 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_spin_unlock(pmadapter->pmoal_handle, + priv->wmm.ra_list_spinlock); + pmadapter->callbacks.moal_get_system_time(pmadapter->pmoal_handle, + &pmbuf->in_ts_sec, + &pmbuf->in_ts_usec); + + 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 sendWmmEvent; + MrvlIEtypes_Data_t *pTlvHdr; + MrvlIEtypes_WmmQueueStatus_t *pTlvWmmQStatus; + IEEEtypes_WmmParameter_t *pWmmParamIe = MNULL; + WmmAcStatus_t *pac_status; + + MrvlIETypes_ActionFrame_t *pTlvAction; + IEEEtypes_Action_WMM_AddTsRsp_t *pAddTsRsp; + IEEEtypes_Action_WMM_DelTs_t *pDelTs; + + ENTER(); + + sendWmmEvent = 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(pTlvHdr->header)) { + pTlvHdr = (MrvlIEtypes_Data_t *) pcurrent; + tlv_len = wlan_le16_to_cpu(pTlvHdr->header.len); + + switch (wlan_le16_to_cpu(pTlvHdr->header.type)) { + case TLV_TYPE_WMMQSTATUS: + pTlvWmmQStatus = (MrvlIEtypes_WmmQueueStatus_t *) pTlvHdr; + PRINTM(MEVENT, "WMM_STATUS: QSTATUS TLV: %d\n", + pTlvWmmQStatus->queue_index); + + PRINTM(MINFO, + "CMD_RESP: WMM_GET_STATUS: QSTATUS TLV: %d, %d, %d\n", + pTlvWmmQStatus->queue_index, + pTlvWmmQStatus->flow_required, pTlvWmmQStatus->disabled); + + pac_status = &priv->wmm.ac_status[pTlvWmmQStatus->queue_index]; + pac_status->disabled = pTlvWmmQStatus->disabled; + pac_status->flow_required = pTlvWmmQStatus->flow_required; + pac_status->flow_created = pTlvWmmQStatus->flow_created; + break; + + case 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 *) pTlvHdr, tlv_len + 4); + + pWmmParamIe = (IEEEtypes_WmmParameter_t *) (pcurrent + 2); + pWmmParamIe->vend_hdr.len = (t_u8) tlv_len; + pWmmParamIe->vend_hdr.element_id = WMM_IE; + + PRINTM(MINFO, "CMD_RESP: WMM_GET_STATUS: WMM Parameter Set: %d\n", + pWmmParamIe->qos_info.para_set_count); + + memcpy(priv->adapter, + (t_u8 *) & priv->curr_bss_params.bss_descriptor.wmm_ie, + pWmmParamIe, MIN(sizeof(IEEEtypes_WmmParameter_t), + (pWmmParamIe->vend_hdr.len + 2))); + sendWmmEvent = MTRUE; + break; + + case TLV_TYPE_IEEE_ACTION_FRAME: + PRINTM(MEVENT, "WMM_STATUS: IEEE Action Frame\n"); + pTlvAction = (MrvlIETypes_ActionFrame_t *) pcurrent; + + if (pTlvAction->actionFrame.wmmAc.tspecAct.category + == IEEE_MGMT_ACTION_CATEGORY_WMM_TSPEC) { + + switch (pTlvAction->actionFrame.wmmAc.tspecAct.action) { + case TSPEC_ACTION_CODE_ADDTS_RSP: + pAddTsRsp = &pTlvAction->actionFrame.wmmAc.addTsRsp; + wlan_send_wmmac_host_event(priv, "ADDTS_RSP", + pTlvAction->srcAddr, + pAddTsRsp->tspecIE.TspecBody. + TSInfo.TID, + pAddTsRsp->tspecIE.TspecBody. + TSInfo.UserPri, + pAddTsRsp->statusCode); + break; + + case TSPEC_ACTION_CODE_DELTS: + pDelTs = &pTlvAction->actionFrame.wmmAc.delTs; + wlan_send_wmmac_host_event(priv, "DELTS_RX", + pTlvAction->srcAddr, + pDelTs->tspecIE.TspecBody.TSInfo. + TID, + pDelTs->tspecIE.TspecBody.TSInfo. + UserPri, pDelTs->reasonCode); + break; + + case TSPEC_ACTION_CODE_ADDTS_REQ: + default: + break; + } + } + break; + + default: + break; + } + + pcurrent += (tlv_len + sizeof(pTlvHdr->header)); + resp_len -= (tlv_len + sizeof(pTlvHdr->header)); + } + + wlan_wmm_setup_queue_priorities(priv, pWmmParamIe); + wlan_wmm_setup_ac_downgrade(priv); + + if (sendWmmEvent) { + 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 ppAssocBuf Output parameter: Pointer to the TLV output buffer, + * modified on return to point after the appended WMM TLV + * @param pWmmIE Pointer to the WMM IE for the BSS we are joining + * @param pHTCap 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 ** ppAssocBuf, + IEEEtypes_WmmParameter_t * pWmmIE, + IEEEtypes_HTCap_t * pHTCap) +{ + MrvlIEtypes_WmmParamSet_t *pwmm_tlv; + t_u32 ret_len = 0; + + ENTER(); + + /* Null checks */ + if (!ppAssocBuf) { + LEAVE(); + return 0; + } + if (!(*ppAssocBuf)) { + LEAVE(); + return 0; + } + + if (!pWmmIE) { + LEAVE(); + return 0; + } + + PRINTM(MINFO, "WMM: process assoc req: bss->wmmIe=0x%x\n", + pWmmIE->vend_hdr.element_id); + + if ((priv->wmm_required + || (pHTCap && (pHTCap->ieee_hdr.element_id == HT_CAPABILITY) + && (priv->adapter->config_bands & BAND_GN + || priv->adapter->config_bands & BAND_AN)) + ) + && pWmmIE->vend_hdr.element_id == WMM_IE) { + pwmm_tlv = (MrvlIEtypes_WmmParamSet_t *) * ppAssocBuf; + 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 (pWmmIE->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); + *ppAssocBuf += 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, queue_delay; + + ENTER(); + + priv->adapter->callbacks.moal_get_system_time(priv->adapter->pmoal_handle, + &out_ts_sec, &out_ts_usec); + + queue_delay = (out_ts_sec - pmbuf->in_ts_sec) * 1000; + queue_delay += (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; + /* 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; +} + +#if defined(UAP_SUPPORT) +/** + * @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 + * @param tx_pause tx_pause flag + * + * @return N/A + */ +static INLINE t_u8 +wlan_del_tx_pkts_in_ralist(pmlan_private priv, + mlan_list_head * ra_list_head, + int tid, t_u8 tx_pause) +{ + 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 || + (!tx_pause && + ra_list->total_pkts > RX_LOW_THRESHOLD))) { + if ((pmbuf = + (pmlan_buffer) util_dequeue_list(pmadapter->pmoal_handle, + &ra_list->buf_head, MNULL, + MNULL))) { + PRINTM(MDATA, + "Drop pkts: tid=%d tx_pause=%d pkts=%d brd_pkts=%d %02x:%02x:%02x:%02x:%02x:%02x\n", + tid, tx_pause, ra_list->total_pkts, + pmadapter->pending_bridge_pkts, ra_list->ra[0], + ra_list->ra[1], ra_list->ra[2], ra_list->ra[3], + ra_list->ra[4], ra_list->ra[5]); + 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) + 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 = 0; + t_u8 tx_pause = 0; + pmlan_adapter pmadapter = priv->adapter; + pmadapter->callbacks.moal_spin_lock(pmadapter->pmoal_handle, + priv->wmm.ra_list_spinlock); + if (wlan_get_tx_pause_station_entry(priv)) + tx_pause = 1; + 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, tx_pause)) { + 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); + if (!ra_list->tx_pause) + 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(); +} +#endif + +#ifdef STA_SUPPORT + +/** + * @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 = presp_addts->command_result; + paddts->dialog_tok = presp_addts->dialog_token; + paddts->status_code = (t_u32) presp_addts->ieee_status_code; + + if (presp_addts->command_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 *pTspecIE; + 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 = presp_delts->command_result; + + PRINTM(MINFO, "TSPEC: DELTS result = %d\n", + presp_delts->command_result); + + if (presp_delts->command_result == 0) { + pTspecIE = (IEEEtypes_WMM_TSPEC_t *) presp_delts->tspec_data; + wlan_send_wmmac_host_event(pmpriv, + "DELTS_TX", + MNULL, + pTspecIE->TspecBody.TSInfo.TID, + pTspecIE->TspecBody.TSInfo.UserPri, + presp_delts->ieee_reason_code); + + } + } + + 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 = presp_qcfg->action; + pwmm->param.q_cfg.access_category = 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 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; +} +#endif /* STA_SUPPORT */ diff --git a/drivers/net/wireless/sd8797/mlan/mlan_wmm.h b/drivers/net/wireless/sd8797/mlan/mlan_wmm.h new file mode 100644 index 000000000000..89a2c8f37e49 --- /dev/null +++ b/drivers/net/wireless/sd8797/mlan/mlan_wmm.h @@ -0,0 +1,182 @@ +/** @file mlan_wmm.h + * + * @brief This file contains related macros, enum, and struct + * of wmm functionalities + * + * Copyright (C) 2008-2011, 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; +} + +/** 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); +/** Setup WMM queues */ +extern void wlan_wmm_setup_queues(pmlan_private priv); +/* Setup default queues */ +void wlan_wmm_default_queue_priorities(pmlan_private priv); + +#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); + +/** 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); +#ifdef UAP_SUPPORT +t_void wlan_wmm_delete_peer_ralist(pmlan_private priv, t_u8 * mac); +#endif + +#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_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_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_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); +/** 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 */ + +#endif /* !_MLAN_WMM_H_ */ diff --git a/drivers/net/wireless/sd8797/mlinux/moal_cfg80211.c b/drivers/net/wireless/sd8797/mlinux/moal_cfg80211.c new file mode 100644 index 000000000000..942fae4e991d --- /dev/null +++ b/drivers/net/wireless/sd8797/mlinux/moal_cfg80211.c @@ -0,0 +1,1263 @@ +/** @file moal_cfg80211.c + * + * @brief This file contains the functions for CFG80211. + * + * Copyright (C) 2011-2012, 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 = 5040,.hw_value = 8,.max_power = 20}, + {.center_freq = 5060,.hw_value = 12,.max_power = 20}, + {.center_freq = 5080,.hw_value = 16,.max_power = 20}, + {.center_freq = 5170,.hw_value = 34,.max_power = 20}, + {.center_freq = 5190,.hw_value = 38,.max_power = 20}, + {.center_freq = 5210,.hw_value = 42,.max_power = 20}, + {.center_freq = 5230,.hw_value = 46,.max_power = 20}, + {.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, +}; + +#ifndef WLAN_CIPHER_SUITE_SMS4 +#define WLAN_CIPHER_SUITE_SMS4 0x00000020 +#endif + +/* 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, +}; + +#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 + +/******************************************************** + 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 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 + * + * @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) +{ + 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(); + + /* 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) { +#ifdef UAP_CFG80211 +#ifdef UAP_SUPPORT + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) { + if (key && key_len) { + priv->key_len = key_len; + memcpy(priv->key_material, key, key_len); + priv->cipher = cipher; + priv->key_index = key_index; + } + if ((cipher == WLAN_CIPHER_SUITE_WEP40) || + (cipher == WLAN_CIPHER_SUITE_WEP104)) { + PRINTM(MIOCTL, "Set WEP key\n"); + ret = MLAN_STATUS_SUCCESS; + goto done; + } + } +#endif +#endif + 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_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 (cipher != WLAN_CIPHER_SUITE_WEP40 && + cipher != WLAN_CIPHER_SUITE_WEP104) { + 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; + } + if (seq && seq_len) { + memcpy(sec->param.encrypt_key.pn, seq, seq_len); + sec->param.encrypt_key.key_flags |= KEY_FLAG_RX_SEQ_VALID; + } + } + } 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 */ + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + done: + if (req && (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 + * + * @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) +{ + 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); + } 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); + } + + LEAVE(); + return ret; +} + +#if defined(WIFI_DIRECT_SUPPORT) +#if LINUX_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION +/** + * @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 + */ +static int +woal_cfg80211_bss_role_cfg(moal_private * priv, t_u16 action, t_u8 * bss_role) +{ + int ret = 0; + mlan_ds_bss *bss = NULL; + mlan_ioctl_req *req = NULL; + struct net_device *dev = priv->netdev; + + 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; + bss->param.bss_role = *bss_role; + + if (req->action == MLAN_ACT_SET) { + /* Reset interface */ + woal_reset_intf(priv, MOAL_IOCTL_WAIT, MFALSE); + } + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + + if (req->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; + + /* Initialize private structures */ + woal_init_priv(priv, MOAL_IOCTL_WAIT); + + 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 WIRELESS_EXT +#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; + init_waitqueue_head(&priv->w_stats_wait_q); + } +#endif /* UAP_WEXT */ +#endif /* WIRELESS_EXT */ + } 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 WIRELESS_EXT +#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; + init_waitqueue_head(&priv->w_stats_wait_q); + } +#endif /* STA_WEXT */ +#endif + } + /* Enable interfaces */ + netif_device_attach(dev); + woal_start_queue(dev); + } + + done: + if (req) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @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_cfg80211_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_cfg80211_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_cfg80211_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; + + 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_cfg80211_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_cfg80211_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_cfg80211_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; + } + } + + 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; + + 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; + } + + /* cancel previous remain on channel */ + if (priv->phandle->remain_on_channel) { + if (woal_cfg80211_remain_on_channel_cfg + (priv->wdev->wiphy, MOAL_IOCTL_WAIT, MTRUE, &channel_status, NULL, + 0, 0)) { + PRINTM(MERROR, "Fail to cancel remain on channel\n"); + ret = -EFAULT; + goto done; + } + if (priv->phandle->cookie) { + cfg80211_remain_on_channel_expired(priv->netdev, + priv->phandle->cookie, + &priv->phandle->chan, + priv->phandle->channel_type, + 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) { + 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_cfg80211_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, u32 * flags, + struct vif_params *params) +{ + int ret = 0; + moal_private *priv = (moal_private *) woal_get_wiphy_priv(wiphy); + mlan_ds_bss *bss = NULL; + mlan_ioctl_req *req = NULL; + + ENTER(); + + if (priv->wdev->iftype == type) { + PRINTM(MINFO, "Already set to required type\n"); + goto done; + } + PRINTM(MIOCTL, "change virturl intf=%d\n", type); +#if defined(WIFI_DIRECT_SUPPORT) +#if LINUX_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION + /** cancel previous remain on channel to avoid firmware hang */ + if (priv->phandle->remain_on_channel) { + t_u8 channel_status; + if (woal_cfg80211_remain_on_channel_cfg + (wiphy, MOAL_IOCTL_WAIT, MTRUE, &channel_status, NULL, 0, 0)) { + PRINTM(MERROR, "Fail to cancel remain on channel\n"); + ret = -EFAULT; + goto done; + } + if (priv->phandle->cookie) { + cfg80211_remain_on_channel_expired(priv->netdev, + priv->phandle->cookie, + &priv->phandle->chan, + priv->phandle->channel_type, + GFP_ATOMIC); + priv->phandle->cookie = 0; + } + priv->phandle->remain_on_channel = MFALSE; + } +#endif +#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 LINUX_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 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 */ + + 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 LINUX_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION + case NL80211_IFTYPE_P2P_CLIENT: + + 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 LINUX_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; + } + } + if (type == NL80211_IFTYPE_P2P_GO) + priv->wdev->iftype = NL80211_IFTYPE_P2P_GO; +#endif +#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; + + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + + done: + 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 = (moal_private *) woal_get_wiphy_priv(wiphy); +#ifdef UAP_CFG80211 +#ifdef UAP_SUPPORT + mlan_uap_bss_param sys_cfg; +#endif +#endif + int frag_thr = wiphy->frag_threshold; + int rts_thr = wiphy->frag_threshold; + int retry = wiphy->retry_long; + + ENTER(); + + 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) { + /* 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)) + goto fail; + } + } +#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; +} + +/** + * @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 + */ +int +woal_cfg80211_add_key(struct wiphy *wiphy, struct net_device *netdev, + t_u8 key_index, +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,36) || defined(COMPAT_WIRELESS) + bool pairwise, +#endif + const t_u8 * mac_addr, struct key_params *params) +{ + moal_private *priv = (moal_private *) woal_get_wiphy_priv(wiphy); + + ENTER(); + + if (woal_cfg80211_set_key(priv, 0, params->cipher, params->key, + params->key_len, params->seq, params->seq_len, + key_index, mac_addr, 0)) { + PRINTM(MERROR, "Error adding the crypto keys\n"); + LEAVE(); + return -EFAULT; + } + + PRINTM(MINFO, "Crypto keys added\n"); + + LEAVE(); + return 0; +} + +/** + * @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 + */ +int +woal_cfg80211_del_key(struct wiphy *wiphy, struct net_device *netdev, + t_u8 key_index, +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,36) || defined(COMPAT_WIRELESS) + bool pairwise, +#endif + const t_u8 * mac_addr) +{ + moal_private *priv = (moal_private *) woal_get_wiphy_priv(wiphy); + + ENTER(); + + if (woal_cfg80211_set_key(priv, 0, 0, NULL, 0, NULL, 0, key_index, + mac_addr, 1)) { + PRINTM(MERROR, "Error deleting the crypto keys\n"); + LEAVE(); + return -EFAULT; + } + + PRINTM(MINFO, "Crypto keys deleted\n"); + LEAVE(); + return 0; +} + +/** + * @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 + */ +int +woal_cfg80211_set_default_key(struct wiphy *wiphy, + struct net_device *netdev, t_u8 key_index +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,37) || defined(COMPAT_WIRELESS) + , bool ucast, bool mcast +#endif + ) +{ + int ret = 0; + moal_private *priv = (moal_private *) woal_get_wiphy_priv(wiphy); + mlan_bss_info bss_info; + + ENTER(); + + 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)) { + ret = -EFAULT; + } + + LEAVE(); + return ret; +} + +/** + * @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 + */ +int +woal_cfg80211_set_channel(struct wiphy *wiphy, +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,34) || defined(COMPAT_WIRELESS) + struct net_device *dev, +#endif + struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type) +{ + int ret = 0; + moal_private *priv = (moal_private *) woal_get_wiphy_priv(wiphy); + + ENTER(); +#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(wiphy, chan, channel_type); + } +#endif +#endif + priv->channel = ieee80211_frequency_to_channel(chan->center_freq); + LEAVE(); + return ret; +} + +/** + * @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) +{ + moal_private *priv = (moal_private *) woal_get_wiphy_priv(wiphy); + mlan_status status = MLAN_STATUS_SUCCESS; + t_u32 mgmt_subtype_mask = 0x0; + static t_u32 last_mgmt_subtype_mask = 0x0; + + ENTER(); +#ifdef UAP_SUPPORT + if ((priv->bss_type == MLAN_BSS_TYPE_UAP) && + (frame_type == IEEE80211_STYPE_PROBE_REQ)) { + LEAVE(); + return; + } +#endif + if (reg == MTRUE) { + /* set mgmt_subtype_mask based on origin value */ + last_mgmt_subtype_mask |= BIT(frame_type >> 4); + } else { + /* clear mgmt_subtype_mask */ + last_mgmt_subtype_mask &= ~BIT(frame_type >> 4); + } + mgmt_subtype_mask = last_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); + + LEAVE(); +} + +/** + * @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 buf Frame buffer + * @param len Frame length + * @param cookie A pointer to frame cookie + * + * @return 0 -- success, otherwise fail + */ +int +woal_cfg80211_mgmt_tx(struct wiphy *wiphy, + struct net_device *dev, + struct ieee80211_channel *chan, bool offchan, + enum nl80211_channel_type channel_type, + bool channel_type_valid, unsigned int wait, + const u8 * buf, size_t len, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,2,0) || defined(COMPAT_WIRELESS) + bool no_cck, +#endif + u64 * cookie) +{ + moal_private *priv = (moal_private *) woal_get_wiphy_priv(wiphy); + 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_u16 framectrl; + t_u32 pkt_type; + t_u32 tx_control; +#if defined(WIFI_DIRECT_SUPPORT) +#if LINUX_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION + t_u8 channel_status; + t_u32 duration; +#endif +#endif + + ENTER(); + + if (buf == NULL || len == 0) { + PRINTM(MERROR, "woal_cfg80211_mgmt_tx() corrupt data\n"); + ret = -EFAULT; + goto done; + } + + /* frame subtype == 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. frame subtype == 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 */ + framectrl = ((const struct ieee80211_mgmt *) buf)->frame_control; + PRINTM(MIOCTL, "Mgmt: framectrl=0x%x\n", framectrl); +#if defined(WIFI_DIRECT_SUPPORT) +#if LINUX_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION + if ((priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT) && + (framectrl == IEEE80211_STYPE_ACTION)) { +#define MGMT_TX_DEFAULT_WAIT_TIME 2000 + /** cancel previous remain on channel */ + if (priv->phandle->remain_on_channel) { + if (woal_cfg80211_remain_on_channel_cfg + (wiphy, MOAL_IOCTL_WAIT, MTRUE, &channel_status, NULL, 0, 0)) { + PRINTM(MERROR, "Fail to cancel remain on channel\n"); + ret = -EFAULT; + goto done; + } + if (priv->phandle->cookie) { + cfg80211_remain_on_channel_expired(priv->netdev, + priv->phandle->cookie, + &priv->phandle->chan, + priv->phandle->channel_type, + GFP_ATOMIC); + priv->phandle->cookie = 0; + } + priv->phandle->remain_on_channel = MFALSE; + } + duration = wait; + if (!wait) + duration = MGMT_TX_DEFAULT_WAIT_TIME; + if (channel_type_valid) + ret = + woal_cfg80211_remain_on_channel_cfg(wiphy, MOAL_IOCTL_WAIT, + MFALSE, &channel_status, + chan, channel_type, + duration); + else + ret = + woal_cfg80211_remain_on_channel_cfg(wiphy, MOAL_IOCTL_WAIT, + MFALSE, &channel_status, + chan, 0, duration); + if (ret) { + PRINTM(MERROR, "Fail to configure remain on channel\n"); + ret = -EFAULT; + goto done; + } + priv->phandle->remain_on_channel = MTRUE; + priv->phandle->channel_type = channel_type; + memcpy(&priv->phandle->chan, chan, sizeof(struct ieee80211_channel)); + PRINTM(MIOCTL, "Mgmt Tx: Set remain channel=%d\n", + ieee80211_frequency_to_channel(chan->center_freq)); + } +#endif +#endif +#define MRVL_PKT_TYPE_MGMT_FRAME 0xE5 + /* 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; + } + *cookie = random32() | 1; + 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; + + 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 20ms 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 */ + mdelay(20); + + /* Notify the mgmt tx status */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37) || defined(COMPAT_WIRELESS) + cfg80211_mgmt_tx_status(dev, *cookie, buf, len, true, GFP_ATOMIC); +#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: + LEAVE(); + return ret; +} diff --git a/drivers/net/wireless/sd8797/mlinux/moal_cfg80211.h b/drivers/net/wireless/sd8797/mlinux/moal_cfg80211.h new file mode 100644 index 000000000000..e1683ed08a89 --- /dev/null +++ b/drivers/net/wireless/sd8797/mlinux/moal_cfg80211.h @@ -0,0 +1,195 @@ +/** @file moal_cfg80211.h + * + * @brief This file contains the CFG80211 specific defines. + * + * Copyright (C) 2011-2012, 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" + +/** RTS/FRAG disabled value */ +#define MLAN_FRAG_RTS_DISABLED (0xFFFFFFFF) + +#ifndef WLAN_CIPHER_SUITE_WAPI +#define WLAN_CIPHER_SUITE_WAPI 0x00000020 +#endif + +/* define for custom ie operation */ +#define MLAN_CUSTOM_IE_AUTO_IDX_MASK 0xffff +#define MLAN_CUSTOM_IE_DELETE_MASK 0x0 +#define TLV_TYPE_MGMT_IE 0x0169 +#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 + +/** + * 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); + +int woal_cfg80211_change_virtual_intf(struct wiphy *wiphy, + struct net_device *dev, + enum nl80211_iftype type, + u32 * flags, 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 LINUX_VERSION_CODE > KERNEL_VERSION(2,6,36) || defined(COMPAT_WIRELESS) + 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 LINUX_VERSION_CODE > KERNEL_VERSION(2,6,36) || defined(COMPAT_WIRELESS) + bool pairwise, +#endif + const t_u8 * mac_addr); + +#ifdef STA_CFG80211 +#ifdef STA_SUPPORT +int woal_set_rf_channel(struct wiphy *wiphy, + struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type); +mlan_status woal_inform_bss_from_scan_result(moal_private * priv, + mlan_802_11_ssid * ssid); +#endif +#endif + +int woal_cfg80211_set_channel(struct wiphy *wiphy, +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,34) || defined(COMPAT_WIRELESS) + struct net_device *dev, +#endif + struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type); + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,37) || defined(COMPAT_WIRELESS) +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 + +void woal_cfg80211_mgmt_frame_register(struct wiphy *wiphy, + struct net_device *dev, t_u16 frame_type, + bool reg); + +int woal_cfg80211_mgmt_tx(struct wiphy *wiphy, + struct net_device *dev, + struct ieee80211_channel *chan, bool offchan, + enum nl80211_channel_type channel_type, + bool channel_type_valid, unsigned int wait, + const u8 * buf, size_t len, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,2,0) || defined(COMPAT_WIRELESS) + bool no_cck, +#endif + u64 * cookie); + +extern struct ieee80211_supported_band cfg80211_band_2ghz; +extern struct ieee80211_supported_band cfg80211_band_5ghz; +extern const u32 cfg80211_cipher_suites[10]; + +typedef struct _monitor_iface +{ + /* The priv data of interface on which the monitor iface is based */ + moal_private *priv; + struct wireless_dev wdev; + 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; +} monitor_iface; +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,37) || defined(COMPAT_WIRELESS) +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 +int woal_cfg80211_del_virtual_intf(struct wiphy *wiphy, struct net_device *dev); + +#if defined(WIFI_DIRECT_SUPPORT) +/** Define kernel version for wifi direct */ +#if !defined(COMPAT_WIRELESS) +#define WIFI_DIRECT_KERNEL_VERSION KERNEL_VERSION(3,0,0) +#else +#define WIFI_DIRECT_KERNEL_VERSION KERNEL_VERSION(2,6,33) +#endif /* COMPAT_WIRELESS */ +#if LINUX_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION +/** Define for remain on channel duration timer */ +#define MAX_REMAIN_ON_CHANNEL_DURATION (1000 * 5) + +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); + +int woal_cfg80211_del_beacon(struct wiphy *wiphy, struct net_device *dev); + +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); + +int woal_cfg80211_remain_on_channel_cfg(struct wiphy *wiphy, + t_u8 wait_option, t_u8 remove, + t_u8 * status, + struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type, + t_u32 duration); +int woal_uap_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev, + u8 * mac, struct station_info *stainfo); +#endif /* KERNEL_VERSION */ +#endif /* WIFI_DIRECT_SUPPORT && V14_FEATURE */ + +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); +#endif /* _MOAL_CFG80211_H_ */ diff --git a/drivers/net/wireless/sd8797/mlinux/moal_debug.c b/drivers/net/wireless/sd8797/mlinux/moal_debug.c new file mode 100644 index 000000000000..fd74c3b5ae92 --- /dev/null +++ b/drivers/net/wireless/sd8797/mlinux/moal_debug.c @@ -0,0 +1,674 @@ +/** @file moal_debug.c + * + * @brief This file contains functions for debug proc file. + * + * Copyright (C) 2008-2011, 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 + {"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)} + , + {"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)} + , + {"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)} + , + {"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_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_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_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)} + , + {"malloc_count", item_handle_size(malloc_count), + item_handle_addr(malloc_count)} + , + {"lock_count", item_handle_size(lock_count), item_handle_addr(lock_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)} + , +#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 + {"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)} + , + {"tx_pkts_queued", item_size(tx_pkts_queued), item_addr(tx_pkts_queued)} + , + {"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_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_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)} + , + {"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)} + , + {"malloc_count", item_handle_size(malloc_count), + item_handle_addr(malloc_count)} + , + {"lock_count", item_handle_size(lock_count), item_handle_addr(lock_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)} + , +#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 */ + +/******************************************************** + Local Functions +********************************************************/ +/** + * @brief Proc read function + * + * @param page Pointer to buffer + * @param s Read data starting position + * @param off Offset + * @param cnt Counter + * @param eof End of file flag + * @param data Output data + * + * @return Number of output data or MLAN_STATUS_FAILURE + */ +static int +woal_debug_read(char *page, char **s, off_t off, int cnt, int *eof, void *data) +{ + int val = 0; + unsigned int i; + char *p = page; + struct debug_data *d = ((struct debug_data_priv *) data)->items; + moal_private *priv = ((struct debug_data_priv *) data)->priv; + + ENTER(); + + if (MODULE_GET == 0) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + /* Get debug information */ + if (woal_get_debug_info(priv, MOAL_PROC_WAIT, &info)) { + *eof = 1; + goto exit; + } + for (i = 0; + i < (unsigned int) ((struct debug_data_priv *) data)->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_ptr *) d[i].addr); + else { + unsigned int j; + p += sprintf(p, "%s=", d[i].name); + for (j = 0; j < d[i].size; j += 2) { + val = *(t_u16 *) (d[i].addr + j); + p += sprintf(p, "0x%x ", val); + } + p += sprintf(p, "\n"); + continue; + } + if (strstr(d[i].name, "id") || strstr(d[i].name, "bitmap")) + p += sprintf(p, "%s=0x%x\n", d[i].name, val); + else + p += sprintf(p, "%s=%d\n", d[i].name, val); + } + if (info.tx_tbl_num) { + p += sprintf(p, "Tx BA stream table:\n"); + for (i = 0; i < info.tx_tbl_num; i++) { + p += sprintf(p, + "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) { + p += sprintf(p, "Rx reorder table:\n"); + for (i = 0; i < info.rx_tbl_num; i++) { + unsigned int j; + + p += sprintf(p, + "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); + p += sprintf(p, "buffer: "); + for (j = 0; j < info.rx_tbl[i].win_size; j++) { + if (info.rx_tbl[i].buffer[j] == MTRUE) + p += sprintf(p, "1 "); + else + p += sprintf(p, "0 "); + } + p += sprintf(p, "\n"); + } + } + exit: + MODULE_PUT; + LEAVE(); + return p - page; +} + +/** + * @brief Proc write function + * + * @param f File pointer + * @param buf Pointer to data buffer + * @param cnt Data number to write + * @param data Data to write + * + * @return Number of data or MLAN_STATUS_FAILURE + */ +static int +woal_debug_write(struct file *f, const char *buf, unsigned long cnt, void *data) +{ + int r, i; + char *pdata; + char *p; + char *p0; + char *p1; + char *p2; + struct debug_data *d = ((struct debug_data_priv *) data)->items; + moal_private *priv = ((struct debug_data_priv *) data)->priv; +#ifdef DEBUG_LEVEL1 + t_u32 last_drvdbg = drvdbg; +#endif + + ENTER(); + + if (MODULE_GET == 0) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + pdata = (char *) kmalloc(cnt, GFP_KERNEL); + if (pdata == NULL) { + MODULE_PUT; + LEAVE(); + return 0; + } + + if (copy_from_user(pdata, buf, cnt)) { + PRINTM(MERROR, "Copy from user failed\n"); + kfree(pdata); + MODULE_PUT; + LEAVE(); + return 0; + } + + if (woal_get_debug_info(priv, MOAL_PROC_WAIT, &info)) { + kfree(pdata); + MODULE_PUT; + LEAVE(); + return 0; + } + + p0 = pdata; + for (i = 0; i < ((struct debug_data_priv *) data)->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_ptr *) d[i].addr) = (t_ptr) r; + break; + } while (MTRUE); + } + kfree(pdata); + +#ifdef DEBUG_LEVEL1 + if (last_drvdbg != drvdbg) + woal_set_drvdbg(priv, drvdbg); +#endif + + /* Set debug information */ + if (woal_set_debug_info(priv, MOAL_PROC_WAIT, &info)) { + MODULE_PUT; + LEAVE(); + return 0; + } + + MODULE_PUT; + LEAVE(); + return cnt; +} + +/******************************************************** + 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; + + ENTER(); + + if (priv->proc_entry == NULL) { + LEAVE(); + return; + } +#ifdef STA_SUPPORT + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) { + priv->items_priv.items = + (struct debug_data *) 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 = sizeof(items) / sizeof(items[0]); + } +#endif +#ifdef UAP_SUPPORT + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) { + priv->items_priv.items = + (struct debug_data *) 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 = + sizeof(uap_items) / sizeof(uap_items[0]); + } +#endif + priv->items_priv.priv = priv; + priv->items_priv.items[priv->items_priv.num_of_items - 1].addr += + (t_ptr) (priv->phandle); + priv->items_priv.items[priv->items_priv.num_of_items - 2].addr += + (t_ptr) (priv->phandle); + priv->items_priv.items[priv->items_priv.num_of_items - 3].addr += + (t_ptr) (priv->phandle); + priv->items_priv.items[priv->items_priv.num_of_items - 4].addr += + (t_ptr) (priv->phandle); + priv->items_priv.items[priv->items_priv.num_of_items - 5].addr += + (t_ptr) (priv->phandle); + priv->items_priv.items[priv->items_priv.num_of_items - 6].addr += + (t_ptr) (priv->phandle); + priv->items_priv.items[priv->items_priv.num_of_items - 7].addr += + (t_ptr) (priv->phandle); +#ifdef SDIO_MMC_DEBUG + priv->items_priv.items[priv->items_priv.num_of_items - 8].addr += + (t_ptr) (priv->phandle); + priv->items_priv.items[priv->items_priv.num_of_items - 9].addr += + (t_ptr) (priv->phandle); +#ifdef SDIO_SUSPEND_RESUME + priv->items_priv.items[priv->items_priv.num_of_items - 10].addr += + (t_ptr) (priv->phandle); + priv->items_priv.items[priv->items_priv.num_of_items - 11].addr += + (t_ptr) (priv->phandle); +#endif +#else +#if defined(SDIO_SUSPEND_RESUME) + priv->items_priv.items[priv->items_priv.num_of_items - 8].addr += + (t_ptr) (priv->phandle); + priv->items_priv.items[priv->items_priv.num_of_items - 9].addr += + (t_ptr) (priv->phandle); +#endif +#endif + + /* Create proc entry */ + r = create_proc_entry("debug", 0644, priv->proc_entry); + if (r == NULL) { + LEAVE(); + return; + } + r->data = &priv->items_priv; + r->read_proc = woal_debug_read; + r->write_proc = woal_debug_write; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30) + r->owner = THIS_MODULE; +#endif + + LEAVE(); +} + +/** + * @brief Remove proc file + * + * @param priv A pointer to a moal_private structure + * + * @return N/A + */ +void +woal_debug_remove(moal_private * priv) +{ + ENTER(); + + if (priv->items_priv.items) + kfree(priv->items_priv.items); + /* Remove proc entry */ + remove_proc_entry("debug", priv->proc_entry); + + LEAVE(); +} +#endif diff --git a/drivers/net/wireless/sd8797/mlinux/moal_eth_ioctl.c b/drivers/net/wireless/sd8797/mlinux/moal_eth_ioctl.c new file mode 100644 index 000000000000..984400cd532f --- /dev/null +++ b/drivers/net/wireless/sd8797/mlinux/moal_eth_ioctl.c @@ -0,0 +1,1143 @@ +/** @file moal_eth_ioctl.c + * + * @brief This file contains private ioctl functions + * + * Copyright (C) 2012, 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 +/******************************************************** + Local Variables +********************************************************/ + +/** 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: HT Tx Cfg */ +#define PRIV_CMD_HTTXCFG "httxcfg" +#define PRIV_CMD_DATARATE "getdatarate" +#define PRIV_CMD_TXRATECFG "txratecfg" + +/** 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 +********************************************************/ + +/******************************************************** + Local Functions +********************************************************/ +/** + * @brief Parse a string to extract arguments + * + * @param pos Pointer to the arguments string + * @param data Pointer to the arguments buffer + * @param len Pointer to the number of arguments extracted + * + * @return MLAN_STATUS_SUCCESS + * + * N.B. No boundary check is done on 'data'. The caller must ensure there + * are enough space for all extracted arguments. + */ +mlan_status +parse_arguments(t_u8 * pos, int *data, int *user_data_len) +{ + unsigned int i, j, k; + char cdata[10]; + int is_hex = 0; + + 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') { + if (is_hex) { + data[j] = woal_atox(cdata); + is_hex = 0; + } else { + woal_atoi(&data[j], cdata); + } + j++; + (*user_data_len)++; + k = 0; + memset(cdata, 0, sizeof(char) * 4); + break; + } else if (pos[i] == ' ') { + if (is_hex) { + data[j] = woal_atox(cdata); + is_hex = 0; + } else { + woal_atoi(&data[j], cdata); + } + j++; + (*user_data_len)++; + k = 0; + memset(cdata, 0, sizeof(char) * 4); + } else { + cdata[k] = pos[i]; + k++; + } + } + + return MLAN_STATUS_SUCCESS; +} + +#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 || ie_len > MAX_IE_SIZE) { + 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') { + PRINTM(MIOCTL, "Ignore set beacon ie\n"); + 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)) + { + 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)) + { + PRINTM(MERROR, "Failed to set assoc resp ie\n"); + ret = -EFAULT; + goto done; + } + } + + done: + LEAVE(); + return ret; +} +#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; +} + +#ifdef WIFI_DIRECT_SUPPORT +/** + * @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 + * + * @return Number of bytes written, negative for failure. + */ +int +woal_priv_hostcmd(moal_private * priv, t_u8 * respbuf, t_u32 respbuflen) +{ + 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; + + 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); + + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto error; + } + sprintf(respbuf, "OK"); + ret = 3; + + error: + if (req) + kfree(req); + + LEAVE(); + return ret; +} +#endif + +/** + * @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; + + 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, &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.sec_chan_offset = adhoc_chan_bandwidth; + } + + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + 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 (req) + 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) +{ + int data[2]; + mlan_ioctl_req *req = NULL; + mlan_ds_11n_cfg *cfg_11n = NULL; + int ret = 0; + int user_data_len = 0; + + 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, &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_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; + } + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + data[0] = cfg_11n->param.tx_cfg.httxcap; + + if (req->action == MLAN_ACT_GET) { + user_data_len = 1; + cfg_11n->param.tx_cfg.httxcap = 0; + cfg_11n->param.tx_cfg.misc_cfg = BAND_SELECT_A; + if (MLAN_STATUS_SUCCESS != + woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + if (cfg_11n->param.tx_cfg.httxcap != data[0]) { + user_data_len = 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]); + } + + sprintf(respbuf, "0x%x", data[0]); + ret = strlen(respbuf) + 1; + + done: + if (req) + 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; + + 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; + + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + 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 (req) + 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; + + 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, &user_data_len); + } + + if (user_data_len >= 4) { + PRINTM(MERROR, "Invalid number of 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; + } + } + + 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] > 15)) + ) { + 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]; + + if (data[0] == MLAN_RATE_FORMAT_HT) { + rate->param.rate_cfg.rate = data[1] + MLAN_RATE_INDEX_MCS0; + } + } + + } + + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + 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 */ + if (rate->param.rate_cfg.rate < MLAN_RATE_INDEX_MCS0) { + ratecfg->rate_format = MLAN_RATE_FORMAT_LG; + ratecfg->rate_index = rate->param.rate_cfg.rate; + } else { + ratecfg->rate_format = MLAN_RATE_FORMAT_HT; + ratecfg->rate_index = + rate->param.rate_cfg.rate - MLAN_RATE_INDEX_MCS0; + } + } + + ret = sizeof(woal_tx_rate_cfg); + + done: + if (req) + 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]; +#endif + int len = 0; + + ENTER(); + if (copy_from_user(&priv_cmd, req->ifr_data, sizeof(android_wifi_priv_cmd))) { + ret = -EFAULT; + goto done; + } + buf = kzalloc(priv_cmd.total_len, GFP_KERNEL); + if (!buf) { + PRINTM(MERROR, "%s: failed to allocate memory\n", __FUNCTION__); + ret = -ENOMEM; + goto done; + } + if (copy_from_user(buf, priv_cmd.buf, priv_cmd.total_len)) { + ret = -EFAULT; + goto done; + } + + PRINTM(MIOCTL, "Android priv cmd: [%s] on [%s]\n", buf, req->ifr_name); + + 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; +#ifdef WIFI_DIRECT_SUPPORT + } 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); + goto handled; +#endif + } 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_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; + } else { + /* Fall through, after stripping off the custom header */ + buf += strlen(CMD_MARVELL); + } + } +#ifdef STA_SUPPORT + 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 +#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 (MLAN_STATUS_SUCCESS != woal_set_powermode(priv, pdata)) { + ret = -EFAULT; + goto done; + } + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "COUNTRY", strlen("COUNTRY")) == 0) { + memset(country_code, 0, sizeof(country_code)); + memcpy(country_code, buf + strlen("COUNTRY") + 1, + strlen(buf) - strlen("COUNTRY") - 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, 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) { + if ((ret == woal_uap_bss_ctrl(priv, MOAL_IOCTL_WAIT, UAP_BSS_START))) + goto done; + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "AP_BSS_STOP", strlen("AP_BSS_STOP")) == 0) { + if ((ret == woal_uap_bss_ctrl(priv, MOAL_IOCTL_WAIT, UAP_BSS_STOP))) + goto done; + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "AP_SET_CFG", strlen("AP_SET_CFG")) == 0) { + pdata = buf + strlen("AP_SET_CFG") + 1; + if ((ret = + woal_uap_set_ap_cfg(priv, pdata, + priv_cmd.used_len - strlen("AP_SET_CFG") - 1))) + 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, "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, "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 (MLAN_STATUS_SUCCESS != woal_stop_bg_scan(priv)) { + 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 + if ((ret = woal_set_rxfilter(priv, MTRUE))) { + goto done; + } +#endif + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "RXFILTER-STOP", strlen("RXFILTER-STOP")) == 0) { +#ifdef MEF_CFG_RX_FILTER + if ((ret = woal_set_rxfilter(priv, MFALSE))) { + goto done; + } +#endif + len = sprintf(buf, "OK\n") + 1; + } 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; + if (MLAN_STATUS_SUCCESS != woal_set_qos_cfg(priv, pdata)) { + ret = -EFAULT; + goto done; + } + 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 (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 + len = sprintf(buf, "OK\n") + 1; + } +#endif + else if (strncmp(buf, "P2P_DEV_ADDR", strlen("P2P_DEV_ADDR")) == 0) { + memset(buf, 0x0, 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, priv_cmd.total_len); + *buf = 0; + len = 1; + } else { + PRINTM(MIOCTL, "Unknow 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 (copy_to_user(priv_cmd.buf, buf, len)) { + PRINTM(MERROR, "%s: failed to copy data to user buffer\n", + __FUNCTION__); + ret = -EFAULT; + } + } else { + ret = len; + } + + done: + if (buf) + kfree(buf); + 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_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) { +#ifdef WIFI_DIRECT_SUPPORT + case WOAL_WIFIDIRECT_HOST_CMD: + ret = woal_hostcmd_ioctl(dev, req); + break; +#endif + 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_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/sd8797/mlinux/moal_eth_ioctl.h b/drivers/net/wireless/sd8797/mlinux/moal_eth_ioctl.h new file mode 100644 index 000000000000..a1481e88ff21 --- /dev/null +++ b/drivers/net/wireless/sd8797/mlinux/moal_eth_ioctl.h @@ -0,0 +1,69 @@ +/** @file moal_eth_ioctl.h + * + * @brief This file contains definition for private IOCTL call. + * + * Copyright (C) 2012, 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 +********************************************************/ + +#ifndef _WOAL_ETH_PRIV_H_ +#define _WOAL_ETH_PRIV_H_ + +#ifdef WIFI_DIRECT_SUPPORT +/** Private command ID to Host command */ +#define WOAL_WIFIDIRECT_HOST_CMD (SIOCDEVPRIVATE + 1) +#endif + +/** 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) + +int woal_do_ioctl(struct net_device *dev, struct ifreq *req, int i); + +typedef struct _android_wifi_priv_cmd +{ + char *buf; + int used_len; + int total_len; +} android_wifi_priv_cmd; + +/** 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; +} woal_tx_rate_cfg; + +mlan_status woal_set_ap_wps_p2p_ie(moal_private * priv, t_u8 * ie, size_t len); + +int woal_android_priv_cmd(struct net_device *dev, struct ifreq *req); + +#endif /* _WOAL_ETH_PRIV_H_ */ diff --git a/drivers/net/wireless/sd8797/mlinux/moal_ioctl.c b/drivers/net/wireless/sd8797/mlinux/moal_ioctl.c new file mode 100644 index 000000000000..7928d37ad015 --- /dev/null +++ b/drivers/net/wireless/sd8797/mlinux/moal_ioctl.c @@ -0,0 +1,4307 @@ +/** @file moal_ioctl.c + * + * @brief This file contains ioctl function to MLAN + * + * Copyright (C) 2008-2011, 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" +#endif + +/******************************************************** + Local Variables +********************************************************/ +/* CAC Measure report default time 60 seconds */ +#define MEAS_REPORT_TIME 60*HZ +#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; + +#ifdef STA_SUPPORT +/** Region code mapping */ +typedef struct _region_code_mapping_t +{ + /** 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 */ + {"CN ", 0x50}, /* China */ + {"JP ", 0xFF}, /* Japan special */ +}; +#endif + +/******************************************************** + 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; + +/******************************************************** + Local Functions +********************************************************/ +#ifdef STA_SUPPORT +/** + * @brief This function converts region string to region code + * + * @param region_string Region string + * + * @return Region code + */ +static t_u8 +region_string_2_region_code(char *region_string) +{ + t_u8 i; + t_u8 size = sizeof(region_code_mapping) / sizeof(region_code_mapping_t); + + ENTER(); + for (i = 0; i < size; i++) { + if (!memcmp(region_string, + region_code_mapping[i].region, strlen(region_string))) { + LEAVE(); + return (region_code_mapping[i].code); + } + } + /* Default is US */ + LEAVE(); + return (region_code_mapping[0].code); +} +#endif + +/** + * @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) +{ + int i = 0; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35) + struct dev_mc_list *mcptr = dev->mc_list; +#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++) { + memcpy(&mlist->mac_list[i], mcptr->dmi_addr, ETH_ALEN); + mcptr = mcptr->next; + } +#else + netdev_for_each_mc_addr(mcptr, dev) + memcpy(&mlist->mac_list[i++], mcptr->addr, ETH_ALEN); +#endif /* < 2.6.35 */ + LEAVE(); + return i; +} + +/** + * @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; + switch (wait_option) { + case MOAL_NO_WAIT: + break; + case MOAL_IOCTL_WAIT: + wait->wait = &priv->ioctl_wait_q; + break; + case MOAL_CMD_WAIT: + wait->wait = &priv->cmd_wait_q; + break; + case MOAL_PROC_WAIT: + wait->wait = &priv->proc_wait_q; + break; +#if defined(STA_WEXT) || defined(UAP_WEXT) + case MOAL_WSTATS_WAIT: + if (IS_STA_OR_UAP_WEXT(cfg80211_wext)) + wait->wait = &priv->w_stats_wait_q; + break; +#endif + } + 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 void +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; + + ENTER(); + + switch (wait_option) { + case MOAL_NO_WAIT: + break; + case MOAL_IOCTL_WAIT: + wait_event_interruptible(priv->ioctl_wait_q, wait->condition); + break; + case MOAL_CMD_WAIT: + wait_event_interruptible(priv->cmd_wait_q, wait->condition); + break; + case MOAL_PROC_WAIT: + wait_event_interruptible(priv->proc_wait_q, wait->condition); + break; +#if defined(STA_WEXT) || defined(UAP_WEXT) + case MOAL_WSTATS_WAIT: + if (IS_STA_OR_UAP_WEXT(cfg80211_wext)) + wait_event_interruptible(priv->w_stats_wait_q, wait->condition); + break; +#endif + } + if (wait->condition == MFALSE) { + req->action = MLAN_ACT_CANCEL; + status = mlan_ioctl(priv->phandle->pmlan_adapter, req); + PRINTM(MIOCTL, + "IOCTL cancel: id=0x%x, sub_id=0x%x, wait_option=%d, action=%d, status=%d\n", + req->req_id, (*(t_u32 *) req->pbuf), wait_option, + (int) req->action, status); + } + LEAVE(); + return; +} + +/** + * @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 || +#ifdef UAP_SUPPORT + sub_command == MLAN_OID_UAP_BSS_CONFIG || +#endif + sub_command == MLAN_OID_BSS_CHANNEL + /* sub_command == MLAN_OID_BSS_ROLE */ ) { + ret = MTRUE; + } + break; + case MLAN_IOCTL_RADIO_CFG: + if (sub_command == MLAN_OID_BAND_CFG) { + ret = MTRUE; + } + break; +#if defined(UAP_SUPPORT) + case MLAN_IOCTL_SNMP_MIB: + if (sub_command == MLAN_OID_SNMP_MIB_DOT11D || + sub_command == MLAN_OID_SNMP_MIB_DOT11H) { + ret = MTRUE; + } + break; +#endif + 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_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; + mlan_status status; + + ENTER(); + + if (priv->phandle->surprise_removed == MTRUE) { + PRINTM(MERROR, + "IOCTL is not allowed while the device is not present\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) { + t_u32 sub_command; + /* CAC checking period left to complete jiffies */ + unsigned long cac_left_jiffies; + + sub_command = *(t_u32 *) req->pbuf; + + /* 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 = MEAS_REPORT_TIME - + (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; + 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); + } + } 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 */ + status = mlan_ioctl(priv->phandle->pmlan_adapter, req); + switch (status) { + case MLAN_STATUS_PENDING: + 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); + atomic_inc(&priv->phandle->ioctl_pending); + /* Status pending, wake up main process */ + queue_work(priv->phandle->workqueue, &priv->phandle->main_work); + + /* Wait for completion */ + if (wait_option) { + woal_wait_ioctl_complete(priv, req, wait_option); + status = wait->status; + } + 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); + default: + 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_CMD_WAIT); + if (status == MLAN_STATUS_SUCCESS) { + memcpy(priv->netdev->dev_addr, priv->current_addr, ETH_ALEN); +#if defined(STA_CFG80211) || defined(UAP_CFG80211) + if (IS_STA_OR_UAP_CFG80211(cfg80211_wext) && priv->wdev && + priv->wdev->wiphy) + memcpy(priv->wdev->wiphy->perm_addr, priv->current_addr, ETH_ALEN); +#endif + 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 (req) + 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; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35) + int mc_count = dev->mc_count; +#else + int mc_count = netdev_mc_count(dev); +#endif + + 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", __FUNCTION__); + 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 || + mc_count > MLAN_MAX_MULTICAST_LIST_SIZE) { + bss->param.multicast_list.mode = MLAN_ALL_MULTI_MODE; + } else { + bss->param.multicast_list.mode = MLAN_MULTICAST_MODE; + if (mc_count) + bss->param.multicast_list.num_multicast_addr = + woal_copy_mcast_addr(&bss->param.multicast_list, dev); + } + + /* 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 + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail + */ +mlan_status +woal_disconnect(moal_private * priv, t_u8 wait_option, t_u8 * mac) +{ + 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((t_u8 *) & bss->param.bssid, mac, 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, wait_option); + + done: + if (req) + 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 if needed */ + woal_stop_queue(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); + + done: + if (req) + 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) +{ + 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(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_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 (req && (status != MLAN_STATUS_PENDING)) + kfree(req); + LEAVE(); + return status; +} + +#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) && + (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 (req && (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) && + (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) && (action == MLAN_ACT_SET)) + priv->wdev->wiphy->rts_threshold = *value; +#endif + + done: + if (req && (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) && + (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) && (action == MLAN_ACT_SET)) + priv->wdev->wiphy->frag_threshold = *value; +#endif + + done: + if (req && (ret != MLAN_STATUS_PENDING)) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @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 + * + * @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) +{ + 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); + } + + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + ret = MLAN_STATUS_FAILURE; + 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 (req) + 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)); + + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) + ret = MLAN_STATUS_FAILURE; + + if (ret == MLAN_STATUS_SUCCESS && power_cfg) + memcpy(power_cfg, &pcfg->param.power_cfg, sizeof(mlan_power_cfg_t)); + + done: + if (req && (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 + * + * @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) +{ + 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 & IW_POWER_TYPE) == IW_POWER_TIMEOUT) { + PRINTM(MERROR, "Setting power timeout is not supported\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } else if ((power_type & IW_POWER_TYPE) == IW_POWER_PERIOD) { + PRINTM(MERROR, "Setting power period is not supported\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + pm_cfg->param.ps_mode = 1; + } + } + + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + ret = MLAN_STATUS_FAILURE; + 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) && + (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 (req) + 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 (req) + 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)); + + if (MLAN_STATUS_SUCCESS == woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + if (datarate && (action == MLAN_ACT_GET)) + memcpy(datarate, &rate->param.rate_cfg, sizeof(mlan_rate_cfg_t)); + } else { + ret = MLAN_STATUS_FAILURE; + } + + done: + if (req) + 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_bss)); + 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; + 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 defined(STA_CFG80211) || defined(UAP_CFG80211) + if (IS_STA_OR_UAP_CFG80211(cfg80211_wext) && priv->wdev && + priv->wdev->wiphy) + memcpy(priv->wdev->wiphy->perm_addr, priv->current_addr, ETH_ALEN); +#endif + 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 (req) + kfree(req); + LEAVE(); + return status; +} + +#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(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_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 (req && (status != MLAN_STATUS_PENDING)) + kfree(req); + LEAVE(); + return status; +} + +/** + * @brief Set 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_set_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(); + + if (!debug_info) { + ret = -EINVAL; + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + /* 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_DEBUG_INFO; + memcpy(&info->param.debug_info, debug_info, sizeof(mlan_debug_info)); + req->req_id = MLAN_IOCTL_GET_INFO; + req->action = MLAN_ACT_SET; + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, wait_option); + done: + if (req && (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; + + 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; + + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + 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 (req) + 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; + + 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; + + if (MLAN_STATUS_SUCCESS != + woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT)) { + 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 (ioctl_req) + 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; + + ENTER(); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "woal_custom_ie_ioctl() corrupt data\n"); + ret = -EFAULT; + goto done; + } + + if (!(custom_ie = kmalloc(sizeof(mlan_ds_misc_custom_ie), GFP_KERNEL))) { + ret = -ENOMEM; + goto done; + } + memset(custom_ie, 0, sizeof(mlan_ds_misc_custom_ie)); + + 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)); + + if (MLAN_STATUS_SUCCESS != + woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT)) { + 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 (ioctl_req) + kfree(ioctl_req); + if (custom_ie) + 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; + + ENTER(); + + /* 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 + pmbuf = + woal_alloc_mlan_buffer(priv->phandle, + MLAN_MIN_DATA_HEADER_LEN + 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; + 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 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; + + 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; + +#ifndef TLV_TYPE_MGMT_IE +#define TLV_TYPE_MGMT_IE (0x169) +#endif + 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); + + if (MLAN_STATUS_SUCCESS != + woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + } + + if (ioctl_req) + kfree(ioctl_req); + LEAVE(); + return ret; +} +#endif /* defined(HOST_TXRX_MGMT_FRAME) && defined(UAP_WEXT) */ + +/** + * @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(WIFI_DIRECT_SUPPORT) +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) +#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; + mlan_ds_bss *bss = NULL; + mlan_ioctl_req *req = NULL; + int bss_role = 0; + struct net_device *dev = priv->netdev; + + ENTER(); + + if (priv->bss_type != MLAN_BSS_TYPE_WIFIDIRECT) { + PRINTM(MWARN, "Command is not allowed for this interface\n"); + ret = -EPERM; + 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_BSS_ROLE; + req->req_id = MLAN_IOCTL_BSS; + 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) { + 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; + } + req->action = MLAN_ACT_SET; + bss->param.bss_role = (t_u8) bss_role; + } else { + req->action = MLAN_ACT_GET; + } + + if (req->action == MLAN_ACT_SET) { + /* Reset interface */ + woal_reset_intf(priv, MOAL_IOCTL_WAIT, MFALSE); + } + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + if (!wrq->u.data.length) { + bss_role = (int) bss->param.bss_role; + if (copy_to_user(wrq->u.data.pointer, &bss_role, sizeof(int))) { + ret = -EFAULT; + goto done; + } + wrq->u.data.length = 1; + } 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; + + /* Initialize private structures */ + woal_init_priv(priv, MOAL_IOCTL_WAIT); + + 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)) { +#ifdef WIRELESS_EXT +#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 /* WIRELESS_EXT */ + init_waitqueue_head(&priv->w_stats_wait_q); + } +#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)) { +#ifdef WIRELESS_EXT +#if WIRELESS_EXT < 21 + dev->get_wireless_stats = woal_get_wireless_stats; +#endif + dev->wireless_handlers = + (struct iw_handler_def *) &woal_handler_def; +#endif + init_waitqueue_head(&priv->w_stats_wait_q); + } +#endif /* STA_WEXT */ + } + /* Enable interfaces */ + netif_device_attach(dev); + woal_start_queue(dev); + } + + done: + if (req) + kfree(req); + LEAVE(); + return ret; +} +#endif +#endif +#endif /* WIFI_DIRECT_SUPPORT && V14_FEATURE */ + +/** + * @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_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 (req && (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) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_hs_cfg hscfg; + + ENTER(); + + /* 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); + + LEAVE(); + return ret; +} + +/** @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; +#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; + } +#ifdef STA_SUPPORT + woal_reconfig_bgscan(priv->phandle); +#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; +} + +/** + * @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_PROC_WAIT); + } + + handle->surprise_removed = MTRUE; + woal_sched_timeout(5); + if (req) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @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 (req && (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, 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; + + 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; + } + } + } + + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + 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 (req) + 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 (req && (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_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 (req && (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 */ + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + if (action == MLAN_ACT_GET) + memcpy(tx_bf_cfg, &bf_cfg->param.tx_bf, sizeof(mlan_ds_11n_tx_bf_cfg)); + + done: + 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 + 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_CMD_WAIT); + if (ret == MLAN_STATUS_SUCCESS) { + if (pm_info) { + memcpy(pm_info, &pmcfg->param.ps_info, sizeof(mlan_ds_ps_info)); + } + } + done: + if (req && (ret != 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 (req && (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; + 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); + } + } + 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 + * + * @return 0 --success, otherwise fail + */ +int +woal_11h_channel_check_ioctl(moal_private * priv) +{ + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_11h_cfg *ds_11hcfg = NULL; + + 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_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 */ + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + + /* set flag from here */ + priv->phandle->cac_period = MTRUE; + priv->phandle->meas_start_jiffies = jiffies; + + done: + if (req) + 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_cfg80211_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 (req) + 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 (req) + kfree(req); + LEAVE(); + return ret; +} +#endif /* WIFI_DIRECT_SUPPORT */ + +#ifdef STA_SUPPORT +/** + * @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 (req && (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(); + + /* 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 (req && (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; + + /* 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 ((ioctl_req) && (status != MLAN_STATUS_PENDING)) + kfree(ioctl_req); + + if (ret == MLAN_STATUS_FAILURE) { + handle->scan_pending_on_block = MFALSE; + 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. + * + * @return MLAN_STATUS_SUCCESS--success, MLAN_STATUS_FAILURE--fail + */ +mlan_status +woal_change_adhoc_chan(moal_private * priv, int channel) +{ + 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, MOAL_IOCTL_WAIT, &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 */ + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + ret = MLAN_STATUS_FAILURE; + 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 */ + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* Do specific SSID scanning */ + if (MLAN_STATUS_SUCCESS != + woal_request_scan(priv, MOAL_IOCTL_WAIT, &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 */ + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + ret = MLAN_STATUS_FAILURE; + } + + done: + if (req) + 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, %02x:%02x:%02x:%02x:%02x:%02x, idx=%d\n", + ssid_bssid->ssid.ssid, mac[0], mac[1], mac[2], mac[3], mac[4], + mac[5], (int) ssid_bssid->idx); + } + + done: + if (req) + 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 (req && (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 (req && (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 (req && (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 (req && (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 (req && (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 (req && (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 (req && (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; + + /* 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 ((ioctl_req) && (status != MLAN_STATUS_PENDING)) + kfree(ioctl_req); + + if (ret == MLAN_STATUS_FAILURE) { + handle->scan_pending_on_block = MFALSE; + MOAL_REL_SEMAPHORE(&handle->async_sem); + } + LEAVE(); + return ret; +} + +/** + * @brief set scan time + * + * @param priv A pointer to moal_private structure + * @param passive_scan_time passive scan time + * @param specific_scan_time specific scan time + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +static mlan_status +woal_set_scan_time(moal_private * priv, 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; + + 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_SET; + memset(&scan->param.scan_cfg, 0, sizeof(mlan_scan_cfg)); + scan->param.scan_cfg.scan_time.specific_scan_time = specific_scan_time; + scan->param.scan_cfg.scan_time.passive_scan_time = passive_scan_time; + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) + ret = MLAN_STATUS_FAILURE; + done: + if (req) + 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 find ssid in scan_table + * + * @param priv A pointer to moal_private + * @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) +{ + int ret = 0; + mlan_scan_resp scan_resp; + struct timeval t; + ENTER(); + + if (MLAN_STATUS_SUCCESS != + woal_get_scan_table(priv, MOAL_IOCTL_WAIT, &scan_resp)) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + 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, MOAL_IOCTL_WAIT, ssid_bssid); + 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 ((ioctl_req) && (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_SUCCESS; + + ENTER(); + memset(&priv->scan_cfg, 0, sizeof(priv->scan_cfg)); + priv->scan_cfg.report_condition = BG_SCAN_SSID_MATCH; + 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; + 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_INTERVAL_SECTION: + 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; +} + +/** + * @brief stop bg scan + * + * @param priv A pointer to moal_private structure + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status +woal_stop_bg_scan(moal_private * priv) +{ + wlan_bgscan_cfg scan_cfg; + ENTER(); + + memset(&scan_cfg, 0, sizeof(scan_cfg)); + scan_cfg.action = BG_SCAN_ACT_SET; + scan_cfg.enable = MFALSE; + return woal_request_bgscan(priv, MOAL_IOCTL_WAIT, &scan_cfg); + + LEAVE(); +} + +/** + * @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 scan_type MLAN_SCAN_TYPE_ACTIVE/MLAN_SCAN_TYPE_PASSIVE + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status +woal_set_rssi_low_threshold(moal_private * priv, char *rssi) +{ + 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 = -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; + misc->param.subscribe_event.evt_bitmap = SUBSCRIBE_EVT_RSSI_LOW; + if (MLAN_STATUS_SUCCESS != woal_atoi(&low_rssi, rssi)) { + ret = -EFAULT; + goto done; + } + misc->param.subscribe_event.low_rssi = low_rssi; + misc->param.subscribe_event.low_rssi_freq = 0; + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + done: + if (req) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @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)) { + 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; + + 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_SET; + memset(&scan->param.scan_cfg, 0, sizeof(mlan_scan_cfg)); + scan->param.scan_cfg.scan_type = scan_type; + + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) + ret = MLAN_STATUS_FAILURE; + done: + if (req) + 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)) + 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, 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; + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + ret = MLAN_STATUS_FAILURE; + 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 (req) + 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(); + 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; + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + ret = MLAN_STATUS_FAILURE; + 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; + 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; + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + done: + if (req) + 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 QoS configuration + * + * @param priv A pointer to moal_private structure + * @param qos_cfg A pointer to QoS configuration structure + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status +woal_set_qos_cfg(moal_private * priv, 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 (MLAN_STATUS_SUCCESS != woal_atoi(&qosinfo, qos_cfg)) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + PRINTM(MIOCTL, "set qosinfo=%d\n", qosinfo); + 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 = MLAN_ACT_SET; + cfg->param.qos_cfg = (t_u8) qosinfo; + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) + ret = MLAN_STATUS_FAILURE; + done: + if (req) + 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 + */ +int +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; + } + + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + done: + if (req) + kfree(req); + LEAVE(); + return ret; +} + +#endif /* STA_SUPPORT */ diff --git a/drivers/net/wireless/sd8797/mlinux/moal_main.c b/drivers/net/wireless/sd8797/mlinux/moal_main.c new file mode 100644 index 000000000000..820c1267cc78 --- /dev/null +++ b/drivers/net/wireless/sd8797/mlinux/moal_main.c @@ -0,0 +1,3773 @@ +/** @file moal_main.c + * + * @brief This file contains the major functions in WLAN + * driver. + * + * Copyright (C) 2008-2012, 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" +#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 +#include +#include "moal_eth_ioctl.h" + +/******************************************************** + Local Variables +********************************************************/ + +#define KERN_VERSION "26" + +/** Driver version */ +char driver_version[] = + "SD8797-%s-M2614" MLAN_RELEASE_VERSION "-GPL" "-(" "FP" FPNUM ")" +#ifdef DEBUG_LEVEL2 + "-dbg" +#endif + " "; + +/** Firmware name */ +char *fw_name = NULL; +int req_fw_nowait = 0; + +/** MAC address */ +char *mac_addr = NULL; + +#ifdef MFG_CMD_SUPPORT +/** Mfg mode */ +int mfg_mode = 0; +#endif + +/** SDIO interrupt mode (0: INT_MODE_SDIO, 1: INT_MODE_GPIO) */ +int intmode = INT_MODE_SDIO; +/** GPIO interrupt pin number */ +int gpiopin = 0; + +/** Auto deep sleep */ +int auto_ds = 0; + +/** IEEE PS mode */ +int ps_mode = 0; + +/** Max Tx buffer size */ +int max_tx_buf = 0; + +#ifdef STA_SUPPORT +/** Max STA interfaces */ +int max_sta_bss = DEF_STA_BSS; +#endif + +#ifdef UAP_SUPPORT +/** Max uAP interfaces */ +int max_uap_bss = DEF_UAP_BSS; +#endif + +#if defined(WIFI_DIRECT_SUPPORT) +/** Max WIFIDIRECT interfaces */ +int max_wfd_bss = DEF_WIFIDIRECT_BSS; +#endif + +#ifdef SDIO_SUSPEND_RESUME +/** PM keep power */ +int pm_keep_power = 1; +#endif + +#if defined(STA_SUPPORT) +/** 802.11d configuration */ +int cfg_11d = 0; +#endif + +/** CAL data config file */ +char *cal_data_cfg = NULL; +/** Init config file (MAC address, register etc.) */ +char *init_cfg = NULL; + +/** Enable minicard power-up/down */ +int minicard_pwrup = 1; +/** Pointer to struct with control hooks */ +struct wifi_platform_data *wifi_control_data = NULL; + +int cfg80211_wext = STA_WEXT_MASK | UAP_WEXT_MASK; + +/** woal_callbacks */ +static mlan_callbacks woal_callbacks = { + .moal_get_fw_data = moal_get_fw_data, + .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_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, +}; + +/** Default Driver mode */ +#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 */ + +/******************************************************** + Global Variables +********************************************************/ + +/** Semaphore for add/remove card */ +struct semaphore AddRemoveCardSem; +/** + * the maximum number of adapter supported + **/ +#define MAX_MLAN_ADAPTER 2 +/** + * The global variable of a pointer to moal_handle + * structure variable + **/ +moal_handle *m_handle[MAX_MLAN_ADAPTER]; + +#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,34) +u16 woal_select_queue(struct net_device *dev, struct sk_buff *skb); +#endif + +mlan_debug_info info; + +/******************************************************** + Local Functions +********************************************************/ + +/** + * @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 + */ +static 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; + + 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; + } +#endif /* WIFI_DIRECT_SUPPORT && V14_FEATURE */ + + /* 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 */ + if (! + (bss_tbl = + (mlan_bss_attr *) kmalloc(sizeof(mlan_bss_attr) * intf_num, + GFP_KERNEL))) { + 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; + 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; + 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; + i++; + } + } +#endif /* WIFI_DIRECT_SUPPORT && V14_FEATURE */ + + /* Clear existing table, if any */ + if (handle->drv_mode.bss_attr != NULL) { + 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; + if (fw_name) { + handle->drv_mode.fw_name = 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 */ + } + + done: + LEAVE(); + return ret; +} + +/** + * @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 (MTRUE +#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 (woal_update_drv_tbl(handle, drv_mode) != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "Could not update driver mode table\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + /* PnP and power profile */ + handle->surprise_removed = MFALSE; + init_waitqueue_head(&handle->init_wait_q); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34) + spin_lock_init(&handle->queue_lock); +#endif + +#if defined(SDIO_SUSPEND_RESUME) + handle->is_suspended = MFALSE; + handle->hs_activated = MFALSE; + 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; + 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); +#ifdef DFS_TESTING_SUPPORT + handle->cac_period_jiffies = 0; +#endif + +#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 */ + + /* Register to MLAN */ + memset(&device, 0, sizeof(mlan_device)); + device.pmoal_handle = handle; + +#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.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 +#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 + + 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; + } + memcpy(&device.callbacks, &woal_callbacks, sizeof(mlan_callbacks)); + + sdio_claim_host(((struct sdio_mmc_card *) handle->card)->func); + if (MLAN_STATUS_SUCCESS == mlan_register(&device, &pmlan)) + handle->pmlan_adapter = pmlan; + else + ret = MLAN_STATUS_FAILURE; + sdio_release_host(((struct sdio_mmc_card *) handle->card)->func); + + 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 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 */ + if (handle->drv_mode.bss_attr != NULL) { + kfree(handle->drv_mode.bss_attr); + handle->drv_mode.bss_attr = NULL; + } + PRINTM(MINFO, "Free Adapter\n"); + if (handle->malloc_count || handle->lock_count || handle->mbufalloc_count) { + PRINTM(MERROR, + "mlan has memory leak: malloc_count=%u lock_count=%u mbufalloc_count=%u\n", + handle->malloc_count, handle->lock_count, + handle->mbufalloc_count); + } + /* 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 = 0; + + 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 (*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 */ + if (MLAN_STATUS_SUCCESS != + woal_request_ioctl(handle->priv[0], ioctl_req, MOAL_IOCTL_WAIT)) { + goto done; + } + PRINTM(MINFO, "Register type: %d, offset: 0x%x, value: 0x%x\n", type, + offset, value); + ret = MLAN_STATUS_SUCCESS; + + done: + if (ioctl_req) + kfree(ioctl_req); + LEAVE(); + return ret; +} + +/** + * @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); + /* 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; + if (intf_s != NULL) + intf_e = strchr(intf_s, ','); + else + intf_e = NULL; + if (intf_s != NULL && 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 (intf_s != NULL) { + 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, sizeof(value)); + } else { + PRINTM(MERROR, "Wrong config file format %d\n", __LINE__); + goto done; + } + + 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 set user defined init data and param + * + * @param handle MOAL handle structure + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +static t_u32 +woal_set_user_init_data(moal_handle * handle) +{ + mlan_status ret = MLAN_STATUS_FAILURE; + t_u8 *cfg_data = NULL; + t_size len; + + ENTER(); + + if ((request_firmware(&handle->user_data, init_cfg, handle->hotplug_device)) + < 0) { + PRINTM(MERROR, "Init 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 (MLAN_STATUS_SUCCESS != woal_process_init_cfg(handle, cfg_data, len)) { + PRINTM(MERROR, "Can't process init config file\n"); + goto done; + } + ret = MLAN_STATUS_SUCCESS; + } + + done: + if (handle->user_data) + release_firmware(handle->user_data); + + LEAVE(); + return ret; +} + +/** + * @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; + + 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 (!woal_add_interface + (handle, handle->priv_num, handle->drv_mode.bss_attr[i].bss_type)) { + ret = MLAN_STATUS_FAILURE; + goto err; + } + handle->priv_num++; + } + if (init_cfg) { + if (MLAN_STATUS_SUCCESS != woal_set_user_init_data(handle)) { + PRINTM(MFATAL, "Set user init data and param failed\n"); + ret = MLAN_STATUS_FAILURE; + goto err; + } + } + + err: + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "Failed to add interface\n"); + for (i = 0; i < 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; + + 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; + sdio_claim_host(((struct sdio_mmc_card *) handle->card)->func); + ret = mlan_dnld_fw(handle->pmlan_adapter, &fw); + sdio_release_host(((struct sdio_mmc_card *) handle->card)->func); + if (ret == MLAN_STATUS_FAILURE) { + PRINTM(MERROR, "WLAN: Download FW with nowwait: %d\n", + req_fw_nowait); + goto done; + } + PRINTM(MMSG, "WLAN FW is active\n"); + } + + /** Cal data request */ + memset(¶m, 0, sizeof(mlan_init_param)); + if (cal_data_cfg) { + 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; + } + } + + 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; + + handle->init_wait_q_woken = MFALSE; + + ret = mlan_set_init_param(handle->pmlan_adapter, ¶m); + + sdio_claim_host(((struct sdio_mmc_card *) handle->card)->func); + ret = mlan_init_fw(handle->pmlan_adapter); + sdio_release_host(((struct sdio_mmc_card *) handle->card)->func); + 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 */ + wait_event_interruptible(handle->init_wait_q, handle->init_wait_q_woken); + if (handle->hardware_status != HardwareStatusReady) { + woal_moal_debug_info(woal_get_priv(handle, MLAN_BSS_ROLE_ANY), handle, + MTRUE); + ret = MLAN_STATUS_FAILURE; + goto done; + } + ret = MLAN_STATUS_SUCCESS; + done: + if (handle->user_data) + release_firmware(handle->user_data); + 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; + + if ((ret = woal_init_fw_dpc(handle))) + goto done; + if ((ret = woal_add_card_dpc(handle))) + 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) { +/** Revision ID register */ +#define REV_ID_REG 0x5c + sdio_claim_host(((struct sdio_mmc_card *) handle->card)->func); + woal_read_reg(handle, REV_ID_REG, &revision_id); + sdio_release_host(((struct sdio_mmc_card *) handle->card)->func); + /* Check revision ID */ + switch (revision_id) { + case SD8797_A0: + handle->drv_mode.fw_name = SD8797_A0_FW_NAME; + break; + case SD8797_B0: + handle->drv_mode.fw_name = SD8797_B0_FW_NAME; + break; + default: + break; + } + } + + if (req_fw_nowait) { +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,32) + if ((err = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + handle->drv_mode.fw_name, + handle->hotplug_device, GFP_KERNEL, + handle, + woal_request_fw_callback)) < 0) { +#else +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,13) + if ((err = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + handle->drv_mode.fw_name, + handle->hotplug_device, handle, + woal_request_fw_callback)) < 0) { +#else + if ((err = request_firmware_nowait(THIS_MODULE, + handle->drv_mode.fw_name, + handle->hotplug_device, handle, + woal_request_fw_callback)) < 0) { +#endif +#endif + PRINTM(MFATAL, + "WLAN: request_firmware_nowait() failed, error code = %d\n", + err); + ret = MLAN_STATUS_FAILURE; + } + } else { + if ((err = + request_firmware(&handle->firmware, handle->drv_mode.fw_name, + handle->hotplug_device)) < 0) { + PRINTM(MFATAL, "WLAN: request_firmware() failed, error code = %d\n", + err); + ret = MLAN_STATUS_FAILURE; + } 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); + if ((ret = woal_request_fw(handle)) < 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; + + ENTER(); + + eth = (struct ethhdr *) skb->data; + + switch (eth->h_proto) { + + case __constant_htons(ETH_P_IP): + tid = (IPTOS_PREC(SKB_TOS(skb)) >> IPTOS_OFFSET); + PRINTM(MDAT_D, "packet type ETH_P_IP: %04x, tid=%#x prio=%#x\n", + eth->h_proto, tid, skb->priority); + break; + + case __constant_htons(ETH_P_ARP): + PRINTM(MDATA, "ARP packet %04x\n", eth->h_proto); + tid = 0; + break; + default: + tid = 0; + 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; +} + +#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 +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34) + .ndo_select_queue = woal_select_queue, +#endif +}; +#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 + */ +static 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; + dev->hard_header_len += MLAN_MIN_DATA_HEADER_LEN + sizeof(mlan_buffer); +#ifdef WIRELESS_EXT +#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 +#endif + dev->flags |= IFF_BROADCAST | IFF_MULTICAST; + + /* Initialize private structure */ + init_waitqueue_head(&priv->ioctl_wait_q); + init_waitqueue_head(&priv->cmd_wait_q); + init_waitqueue_head(&priv->proc_wait_q); +#ifdef STA_WEXT + if (IS_STA_WEXT(cfg80211_wext)) + init_waitqueue_head(&priv->w_stats_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 +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34) + .ndo_select_queue = woal_select_queue, +#endif +}; +#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 + */ +static 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; + dev->hard_header_len += MLAN_MIN_DATA_HEADER_LEN + sizeof(mlan_buffer); +#ifdef UAP_WEXT +#ifdef WIRELESS_EXT + 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 /* WIRELESS_EXT */ +#endif /* UAP_WEXT */ + dev->flags |= IFF_BROADCAST | IFF_MULTICAST; + + /* Initialize private structure */ + init_waitqueue_head(&priv->ioctl_wait_q); + init_waitqueue_head(&priv->cmd_wait_q); + init_waitqueue_head(&priv->proc_wait_q); +#ifdef UAP_WEXT + if (IS_UAP_WEXT(cfg80211_wext)) + init_waitqueue_head(&priv->w_stats_wait_q); +#endif + + 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; + + ENTER(); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34) +#define MAX_WMM_QUEUE 4 + /* Allocate an Ethernet device */ + if (!(dev = alloc_etherdev_mq(sizeof(moal_private), MAX_WMM_QUEUE))) { +#else + if (!(dev = alloc_etherdev(sizeof(moal_private)))) { +#endif + PRINTM(MFATAL, "Init virtual ethernet device failed\n"); + goto error; + } +#ifdef STA_SUPPORT + /* Allocate device name */ + if ((bss_type == MLAN_BSS_TYPE_STA) && (dev_alloc_name(dev, "mlan%d") < 0)) { + PRINTM(MERROR, "Could not allocate mlan device name\n"); + goto error; + } +#endif +#ifdef UAP_SUPPORT + if ((bss_type == MLAN_BSS_TYPE_UAP) && (dev_alloc_name(dev, "uap%d") < 0)) { + PRINTM(MERROR, "Could not allocate uap device name\n"); + goto error; + } +#endif +#if defined(WIFI_DIRECT_SUPPORT) + if ((bss_type == MLAN_BSS_TYPE_WIFIDIRECT) && + (dev_alloc_name(dev, "wfd%d") < 0)) { + PRINTM(MERROR, "Could not allocate wifidirect device name\n"); + goto error; + } +#endif + 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; + 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 +#if defined(STA_CFG80211) || defined(UAP_CFG80211) + priv->probereq_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; +#endif +#ifdef STA_SUPPORT +#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 + ) + 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 +#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 LINUX_VERSION_CODE >= KERNEL_VERSION(3,0,0) || defined(COMPAT_WIRELESS) + || bss_type == MLAN_BSS_TYPE_WIFIDIRECT +#endif +#endif + ) + /* 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; + } + } +#endif +#endif /* UAP_CFG80211 */ + + /* Initialize priv structure */ + woal_init_priv(priv, MOAL_CMD_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 (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); + } +#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: + if (dev && 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)) { + wiphy_unregister(priv->wdev->wiphy); + wiphy_free(priv->wdev->wiphy); + /* Free wireless device */ + kfree(priv->wdev); + priv->wdev = NULL; + } +#endif + 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 + + ENTER(); + if (!priv) + 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 + } +#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)) { + wiphy_unregister(priv->wdev->wiphy); + wiphy_free(priv->wdev->wiphy); + /* Free wireless device */ + kfree(priv->wdev); + priv->wdev = NULL; + } +#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 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); + + done: + if (req) + 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 + */ +static 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_WEXT +#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; + } + + LEAVE(); +} + +/* Support for Cardhu wifi gpio toggling */ +/** + * @brief Sets the card detect state + * + * @param on 0: Card will be made undetected + * 1: Card will be detected + * + * @return 0 + */ +static int +wifi_set_carddetect(int on) +{ + ENTER(); + PRINTM(MMSG, "%s = %d\n", __FUNCTION__, on); + if (wifi_control_data && wifi_control_data->set_carddetect) { + wifi_control_data->set_carddetect(on); + } + LEAVE(); + return 0; +} + +/** + * @brief Sets the power state to On or Off after 'msec' millisecond + * + * @param on 0: Card will be powered on + * 1: Card will be powered off + * @param msec Delay in millisecond + * + * @return 0 + */ +static int +wifi_set_power(int on, unsigned long msec) +{ + ENTER(); + PRINTM(MMSG, "%s = %d\n", __FUNCTION__, on); + if (wifi_control_data && wifi_control_data->set_power) { + wifi_control_data->set_power(on); + } + if (msec) + mdelay(msec); + LEAVE(); + return 0; +} + +/** + * @brief Probes an wifi device + * + * @param pdev A pointer to the platform_device structure + * + * @return 0 + */ +static int +wifi_probe(struct platform_device *pdev) +{ + struct wifi_platform_data *wifi_ctrl = + (struct wifi_platform_data *) (pdev->dev.platform_data); + + ENTER(); + + wifi_control_data = wifi_ctrl; + wifi_set_power(1, 0); /* Power On */ + wifi_set_carddetect(1); /* CardDetect (0->1) */ + + LEAVE(); + return 0; +} + +/** + * @brief Removes an wifi device + * + * @param pdev A pointer to the platform_device structure + * + * @return 0 + */ +static int +wifi_remove(struct platform_device *pdev) +{ + struct wifi_platform_data *wifi_ctrl = + (struct wifi_platform_data *) (pdev->dev.platform_data); + + ENTER(); + + wifi_control_data = wifi_ctrl; + wifi_set_power(0, 0); /* Power Off */ + wifi_set_carddetect(0); /* CardDetect (1->0) */ + + LEAVE(); + return 0; +} + +static struct platform_driver wifi_device = { + .probe = wifi_probe, + .remove = wifi_remove, + .driver = { + .name = "mrvl8797_wlan", + } +}; + +/** + * @brief Adds an wifi device + * @return 0 --success, otherwise fail + */ +static int +wifi_add_dev(void) +{ + int ret = 0; + + ENTER(); + + if (minicard_pwrup) + ret = platform_driver_register(&wifi_device); + + LEAVE(); + return ret; +} + +/** + * @brief Removes an wifi device + * + * @return N/A + */ +static void +wifi_del_dev(void) +{ + ENTER(); + + if (minicard_pwrup) + platform_driver_unregister(&wifi_device); + + 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 (!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 (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_stop_queue(priv->netdev); + + 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(); + + 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 (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 Display MLAN debug information + * + * @param priv A pointer to moal_private + * + * @return N/A + */ +void +woal_mlan_debug_info(moal_private * priv) +{ + int i; + + ENTER(); + + if (!priv || woal_get_debug_info(priv, MOAL_CMD_WAIT, &info)) { + PRINTM(MERROR, "Could not retrieve debug information from MLAN\n"); + LEAVE(); + return; + } + + 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); + + PRINTM(MERROR, "last_cmd_index = %d\n", info.last_cmd_index); + PRINTM(MERROR, "last_cmd_id = "); + for (i = 0; i < DBG_CMD_NUM; i++) { + PRINTM(MERROR, "0x%x ", info.last_cmd_id[i]); + } + PRINTM(MERROR, "\n"); + PRINTM(MERROR, "last_cmd_act = "); + for (i = 0; i < DBG_CMD_NUM; i++) { + PRINTM(MERROR, "0x%x ", info.last_cmd_act[i]); + } + PRINTM(MERROR, "\n"); + PRINTM(MERROR, "last_cmd_resp_index = %d\n", info.last_cmd_resp_index); + PRINTM(MERROR, "last_cmd_resp_id = "); + for (i = 0; i < DBG_CMD_NUM; i++) { + PRINTM(MERROR, "0x%x ", info.last_cmd_resp_id[i]); + } + PRINTM(MERROR, "\n"); + PRINTM(MERROR, "last_event_index = %d\n", info.last_event_index); + PRINTM(MERROR, "last_event = "); + for (i = 0; i < DBG_CMD_NUM; i++) { + PRINTM(MERROR, "0x%x ", info.last_event[i]); + } + PRINTM(MERROR, "\n"); + + 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_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); + + LEAVE(); +} + +/** + * @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 : Tx timeout (%d), bss_index=%d\n", + jiffies, priv->num_tx_timeout, priv->bss_index); + 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); + } + + 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,34) +/** + * @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) +{ + moal_private *priv = (moal_private *) netdev_priv(dev); + struct ethhdr *eth = NULL; + struct iphdr *iph; + t_u8 tid = 0; + t_u8 index = 0; + + ENTER(); + + eth = (struct ethhdr *) skb->data; + switch (eth->h_proto) { + case __constant_htons(ETH_P_IP): + iph = ip_hdr(skb); + tid = IPTOS_PREC(iph->tos); + break; + case __constant_htons(ETH_P_ARP): + default: + break; + } +/** Offset for TOS field in the IP header */ +#define IPTOS_OFFSET 5 + tid = (tid >> IPTOS_OFFSET); + 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 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; + + ENTER(); + + PRINTM(MDATA, "%lu BSS(%d): Data <= kernel\n", jiffies, 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->cloned || + (skb_headroom(skb) < + (MLAN_MIN_DATA_HEADER_LEN + sizeof(mlan_buffer)))) { + 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)); + 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); + status = mlan_send_packet(priv->phandle->pmlan_adapter, pmbuf); + switch (status) { + case MLAN_STATUS_PENDING: + atomic_inc(&priv->phandle->tx_pending); + if (atomic_read(&priv->phandle->tx_pending) >= MAX_TX_PENDING) + woal_stop_queue(priv->netdev); + 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; + + ENTER(); + + len = strlen(a); + 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 { + PRINTM(MERROR, "Invalid char %c in string %s\n", a[i], a); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + } + *data = 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 = buf, *end; + int i; + + ENTER(); + + for (i = 0; i < ETH_ALEN; ++i) { + end = woal_strsep(&begin, ':', '/'); + if (end) + mac_addr[i] = woal_atox(end); + } + + 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; +#endif +#ifdef STA_CFG80211 + if (IS_STA_CFG80211(cfg80211_wext)) { + if (priv->bss_type == MLAN_BSS_TYPE_STA +#if defined(WIFI_DIRECT_SUPPORT) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,0,0) || defined(COMPAT_WIRELESS) + || priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT +#endif +#endif + ) + woal_cfg80211_sta_init_wiphy(priv, wait_option); + } +#endif + } +#endif /* STA_SUPPORT */ +#ifdef UAP_SUPPORT + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) { + priv->bss_started = MFALSE; +#ifdef UAP_CFG80211 + if (IS_UAP_CFG80211(cfg80211_wext)) + woal_cfg80211_uap_init_wiphy(priv, wait_option); +#endif + } +#endif + priv->media_connected = MFALSE; + woal_request_get_fw_info(priv, wait_option, NULL); + + 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 = priv->phandle; + mlan_bss_info bss_info; + + ENTER(); + + /* 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 + /* If scan is in process, cancel the scan command */ + if (handle->scan_pending_on_block == MTRUE) { + mlan_ioctl_req *req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_scan)); + if (req == NULL) { + ret = -EFAULT; + goto err_cancel_scan; + } + req->req_id = MLAN_IOCTL_SCAN; + req->action = MLAN_ACT_SET; + ((mlan_ds_scan *) req->pbuf)->sub_command = MLAN_OID_SCAN_CANCEL; + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, wait_option)) { + ret = -EFAULT; + goto err_cancel_scan; + } + err_cancel_scan: + handle->scan_pending_on_block = MFALSE; + MOAL_REL_SEMAPHORE(&handle->async_sem); + if (req) + kfree(req); + } +#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); + 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); + 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; + } else { + handle->reassoc_on = 0; + for (intf_num = 0; intf_num < handle->priv_num; intf_num++) { + handle->priv[intf_num]->reassoc_on = MFALSE; + } + } + if (!handle->reassoc_on && handle->is_reassoc_timer_set) { + woal_cancel_timer(&handle->reassoc_timer); + handle->is_reassoc_timer_set = MFALSE; + } +#endif /* REASSOCIATION */ + + 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; + + ENTER(); + if (!(pmbuf = kmalloc(sizeof(mlan_buffer), GFP_ATOMIC))) { + PRINTM(MERROR, "%s: Fail to alloc mlan buffer\n", __FUNCTION__); + LEAVE(); + return NULL; + } + memset((t_u8 *) pmbuf, 0, sizeof(mlan_buffer)); + if (!(skb = dev_alloc_skb(size))) { + PRINTM(MERROR, "%s: No free skb\n", __FUNCTION__); + kfree(pmbuf); + LEAVE(); + return NULL; + } + pmbuf->pdesc = (t_void *) skb; + pmbuf->pbuf = (t_u8 *) skb->data; + 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; + + ENTER(); + + if (! + (req = + (mlan_ioctl_req *) + kmalloc((sizeof(mlan_ioctl_req) + size + sizeof(int) + + sizeof(wait_queue)), GFP_ATOMIC))) { + PRINTM(MERROR, "%s: Fail to alloc ioctl buffer\n", __FUNCTION__); + LEAVE(); + return NULL; + } + memset((t_u8 *) req, 0, (sizeof(mlan_ioctl_req) + size + + sizeof(int) + sizeof(wait_queue))); + 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); + kfree(pmbuf); + handle->mbufalloc_count--; + LEAVE(); + return; +} + +#ifdef STA_WEXT +#endif + +/** + * @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 sock *sk = handle->nl_sk; + + ENTER(); + /* interface name to be prepended to event */ + if ((len + IFNAMSIZ) > NL_MAX_PAYLOAD) { + PRINTM(MERROR, "event size is too big, len=%d\n", (int) len); + ret = MLAN_STATUS_FAILURE; + goto done; + } + if (sk) { + /* Allocate skb */ + if (!(skb = alloc_skb(NLMSG_SPACE(NL_MAX_PAYLOAD), GFP_ATOMIC))) { + PRINTM(MERROR, "Could not allocate skb for netlink\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + 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), priv->netdev->name, IFNAMSIZ); + memcpy(((t_u8 *) (NLMSG_DATA(nlh))) + IFNAMSIZ, payload, len); + + /* From Kernel */ + NETLINK_CB(skb).pid = 0; + +#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 */ + netlink_broadcast(sk, skb, 0, NL_MULTICAST_GROUP, GFP_ATOMIC); + + 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; + wait_queue_t wait; + 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; + + 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; + + for (i = 0; i < handle->priv_num && (priv = handle->priv[i]); i++) { + + if (priv->reassoc_required == MFALSE) + continue; + + memset(&bss_info, 0x00, sizeof(bss_info)); + + if (MLAN_STATUS_SUCCESS != woal_get_bss_info(priv, + MOAL_CMD_WAIT, + &bss_info)) { + PRINTM(MINFO, "Ressoc: Fail to get bss info\n"); + priv->reassoc_required = 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; + continue; + } + + /* 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_CMD_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; + } + + /* Search AP by BSSID first */ + PRINTM(MINFO, "Reassoc: Search AP by BSSID first\n"); + memset(&ssid_bssid, 0, sizeof(mlan_ssid_bssid)); + memcpy(&ssid_bssid.bssid, + &priv->prev_ssid_bssid.bssid, MLAN_MAC_ADDR_LENGTH); + status = woal_find_best_network(priv, MOAL_CMD_WAIT, &ssid_bssid); + + if (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_CMD_WAIT, &ssid_bssid); + } + + if (status == MLAN_STATUS_SUCCESS) { + /* set the wep key */ + if (bss_info.wep_status) + woal_enable_wep_key(priv, MOAL_CMD_WAIT); + /* Zero SSID implies use BSSID to connect */ + memset(&ssid_bssid.ssid, 0, sizeof(mlan_802_11_ssid)); + status = woal_bss_start(priv, MOAL_CMD_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->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; + + if (MLAN_STATUS_SUCCESS + != woal_request_ioctl(priv, req, MOAL_CMD_WAIT)) { + kfree(req); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + if (req) + kfree(req); + } + } + + MOAL_REL_SEMAPHORE(&handle->reassoc_sem); + } + + if (handle->surprise_removed) + break; + + if (reassoc_timer_req == MTRUE) { + PRINTM(MEVENT, + "Reassoc: No AP found or assoc failed. " + "Restarting re-assoc Timer: %d\n", (int) timer_val); + handle->is_reassoc_timer_set = MTRUE; + woal_mod_timer(&handle->reassoc_timer, timer_val); + } + } + 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 Sends disconnect event + * + * @param priv A pointer to moal_private struct + * @return N/A + */ +t_void +woal_send_disconnect_to_system(moal_private * priv) +{ +#ifdef STA_WEXT + union iwreq_data wrqu; +#endif + + ENTER(); + priv->media_connected = MFALSE; + woal_stop_queue(priv->netdev); + if (netif_carrier_ok(priv->netdev)) + netif_carrier_off(priv->netdev); +#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 + LEAVE(); +} +#endif /* STA_SUPPORT */ + +/** + * @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 reg_table[] = { 0x28, 0x30, 0x34, 0x38, 0x3c }; + char buf[128], *ptr; + + 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 < 2) { + /* Read the registers of SDIO function0 and function1 */ + func = loop; + reg_start = 0; + reg_end = 9; + } 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; +#define SDIO_SCRATCH_REG 0x60 + reg_start = SDIO_SCRATCH_REG; + reg_end = SDIO_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]; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34) + int i = 0; +#endif + + 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)); + PRINTM(MERROR, "rx_pending = %d\n", atomic_read(&phandle->rx_pending)); + PRINTM(MERROR, "malloc_count = %u\n", phandle->malloc_count); + PRINTM(MERROR, "lock_count = %u\n", phandle->lock_count); + PRINTM(MERROR, "mbufalloc_count = %u\n", 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,34) + 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, 0))) ? "stopped" : + "started")); + } +#else + PRINTM(MERROR, "tx queue %s\n", + ((netif_queue_stopped(priv->netdev)) ? "stopped" : "started")); +#endif + } + + /* Display SDIO registers */ + if (flag && phandle->main_state == MOAL_END_MAIN_PROCESS) { + woal_sdio_reg_dbg(phandle); + } else { + phandle->sdio_reg_dbg = MTRUE; + queue_work(phandle->workqueue, &phandle->main_work); + } + + LEAVE(); + return; +} + +/** + * @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); + + ENTER(); + + if (handle->surprise_removed == MTRUE) { + LEAVE(); + return; + } + + if (handle->sdio_reg_dbg == MTRUE) { + handle->sdio_reg_dbg = MFALSE; + woal_sdio_reg_dbg(handle); + LEAVE(); + return; + } + + handle->main_state = MOAL_ENTER_WORK_QUEUE; + sdio_claim_host(((struct sdio_mmc_card *) handle->card)->func); + handle->main_state = MOAL_START_MAIN_PROCESS; + /* Call MLAN main process */ + mlan_main_process(handle->pmlan_adapter); + handle->main_state = MOAL_END_MAIN_PROCESS; + sdio_release_host(((struct sdio_mmc_card *) handle->card)->func); + + LEAVE(); +} + +/** + * @brief Handles interrupt + * + * @param handle A pointer to moal_handle struct + * + * @return N/A + */ +void +woal_interrupt(moal_handle * handle) +{ + ENTER(); + handle->main_state = MOAL_RECV_INT; + PRINTM(MINTR, "*\n"); + if (handle->surprise_removed == MTRUE) { + LEAVE(); + return; + } + /* call mlan_interrupt to read int status */ + mlan_interrupt(handle->pmlan_adapter); + 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(); +} + +/** + * @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; + + ENTER(); + + if (MOAL_ACQ_SEMAPHORE_BLOCK(&AddRemoveCardSem)) + goto exit_sem_err; + + /* Allocate buffer for moal_handle */ + if (!(handle = kmalloc(sizeof(moal_handle), GFP_KERNEL))) { + PRINTM(MERROR, "Allocate buffer for moal_handle failed!\n"); + goto err_handle; + } + + /* Init moal_handle */ + memset(handle, 0, sizeof(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) { + 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); + } + } + + ((struct sdio_mmc_card *) card)->handle = handle; +#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 + handle->nl_sk = + netlink_kernel_create(&init_net, netlink_num, NL_MULTICAST_GROUP, + NULL, NULL, THIS_MODULE); +#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 + handle->workqueue = create_workqueue("MOAL_WORK_QUEUE"); +#endif + if (!handle->workqueue) + goto err_kmalloc; + + MLAN_INIT_WORK(&handle->main_work, woal_main_work_queue); + +#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; + } + + /* 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: + /* 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 */ + woal_terminate_workqueue(handle); + err_kmalloc: + 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); + } + 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; + + /* Stop data */ + for (i = 0; i < MIN(handle->priv_num, MLAN_MAX_BSS_NUM); i++) { + if ((priv = handle->priv[i])) { + 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)); + } + + /* Remove interface */ + for (i = 0; i < handle->priv_num; i++) + woal_remove_interface(handle, i); + + woal_terminate_workqueue(handle); + +#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); + /* 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; +} + +/** + * @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_PROC_WAIT, MTRUE); + + status = woal_shutdown_fw(priv, MOAL_CMD_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)); + } + + /* Remove interface */ + for (i = 0; i < handle->priv_num; i++) + woal_remove_interface(handle, i); + + /* Unregister mlan */ + if (handle->pmlan_adapter) { + mlan_unregister(handle->pmlan_adapter); + if (handle->malloc_count || handle->lock_count) { + PRINTM(MERROR, + "mlan has memory leak: malloc_count=%u lock_count=%u\n", + handle->malloc_count, handle->lock_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; +} + +/** + * @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(); + + /* 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); + + /* Register with bus */ + ret = woal_bus_register(); + + wifi_add_dev(); + + 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; + + ENTER(); + + 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; +#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 +#endif /* SDIO_SUSPEND_RESUME */ + + 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_CMD_WAIT, NULL); +#ifdef STA_CFG80211 + if (handle->priv[i]->scan_request) { + cfg80211_scan_done(handle->priv[i]->scan_request, MTRUE); + handle->priv[i]->scan_request = NULL; + } +#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_CMD_WAIT, NULL); + } +#endif +#if defined(STA_CFG80211) || defined(UAP_CFG80211) + if (handle->priv[i]->probereq_index != MLAN_CUSTOM_IE_AUTO_IDX_MASK) + woal_cfg80211_mgmt_frame_ie(handle->priv[i], NULL, 0, NULL, 0, + NULL, 0, NULL, 0, + MGMT_MASK_PROBE_REQ); +#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_CMD_WAIT, 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_CMD_WAIT); + } + + wifi_del_dev(); + + exit: + MOAL_REL_SEMAPHORE(&AddRemoveCardSem); + exit_sem_err: + /* Unregister from bus */ + woal_bus_unregister(); + LEAVE(); +} + +module_init(woal_init_module); +module_exit(woal_cleanup_module); + +module_param(fw_name, charp, 0); +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(mac_addr, charp, 0); +MODULE_PARM_DESC(mac_addr, "MAC address"); +#ifdef MFG_CMD_SUPPORT +module_param(mfg_mode, int, 0); +MODULE_PARM_DESC(mfg_mode, + "0: Download normal firmware; 1: Download MFG firmware"); +#endif /* MFG_CMD_SUPPORT */ +module_param(drv_mode, int, 0); +#if defined(WIFI_DIRECT_SUPPORT) +MODULE_PARM_DESC(drv_mode, "Bit 0: STA; Bit 1: uAP; Bit 2: WIFIDIRECT"); +#else +MODULE_PARM_DESC(drv_mode, "Bit 0: STA; Bit 1: uAP"); +#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)"); +#endif /* STA_SUPPORT */ +#ifdef UAP_SUPPORT +module_param(max_uap_bss, int, 0); +MODULE_PARM_DESC(max_uap_bss, "Number of uAP interfaces (1)"); +#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)"); +#endif /* WIFI_DIRECT_SUPPORT && V14_FEATURE */ +#ifdef DEBUG_LEVEL1 +module_param(drvdbg, uint, 0); +MODULE_PARM_DESC(drvdbg, "Driver debug"); +#endif /* DEBUG_LEVEL1 */ +module_param(auto_ds, int, 0); +MODULE_PARM_DESC(auto_ds, + "0: MLAN default; 1: Enable auto deep sleep; 2: Disable auto deep sleep"); +module_param(ps_mode, int, 0); +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)"); +#ifdef SDIO_SUSPEND_RESUME +module_param(pm_keep_power, int, 1); +MODULE_PARM_DESC(pm_keep_power, "1: PM keep power; 0: PM no power"); +#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(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(minicard_pwrup, int, 0); +MODULE_PARM_DESC(minicard_pwrup, + "1: Driver load clears PDn/Rst, unload sets (default); 0: Don't do this."); +module_param(cfg80211_wext, int, 0); +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_DESCRIPTION("M-WLAN Driver"); +MODULE_AUTHOR("Marvell International Ltd."); +MODULE_VERSION(MLAN_RELEASE_VERSION); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/wireless/sd8797/mlinux/moal_main.h b/drivers/net/wireless/sd8797/mlinux/moal_main.h new file mode 100644 index 000000000000..0a753dabe488 --- /dev/null +++ b/drivers/net/wireless/sd8797/mlinux/moal_main.h @@ -0,0 +1,1510 @@ +/** @file moal_main.h + * + * @brief This file contains wlan driver specific defines etc. + * + * Copyright (C) 2008-2011, 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 + +#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 +#include + +/* Net header files */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "mlan.h" +#include "moal_shim.h" +/* Wireless header */ +#include +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#include +#include +#include +#endif +#if defined(STA_WEXT) || defined(UAP_WEXT) +#include +#include "moal_wext.h" +#endif +#ifdef STA_WEXT +#include "moal_priv.h" +#endif + +/** 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; + +/** moal_wait_option */ +enum +{ + MOAL_NO_WAIT, + MOAL_IOCTL_WAIT, + MOAL_CMD_WAIT, + MOAL_PROC_WAIT, + MOAL_WSTATS_WAIT +}; + +/** 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; + +#ifndef MIN +/** Find minimum */ +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + +/* + * 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; + +/** + * @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) +{ + del_timer(&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 + +/** Default watchdog timeout */ +#define MRVDRV_DEFAULT_WATCHDOG_TIMEOUT (5 * 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 + +/** Threshold value of number of times the Tx timeout happened */ +#define NUM_TX_TIMEOUT_THRESHOLD 5 + +/** 10 seconds */ +#define MOAL_TIMER_10S 10000 +/** 5 seconds */ +#define MOAL_TIMER_5S 5000 +/** 1 second */ +#define MOAL_TIMER_1S 1000 + +/** 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 + +/** 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 + +/** 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 + +/** Offset for TOS field in the IP header */ +#define IPTOS_OFFSET 5 + +/** Offset for DSCP in the tos field */ +#define DSCP_OFFSET 2 + +/** wait_queue structure */ +typedef struct _wait_queue +{ + /** Pointer to wait_queue_head */ + wait_queue_head_t *wait; + /** Wait condition */ + BOOLEAN condition; + /** Start time */ + t_u32 start_time; + /** Status from MLAN */ + mlan_status status; +} 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 1 +/** Default WIFIDIRECT BSS */ +#define DEF_WIFIDIRECT_BSS 1 +#endif /* WIFI_DIRECT_SUPPORT && V14_FEATURE */ + +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 + +/** 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; + /** MAC address information */ + t_u8 current_addr[ETH_ALEN]; + /** Media connection status */ + BOOLEAN media_connected; +#ifdef UAP_SUPPORT + /** uAP started or not */ + BOOLEAN bss_started; +#endif +#ifdef STA_SUPPORT + /** scan type */ + t_u8 scan_type; + /** bg_scan_start */ + t_u8 bg_scan_start; + /** bg_scan reported */ + t_u8 bg_scan_reported; + /** bg_scan config */ + wlan_bgscan_cfg scan_cfg; +#endif + /** Net device pointer */ + struct net_device *netdev; + /** Net device statistics structure */ + struct net_device_stats stats; +#if defined(STA_CFG80211) || defined(UAP_CFG80211) + /** Country code for regulatory domain */ + t_u8 country_code[COUNTRY_CODE_LEN]; + /** Wireless device pointer */ + struct wireless_dev *wdev; + /** channel parameter for UAP/GO */ + t_u16 channel; + /** cipher */ + t_u32 cipher; + /** key index */ + t_u8 key_index; + /** key len */ + t_u16 key_len; + /** key data */ + t_u8 key_material[MLAN_MAX_KEY_LENGTH]; + /** probereq index for mgmt ie */ + t_u16 probereq_index; +#endif +#ifdef STA_CFG80211 +#ifdef STA_SUPPORT + /** CFG80211 scan request description */ + struct cfg80211_scan_request *scan_request; + /** CFG80211 association description */ + t_u8 cfg_bssid[ETH_ALEN]; + /** Disconnect request from CFG80211 */ + bool cfg_disconnect; +#endif /* STA_SUPPORT */ +#endif /* STA_CFG80211 */ + /** IOCTL wait queue */ + wait_queue_head_t ioctl_wait_q __ATTRIB_ALIGN__; + /** CMD wait queue */ + wait_queue_head_t cmd_wait_q __ATTRIB_ALIGN__; +#ifdef CONFIG_PROC_FS + /** Proc entry */ + struct proc_dir_entry *proc_entry; + /** Proc entry name */ + t_s8 proc_entry_name[IFNAMSIZ]; + /** PROC wait queue */ + wait_queue_head_t proc_wait_q __ATTRIB_ALIGN__; +#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; +#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; + /** w_stats wait queue */ + wait_queue_head_t w_stats_wait_q __ATTRIB_ALIGN__; +#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 + +#ifdef PROC_DEBUG + /** MLAN debug info */ + struct debug_data_priv items_priv; +#endif +}; + +/** 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; + /** 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 *user_data; + /** Hotplug device */ + struct device *hotplug_device; + /** STATUS variables */ + MOAL_HARDWARE_STATUS hardware_status; + /** POWER MANAGEMENT AND PnP SUPPORT */ + BOOLEAN surprise_removed; + /** Firmware release number */ + t_u32 fw_release_number; + /** Init wait queue token */ + t_u16 init_wait_q_woken; + /** Init wait queue */ + wait_queue_head_t init_wait_q __ATTRIB_ALIGN__; + /** 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__; + /** 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; + /** Malloc count */ + t_u32 malloc_count; + /** lock count */ + t_u32 lock_count; + /** mlan buffer alloc count */ + t_u32 mbufalloc_count; + /** hs skip count */ + t_u32 hs_skip_count; + /** hs force count */ + t_u32 hs_force_count; + /** suspend_fail flag */ + BOOLEAN suspend_fail; +#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; +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#ifdef WIFI_DIRECT_SUPPORT + /** remain on channel flag */ + t_u8 remain_on_channel; + /** ieee802_11_channel */ + struct ieee80211_channel chan; + /** channel type */ + enum nl80211_channel_type channel_type; + /** cookie */ + t_u64 cookie; +#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 */ + t_u32 meas_start_jiffies; + /** CAC checking period flag */ + BOOLEAN cac_period; + /** BSS START command delay executing flag */ + BOOLEAN delay_bss_start; + /** SSID,BSSID parameter of delay executing */ + mlan_ssid_bssid delay_ssid_bssid; +#ifdef DFS_TESTING_SUPPORT + /** cac period length, valid only when dfs testing is enabled */ + t_u32 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; + /** Async scan semaphore */ + struct semaphore async_sem; + +#endif + /** main state */ + t_u8 main_state; + /** 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,34) + /** spinlock to stop_queue/wake_queue*/ + spinlock_t queue_lock; +#endif +}; + +/** + * @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,34) + unsigned int i; + for (i = 0; i < dev->num_tx_queues; i++) { + netdev_get_tx_queue(dev, i)->trans_start = jiffies; + } +#endif + dev->trans_start = jiffies; +} + +/** + * @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,34) + netif_start_queue(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,34) + 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,34) + 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 +} + +/** Max number of char in custom event - use multiple of them if needed */ +#define IW_CUSTOM_MAX 256 /* In bytes */ + +/** Debug Macro definition*/ +#ifdef DEBUG_LEVEL1 +extern t_u32 drvdbg; + +#ifdef DEBUG_LEVEL2 +#define PRINTM_MINFO(msg...) do {if (drvdbg & MINFO) printk(KERN_DEBUG msg);} while(0) +#define PRINTM_MWARN(msg...) do {if (drvdbg & MWARN) printk(KERN_DEBUG msg);} while(0) +#define PRINTM_MENTRY(msg...) do {if (drvdbg & MENTRY) printk(KERN_DEBUG msg);} while(0) +#else +#define PRINTM_MINFO(msg...) do {} while (0) +#define PRINTM_MWARN(msg...) do {} while (0) +#define PRINTM_MENTRY(msg...) do {} while (0) +#endif /* DEBUG_LEVEL2 */ + +#define PRINTM_MFW_D(msg...) do {if (drvdbg & MFW_D) printk(KERN_DEBUG msg);} while(0) +#define PRINTM_MCMD_D(msg...) do {if (drvdbg & MCMD_D) printk(KERN_DEBUG msg);} while(0) +#define PRINTM_MDAT_D(msg...) do {if (drvdbg & MDAT_D) printk(KERN_DEBUG msg);} while(0) +#define PRINTM_MIF_D(msg...) do {if (drvdbg & MIF_D) printk(KERN_DEBUG msg);} while(0) + +#define PRINTM_MIOCTL(msg...) do {if (drvdbg & MIOCTL) printk(KERN_DEBUG msg);} while(0) +#define PRINTM_MINTR(msg...) do {if (drvdbg & MINTR) printk(KERN_DEBUG msg);} while(0) +#define PRINTM_MEVENT(msg...) do {if (drvdbg & MEVENT) printk(msg);} while(0) +#define PRINTM_MCMND(msg...) do {if (drvdbg & MCMND) printk(KERN_DEBUG msg);} while(0) +#define PRINTM_MDATA(msg...) do {if (drvdbg & MDATA) printk(KERN_DEBUG msg);} while(0) +#define PRINTM_MERROR(msg...) do {if (drvdbg & MERROR) printk(KERN_ERR msg);} while(0) +#define PRINTM_MFATAL(msg...) do {if (drvdbg & MFATAL) printk(KERN_ERR msg);} while(0) +#define PRINTM_MMSG(msg...) do {if (drvdbg & MMSG) printk(KERN_ALERT msg);} while(0) + +#define PRINTM(level,msg...) PRINTM_##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", __FUNCTION__, __LINE__); \ + panic("Assert failed: Panic!"); \ + } \ +} 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__) + +#ifdef DEBUG_LEVEL1 +#define DBG_DUMP_BUF_LEN 64 +#define MAX_DUMP_PER_LINE 16 + +static inline void +hexdump(char *prompt, t_u8 * buf, int len) +{ + int i; + char dbgdumpbuf[DBG_DUMP_BUF_LEN]; + char *ptr = dbgdumpbuf; + + 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; + printk(KERN_DEBUG "%s\n", dbgdumpbuf); + ptr = dbgdumpbuf; + } + } + if (len % MAX_DUMP_PER_LINE) { + *ptr = 0; + printk(KERN_DEBUG "%s\n", dbgdumpbuf); + } +} + +#define DBG_HEXDUMP_MERROR(x,y,z) do {if (drvdbg & MERROR) hexdump(x,y,z);} while(0) +#define DBG_HEXDUMP_MCMD_D(x,y,z) do {if (drvdbg & MCMD_D) hexdump(x,y,z);} while(0) +#define DBG_HEXDUMP_MDAT_D(x,y,z) do {if (drvdbg & MDAT_D) hexdump(x,y,z);} while(0) +#define DBG_HEXDUMP_MIF_D(x,y,z) do {if (drvdbg & MIF_D) hexdump(x,y,z);} while(0) +#define DBG_HEXDUMP_MEVT_D(x,y,z) do {if (drvdbg & MEVT_D) hexdump(x,y,z);} while(0) +#define DBG_HEXDUMP_MFW_D(x,y,z) do {if (drvdbg & MFW_D) hexdump(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) hexdump(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; +} + +/** 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' + +/** 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); +/** 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); +/** switch driver mode */ +mlan_status woal_switch_drv_mode(moal_handle * handle, t_u32 mode); + +/** Interrupt handler */ +void woal_interrupt(moal_handle * handle); + +#ifdef STA_WEXT +#endif +/** 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); +/** 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 +/** 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); +mlan_status woal_request_soft_reset(moal_handle * handle); +#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); +/** 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); +/** 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); +/** Enable Host Sleep configuration */ +int woal_enable_hs(moal_private * priv); +/** hs active timeout 2 second */ +#define HS_ACTIVE_TIMEOUT (2 * HZ) + +/** set deep sleep */ +int woal_set_deep_sleep(moal_private * priv, t_u8 wait_option, + BOOLEAN bdeep_sleep, t_u16 idletime); + +/** 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); +#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); +/** Get signal information */ +mlan_status woal_get_signal_info(moal_private * priv, t_u8 wait_option, + mlan_ds_get_signal * signal); +#ifdef STA_WEXT +/** Get mode */ +t_u32 woal_get_mode(moal_private * priv, t_u8 wait_option); +/** 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, t_s8 * str); +/** Get statistics information */ +mlan_status woal_get_stats_info(moal_private * priv, t_u8 wait_option, + mlan_ds_get_stats * stats); +/** Get channel list */ +mlan_status woal_get_channel_list(moal_private * priv, t_u8 wait_option, + mlan_chan_list * chanlist); +#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 generic element */ +mlan_status woal_set_get_gen_ie(moal_private * priv, t_u32 action, t_u8 * ie, + int *ie_len); +/** 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); +/** 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); + +/** 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); + +/** 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_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); +/** 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) + +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(WIFI_DIRECT_SUPPORT) +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) +#if defined(STA_WEXT) || defined(UAP_WEXT) +int woal_set_get_bss_role(moal_private * priv, struct iwreq *wrq); +#endif +#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 + +#if defined(WIFI_DIRECT_SUPPORT) +mlan_status woal_set_remain_channel_ioctl(moal_private * priv, t_u8 wait_option, + mlan_ds_remain_chan * pchan); +mlan_status woal_cfg80211_wifi_direct_mode_cfg(moal_private * priv, + t_u16 action, t_u16 * mode); +#endif /* WIFI_DIRECT_SUPPORT */ + +#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); + +#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); + +int woal_hard_start_xmit(struct sk_buff *skb, struct net_device *dev); +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); +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_set_powermode(moal_private * priv, char *powermode); +int woal_find_essid(moal_private * priv, mlan_ssid_bssid * ssid_bssid); +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_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_set_qos_cfg(moal_private * priv, char *qos_cfg); +int woal_set_sleeppd(moal_private * priv, char *psleeppd); +mlan_status woal_set_rssi_low_threshold(moal_private * priv, char *rssi); +mlan_status woal_set_bg_scan(moal_private * priv, char *buf, int length); +mlan_status woal_stop_bg_scan(moal_private * priv); +void woal_reconfig_bgscan(moal_handle * handle); +#endif + +#endif /* _MOAL_MAIN_H */ diff --git a/drivers/net/wireless/sd8797/mlinux/moal_priv.c b/drivers/net/wireless/sd8797/mlinux/moal_priv.c new file mode 100644 index 000000000000..dc5cd82b7f44 --- /dev/null +++ b/drivers/net/wireless/sd8797/mlinux/moal_priv.c @@ -0,0 +1,6596 @@ +/** @file moal_priv.c + * + * @brief This file contains standard ioctl functions + * + * Copyright (C) 2008-2011, 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" + +#if defined(STA_CFG80211) && defined(UAP_CFG80211) +#include "moal_cfg80211.h" +#endif +#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 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; + + 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); + + /* 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; + if (MLAN_STATUS_SUCCESS != + woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + 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 Deep Sleep + * + * @param priv Pointer to the moal_private driver data struct + * @param deep_sleep Pointer to return deep_sleep setting + * + * @return 0 --success, otherwise fail + */ +static 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; + + 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; + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + *data = pm->param.auto_deep_sleep.auto_ds; + *(data + 1) = pm->param.auto_deep_sleep.idletime; + + done: + if (req) + kfree(req); + + 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; + t_u32 deep_sleep = DEEP_SLEEP_OFF; + t_u32 data[2]; + t_u16 idletime = DEEP_SLEEP_IDLE_TIME; + + ENTER(); + + if (wrq->u.data.length == 1 || wrq->u.data.length == 2) { + if (copy_from_user + (&data, wrq->u.data.pointer, wrq->u.data.length * sizeof(int))) { + 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 (wrq->u.data.length == 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 (wrq->u.data.length > 2) { + PRINTM(MERROR, "Invalid number of arguments %d\n", wrq->u.data.length); + 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]; + mlan_ioctl_req *req = NULL; + mlan_ds_11n_cfg *cfg_11n = NULL; + int ret = 0; + int data_length = wrq->u.data.length; + + ENTER(); + + if (data_length > 2) { + PRINTM(MERROR, "Invalid number of arguments\n"); + ret = -EINVAL; + goto done; + } + + if (((req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg))) == 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 (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, data_length * sizeof(int))) { + 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; + } + + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + 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; + if (MLAN_STATUS_SUCCESS != + woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + 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 (req) + 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]; + mlan_ioctl_req *req = NULL; + mlan_ds_11n_cfg *cfg_11n = NULL; + int ret = 0; + + ENTER(); + + if ((wrq->u.data.length != 0) && (wrq->u.data.length != 1)) { + 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_CFG_AMSDU_AGGR_CTRL; + req->req_id = MLAN_IOCTL_11N_CFG; + + if (wrq->u.data.length == 0) { + /* Get 11n tx parameters from MLAN */ + req->action = MLAN_ACT_GET; + } else if (wrq->u.data.length == 1) { + if (copy_from_user(data, wrq->u.data.pointer, + wrq->u.data.length * sizeof(int))) { + 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; + } + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + 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 (req) + 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]; + mlan_ioctl_req *req = NULL; + mlan_ds_11n_cfg *cfg_11n = NULL; + int ret = 0; + int data_length = wrq->u.data.length; + + 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_CFG_TX; + req->req_id = MLAN_IOCTL_11N_CFG; + + if (wrq->u.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, data_length * sizeof(int))) { + 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; + } + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + 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; + if (MLAN_STATUS_SUCCESS != + woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + 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 (req) + 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; + mlan_ioctl_req *req = NULL; + mlan_ds_11n_cfg *cfg_11n = NULL; + int ret = 0; + + ENTER(); + + if ((wrq->u.data.pointer == NULL)) { + LEAVE(); + return -EINVAL; + } + + 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 (wrq->u.data.length == 0) { + /* Get aggr priority table from MLAN */ + req->action = MLAN_ACT_GET; + if (MLAN_STATUS_SUCCESS != + woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + 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 (wrq->u.data.length == 16) { + if (copy_from_user(data, wrq->u.data.pointer, + sizeof(int) * wrq->u.data.length)) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto error; + } + for (i = 0, j = 0; i < (wrq->u.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; + if (MLAN_STATUS_SUCCESS != + woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto error; + } + } else { + PRINTM(MERROR, "Invalid number of arguments\n"); + ret = -EINVAL; + goto error; + } + + error: + if (req) + 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; + mlan_ioctl_req *req = NULL; + mlan_ds_11n_cfg *cfg_11n = NULL; + + ENTER(); + + 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 (wrq->u.data.length == 0) { + PRINTM(MERROR, "Addba reject moal\n"); + /* Get aggr priority table from MLAN */ + req->action = MLAN_ACT_GET; + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, + MOAL_IOCTL_WAIT)) { + 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 (wrq->u.data.length == 8) { + if (copy_from_user(data, wrq->u.data.pointer, + sizeof(int) * wrq->u.data.length)) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto error; + } + for (i = 0; i < (wrq->u.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; + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, + MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto error; + } + } else { + PRINTM(MERROR, "Invalid number of arguments\n"); + ret = -EINVAL; + goto error; + } + error: + if (req) + 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; + mlan_ioctl_req *req = NULL; + mlan_ds_11n_cfg *cfg_11n = NULL; + + ENTER(); + + 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 (wrq->u.data.length == 0) { + /* Get Add BA parameters from MLAN */ + req->action = MLAN_ACT_GET; + if (MLAN_STATUS_SUCCESS != + woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + 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 (wrq->u.data.length == 5) { + if (copy_from_user + (data, wrq->u.data.pointer, wrq->u.data.length * sizeof(int))) { + 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; + if (MLAN_STATUS_SUCCESS != + woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + ret = MLAN_STATUS_FAILURE; + goto error; + } + } else { + PRINTM(MERROR, "Invalid number of arguments\n"); + ret = -EINVAL; + goto error; + } + + error: + if (req) + 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; + + 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; + } + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + 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 (req) + 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]; + 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)); + + 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, sizeof(int) * data_length)) { + 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]; + 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; + + ENTER(); + + 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, sizeof(int) * data_length)) { + 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]; + } + + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + 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 (req) + 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]; + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *cfg = NULL; + int data_length = wrq->u.data.length; + int i = 0; + + ENTER(); + + memset(data, 0, sizeof(data)); + + 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, sizeof(int) * data_length)) { + 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; + if (MLAN_STATUS_SUCCESS != + woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + 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; + if (MLAN_STATUS_SUCCESS != + woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + 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]; + } + + if (MLAN_STATUS_SUCCESS != + woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + } + done: + if (req) + 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; + 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; + + 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; + } + } + + 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; + if (MLAN_STATUS_SUCCESS != + woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto error; + } + data[0] = radio_cfg->param.band_cfg.config_bands; /* Infra Band */ + data[1] = radio_cfg->param.band_cfg.adhoc_start_band; /* Adhoc Band */ + data[2] = radio_cfg->param.band_cfg.adhoc_channel; /* 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) { + data[3] = radio_cfg->param.band_cfg.sec_chan_offset; + 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, sizeof(int) * user_data_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.sec_chan_offset = adhoc_chan_bandwidth; + if (MLAN_STATUS_SUCCESS != + woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto error; + } + } + + error: + if (req) + 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]; + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_reg_mem *reg = NULL; + int data_length = wrq->u.data.length; + + ENTER(); + + memset(data, 0, sizeof(data)); + + 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, sizeof(int) * data_length)) { + 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]; + + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + 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 (req) + 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]; + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_reg_mem *reg = NULL; + int data_length = wrq->u.data.length; + + ENTER(); + + memset(data, 0, sizeof(data)); + + 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, sizeof(int) * data_length)) { + 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]; + + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + 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 (req) + 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; + + ENTER(); + + memset(data, 0, sizeof(data)); + + 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, sizeof(int) * data_length)) { + 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); + + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + 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 (req) + 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; + + ENTER(); + + PRINTM(MINFO, " GET STATS\n"); + if (!(buf = kmalloc(GETLOG_BUFSIZE, GFP_KERNEL))) { + 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", + 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]); + 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: + if (buf) + 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)) { + ret = -EFAULT; + goto done; + } + } else { + if (MLAN_STATUS_SUCCESS != woal_disconnect(priv, MOAL_IOCTL_WAIT, NULL)) + 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; + int ret = 0; + mlan_bss_info bss_info; + mlan_ds_power_cfg *pcfg = NULL; + mlan_ioctl_req *req = NULL; + 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; + + 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, sizeof(int) * user_data_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 ((unsigned int) 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 ((unsigned int) 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; + pcfg->param.power_ext.len = user_data_len; + memcpy((t_u8 *) & pcfg->param.power_ext.power_data, (t_u8 *) data, + sizeof(data)); + } + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + if (!user_data_len) { + if (copy_to_user + (wrq->u.data.pointer, (t_u8 *) & pcfg->param.power_ext.power_data, + sizeof(int) * pcfg->param.power_ext.len)) { + ret = -EFAULT; + goto done; + } + wrq->u.data.length = pcfg->param.power_ext.len; + } + done: + if (req) + 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; + + 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; + + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + 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 (req) + 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; + + 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; + } + + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + 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 (req) + 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; + + 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; + } + + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + 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 (req) + 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; + + 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_MCS15))) { + ret = -EINVAL; + goto done; + } + } + rate->param.rate_cfg.rate = rateindex; + } + + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + 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 (req) + 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; + + 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; + } + + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + 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 (req) + 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]; + int ret = 0; + + ENTER(); + + if (!wrq->u.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 (wrq->u.data.length < 3) { + /* Get the driver debug bit masks from user */ + if (copy_from_user + (data, wrq->u.data.pointer, sizeof(int) * wrq->u.data.length)) { + 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 "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; + + 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; + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + 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 (req) + 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; + + 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; + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + 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 (req) + 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; + + 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; + + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + 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 (req) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Configure sleep parameters + * + * @param priv A pointer to moal_private structure + * @param req A pointer to ifreq 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; +#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 + + 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_SLEEP_PARAMS; + req->req_id = MLAN_IOCTL_PM_CFG; + psleep_params = (pmlan_ds_sleep_params) & pm->param.sleep_params; + + if (wrq->u.data.length == 0) { + req->action = MLAN_ACT_GET; + } else if (wrq->u.data.length == 6) { + if (copy_from_user(data, wrq->u.data.pointer, sizeof(int) * + wrq->u.data.length)) { + /* 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; + } + + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + 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 (req) + 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; + + 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; + + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + 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 (req) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/get HT stream configurations + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * @return 0 --success, otherwise fail + */ +static int +woal_ht_stream_cfg_ioctl(moal_private * priv, struct iwreq *wrq) +{ + int ret = 0, data = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_11n_cfg *cfg = NULL; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + cfg = (mlan_ds_11n_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; + } + if (data != HT_STREAM_MODE_1X1 && data != HT_STREAM_MODE_2X2) { + PRINTM(MINFO, "Invalid argument\n"); + ret = -EINVAL; + goto done; + } + cfg->param.stream_cfg = data; + req->action = MLAN_ACT_SET; + } else + req->action = MLAN_ACT_GET; + + cfg->sub_command = MLAN_OID_11N_CFG_STREAM_CFG; + req->req_id = MLAN_IOCTL_11N_CFG; + + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + + /* Copy response to user */ + data = (int) cfg->param.stream_cfg; + 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 (req) + 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; + + 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; + + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + 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 (req) + 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; + + 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; + + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + 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 (req) + 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 user data + * + * @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; + + 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; + } + + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + 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 (req) + 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 user data + * + * @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; + + 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; + } + + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + 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 (req) + 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 user data + * + * @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; + + 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; + + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + + done: + if (req) + 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 user data + * + * @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; + + 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; + + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + + done: + if (req) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set WPA passphrase and SSID + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to user data + * + * @return 0 --success, otherwise fail + */ +static int +woal_passphrase(moal_private * priv, struct iwreq *wrq) +{ + t_u16 len = 0; + static char buf[256]; + char *begin, *end, *opt; + 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; + + ENTER(); + + if (!wrq->u.data.length || wrq->u.data.length >= sizeof(buf)) { + PRINTM(MERROR, "Argument missing or too long for setpassphrase\n"); + ret = -EINVAL; + goto done; + } + + if (copy_from_user(buf, wrq->u.data.pointer, wrq->u.data.length)) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + buf[wrq->u.data.length] = '\0'; + + /* Parse the buf to get the cmd_action */ + begin = buf; + 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; + } + 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, strlen(end)); + 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; + strncpy(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; + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + 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 (req) + 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; + + 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; + + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + 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 (req) + 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 user data + * + * @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; + 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 }; + + ENTER(); + + memset(key_ascii, 0x00, sizeof(key_ascii)); + memset(key_hex, 0x00, sizeof(key_hex)); + + /* 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 (wrq->u.data.length) { + if (wrq->u.data.length >= sizeof(buf)) { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + if (copy_from_user(buf, wrq->u.data.pointer, wrq->u.data.length)) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + buf[wrq->u.data.length] = '\0'; + + if (wrq->u.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; + if (MLAN_STATUS_SUCCESS != + woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + 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 (wrq->u.data.length >= 2) { + /* 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); + + if (MLAN_STATUS_SUCCESS != + woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + } else if (action == 2) { + /* 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)); + + if (MLAN_STATUS_SUCCESS != + woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + } else { + PRINTM(MERROR, "Invalid argument\n"); + ret = -EINVAL; + 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 (req) + 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; + + 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; + misc->param.gen_ie.len = wrq->u.data.length; + + /* get the whole command from user */ + if (copy_from_user + (misc->param.gen_ie.ie_data, wrq->u.data.pointer, wrq->u.data.length)) { + PRINTM(MERROR, "copy from user failed\n"); + ret = -EFAULT; + goto done; + } + + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + done: + if (req) + 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]; + struct iwreq *wreq = (struct iwreq *) wrq; + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_misc_cfg *misc = NULL; + int ret = 0, op_code = 0, data_length = wrq->u.data.length; + + 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, wreq->u.data.pointer, + MIN(IPADDR_MAX_BUF - 1, wreq->u.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), (wreq->u.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[0]) != 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 */ + if (MLAN_STATUS_SUCCESS != + woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT)) { + 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 (ioctl_req) + 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; + + 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 */ + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + 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 (req) + 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 TRIGGER_SOUNDING_FOR_PEER: + data_length += sprintf(buf + data_length, + "%02x:%02x:%02x:%02x:%02x:%02x", + bf_sound->peer_mac[0], bf_sound->peer_mac[1], + bf_sound->peer_mac[2], bf_sound->peer_mac[3], + bf_sound->peer_mac[4], + bf_sound->peer_mac[5]); + data_length += sprintf(buf + data_length, "%c", ';'); + data_length += sprintf(buf + data_length, "%d", bf_sound->status); + 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; + default: + ret = -EINVAL; + goto done; + } + 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 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 + */ +static 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(); + + if (copy_to_user(ptmp_buf, pbss_desc->time_stamp, + sizeof(pbss_desc->time_stamp))) { + PRINTM(MINFO, "Copy to user failed\n"); + LEAVE(); + return; + } + ptmp_buf += sizeof(pbss_desc->time_stamp); + + if (copy_to_user(ptmp_buf, &pbss_desc->beacon_period, + sizeof(pbss_desc->beacon_period))) { + PRINTM(MINFO, "Copy to user failed\n"); + LEAVE(); + return; + } + ptmp_buf += sizeof(pbss_desc->beacon_period); + + if (copy_to_user + (ptmp_buf, &pbss_desc->cap_info, sizeof(pbss_desc->cap_info))) { + PRINTM(MINFO, "Copy to user failed\n"); + LEAVE(); + return; + } + 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; + if (copy_to_user(ptmp_buf, tmp_ssid_hdr, sizeof(tmp_ssid_hdr))) { + PRINTM(MINFO, "Copy to user failed\n"); + LEAVE(); + return; + } + ptmp_buf += sizeof(tmp_ssid_hdr); + + if (copy_to_user(ptmp_buf, pbss_desc->ssid.ssid, pbss_desc->ssid.ssid_len)) { + PRINTM(MINFO, "Copy to user failed\n"); + LEAVE(); + return; + } + 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; + if (copy_to_user(ptmp_buf, &pbss_desc->wmm_ie, ie_len)) { + PRINTM(MINFO, "Copy to user failed\n"); + LEAVE(); + return; + } + + 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; + if (copy_to_user(ptmp_buf, pbss_desc->pwpa_ie, ie_len)) { + PRINTM(MINFO, "Copy to user failed\n"); + LEAVE(); + return; + } + } + + 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; + if (copy_to_user(ptmp_buf, pbss_desc->prsn_ie, ie_len)) { + PRINTM(MINFO, "Copy to user failed\n"); + LEAVE(); + return; + } + } + + 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 + */ +static 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 + */ + if (copy_to_user(pcurrent, &tmp_rsp_entry, fixed_size)) { + PRINTM(MINFO, "Copy to user failed\n"); + LEAVE(); + return -EFAULT; + } + + pcurrent += fixed_size; + + if (pbss_desc->pbeacon_buf) { + /* + * Copy variable length elements to user space + */ + if (copy_to_user(pcurrent, pbss_desc->pbeacon_buf, + pbss_desc->beacon_buf_size)) { + PRINTM(MINFO, "Copy to user failed\n"); + LEAVE(); + return -EFAULT; + } + + pcurrent += pbss_desc->beacon_buf_size; + } else { + wlan_scan_create_brief_table_entry(&pcurrent, pbss_desc); + } + + *ppbuffer = pcurrent; + + LEAVE(); + + return MLAN_STATUS_SUCCESS; +} + +/** + * @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; + t_u32 scan_start; + 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) { + 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 (req && (status != MLAN_STATUS_PENDING)) + kfree(req); + LEAVE(); + return status; +} + +/** + * @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; + + /* 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; + MOAL_REL_SEMAPHORE(&handle->async_sem); + + done: + if (req && (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(); + + if (!(buf = (t_u8 *) kmalloc(WOAL_2K_BYTES, GFP_KERNEL))) { + PRINTM(MERROR, "Cannot allocate buffer for command!\n"); + ret = -EFAULT; + goto done; + } + if (!(data = (t_u8 *) kmalloc(WOAL_2K_BYTES, GFP_KERNEL))) { + 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, sizeof(data)); + + /* 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: + if (buf) + kfree(buf); + if (data) + 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; + int ret = 0; + mlan_ds_misc_cfg *misc = NULL; + mlan_ioctl_req *req = NULL; + + ENTER(); + + if (sizeof(int) * wrq->u.data.length > sizeof(data)) { + PRINTM(MERROR, "Too many arguments\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(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; + if ((ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) != + 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 = sizeof(data) / sizeof(int); + goto done; + } + + if (copy_from_user(data, wrq->u.data.pointer, sizeof(int) * data_length)) { + 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; + } + + if ((ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT) != + MLAN_STATUS_SUCCESS)) { + PRINTM(MERROR, "woal_request_ioctl returned %d\n", ret); + ret = -EFAULT; + goto done; + } + + done: + if (req) + 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]; + 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 = -ENOMEM; + goto done; + } + if (wrq->u.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 (wrq->u.data.length) { + if (copy_from_user + (data, wrq->u.data.pointer, (wrq->u.data.length * sizeof(int)))) { + 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; + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + if (!wrq->u.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 = sizeof(data) / sizeof(int); + } + done: + if (req) + 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], ret = 0; + mlan_ds_pm_cfg *pm_cfg = NULL; + mlan_ioctl_req *req = NULL; + int allowed = 3; + int i = 3; + + 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; + } + if (wrq->u.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 (wrq->u.data.length) { + if (copy_from_user + (data, wrq->u.data.pointer, (wrq->u.data.length * sizeof(int)))) { + 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 (wrq->u.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; + + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + 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; + woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + } + + done: + if (req) + 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; + + 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 = (t_u8) addts_ioctl.ie_data_len; + + memcpy(cfg->param.addts.ie_data, + addts_ioctl.ie_data, cfg->param.addts.ie_data_len); + + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, + MOAL_IOCTL_WAIT)) { + 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 (req) + 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; + + 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; + + memcpy(cfg->param.delts.ie_data, + delts_ioctl.ie_data, cfg->param.delts.ie_data_len); + + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, + MOAL_IOCTL_WAIT)) { + 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 (req) + 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; + + 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; + + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, + MOAL_IOCTL_WAIT)) { + 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 (req) + 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; + + 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); + + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, + MOAL_IOCTL_WAIT)) { + 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 (req) + 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; + + 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)) { + if (MLAN_STATUS_SUCCESS != + woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + 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 (req) + 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; + + 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; + + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, + MOAL_IOCTL_WAIT)) { + 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 (req) + 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; + 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 */ + if (woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT) != 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 (req) + 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]; + int data_length = wrq->u.data.length; + ENTER(); + + /* 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, sizeof(int) * data_length)) { + 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 */ + if (woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT) != 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 (req) + 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; + + 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 */ + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + 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 (req) + 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[2] = { 0 }; + + ENTER(); + + if (wrq->u.data.length > 2) { + PRINTM(MERROR, "Invalid number of argument!\n"); + ret = -EFAULT; + 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; + if (wrq->u.data.length) { + if (copy_from_user(data, wrq->u.data.pointer, sizeof(data))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + radio->param.ant_cfg.tx_antenna = data[0]; + radio->param.ant_cfg.rx_antenna = data[0]; + if (wrq->u.data.length == 2) + radio->param.ant_cfg.rx_antenna = data[1]; + req->action = MLAN_ACT_SET; + } else + req->action = MLAN_ACT_GET; + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + if (!wrq->u.data.length) { + wrq->u.data.length = 1; + data[0] = radio->param.ant_cfg.tx_antenna; + data[1] = radio->param.ant_cfg.rx_antenna; + if (data[0] && data[1] && (data[0] != data[1])) + wrq->u.data.length = 2; + if (copy_to_user(wrq->u.data.pointer, data, sizeof(data))) { + ret = -EFAULT; + goto done; + } + } + done: + if (req) + 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_AUTH_TYPE: + ret = woal_auth_type(priv, wrq); + break; + case WOAL_PORT_CTRL: + ret = woal_port_ctrl(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_HT_STREAM_CFG: + ret = woal_ht_stream_cfg_ioctl(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; +#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_SET_GET_TX_RX_ANT: + ret = woal_set_get_tx_rx_ant(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_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_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)) { + ret = -EFAULT; + } + break; +#endif + default: + ret = -EINVAL; + break; + } + + LEAVE(); + return ret; +} + +/** + * @brief Get mode + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option (MOAL_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 = priv->w_stats.status; + 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_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 = IW_MODE_INFRA; + break; + case MLAN_BSS_MODE_IBSS: + mode = IW_MODE_ADHOC; + break; + default: + mode = IW_MODE_AUTO; + break; + } + } + done: + if (req && (status != MLAN_STATUS_PENDING)) + kfree(req); + LEAVE(); + return mode; +} + +/** + * @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; + info->sub_command = MLAN_OID_GET_STATS; + 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)); + 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; + } + done: + if (req && (status != MLAN_STATUS_PENDING)) + kfree(req); + LEAVE(); + return status; +} + +/** + * @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 (req && (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 (req && (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/sd8797/mlinux/moal_priv.h b/drivers/net/wireless/sd8797/mlinux/moal_priv.h new file mode 100644 index 000000000000..de491f6d6a29 --- /dev/null +++ b/drivers/net/wireless/sd8797/mlinux/moal_priv.h @@ -0,0 +1,715 @@ +/** @file moal_priv.h + * + * @brief This file contains definition for extended private IOCTL call. + * + * Copyright (C) 2008-2011, 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_ + +/** 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 + +/** 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 +/** 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 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 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 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 auth type */ +#define WOAL_AUTH_TYPE 18 +/** Private command ID to set/get port control */ +#define WOAL_PORT_CTRL 19 +#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 11N HT stream configuration */ +#define WOAL_HT_STREAM_CFG 23 +/** 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 +/** 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 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_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"}, +#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_HT_STREAM_CFG, + IW_PRIV_TYPE_INT | 1, + IW_PRIV_TYPE_INT | 1, + "htstreamcfg"}, + { + 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"}, +#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_SET_GET_TX_RX_ANT, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_INT | 16, + "antcfg"}, + { + 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_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_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/sd8797/mlinux/moal_proc.c b/drivers/net/wireless/sd8797/mlinux/moal_proc.c new file mode 100644 index 000000000000..cd292351f827 --- /dev/null +++ b/drivers/net/wireless/sd8797/mlinux/moal_proc.c @@ -0,0 +1,631 @@ +/** @file moal_proc.c + * + * @brief This file contains functions for proc file. + * + * Copyright (C) 2008-2010, 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/" +/** Proc top level directory entry */ +struct proc_dir_entry *proc_mwlan = NULL; +#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 page Pointer to buffer + * @param start Read data starting position + * @param offset Offset + * @param count Counter + * @param eof End of file flag + * @param data Data to output + * + * @return Number of output data + */ +static int +woal_info_proc_read(char *page, char **start, off_t offset, + int count, int *eof, void *data) +{ + char *p = page; + struct net_device *netdev = data; + char fmt[MLAN_MAX_VER_STR_LEN]; + moal_private *priv = (moal_private *) netdev_priv(netdev); +#ifdef STA_SUPPORT + int i = 0; + moal_handle *handle = priv->phandle; + 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,34) + int i = 0; +#endif /* >= 2.6.34 */ +#endif +#ifdef UAP_SUPPORT + mlan_ds_uap_stats ustats; +#endif + + ENTER(); + + if (offset) { + *eof = 1; + goto exit; + } + memset(fmt, 0, sizeof(fmt)); +#ifdef UAP_SUPPORT + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) { + p += sprintf(p, "driver_name = " "\"uap\"\n"); + woal_uap_get_version(priv, fmt, sizeof(fmt) - 1); + if (MLAN_STATUS_SUCCESS != + woal_uap_get_stats(priv, MOAL_PROC_WAIT, &ustats)) { + *eof = 1; + goto exit; + } + } +#endif /* UAP_SUPPORT */ +#ifdef STA_SUPPORT + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) { + woal_get_version(handle, fmt, sizeof(fmt) - 1); + memset(&info, 0, sizeof(info)); + if (MLAN_STATUS_SUCCESS != + woal_get_bss_info(priv, MOAL_PROC_WAIT, &info)) { + *eof = 1; + goto exit; + } + p += sprintf(p, "driver_name = " "\"wlan\"\n"); + } +#endif + p += sprintf(p, "driver_version = %s", fmt); + p += sprintf(p, "\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) + p += sprintf(p, "bss_mode = \"WIFIDIRECT-Client\"\n"); + else + p += sprintf(p, "bss_mode = \"WIFIDIRECT-GO\"\n"); + } +#endif +#ifdef STA_SUPPORT + if (priv->bss_type == MLAN_BSS_TYPE_STA) + p += sprintf(p, "bss_mode =\"%s\"\n", szModes[info.bss_mode]); +#endif + p += sprintf(p, "media_state=\"%s\"\n", + ((priv->media_connected == + MFALSE) ? "Disconnected" : "Connected")); + p += sprintf(p, "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) { + p += sprintf(p, "multicast_count=\"%d\"\n", mc_count); + p += sprintf(p, "essid=\"%s\"\n", info.ssid.ssid); + p += sprintf(p, "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]); + p += sprintf(p, "channel=\"%d\"\n", (int) info.bss_chan); + p += sprintf(p, "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++) { + p += sprintf(p, + "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) + p += sprintf(p, + "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 + p += sprintf(p, "num_tx_bytes = %lu\n", priv->stats.tx_bytes); + p += sprintf(p, "num_rx_bytes = %lu\n", priv->stats.rx_bytes); + p += sprintf(p, "num_tx_pkts = %lu\n", priv->stats.tx_packets); + p += sprintf(p, "num_rx_pkts = %lu\n", priv->stats.rx_packets); + p += sprintf(p, "num_tx_pkts_dropped = %lu\n", priv->stats.tx_dropped); + p += sprintf(p, "num_rx_pkts_dropped = %lu\n", priv->stats.rx_dropped); + p += sprintf(p, "num_tx_pkts_err = %lu\n", priv->stats.tx_errors); + p += sprintf(p, "num_rx_pkts_err = %lu\n", priv->stats.rx_errors); + p += sprintf(p, "carrier %s\n", + ((netif_carrier_ok(priv->netdev)) ? "on" : "off")); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34) + for (i = 0; i < netdev->num_tx_queues; i++) { + p += sprintf(p, "tx queue %d: %s\n", i, + ((netif_tx_queue_stopped(netdev_get_tx_queue(netdev, 0))) ? + "stopped" : "started")); + } +#else + p += sprintf(p, "tx queue %s\n", + ((netif_queue_stopped(priv->netdev)) ? "stopped" : "started")); +#endif +#ifdef UAP_SUPPORT + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) { + p += sprintf(p, "tkip_mic_failures = %u\n", ustats.tkip_mic_failures); + p += sprintf(p, "ccmp_decrypt_errors = %u\n", + ustats.ccmp_decrypt_errors); + p += sprintf(p, "wep_undecryptable_count = %u\n", + ustats.wep_undecryptable_count); + p += sprintf(p, "wep_icv_error_count = %u\n", + ustats.wep_icv_error_count); + p += sprintf(p, "decrypt_failure_count = %u\n", + ustats.decrypt_failure_count); + p += sprintf(p, "mcast_tx_count = %u\n", ustats.mcast_tx_count); + p += sprintf(p, "failed_count = %u\n", ustats.failed_count); + p += sprintf(p, "retry_count = %u\n", ustats.retry_count); + p += sprintf(p, "multiple_retry_count = %u\n", + ustats.multi_retry_count); + p += sprintf(p, "frame_duplicate_count = %u\n", ustats.frame_dup_count); + p += sprintf(p, "rts_success_count = %u\n", ustats.rts_success_count); + p += sprintf(p, "rts_failure_count = %u\n", ustats.rts_failure_count); + p += sprintf(p, "ack_failure_count = %u\n", ustats.ack_failure_count); + p += sprintf(p, "rx_fragment_count = %u\n", ustats.rx_fragment_count); + p += sprintf(p, "mcast_rx_frame_count = %u\n", + ustats.mcast_rx_frame_count); + p += sprintf(p, "fcs_error_count = %u\n", ustats.fcs_error_count); + p += sprintf(p, "tx_frame_count = %u\n", ustats.tx_frame_count); + p += sprintf(p, "rsna_tkip_cm_invoked = %u\n", + ustats.rsna_tkip_cm_invoked); + p += sprintf(p, "rsna_4way_hshk_failures = %u\n", + ustats.rsna_4way_hshk_failures); + } +#endif /* UAP_SUPPORT */ + exit: + LEAVE(); + return (p - page); +} + +#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 __user * buffer, size_t len, int *func, int *reg, + int *val) +{ + int ret = MLAN_STATUS_SUCCESS; + char *string = NULL; + char *pos = NULL; + + ENTER(); + + string = (char *) kmalloc(CMD52_STR_LEN, GFP_KERNEL); + memset(string, 0, CMD52_STR_LEN); + memcpy(string, buffer + strlen("sdcmd52rw="), len - strlen("sdcmd52rw=")); + string = strstrip(string); + + *func = -1; + *reg = -1; + *val = -1; + + /* Get func */ + pos = strsep(&string, " \t"); + if (pos) { + *func = 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); + } + if (string) + kfree(string); + LEAVE(); + return ret; +} + +/** + * @brief config proc write function + * + * @param f file pointer + * @param buf pointer to data buffer + * @param cnt data number to write + * @param data data to write + * @return number of data + */ +static int +woal_config_write(struct file *f, const char *buf, unsigned long cnt, + void *data) +{ + char databuf[101]; + char *line; + t_u32 config_data = 0; + moal_handle *handle = (moal_handle *) data; + int func, reg, val; + + ENTER(); + if (!MODULE_GET) { + LEAVE(); + return 0; + } + + if (cnt >= sizeof(databuf)) { + MODULE_PUT; + LEAVE(); + return (int) cnt; + } + memset(databuf, 0, sizeof(databuf)); + if (copy_from_user(databuf, buf, cnt)) { + 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="))) { + parse_cmd52_string(databuf, (size_t) cnt, &func, ®, &val); + woal_sdio_read_write_cmd52(handle, func, reg, val); + } + MODULE_PUT; + LEAVE(); + return (int) cnt; +} + +/** + * @brief config proc read function + * + * @param page pointer to buffer + * @param s read data starting position + * @param off offset + * @param cnt counter + * @param eof end of file flag + * @param data data to output + * @return number of output data + */ +static int +woal_config_read(char *page, char **s, off_t off, int cnt, int *eof, void *data) +{ + char *p = page; + moal_handle *handle = (moal_handle *) data; + + ENTER(); + if (!MODULE_GET) { + LEAVE(); + return 0; + } + p += sprintf(p, "hardware_status=%d\n", (int) handle->hardware_status); + p += sprintf(p, "netlink_num=%d\n", (int) handle->netlink_num); + p += sprintf(p, "drv_mode=%d\n", (int) drv_mode); + p += sprintf(p, "sdcmd52rw=%d 0x%0x 0x%02X\n", handle->cmd52_func, + handle->cmd52_reg, handle->cmd52_val); + MODULE_PUT; + LEAVE(); + return p - page; +} + +/******************************************************** + 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", 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_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_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"); + + r = create_proc_entry(config_proc_dir, 0644, handle->proc_mwlan); + + if (r) { + r->data = handle; + r->read_proc = woal_config_read; + r->write_proc = woal_config_write; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30) + r->owner = THIS_MODULE; +#endif + } else + PRINTM(MMSG, "Fail to create proc config\n"); + } +#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); + + /* 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 { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) + atomic_dec(&(handle->proc_mwlan->count)); +#endif + remove_proc_entry("mwlan", PROC_DIR); + handle->proc_mwlan = NULL; +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,26) + proc_mwlan = NULL; +#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 net_device *dev = priv->netdev; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) + char proc_dir_name[20]; +#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); + 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 (!priv->phandle->proc_mwlan) { + priv->phandle->proc_mwlan = priv->proc_entry->parent; + } + atomic_inc(&(priv->phandle->proc_mwlan->count)); + } else { + /* Failure. mwlan may not exist. Try to create that first */ + priv->phandle->proc_mwlan = proc_mkdir("mwlan", 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); + atomic_inc(&(priv->phandle->proc_mwlan->count)); + } + } +#else + if (priv->phandle->proc_mwlan && !priv->proc_entry) { + priv->proc_entry = proc_mkdir(dev->name, priv->phandle->proc_mwlan); + atomic_inc(&(priv->phandle->proc_mwlan->count)); +#endif + strcpy(priv->proc_entry_name, dev->name); + if (priv->proc_entry) { + create_proc_read_entry("info", 0, priv->proc_entry, + woal_info_proc_read, dev); + } + } + + 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); + atomic_dec(&(priv->phandle->proc_mwlan->count)); + priv->proc_entry = NULL; + } + LEAVE(); +} +#endif diff --git a/drivers/net/wireless/sd8797/mlinux/moal_sdio.h b/drivers/net/wireless/sd8797/mlinux/moal_sdio.h new file mode 100644 index 000000000000..5a5bc2c3718c --- /dev/null +++ b/drivers/net/wireless/sd8797/mlinux/moal_sdio.h @@ -0,0 +1,140 @@ +/** @file moal_sdio.h + * + * @brief This file contains definitions for SDIO interface. + * driver. + * + * Copyright (C) 2008-2011, 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 + +/** SD8797 chip revision ID */ +#define SD8797_A0 0x00 +#define SD8797_B0 0x10 + +#define SD8797_A0_FW_NAME "mrvl/sd8797_uapsta_a0.bin" +#define SD8797_B0_FW_NAME "mrvl/sd8797_uapsta.bin" + +#ifdef STA_SUPPORT +/** Default firmware name */ + +#define DEFAULT_FW_NAME "mrvl/sd8797_uapsta.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/sd8797_uapsta.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/sd8797_uapsta.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/sd8797/mlinux/moal_sdio_mmc.c b/drivers/net/wireless/sd8797/mlinux/moal_sdio_mmc.c new file mode 100644 index 000000000000..94d2a1eef004 --- /dev/null +++ b/drivers/net/wireless/sd8797/mlinux/moal_sdio_mmc.c @@ -0,0 +1,728 @@ +/** @file moal_sdio_mmc.c + * + * @brief This file contains SDIO MMC IF (interface) module + * related functions. + * + * Copyright (C) 2008-2011, 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 + +/******************************************************** + Local Variables +********************************************************/ + +/******************************************************** + Global Variables +********************************************************/ + +#ifdef SDIO_SUSPEND_RESUME +/** PM keep power */ +extern int pm_keep_power; +#endif + +/** Device ID for SD8797 */ +#define SD_DEVICE_ID_8797 (0x9129) + +/** WLAN IDs */ +static const struct sdio_device_id wlan_ids[] = { + {SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8797)}, + {}, +}; + +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, +}; +#endif +#endif +static struct sdio_driver 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, +#endif +#endif + } +#else +#ifdef SDIO_SUSPEND_RESUME +#ifdef MMC_PM_KEEP_POWER + .drv = { + .pm = &wlan_sdio_pm_ops, + } +#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; + data = + sdio_f0_readb(((struct sdio_mmc_card *) handle->card)->func, 0x05, + &ret); + PRINTM(MMSG, "fun0: reg 0x05=0x%x ret=%d\n", data, ret); + data = + sdio_f0_readb(((struct sdio_mmc_card *) handle->card)->func, 0x04, + &ret); + PRINTM(MMSG, "fun0: reg 0x04=0x%x ret=%d\n", data, ret); + data = + sdio_readb(((struct sdio_mmc_card *) handle->card)->func, 0x03, &ret); + PRINTM(MMSG, "fun1: reg 0x03=0x%x ret=%d\n", data, ret); + data = + sdio_readb(((struct sdio_mmc_card *) handle->card)->func, 0x04, &ret); + PRINTM(MMSG, "fun1: reg 0x04=0x%x ret=%d\n", data, ret); + data = + sdio_readb(((struct sdio_mmc_card *) handle->card)->func, 0x05, &ret); + PRINTM(MMSG, "fun1: reg 0x05=0x%x ret=%d\n", data, ret); + data = + sdio_readb(((struct sdio_mmc_card *) handle->card)->func, 0x60, &ret); + PRINTM(MMSG, "fun1: reg 0x60=0x%x ret=%d\n", data, ret); + data = + sdio_readb(((struct sdio_mmc_card *) handle->card)->func, 0x61, &ret); + PRINTM(MMSG, "fun1: reg 0x61=0x%x ret=%d\n", 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(MINFO, "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(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(); + + PRINTM(MINFO, "SDIO func=%d\n", func->num); + + if (func) { + 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 + +/** @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; + int ret = MLAN_STATUS_SUCCESS; + int hs_actived = 0; + mlan_ds_ps_info pm_info; + + ENTER(); + PRINTM(MCMND, "<--- Enter woal_sdio_suspend --->\n"); + if (func) { + 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; + } + } else { + PRINTM(MERROR, "sdio_func is not specified\n"); + LEAVE(); + return MLAN_STATUS_SUCCESS; + } + + handle = cardp->handle; + handle->suspend_fail = MFALSE; + memset(&pm_info, 0, sizeof(pm_info)); + if (MLAN_STATUS_SUCCESS == + woal_get_pm_info(woal_get_priv(handle, MLAN_BSS_ROLE_ANY), &pm_info)) { + if (pm_info.is_suspend_allowed == MFALSE) { + PRINTM(MMSG, "suspend not allowed!"); + 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!"); + 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"); + if (func) { + 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; + } + } else { + PRINTM(MERROR, "sdio_func is not specified\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; + 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; + sdio_writeb(((struct sdio_mmc_card *) handle->card)->func, (t_u8) data, reg, + (int *) &ret); + 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; + + val = + sdio_readb(((struct sdio_mmc_card *) handle->card)->func, reg, + (int *) &ret); + *data = val; + + return ret; +} + +/** + * @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); +#ifdef SDIO_MMC_DEBUG + handle->cmd53w = 1; +#endif + if (!sdio_writesb + (((struct sdio_mmc_card *) handle->card)->func, ioport, buffer, + blkcnt * blksz)) + ret = MLAN_STATUS_SUCCESS; +#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); + +#ifdef SDIO_MMC_DEBUG + handle->cmd53r = 1; +#endif + if (!sdio_readsb + (((struct sdio_mmc_card *) handle->card)->func, buffer, ioport, + blkcnt * blksz)) + ret = MLAN_STATUS_SUCCESS; + else + woal_dump_sdio_reg(handle); +#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; + } + + 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); + + 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(); + + 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/sd8797/mlinux/moal_shim.c b/drivers/net/wireless/sd8797/mlinux/moal_shim.c new file mode 100644 index 000000000000..51f192a160f9 --- /dev/null +++ b/drivers/net/wireless/sd8797/mlinux/moal_shim.c @@ -0,0 +1,1458 @@ +/** @file moal_shim.c + * + * @brief This file contains the callback functions registered to MLAN + * + * Copyright (C) 2008-2011, 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 +#ifdef STA_CFG80211 +#include "moal_cfg80211.h" +#endif + +/******************************************************** + Local Variables +********************************************************/ +/** moal_lock */ +typedef struct _moal_lock +{ + /** Lock */ + spinlock_t lock; + /** Flags */ + unsigned long flags; +} moal_lock; + +/******************************************************** + Global Variables +********************************************************/ +extern int cfg80211_wext; + +/******************************************************** + 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 = GFP_ATOMIC; /* Default type: GFP_ATOMIC */ + + if (flag & MLAN_MEM_DMA) + mem_flag |= GFP_DMA; + + if (!(*ppbuf = kmalloc(size, mem_flag))) { + PRINTM(MERROR, "%s: allocate buffer %d failed!\n", __FUNCTION__, + (int) size); + return MLAN_STATUS_FAILURE; + } + 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); + handle->malloc_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; + + if (! + (timer = + (moal_drv_timer *) kmalloc(sizeof(moal_drv_timer), GFP_KERNEL))) + 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; + + if (!(mlock = (moal_lock *) kmalloc(sizeof(moal_lock), GFP_ATOMIC))) + return MLAN_STATUS_FAILURE; + spin_lock_init(&mlock->lock); + *pplock = (t_void *) mlock; + + 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; + + if (mlock) { + kfree(mlock); + 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 + * + * @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; + 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); + + wait = (wait_queue *) pioctl_req->reserved_1; + 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); + if (wait) { + wait->condition = MTRUE; + wait->status = status; + 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); + } + } else { + if (priv && (status == MLAN_STATUS_SUCCESS) && + (pioctl_req->action == MLAN_ACT_GET)) + woal_process_ioctl_resp(priv, pioctl_req); + if (status != MLAN_STATUS_SUCCESS) + PRINTM(MERROR, + "IOCTL failed: id=0x%x, action=%d, status_code=0x%x\n", + pioctl_req->req_id, (int) pioctl_req->action, + pioctl_req->status_code); + 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) +{ + if (NULL == + (*pmbuf = woal_alloc_mlan_buffer((moal_handle *) pmoal_handle, size))) + 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; + int i; + 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 (atomic_dec_return(&handle->tx_pending) < LOW_TX_PENDING) { + 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 + } + } + } + } + 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); +} + +/** + * @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; + 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); + pmbuf->pdesc = NULL; + pmbuf->pbuf = NULL; + pmbuf->data_offset = pmbuf->data_len = 0; + } else { + PRINTM(MERROR, "%s without skb attach!!!\n", __FUNCTION__); + if (!(skb = dev_alloc_skb(pmbuf->data_len + MLAN_NET_IP_ALIGN))) { + PRINTM(MERROR, "%s fail to alloc skb\n", __FUNCTION__); + 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++; + if (in_interrupt()) + 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) +{ + moal_private *priv = NULL; +#if defined(STA_WEXT) || 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 + ENTER(); + + PRINTM(MEVENT, "event id:0x%x\n", pmevent->event_id); + priv = woal_bss_index_to_priv(pmoal_handle, pmevent->bss_index); + if (priv == NULL) { + PRINTM(MERROR, "%s: priv is null\n", __FUNCTION__); + 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 + 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 + 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); +#endif + 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->phandle->scan_pending_on_block == MTRUE) { + priv->phandle->scan_pending_on_block = MFALSE; + MOAL_REL_SEMAPHORE(&priv->phandle->async_sem); + } + + if (priv->report_scan_result) { +#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)) { + woal_inform_bss_from_scan_result(priv, NULL); + PRINTM(MINFO, "Reporting scan results\n"); + if (priv->scan_request) { + cfg80211_scan_done(priv->scan_request, MFALSE); + priv->scan_request = NULL; + } + } +#endif /* STA_CFG80211 */ + + priv->report_scan_result = MFALSE; + } + break; + + case MLAN_EVENT_ID_DRV_OBSS_SCAN_PARAM: +#ifdef STA_WEXT + if (IS_STA_WEXT(cfg80211_wext)) { + memset(&wrqu, 0, sizeof(union iwreq_data)); + 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; + + 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: +#ifdef STA_WEXT + if (IS_STA_WEXT(cfg80211_wext)) { + memset(&wrqu, 0, sizeof(union iwreq_data)); + 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; + + 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_CFG80211 + if (IS_STA_CFG80211(cfg80211_wext)) { + if (!priv->cfg_disconnect && + priv->wdev->iftype != NL80211_IFTYPE_ADHOC) { + PRINTM(MINFO, "Successfully disconnected from %pM:" + " Reason code %d\n", priv->cfg_bssid, + WLAN_REASON_DEAUTH_LEAVING); + /* 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, + GFP_KERNEL); + } + priv->cfg_disconnect = 0; + } +#endif /* STA_CFG80211 */ + /* Reset wireless stats signal info */ +#ifdef STA_WEXT + 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 */ + 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 */ + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + break; + + case MLAN_EVENT_ID_DRV_DEFER_HANDLING: + queue_work(priv->phandle->workqueue, &priv->phandle->main_work); + break; + case MLAN_EVENT_ID_DRV_DBG_DUMP: + woal_moal_debug_info(priv, NULL, MFALSE); + 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)) { + woal_send_iwevcustom_event(priv, CUS_EVT_BG_SCAN); + memset(&wrqu, 0, sizeof(union iwreq_data)); + wireless_send_event(priv->netdev, SIOCGIWSCAN, &wrqu, NULL); + } +#endif + break; + 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 + break; +#endif /* STA_SUPPORT */ + 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_WEXT +#ifdef STA_SUPPORT + if (IS_STA_WEXT(cfg80211_wext) && (pmpriv = woal_get_priv((moal_handle + *) + pmoal_handle, + MLAN_BSS_ROLE_STA))) + woal_send_iwevcustom_event(pmpriv, CUS_EVT_HS_WAKEUP); +#endif /* STA_SUPPORT */ +#endif /* STA_WEXT */ +#ifdef UAP_SUPPORT + if ((pmpriv = + woal_get_priv((moal_handle *) pmoal_handle, MLAN_BSS_ROLE_UAP))) { + 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_WEXT +#ifdef STA_SUPPORT + if (IS_STA_WEXT(cfg80211_wext) && (pmpriv = woal_get_priv((moal_handle + *) + pmoal_handle, + MLAN_BSS_ROLE_STA))) + { + woal_send_iwevcustom_event(pmpriv, CUS_EVT_HS_ACTIVATED); + } +#endif /* STA_SUPPORT */ +#endif /* STA_WEXT */ +#if defined(UAP_SUPPORT) + if ((pmpriv = + woal_get_priv((moal_handle *) pmoal_handle, MLAN_BSS_ROLE_UAP))) { + 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_WEXT +#ifdef STA_SUPPORT + if (IS_STA_WEXT(cfg80211_wext) && (pmpriv = woal_get_priv((moal_handle + *) + pmoal_handle, + MLAN_BSS_ROLE_STA))) + { + woal_send_iwevcustom_event(pmpriv, CUS_EVT_HS_DEACTIVATED); + } +#endif +#endif +#if defined(UAP_SUPPORT) + if ((pmpriv = + woal_get_priv((moal_handle *) pmoal_handle, MLAN_BSS_ROLE_UAP))) { + 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: + priv->bss_started = MTRUE; + 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); + break; + 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_stop_queue(priv->netdev); + if (netif_carrier_ok(priv->netdev)) + netif_carrier_off(priv->netdev); + woal_broadcast_event(priv, pmevent->event_buf, pmevent->event_len); + break; +#ifdef WIFI_DIRECT_SUPPORT +#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_CH0ANNEL_EXPIRED cookie = %#llx\n", + priv->phandle->cookie); + priv->phandle->remain_on_channel = MFALSE; + if (priv->phandle->cookie) { + cfg80211_remain_on_channel_expired(priv->netdev, + priv->phandle->cookie, + &priv->phandle->chan, + priv->phandle->channel_type, + GFP_ATOMIC); + priv->phandle->cookie = 0; + } + } + break; +#endif +#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); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,2,0) || defined(COMPAT_WIRELESS) + if (pmevent->event_len > ETH_ALEN) { + /* set station info filled flag */ + sinfo.filled |= STATION_INFO_ASSOC_REQ_IES; + /* 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) + cfg80211_new_sta(priv->netdev, + (t_u8 *) addr, &sinfo, GFP_KERNEL); + } +#endif /* UAP_CFG80211 */ +#ifdef UAP_WEXT + if (IS_UAP_WEXT(cfg80211_wext)) { + memset(&wrqu, 0, sizeof(union iwreq_data)); + 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; + 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 LINUX_VERSION_CODE >= KERNEL_VERSION(3,0,0) || defined(COMPAT_WIRELESS) + /* skip 2 bytes extra header will get the mac address */ + cfg80211_del_sta(priv->netdev, pmevent->event_buf + 2, GFP_KERNEL); +#endif /* KERNEL_VERSION */ + } +#endif /* UAP_CFG80211 */ +#ifdef UAP_WEXT + if (IS_UAP_WEXT(cfg80211_wext)) { + memset(&wrqu, 0, sizeof(union iwreq_data)); + 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; + + 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: +#ifdef WIFI_DIRECT_SUPPORT +#if defined(STA_CFG80211) || defined(UAP_CFG80211) + if (IS_STA_OR_UAP_CFG80211(cfg80211_wext)) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,0,0) || defined(COMPAT_WIRELESS) + if (priv->netdev && priv->netdev->ieee80211_ptr->wiphy->mgmt_stypes) { + /* frmctl + durationid + addr1 + addr2 + addr3 + seqctl */ +#define PACKET_ADDR4_POS (2 + 2 + 6 + 6 + 6 + 2) + t_u8 *pkt; + 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); + cfg80211_rx_mgmt(priv->netdev, priv->phandle->chan.center_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 /* KERNEL_VERSION */ + } +#endif /* STA_CFG80211 || UAP_CFG80211 */ +#endif /* WIFI_DIRECT_SUPPORT */ +#ifdef UAP_WEXT + if (IS_UAP_WEXT(cfg80211_wext)) { + woal_broadcast_event(priv, pmevent->event_buf, pmevent->event_len); + } +#endif /* UAP_WEXT */ + 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_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)); + 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(MMSG, "Delayed BSS Start operation failed!\n"); + kfree(req); + } + + PRINTM(MMSG, "BSS START Complete!\n"); + } + } + 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 t_s8 * 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 { + 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 && (drvdbg & level)) { + if ((bss_index < MLAN_MAX_BSS_NUM) && phandle->priv[bss_index] && + phandle->priv[bss_index]->netdev) + 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!"); +} diff --git a/drivers/net/wireless/sd8797/mlinux/moal_shim.h b/drivers/net/wireless/sd8797/mlinux/moal_shim.h new file mode 100644 index 000000000000..ee50a2105f0f --- /dev/null +++ b/drivers/net/wireless/sd8797/mlinux/moal_shim.h @@ -0,0 +1,95 @@ +/** @file moal_shim.h + * + * @brief This file contains declaration referring to + * functions defined in moal module + * + * Copyright (C) 2008-2011, 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_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); +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 t_s8 * 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); +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/sd8797/mlinux/moal_sta_cfg80211.c b/drivers/net/wireless/sd8797/mlinux/moal_sta_cfg80211.c new file mode 100644 index 000000000000..dfb4aefcbb33 --- /dev/null +++ b/drivers/net/wireless/sd8797/mlinux/moal_sta_cfg80211.c @@ -0,0 +1,2476 @@ +/** @file moal_sta_cfg80211.c + * + * @brief This file contains the functions for STA CFG80211. + * + * Copyright (C) 2011-2012, 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_sta_cfg80211.h" +static int woal_cfg80211_reg_notifier(struct wiphy *wiphy, + struct regulatory_request *request); + +static int woal_cfg80211_scan(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_scan_request *request); + +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, + t_u8 * mac, 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_set_power_mgmt(struct wiphy *wiphy, + struct net_device *dev, bool enabled, + int timeout); + +static int woal_cfg80211_set_tx_power(struct wiphy *wiphy, +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) && !defined(COMPAT_WIRELESS) + enum tx_power_setting type, +#else + enum nl80211_tx_power_setting type, +#endif + int dbm); + +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 defined(WIFI_DIRECT_SUPPORT) +#if LINUX_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION +static int woal_cfg80211_mgmt_tx_cancel_wait(struct wiphy *wiphy, + struct net_device *dev, + u64 cookie); + +void woal_cfg80211_remain_on_channel_done(void *context); + +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); + +static int woal_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy, + struct net_device *dev, + u64 cookie); +#endif /* KERNEL_VERSION */ +#endif /* WIFI_DIRECT_SUPPORT && V14_FEATURE */ + +/** cfg80211 STA operations */ +static struct cfg80211_ops woal_cfg80211_sta_ops = { + .change_virtual_intf = woal_cfg80211_change_virtual_intf, + .scan = woal_cfg80211_scan, + .connect = woal_cfg80211_connect, + .disconnect = woal_cfg80211_disconnect, + .get_station = woal_cfg80211_get_station, + .dump_station = woal_cfg80211_dump_station, + .set_wiphy_params = woal_cfg80211_set_wiphy_params, + .set_channel = woal_cfg80211_set_channel, + .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, + .set_power_mgmt = woal_cfg80211_set_power_mgmt, + .set_tx_power = woal_cfg80211_set_tx_power, +}; + +#if defined(WIFI_DIRECT_SUPPORT) +#if LINUX_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION +/** cfg80211 Wifi Direct operations */ +static struct cfg80211_ops woal_cfg80211_wifi_direct_ops = { + .add_virtual_intf = woal_cfg80211_add_virtual_intf, + .del_virtual_intf = woal_cfg80211_del_virtual_intf, + .change_virtual_intf = woal_cfg80211_change_virtual_intf, + .scan = woal_cfg80211_scan, + .connect = woal_cfg80211_connect, + .disconnect = woal_cfg80211_disconnect, + .get_station = woal_cfg80211_get_station, + .dump_station = woal_cfg80211_dump_station, + .set_wiphy_params = woal_cfg80211_set_wiphy_params, + .set_channel = woal_cfg80211_set_channel, + .add_key = woal_cfg80211_add_key, + .del_key = woal_cfg80211_del_key, + .add_beacon = woal_cfg80211_add_beacon, + .set_beacon = woal_cfg80211_set_beacon, + .del_beacon = woal_cfg80211_del_beacon, + .mgmt_frame_register = woal_cfg80211_mgmt_frame_register, + .mgmt_tx = woal_cfg80211_mgmt_tx, + .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, + .set_default_key = woal_cfg80211_set_default_key, + .set_power_mgmt = woal_cfg80211_set_power_mgmt, + .set_tx_power = woal_cfg80211_set_tx_power, +}; +#endif /* KERNEL_VERSION */ +#endif /* WIFI_DIRECT_SUPPORT && V14_FEATURE */ + +/******************************************************** + Local Variables +********************************************************/ +#if defined(WIFI_DIRECT_SUPPORT) +#if LINUX_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION +static const struct ieee80211_txrx_stypes + ieee80211_mgmt_stypes[NUM_NL80211_IFTYPES] = { + [NL80211_IFTYPE_ADHOC] = { + .tx = 0x0000, + .rx = 0x0000, + }, + [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, + }, + [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) | + 0, + .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_MESH_POINT] = { + .tx = 0x0000, + .rx = 0x0000, + }, + +}; +#endif +#endif /* WIFI_DIRECT_SUPPORT && V14_FEATURE */ + +/******************************************************** + Global Variables +********************************************************/ + +/******************************************************** + Local Functions +********************************************************/ +/** + * @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 IW_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 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 -> NO_SEC_CHANNEL + * NL80211_CHAN_HT20 -> NO_SEC_CHANNEL + * NL80211_CHAN_HT40PLUS -> SEC_CHANNEL_ABOVE + * NL80211_CHAN_HT40MINUS -> SEC_CHANNEL_BELOW + * Others -> NO_SEC_CHANNEL + * + * @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 = NO_SEC_CHANNEL; + break; + case NL80211_CHAN_HT40PLUS: + channel = SEC_CHANNEL_ABOVE; + break; + case NL80211_CHAN_HT40MINUS: + channel = SEC_CHANNEL_BELOW; + break; + default: + channel = NO_SEC_CHANNEL; + } + LEAVE(); + return channel; +} + +/** + * @brief Convert secondary channel type to NL80211 channel type + * + * The mapping is as follows - + * NO_SEC_CHANNEL -> NL80211_CHAN_HT20 + * SEC_CHANNEL_ABOVE -> NL80211_CHAN_HT40PLUS + * SEC_CHANNEL_BELOW -> NL80211_CHAN_HT40MINUS + * Others -> NL80211_CHAN_HT20 + * + * @param channel_type Driver channel type + * + * @return nl80211_channel_type type + */ +static enum nl80211_channel_type +woal_channel_to_nl80211_channel_type(int channel_type) +{ + enum nl80211_channel_type channel; + + ENTER(); + + switch (channel_type) { + case NO_SEC_CHANNEL: + channel = NL80211_CHAN_HT20; + break; + case SEC_CHANNEL_ABOVE: + channel = NL80211_CHAN_HT40PLUS; + break; + case SEC_CHANNEL_BELOW: + channel = NL80211_CHAN_HT40MINUS; + break; + default: + channel = NL80211_CHAN_HT20; + } + LEAVE(); + return channel; +} + +/** + * @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 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; + + 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; + + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + + done: + if (req) + 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 + * + * @return 0 -- success, otherwise fail + */ +static int +woal_cfg80211_assoc_ies_cfg(moal_private * priv, t_u8 * ie, int ie_len) +{ + 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)) { + 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(wps_oui))) { + 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)) { + 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; + default: + if (MLAN_STATUS_SUCCESS != + woal_set_get_gen_ie(priv, MLAN_ACT_SET, pcurrent_ptr, + &total_ie_len)) { + 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 wiphy A pointer to wiphy structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status +woal_send_domain_info_cmd_fw(struct wiphy *wiphy) +{ + moal_private *priv = (moal_private *) woal_get_wiphy_priv(wiphy); + 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_ds_radio_cfg *radio_cfg = NULL; + mlan_ioctl_req *req = NULL; + + ENTER(); + + /* Allocate an IOCTL request buffer */ + 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; + req->action = MLAN_ACT_GET; + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + band = woal_band_cfg_to_ieee_band(radio_cfg->param.band_cfg.config_bands); + if (!wiphy->bands[band]) { + PRINTM(MERROR, "11D: setting domain info in FW failed"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + kfree(req); + + /* 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->country_code[0]; + cfg_11d->param.domain_info.country_code[1] = priv->country_code[1]; + cfg_11d->param.domain_info.country_code[2] = ' '; + cfg_11d->param.domain_info.band = band; + + sband = 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) { + 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 */ + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + ret = MLAN_STATUS_FAILURE; + PRINTM(MERROR, "11D: Error setting domain info in FW\n"); + goto done; + } + + done: + if (req) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Request the driver to change the channel and + * change domain info according to that 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 + */ +int +woal_set_rf_channel(struct wiphy *wiphy, + struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type) +{ + moal_private *priv = (moal_private *) woal_get_wiphy_priv(wiphy); + int ret = 0; + t_u32 mode, config_bands = 0; + mlan_ioctl_req *req1 = NULL, *req2 = NULL; + mlan_ds_radio_cfg *radio_cfg = NULL; + mlan_ds_bss *bss = NULL; + + ENTER(); + + mode = woal_nl80211_iftype_to_mode(priv->wdev->iftype); + + req1 = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_radio_cfg)); + if (req1 == NULL) { + ret = -ENOMEM; + goto done; + } + + radio_cfg = (mlan_ds_radio_cfg *) req1->pbuf; + radio_cfg->sub_command = MLAN_OID_BAND_CFG; + req1->req_id = MLAN_IOCTL_RADIO_CFG; + + if (chan) { + req1->action = MLAN_ACT_SET; + /* 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.config_bands = config_bands; + /* Set channel offset */ + radio_cfg->param.band_cfg.sec_chan_offset = + woal_cfg80211_channel_type_to_channel(channel_type); + + if (MLAN_STATUS_SUCCESS != + woal_request_ioctl(priv, req1, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + woal_send_domain_info_cmd_fw(wiphy); + } + + PRINTM(MINFO, "Setting band %d, channel bandwidth %d and mode = %d\n", + config_bands, radio_cfg->param.band_cfg.sec_chan_offset, mode); + + if (!chan) + goto done; + + req2 = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req2 == NULL) { + ret = -ENOMEM; + goto done; + } + + bss = (mlan_ds_bss *) req2->pbuf; + + bss->param.bss_chan.freq = chan->center_freq; + /* Convert frequency to channel */ + bss->param.bss_chan.channel = + ieee80211_frequency_to_channel(chan->center_freq); + + bss->sub_command = MLAN_OID_BSS_CHANNEL; + req2->req_id = MLAN_IOCTL_BSS; + req2->action = MLAN_ACT_SET; + + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req2, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + + if (MLAN_STATUS_SUCCESS != + woal_change_adhoc_chan(priv, bss->param.bss_chan.channel)) { + ret = -EFAULT; + goto done; + } + + done: + if (req1) + kfree(req1); + if (req2) + kfree(req2); + + 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 */ + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, wait_option)) + 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 (req && (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 + * + * @return 0 -- success, otherwise fail + */ +static int +woal_cfg80211_set_auth(moal_private * priv, int encrypt_mode, int wpa_enabled) +{ + int ret = 0; + + ENTER(); + + if (MLAN_STATUS_SUCCESS != + woal_set_encrypt_mode(priv, MOAL_IOCTL_WAIT, encrypt_mode)) + ret = -EFAULT; + + if (wpa_enabled) { + if (MLAN_STATUS_SUCCESS != + woal_set_wpa_enable(priv, MOAL_IOCTL_WAIT, 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 A pointer to mlan_802_11_ssid structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +woal_inform_bss_from_scan_result(moal_private * priv, mlan_802_11_ssid * ssid) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + struct ieee80211_channel *chan; + mlan_scan_resp scan_resp; + BSSDescriptor_t *scan_table; + t_u8 *ie, *tmp, *ie_buf; + __le32 ie_len; + t_u64 ts = 0; + t_u8 *cap; + t_u8 *beacon; + int beacon_size, i, j; + IEEEtypes_ElementId_e element_id; + t_u8 element_len; + struct cfg80211_bss *pub = NULL; + + ENTER(); + + memset(&scan_resp, 0, sizeof(scan_resp)); + if (MLAN_STATUS_SUCCESS != woal_get_scan_table(priv, + MOAL_IOCTL_WAIT, + &scan_resp)) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + if (scan_resp.num_in_scan_table) { +#define MAX_IE_BUF 2048 + ie_buf = kzalloc(MAX_IE_BUF, GFP_KERNEL); + if (ie_buf == NULL) { + PRINTM(MERROR, "%s: failed to allocate ie_buf\n", __func__); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + scan_table = (BSSDescriptor_t *) scan_resp.pscan_table; + for (i = 0; i < scan_resp.num_in_scan_table; i++) { + if (ssid) { + /* Inform specific BSS only */ + if (memcmp(ssid->ssid, scan_table[i].ssid.ssid, ssid->ssid_len)) + continue; + } + if (!scan_table[i].freq) { + PRINTM(MERROR, "Invalid channel number %d\n", + (int) scan_table[i].channel); + continue; + } + memset(ie_buf, 0, MAX_IE_BUF); + ie_buf[0] = WLAN_EID_SSID; + ie_buf[1] = scan_table[i].ssid.ssid_len; + memcpy(&ie_buf[sizeof(IEEEtypes_Header_t)], + scan_table[i].ssid.ssid, ie_buf[1]); + + ie = ie_buf + ie_buf[1] + sizeof(IEEEtypes_Header_t); + ie_len = ie_buf[1] + sizeof(IEEEtypes_Header_t); + + ie[0] = WLAN_EID_SUPP_RATES; + + for (j = 0; j < sizeof(scan_table[i].supported_rates); j++) { + if (!scan_table[i].supported_rates[j]) + break; + else { + ie[j + sizeof(IEEEtypes_Header_t)] = + scan_table[i].supported_rates[j]; + } + } + + ie[1] = j; + ie_len += ie[1] + sizeof(IEEEtypes_Header_t); + + beacon = scan_table[i].pbeacon_buf; + beacon_size = scan_table[i].beacon_buf_size; + + /* Skip time stamp, beacon interval and capability */ + + if (beacon) { + beacon += 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 (beacon_size >= sizeof(IEEEtypes_Header_t)) { + ie = ie_buf + ie_len; + element_id = *beacon; + element_len = *(beacon + 1); + if (beacon_size < (int) element_len + + sizeof(IEEEtypes_Header_t)) { + PRINTM(MERROR, + "Get scan: Error in processing IE, " + "bytes left < IE length\n"); + break; + } + switch (element_id) { + case WLAN_EID_FH_PARAMS: + case WLAN_EID_DS_PARAMS: + case WLAN_EID_CF_PARAMS: + case WLAN_EID_IBSS_PARAMS: + case WLAN_EID_COUNTRY: + case WLAN_EID_PWR_CONSTRAINT: + case WLAN_EID_PWR_CAPABILITY: + case WLAN_EID_TPC_REQUEST: + case WLAN_EID_TPC_REPORT: + case WLAN_EID_CHANNEL_SWITCH: + case WLAN_EID_QUIET: + case WLAN_EID_IBSS_DFS: + case WLAN_EID_SUPPORTED_CHANNELS: + case WLAN_EID_ERP_INFO: + case WLAN_EID_EXT_SUPP_RATES: + case WLAN_EID_HT_CAPABILITY: + case WLAN_EID_HT_INFORMATION: + case EXT_CAPABILITY: // TODO: Replace when kernel macro + // available + case WLAN_EID_RSN: + case WAPI_IE: // TODO: Replace when kernel macro available + case WLAN_EID_VENDOR_SPECIFIC: + ie[0] = element_id; + ie[1] = element_len; + tmp = (t_u8 *) beacon; + memcpy(&ie[sizeof(IEEEtypes_Header_t)], + tmp + sizeof(IEEEtypes_Header_t), element_len); + ie_len += ie[1] + sizeof(IEEEtypes_Header_t); + break; + default: + break; + } + beacon += element_len + sizeof(IEEEtypes_Header_t); + beacon_size -= (element_len + sizeof(IEEEtypes_Header_t)); + } + chan = ieee80211_get_channel(priv->wdev->wiphy, scan_table[i].freq); + cap = (t_u8 *) & scan_table[i].cap_info; + pub = cfg80211_inform_bss(priv->wdev->wiphy, chan, + scan_table[i].mac_address, + ts, (__le16) (*cap), + scan_table[i].beacon_period, ie_buf, + ie_len, + -RSSI_DBM_TO_MDM(scan_table[i].rssi), + GFP_KERNEL); + pub->len_information_elements = pub->len_beacon_ies; + } + kfree(ie_buf); + } + + 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; + + 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; + } + + cfg80211_inform_bss(priv->wdev->wiphy, chan, + bss_info.bssid, 0, WLAN_CAPABILITY_IBSS, + beacon_interval, ie_buf, ie_len, signal.bcn_rssi_avg, + GFP_KERNEL); + + done: + LEAVE(); + return ret; +} + +/** + * @brief Request the driver for (re)association + * + * @param wiphy A pointer to wiphy structure + * @param sme A pointer to connect parameters + * + * @return 0 -- success, otherwise fail + */ +static int +woal_cfg80211_assoc(struct wiphy *wiphy, void *sme) +{ + moal_private *priv = (moal_private *) woal_get_wiphy_priv(wiphy); + 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_ds_radio_cfg *radio_cfg = NULL; + 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; + struct ieee80211_channel *channel = NULL; + t_u16 beacon_interval = 0; + bool privacy; + + ENTER(); + + mode = woal_nl80211_iftype_to_mode(priv->wdev->iftype); + + if (mode == MLAN_BSS_MODE_IBSS) { + ibss_param = (struct cfg80211_ibss_params *) sme; + ssid = ibss_param->ssid; + ssid_len = ibss_param->ssid_len; + bssid = ibss_param->bssid; + channel = ibss_param->channel; + if (ibss_param->ie_len) + ie = 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 = conn_param->ssid; + ssid_len = conn_param->ssid_len; + bssid = conn_param->bssid; + channel = conn_param->channel; + if (conn_param->ie_len) + ie = conn_param->ie; + ie_len = conn_param->ie_len; + privacy = conn_param->privacy; + } + + 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 > IW_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 (channel) { + /* Get the secondary channel offset */ + 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; + req->action = MLAN_ACT_GET; + + if (MLAN_STATUS_SUCCESS != + woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + if (MLAN_STATUS_SUCCESS != woal_set_rf_channel(wiphy, + channel, + woal_channel_to_nl80211_channel_type + (radio_cfg->param. + band_cfg. + sec_chan_offset))) { + ret = -EFAULT; + goto done; + } + } + + if (MLAN_STATUS_SUCCESS != + woal_set_ewpa_mode(priv, MOAL_IOCTL_WAIT, &ssid_bssid)) { + ret = -EFAULT; + goto done; + } + + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_set_key(priv, 0, 0, NULL, 0, NULL, 0, 0, NULL, 1)) { + /* Disable keys */ + ret = -EFAULT; + goto done; + } + + if (ie && ie_len) { /* Set the IE */ + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_assoc_ies_cfg(priv, ie, ie_len)) { + 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 + auth_type = MLAN_AUTH_MODE_AUTO; + + if (MLAN_STATUS_SUCCESS != + woal_set_auth_mode(priv, MOAL_IOCTL_WAIT, 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); + 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); + 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)) { + 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)) { + 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; + } + if (req) + 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; + if (MLAN_STATUS_SUCCESS != + woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + 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, MOAL_IOCTL_WAIT, + MLAN_AUTH_MODE_OPEN)) { + ret = -EFAULT; + goto done; + } + + wpa_enabled = 0; + ret = woal_cfg80211_set_auth(priv, + MLAN_ENCRYPTION_MODE_WEP104, + wpa_enabled); + 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)) { + /* Do specific SSID scanning */ + if (MLAN_STATUS_SUCCESS != + woal_request_scan(priv, MOAL_IOCTL_WAIT, &req_ssid)) { + ret = -EFAULT; + goto done; + } + } + + /* Disconnect before try to associate */ + woal_disconnect(priv, MOAL_IOCTL_WAIT, NULL); + + if (mode != MLAN_BSS_MODE_IBSS) { + if (MLAN_STATUS_SUCCESS != + woal_find_best_network(priv, MOAL_IOCTL_WAIT, &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, &req_ssid)) { + ret = -EFAULT; + goto done; + } + } 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); + + PRINTM(MINFO, "Trying to associate to %s and bssid %pM\n", + (char *) req_ssid.ssid, 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 (MLAN_STATUS_SUCCESS != + woal_bss_start(priv, MOAL_IOCTL_WAIT, &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 IE */ + ie_len = 0; + woal_set_get_gen_ie(priv, MLAN_ACT_SET, NULL, &ie_len); + } + if (req) + 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_ds_get_signal signal; + mlan_ioctl_req *req = NULL; + mlan_ds_rate *rate = NULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + 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; + + /* 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; + } + + 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; + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + sinfo->txrate.flags = RATE_INFO_FLAGS_MCS; + if (rate->param.data_rate.tx_ht_bw == MLAN_HT_BW40) { + sinfo->txrate.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH; + } + 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_data_rate; + 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; + + done: + if (req) + kfree(req); + + LEAVE(); + return ret; +} + +/******************************************************** + Global Functions +********************************************************/ + +/** + * @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 + */ +static int +woal_cfg80211_reg_notifier(struct wiphy *wiphy, + struct regulatory_request *request) +{ + moal_private *priv = (moal_private *) woal_get_wiphy_priv(wiphy); + int ret = 0; + + ENTER(); + + PRINTM(MINFO, "cfg80211 regulatory domain callback " + "%c%c\n", request->alpha2[0], request->alpha2[1]); + + if (MLAN_STATUS_SUCCESS != woal_set_region_code(priv, request->alpha2)) + PRINTM(MERROR, "Set country code failed!\n"); + memcpy(priv->country_code, request->alpha2, COUNTRY_CODE_LEN); + + switch (request->initiator) { + case NL80211_REGDOM_SET_BY_DRIVER: + PRINTM(MINFO, "Regulatory domain BY_DRIVER\n"); + break; + case NL80211_REGDOM_SET_BY_CORE: + PRINTM(MINFO, "Regulatory domain BY_CORE\n"); + break; + case NL80211_REGDOM_SET_BY_USER: + PRINTM(MINFO, "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(MINFO, "Regulatory domain BY_COUNTRY_IE\n"); + break; + } + + if (MLAN_STATUS_SUCCESS != woal_send_domain_info_cmd_fw(wiphy)) + ret = -EFAULT; + + LEAVE(); + return ret; +} + +/** + * @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) +{ + moal_private *priv = (moal_private *) woal_get_wiphy_priv(wiphy); + wlan_user_scan_cfg scan_req; + struct ieee80211_channel *chan; + int ret = 0, i; + + ENTER(); + + PRINTM(MINFO, "Received scan request on %s\n", dev->name); + + if (priv->scan_request && priv->scan_request != request) { + LEAVE(); + return -EBUSY; + } + priv->scan_request = request; + + memset(&scan_req, 0x00, sizeof(scan_req)); + for (i = 0; i < priv->scan_request->n_ssids; i++) { + memcpy(scan_req.ssid_list[i].ssid, + priv->scan_request->ssids[i].ssid, + priv->scan_request->ssids[i].ssid_len); + if (priv->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 LINUX_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION + if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT && + priv->scan_request->n_ssids) { + if (!memcmp(scan_req.ssid_list[i - 1].ssid, "DIRECT-", 7)) { + /* Enable wildcard ssid scan */ + memcpy(scan_req.ssid_list[i].ssid, "DIRECT-*", 8); + scan_req.ssid_list[i].max_len = 0xff; + } + } +#endif +#endif + for (i = 0; i < priv->scan_request->n_channels; i++) { + chan = priv->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_DISABLED) + scan_req.chan_list[i].scan_type = MLAN_SCAN_TYPE_PASSIVE; + else + scan_req.chan_list[i].scan_type = MLAN_SCAN_TYPE_ACTIVE; + scan_req.chan_list[i].scan_time = 0; + } + if (priv->scan_request->ie && priv->scan_request->ie_len) { + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_mgmt_frame_ie(priv, NULL, 0, + NULL, 0, NULL, 0, + (t_u8 *) priv->scan_request->ie, + priv->scan_request->ie_len, + MGMT_MASK_PROBE_REQ)) { + ret = -EFAULT; + goto done; + } + } else { + /** Clear SCAN IE in Firmware */ + woal_cfg80211_mgmt_frame_ie(priv, NULL, 0, NULL, 0, NULL, 0, NULL, 0, + MGMT_MASK_PROBE_REQ); + } + if (MLAN_STATUS_SUCCESS != woal_do_scan(priv, &scan_req)) { + ret = -EAGAIN; + } + done: + LEAVE(); + return 0; +} + +/** + * @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_wiphy_priv(wiphy); + int ret = 0; + + ENTER(); + PRINTM(MINFO, "Received association request on %s\n", dev->name); + + if (priv->wdev->iftype != NL80211_IFTYPE_STATION +#if defined(WIFI_DIRECT_SUPPORT) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37) || defined(COMPAT_WIRELESS) + && 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; + } +#if defined(WIFI_DIRECT_SUPPORT) +#if LINUX_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; + } + } +#endif +#endif + + if (priv->scan_type == MLAN_SCAN_TYPE_PASSIVE) + woal_set_scan_type(priv, MLAN_SCAN_TYPE_ACTIVE); + ret = woal_cfg80211_assoc(wiphy, (void *) sme); + + if (priv->scan_type == MLAN_SCAN_TYPE_PASSIVE) + woal_set_scan_type(priv, MLAN_SCAN_TYPE_PASSIVE); + + if (!ret) { + cfg80211_connect_result(priv->netdev, priv->cfg_bssid, NULL, 0, + NULL, 0, WLAN_STATUS_SUCCESS, GFP_KERNEL); + PRINTM(MINFO, "Associated to bssid %pM successfully\n", + priv->cfg_bssid); + } else { + PRINTM(MINFO, "Association to bssid %pM failed\n", priv->cfg_bssid); + cfg80211_connect_result(priv->netdev, priv->cfg_bssid, NULL, 0, + NULL, 0, WLAN_STATUS_UNSPECIFIED_FAILURE, + GFP_KERNEL); + memset(priv->cfg_bssid, 0, ETH_ALEN); + } + + LEAVE(); + return ret; +} + +/** + * @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_wiphy_priv(wiphy); + + ENTER(); + PRINTM(MINFO, "Received disassociation request on %s\n", dev->name); + + if (priv->cfg_disconnect) { + PRINTM(MERROR, "Disassociation already in progress\n"); + LEAVE(); + return -EBUSY; + } + + if (priv->media_connected == MFALSE) { + LEAVE(); + return -EINVAL; + } + + priv->cfg_disconnect = 1; + + if (woal_disconnect(priv, MOAL_IOCTL_WAIT, priv->cfg_bssid) != + MLAN_STATUS_SUCCESS) { + LEAVE(); + return -EFAULT; + } + + PRINTM(MINFO, "Successfully disconnected from %pM: Reason code %d\n", + priv->cfg_bssid, reason_code); + + memset(priv->cfg_bssid, 0, ETH_ALEN); + + 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, + t_u8 * mac, struct station_info *sinfo) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + moal_private *priv = (moal_private *) woal_get_wiphy_priv(wiphy); + + ENTER(); + +#if defined(WIFI_DIRECT_SUPPORT) +#if LINUX_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) { + LEAVE(); + return woal_uap_cfg80211_get_station(wiphy, dev, mac, sinfo); + } +#endif +#endif + if (priv->media_connected == MFALSE) { + PRINTM(MINFO, "cfg80211: Media not connected!\n"); + LEAVE(); + return -ENOENT; + } + if (memcmp(mac, priv->cfg_bssid, ETH_ALEN)) { + PRINTM(MINFO, "cfg80211: Request not for this station!\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; + } + + 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_wiphy_priv(wiphy); + + ENTER(); + + 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 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_wiphy_priv(wiphy); + 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(wiphy, (void *) params); + + if (!ret) { + cfg80211_ibss_joined(priv->netdev, priv->cfg_bssid, GFP_KERNEL); + PRINTM(MINFO, "Joined/created adhoc network with bssid" + " %pM successfully\n", 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_wiphy_priv(wiphy); + + 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 %pM\n", priv->cfg_bssid); + if (woal_disconnect(priv, MOAL_IOCTL_WAIT, priv->cfg_bssid) != + MLAN_STATUS_SUCCESS) { + LEAVE(); + return -EFAULT; + } + + 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_wiphy_priv(wiphy); + + ENTER(); + + if (enabled) + disabled = 0; + else + disabled = 1; + + if (MLAN_STATUS_SUCCESS != + woal_set_get_power_mgmt(priv, MLAN_ACT_SET, &disabled, timeout)) { + ret = -EOPNOTSUPP; + } + + 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 LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) && !defined(COMPAT_WIRELESS) + enum tx_power_setting type, +#else + enum nl80211_tx_power_setting type, +#endif + int dbm) +{ + int ret = 0; + moal_private *priv = (moal_private *) woal_get_wiphy_priv(wiphy); + mlan_power_cfg_t power_cfg; + + ENTER(); + + 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; +} + +/** + * @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 + */ +static void +woal_cfg80211_setup_sta_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 = 0x6; + + 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 |= 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; + ht_info->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; + + LEAVE(); +} + +#if defined(WIFI_DIRECT_SUPPORT) +#if LINUX_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION +/** + * @brief remain on channel config + * + * @param wiphy A pointer to wiphy 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(struct wiphy *wiphy, + 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; + moal_private *priv = (moal_private *) woal_get_wiphy_priv(wiphy); + + int ret = 0; + + ENTER(); + memset(&chan_cfg, 0, sizeof(mlan_ds_remain_chan)); + if (remove) { + chan_cfg.remove = MTRUE; + } else { + if (chan->band == IEEE80211_BAND_2GHZ) + chan_cfg.bandcfg = 0; + else if (chan->band == IEEE80211_BAND_5GHZ) + chan_cfg.bandcfg = 1; +/** secondary channel is below */ +#define SEC_CHAN_BELOW 0x30 +/** secondary channel is above */ +#define SEC_CHAN_ABOVE 0x10 + switch (channel_type) { + case NL80211_CHAN_HT40MINUS: + chan_cfg.bandcfg |= SEC_CHANNEL_BELOW; + break; + case NL80211_CHAN_HT40PLUS: + chan_cfg.bandcfg |= SEC_CHANNEL_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; +} + +/** + * @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) +{ + moal_private *priv = (moal_private *) woal_get_wiphy_priv(wiphy); + int ret = 0; + t_u8 status = 1; + + ENTER(); + + if (priv->phandle->remain_on_channel) { + if (woal_cfg80211_remain_on_channel_cfg(priv->wdev->wiphy, + MOAL_IOCTL_WAIT, MTRUE, &status, + NULL, 0, 0)) { + PRINTM(MERROR, "Fail to cancel remain on channel\n"); + ret = -EFAULT; + goto done; + } + if (priv->phandle->cookie) { + cfg80211_remain_on_channel_expired(priv->netdev, + priv->phandle->cookie, + &priv->phandle->chan, + priv->phandle->channel_type, + GFP_ATOMIC); + priv->phandle->cookie = 0; + } + priv->phandle->remain_on_channel = MFALSE; + } + + done: + LEAVE(); + return ret; +} + +/** + * @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) +{ + moal_private *priv = (moal_private *) woal_get_wiphy_priv(wiphy); + int ret = 0; + t_u8 status = 1; + + 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) { + if (woal_cfg80211_remain_on_channel_cfg + (wiphy, MOAL_IOCTL_WAIT, MTRUE, &status, NULL, 0, 0)) { + PRINTM(MERROR, "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(wiphy, MOAL_IOCTL_WAIT, + MFALSE, &status, chan, channel_type, + (t_u32) duration)) { + ret = -EFAULT; + goto done; + } + if (status == 0) { + /* remain on channel operation success */ + /* we need update the value cookie */ + *cookie = (u64) random32() | 1; + priv->phandle->remain_on_channel = MTRUE; + priv->phandle->cookie = *cookie; + priv->phandle->channel_type = channel_type; + memcpy(&priv->phandle->chan, chan, sizeof(struct ieee80211_channel)); + cfg80211_ready_on_channel(dev, *cookie, chan, + channel_type, duration, GFP_KERNEL); + PRINTM(MIOCTL, "Set remain on Channel: channel=%d cookie = %#llx\n", + ieee80211_frequency_to_channel(chan->center_freq), + priv->phandle->cookie); + } + + done: + LEAVE(); + return ret; +} + +/** + * @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) +{ + moal_private *priv = (moal_private *) woal_get_wiphy_priv(wiphy); + int ret = 0; + t_u8 status = 1; + + ENTER(); + PRINTM(MIOCTL, "Cancel remain on Channel: cookie = %#llx\n", cookie); + if (woal_cfg80211_remain_on_channel_cfg + (wiphy, MOAL_IOCTL_WAIT, MTRUE, &status, NULL, 0, 0)) { + PRINTM(MERROR, "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 */ +#endif /* WIFI_DIRECT_SUPPORT && V14_FEATURE */ + +/** + * @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_sta_init_wiphy(moal_private * priv, t_u8 wait_option) +{ + int retry_count, rts_thr, frag_thr, disabled; + struct wiphy *wiphy = NULL; + mlan_ioctl_req *req = NULL; + mlan_ds_11n_cfg *cfg_11n = NULL; + t_u32 hw_dev_cap; + + ENTER(); + + if (priv->wdev) + wiphy = priv->wdev->wiphy; + else { + PRINTM(MERROR, "Invalid parameter when init wiphy.\n"); + goto done; + } + + /* Get 11n tx parameters from MLAN */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); + if (req == NULL) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + 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; + + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, wait_option)) { + kfree(req); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + 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; + + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, wait_option)) { + kfree(req); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + /* Initialize parameters for 2GHz and 5GHz bands */ + woal_cfg80211_setup_sta_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 + woal_cfg80211_setup_sta_ht_cap(&wiphy->bands[IEEE80211_BAND_5GHZ]->ht_cap, + hw_dev_cap, cfg_11n->param.supported_mcs_set); */ + if (req) + kfree(req); + + /* Set retry limit count to wiphy */ + if (MLAN_STATUS_SUCCESS != + woal_set_get_retry(priv, MLAN_ACT_GET, wait_option, &retry_count)) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + wiphy->retry_long = (t_u8) retry_count; + wiphy->retry_short = (t_u8) retry_count; + wiphy->max_scan_ie_len = MAX_IE_SIZE; + +#if defined(WIFI_DIRECT_SUPPORT) +#if LINUX_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION + /* we should add mgmt_stypes for both STA & Wifi Direct, while only + initialize duration_timer for Wifi Direct */ + wiphy->mgmt_stypes = ieee80211_mgmt_stypes; + wiphy->max_remain_on_channel_duration = MAX_REMAIN_ON_CHANNEL_DURATION; +#endif /* KERNEL_VERSION */ +#endif /* WIFI_DIRECT_SUPPORT && V14_FEATURE */ + + /* Set RTS threshold to wiphy */ + if (MLAN_STATUS_SUCCESS != + woal_set_get_rts(priv, MLAN_ACT_GET, wait_option, &rts_thr)) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + 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)) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + 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; + + /* Get IEEE power save mode */ + if (MLAN_STATUS_SUCCESS != woal_set_get_power_mgmt(priv, + MLAN_ACT_GET, &disabled, + 0)) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + /* 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 (disabled) + priv->wdev->ps = MFALSE; + else + priv->wdev->ps = MTRUE; + + done: + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @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); + void *wdev_priv = NULL; + struct wireless_dev *wdev = NULL; + mlan_fw_info fw_info; + + ENTER(); + + /* Allocate wireless device */ + wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL); + if (!wdev) { + PRINTM(MERROR, "Could not allocate wireless device\n"); + ret = MLAN_STATUS_FAILURE; + goto err_wdev; + } + + if (bss_type == MLAN_BSS_TYPE_STA) + /* Allocate wiphy */ + wdev->wiphy = wiphy_new(&woal_cfg80211_sta_ops, sizeof(moal_private *)); +#if defined(WIFI_DIRECT_SUPPORT) +#if LINUX_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION + else if (bss_type == MLAN_BSS_TYPE_WIFIDIRECT) + /* Allocate wiphy */ + wdev->wiphy = + wiphy_new(&woal_cfg80211_wifi_direct_ops, sizeof(moal_private *)); +#endif +#endif + else { + PRINTM(MERROR, "Unexpected bss_type when register cfg80211\n"); + ret = MLAN_STATUS_FAILURE; + goto err_wdev; + } + + if (!wdev->wiphy) { + PRINTM(MERROR, "Could not allocate wiphy device\n"); + ret = MLAN_STATUS_FAILURE; + goto err_wdev; + } + if (bss_type == MLAN_BSS_TYPE_STA) { + wdev->iftype = NL80211_IFTYPE_STATION; + wdev->wiphy->interface_modes = + MBIT(NL80211_IFTYPE_STATION) | MBIT(NL80211_IFTYPE_ADHOC); + wdev->wiphy->max_scan_ssids = 10; + } +#if defined(WIFI_DIRECT_SUPPORT) +#if LINUX_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION + if (bss_type == MLAN_BSS_TYPE_WIFIDIRECT) { + wdev->iftype = NL80211_IFTYPE_STATION; + wdev->wiphy->interface_modes = MBIT(NL80211_IFTYPE_STATION) | + MBIT(NL80211_IFTYPE_P2P_GO) | + MBIT(NL80211_IFTYPE_P2P_CLIENT) | + MBIT(NL80211_IFTYPE_ADHOC) | MBIT(NL80211_IFTYPE_AP) | + MBIT(NL80211_IFTYPE_MONITOR); + wdev->wiphy->max_scan_ssids = 10; + } +#endif +#endif + + /* Set phy name like net device name */ + dev_set_name(&wdev->wiphy->dev, dev->name); + + /* Make this wiphy known to this driver only */ + wdev->wiphy->privid = mrvl_wiphy_privid; + + /* Supported bands */ + wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = &cfg80211_band_2ghz; + if (MLAN_STATUS_SUCCESS == + woal_request_get_fw_info(priv, MOAL_CMD_WAIT, &fw_info)) { + if (fw_info.fw_bands & BAND_A) + wdev->wiphy->bands[IEEE80211_BAND_5GHZ] = &cfg80211_band_5ghz; + } + /* Initialize cipher suits */ + wdev->wiphy->cipher_suites = cfg80211_cipher_suites; + wdev->wiphy->n_cipher_suites = ARRAY_SIZE(cfg80211_cipher_suites); + + wdev->wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; + + /* We are using custom domains */ + wdev->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY; + + wdev->wiphy->reg_notifier = woal_cfg80211_reg_notifier; + + /* Set moal_private pointer in wiphy_priv */ + wdev_priv = wiphy_priv(wdev->wiphy); + + *(unsigned long *) wdev_priv = (unsigned long) priv; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39) || defined(COMPAT_WIRELESS) + set_wiphy_dev(wdev->wiphy, (struct device *) priv->phandle->hotplug_device); +#endif + + if (wiphy_register(wdev->wiphy) < 0) { + PRINTM(MERROR, "Wiphy device registration failed!\n"); + ret = MLAN_STATUS_FAILURE; + goto err_wdev; + } + + dev_net_set(dev, wiphy_net(wdev->wiphy)); + dev->ieee80211_ptr = wdev; + SET_NETDEV_DEV(dev, wiphy_dev(wdev->wiphy)); + priv->wdev = wdev; + + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "Wiphy device registration failed!\n"); + } else { + PRINTM(MINFO, "Successfully registered wiphy device\n"); + LEAVE(); + return ret; + } + + wiphy_unregister(wdev->wiphy); + err_wdev: + dev->ieee80211_ptr = NULL; + if (wdev && wdev->wiphy) + wiphy_free(wdev->wiphy); + kfree(wdev); + LEAVE(); + return ret; +} diff --git a/drivers/net/wireless/sd8797/mlinux/moal_sta_cfg80211.h b/drivers/net/wireless/sd8797/mlinux/moal_sta_cfg80211.h new file mode 100644 index 000000000000..a83957c5c031 --- /dev/null +++ b/drivers/net/wireless/sd8797/mlinux/moal_sta_cfg80211.h @@ -0,0 +1,41 @@ +/** @file moal_sta_cfg80211.h + * + * @brief This file contains the STA CFG80211 specific defines. + * + * Copyright (C) 2011, 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_sta_init_wiphy(moal_private * priv, t_u8 wait_option); + +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); + +mlan_status +woal_cfg80211_set_wep_keys(moal_private * priv, const t_u8 * key, int key_len, + t_u8 index); + +#endif /* _MOAL_STA_CFG80211_H_ */ diff --git a/drivers/net/wireless/sd8797/mlinux/moal_uap.c b/drivers/net/wireless/sd8797/mlinux/moal_uap.c new file mode 100644 index 000000000000..7dac877abe00 --- /dev/null +++ b/drivers/net/wireless/sd8797/mlinux/moal_uap.c @@ -0,0 +1,2692 @@ +/** @file moal_uap.c + * + * @brief This file contains the major functions in UAP + * driver. + * + * Copyright (C) 2008-2012, 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 +********************************************************/ + +/******************************************************** + 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; + + 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; + } + if (MLAN_STATUS_SUCCESS != + woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT)) { + 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 (ioctl_req) + 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; + + 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 *) & param, 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]; + } + } + if (MLAN_STATUS_SUCCESS != + woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT)) { + 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 (ioctl_req) + 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; + + 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 *) & param, 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]; + } + } + if (MLAN_STATUS_SUCCESS != + woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT)) { + 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 (ioctl_req) + 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; + /* 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; + + 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 *) & param, 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; + } + } + if (MLAN_STATUS_SUCCESS != + woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT)) { + 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 (ioctl_req) + 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; + + 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 *) & param, 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; + } + if (MLAN_STATUS_SUCCESS != + woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT)) { + 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 (ioctl_req) + 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; + + 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 *) & param, 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", __FUNCTION__, + 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; + } + if (MLAN_STATUS_SUCCESS != + woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT)) { + 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 (ioctl_req) + 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; + + 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 *) & param, 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))); + } + if (MLAN_STATUS_SUCCESS != + woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT)) { + 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 (ioctl_req) + 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; + + 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 *) & param, 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; + } + if (MLAN_STATUS_SUCCESS != + woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT)) { + 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 (ioctl_req) + kfree(ioctl_req); + LEAVE(); + return ret; +} +#endif + +/** + * @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 uap hs_cfg 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_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 priv A pointer to moal_private structure + * @param wrq A pointer to iwreq 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; + + 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; + } + 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: + 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; + + 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 { + mreq->action = MLAN_ACT_SET; + if (tx_rate_config.rate == AUTO_RATE) + rate->param.rate_cfg.is_rate_auto = 1; + else { + if ((tx_rate_config.rate != MLAN_RATE_INDEX_MCS32) && + ((tx_rate_config.rate < 0) || + (tx_rate_config.rate > MLAN_RATE_INDEX_MCS15))) { + ret = -EINVAL; + goto done; + } + } + rate->param.rate_cfg.rate = tx_rate_config.rate; + } + + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, mreq, MOAL_IOCTL_WAIT)) { + 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 = AUTO_RATE; + else + 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 (mreq) + kfree(mreq); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get RF antenna 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_antenna_cfg(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; + ant_cfg_t antenna_config; + + ENTER(); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "uap_antenna_cfg() corrupt data\n"); + ret = -EFAULT; + goto done; + } + + memset(&antenna_config, 0, sizeof(ant_cfg_t)); + /* Get user data */ + if (copy_from_user(&antenna_config, req->ifr_data, sizeof(ant_cfg_t))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + 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_ANT_CFG; + mreq->req_id = MLAN_IOCTL_RADIO_CFG; + if (!(antenna_config.action)) + mreq->action = MLAN_ACT_GET; + else { + mreq->action = MLAN_ACT_SET; + radio->param.ant_cfg.tx_antenna = antenna_config.tx_mode; + radio->param.ant_cfg.rx_antenna = antenna_config.rx_mode; + } + + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, mreq, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + if (mreq->action == MLAN_ACT_GET) { + antenna_config.tx_mode = radio->param.ant_cfg.tx_antenna; + antenna_config.rx_mode = radio->param.ant_cfg.rx_antenna; + if (copy_to_user(req->ifr_data, &antenna_config, sizeof(ant_cfg_t))) { + PRINTM(MERROR, "Copy to user failed\n"); + ret = -EFAULT; + } + } + done: + if (mreq) + kfree(mreq); + 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; + case UAP_DOMAIN_INFO: + ret = woal_uap_domain_info(dev, req); + break; +#ifdef DFS_TESTING_SUPPORT + case UAP_DFS_TESTING: + ret = woal_uap_dfs_testing(dev, req); + break; +#endif + case UAP_TX_BF_CFG: + ret = woal_uap_tx_bf_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_ANTENNA_CFG: + ret = woal_uap_antenna_cfg(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; + + 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: %02x:%02x:%02x:%02x:%02x:%02x, reason=%d\n", + deauth_param.mac_addr[0], deauth_param.mac_addr[1], + deauth_param.mac_addr[2], deauth_param.mac_addr[3], + deauth_param.mac_addr[4], deauth_param.mac_addr[5], + 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)); + if (MLAN_STATUS_SUCCESS != + woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT)) { + 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 (ioctl_req) + 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; + + 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]; + if (MLAN_STATUS_SUCCESS != + woal_request_ioctl(priv, mreq, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + } + if (mreq) + 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 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; + + 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; + } + + if (MLAN_STATUS_SUCCESS != + woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT)) { + 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 (ioctl_req) + 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; + + 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; + } + } + + if (MLAN_STATUS_SUCCESS != + woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT)) { + 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 (ioctl_req) + 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; + + 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; + + if (MLAN_STATUS_SUCCESS != + woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT)) { + 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 (ioctl_req) + 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 }; + + 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); + + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) + ret = -EFAULT; + done: + if (req) + 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 (req) + 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; + + 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; + } + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + woal_enable_wapi(priv, MTRUE); + done: + if (req) + 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 (req && (status != MLAN_STATUS_PENDING)) + kfree(req); + + LEAVE(); + return status; +} + +/** + * @brief Set 11n status based on the configured security mode + * + * @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(mlan_uap_bss_param * sys_cfg, t_u8 action) +{ + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + if (action == MLAN_ACT_DISABLE) { + if ((sys_cfg->supported_mcs_set[0] == 0) + && (sys_cfg->supported_mcs_set[4] == 0) + && (sys_cfg->supported_mcs_set[1] == 0) + ) { + goto done; + } else { + sys_cfg->supported_mcs_set[0] = 0; + sys_cfg->supported_mcs_set[4] = 0; + sys_cfg->supported_mcs_set[1] = 0; + } + } + + if (action == MLAN_ACT_ENABLE) { + if ((sys_cfg->supported_mcs_set[0] != 0) + || (sys_cfg->supported_mcs_set[4] != 0) + || (sys_cfg->supported_mcs_set[1] != 0) + ) { + goto done; + } else { + sys_cfg->supported_mcs_set[0] = 0xFF; + sys_cfg->supported_mcs_set[4] = 0x01; + sys_cfg->supported_mcs_set[1] = 0xFF; + } + } + + 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, t_s8 * 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; + t_s8 *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, 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, 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, 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 t_s8 buf[MAX_BUF_LEN]; + mlan_uap_bss_param sys_config; + 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; + } + + memset(buf, 0, MAX_BUF_LEN); + memcpy(buf, data, len); + + /* 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; + + if ((ret = woal_uap_ap_cfg_parse_data(&sys_config, buf))) + goto done; + + /* If BSS already started stop it first and restart after changing the + setting */ + if (priv->bss_started == MTRUE) { + if ((ret = woal_uap_bss_ctrl(priv, MOAL_IOCTL_WAIT, UAP_BSS_STOP))) + 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(&sys_config, MLAN_ACT_DISABLE)) { + ret = -EFAULT; + goto done; + } + } else { + if (MLAN_STATUS_SUCCESS != + woal_uap_set_11n_status(&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: + 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; + + 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 { + /* about to start bss: issue channel check */ + woal_11h_channel_check_ioctl(priv); + } + bss->sub_command = MLAN_OID_BSS_START; + break; + case UAP_BSS_STOP: + if (priv->bss_started == MFALSE) { + PRINTM(MWARN, "Warning: BSS already stopped!\n"); + // goto done; + } + 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; + + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + + if (data == UAP_BSS_STOP || data == UAP_BSS_RESET) + priv->bss_started = MFALSE; + + done: + if (req) + 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 UAP_HOSTCMD: + ret = woal_hostcmd_ioctl(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 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; +} + +/** + * @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_PROC_WAIT); + if (status == MLAN_STATUS_SUCCESS) { + PRINTM(MINFO, "MOAL UAP VERSION: %s\n", + info->param.ver_ext.version_str); + snprintf(version, max_len, driver_version, + info->param.ver_ext.version_str); + } + + if (req && (status != MLAN_STATUS_PENDING)) + kfree(req); + + LEAVE(); + return; +} + +/** + * @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 (req && (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 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)); + } + + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, wait_option)) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + if (action == MLAN_ACT_GET) { + memcpy(sys_cfg, &bss->param.bss_config, sizeof(mlan_uap_bss_param)); + } + + done: + if (req) + 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/sd8797/mlinux/moal_uap.h b/drivers/net/wireless/sd8797/mlinux/moal_uap.h new file mode 100644 index 000000000000..a30d0ceec87f --- /dev/null +++ b/drivers/net/wireless/sd8797/mlinux/moal_uap.h @@ -0,0 +1,410 @@ +/** @file moal_uap.h + * + * @brief This file contains uap driver specific defines etc. + * + * Copyright (C) 2009-2011, 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 Host command */ +#define UAP_HOSTCMD (SIOCDEVPRIVATE + 1) +/** 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 + +/** Subcommand ID to set/get antenna configuration */ +#define UAP_ANTENNA_CFG 15 + +/** 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 +/** Band config 5GHz */ +#define BAND_CONFIG_5GHZ 0x01 + +/** 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; + +typedef struct _tx_rate_cfg_t +{ + /** sub command */ + int subcmd; + /** Action */ + int action; + /** Rate configured */ + int rate; + /** Rate bitmap */ + t_u16 bitmap_rates[MAX_BITMAP_RATES_SIZE]; +} tx_rate_cfg_t; + +/** ant_cfg structure */ +typedef struct _ant_cfg_t +{ + /** Subcommand */ + int subcmd; + /** Action */ + int action; + /** TX mode configured */ + int tx_mode; + /** RX mode configured */ + int rx_mode; +} ant_cfg_t; + +/** 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 + +/** 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) + +/** 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_info; + +typedef struct _tx_bf_cfg_para_hdr +{ + /** Sub command */ + t_u32 subcmd; + /** Action: Set/Get */ + t_u32 action; +} tx_bf_cfg_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 + +/** 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)) + +#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_u16 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 + +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); +void woal_uap_get_version(moal_private * priv, char *version, int max_len); +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); +int woal_uap_set_ap_cfg(moal_private * priv, t_u8 * data, int len); +mlan_status woal_uap_set_11n_status(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/sd8797/mlinux/moal_uap_cfg80211.c b/drivers/net/wireless/sd8797/mlinux/moal_uap_cfg80211.c new file mode 100644 index 000000000000..e83d4de4d282 --- /dev/null +++ b/drivers/net/wireless/sd8797/mlinux/moal_uap_cfg80211.c @@ -0,0 +1,1762 @@ +/** @file moal_uap_cfg80211.c + * + * @brief This file contains the functions for uAP CFG80211. + * + * Copyright (C) 2011-2012, 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" + +/* these 3 function will be called in woal_cfg80211_wifi_direct_ops */ +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); + +int woal_cfg80211_del_beacon(struct wiphy *wiphy, struct net_device *dev); + +static int woal_uap_cfg80211_scan(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_scan_request *request); + +static int woal_uap_cfg80211_connect(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_connect_params *sme); + +static int woal_uap_cfg80211_disconnect(struct wiphy *wiphy, + struct net_device *dev, + t_u16 reason_code); + +int woal_uap_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev, + u8 * mac, struct station_info *stainfo); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37) || defined(COMPAT_WIRELESS) +static const struct ieee80211_txrx_stypes + ieee80211_uap_mgmt_stypes[NUM_NL80211_IFTYPES] = { + [NL80211_IFTYPE_ADHOC] = { + .tx = 0x0000, + .rx = 0x0000, + }, + [NL80211_IFTYPE_STATION] = { + .tx = BIT(IEEE80211_STYPE_ACTION >> 4), + .rx = BIT(IEEE80211_STYPE_ACTION >> 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, + }, + [NL80211_IFTYPE_P2P_CLIENT] = { + .tx = 0x0000, + .rx = 0x0000, + }, + [NL80211_IFTYPE_P2P_GO] = { + .tx = 0x0000, + .rx = 0x0000, + }, + [NL80211_IFTYPE_MESH_POINT] = { + .tx = 0x0000, + .rx = 0x0000, + }, +}; +#endif + +/** cfg80211 uAP operations */ +static struct cfg80211_ops woal_cfg80211_uap_ops = { + .add_virtual_intf = woal_cfg80211_add_virtual_intf, + .del_virtual_intf = woal_cfg80211_del_virtual_intf, + .set_channel = woal_cfg80211_set_channel, + .scan = woal_uap_cfg80211_scan, + .connect = woal_uap_cfg80211_connect, + .disconnect = woal_uap_cfg80211_disconnect, + .set_wiphy_params = woal_cfg80211_set_wiphy_params, + .change_virtual_intf = woal_cfg80211_change_virtual_intf, + .add_key = woal_cfg80211_add_key, + .del_key = woal_cfg80211_del_key, + .set_default_key = woal_cfg80211_set_default_key, + .add_beacon = woal_cfg80211_add_beacon, + .set_beacon = woal_cfg80211_set_beacon, + .del_beacon = woal_cfg80211_del_beacon, + .get_station = woal_uap_cfg80211_get_station, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37) || defined(COMPAT_WIRELESS) + .mgmt_frame_register = woal_cfg80211_mgmt_frame_register, + .mgmt_tx = woal_cfg80211_mgmt_tx, +#endif +}; + +/******************************************************** + Local Variables +********************************************************/ + +/******************************************************** + Global Variables +********************************************************/ + +/******************************************************** + Local Functions +********************************************************/ + +/******************************************************** + Global Functions +********************************************************/ +/** + * @brief Filter specific IE in ie buf + * + * @param ie Pointer to IEs + * @param len Total length of ie + * @param ie_out Pointer to out IE buf + * + * @return out IE length + */ +static t_u16 +woal_filter_beacon_ies(const t_u8 * ie, int len, t_u8 * ie_out) +{ + int left_len = len; + const t_u8 *pos = ie; + int length; + t_u8 id = 0; + t_u16 out_len = 0; + + /* ERP_INFO and RSN IE will be fileter out */ + while (left_len >= 2) { + length = *(pos + 1); + id = *pos; + if ((length + 2) > left_len) + break; + switch (id) { + case ERP_INFO: + case RSN_IE: + break; + default: + memcpy(ie_out + out_len, pos, length + 2); + out_len += length + 2; + break; + } + pos += (length + 2); + left_len -= (length + 2); + } + return out_len; +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,2,0) && !defined(COMPAT_WIRELESS) +/** + * @brief Verify RSN IE + * + * @param rsn_ie Pointer RSN IE + * @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; + /* 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; + } + } + 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(t_u8 * ie, int len, mlan_uap_bss_param * sys_config) +{ + int bytes_left = len; + 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(wpa_oui))) + 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; +} +#endif + +/** + * @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 + */ +static 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 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 * + * + * @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) +{ + 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; + + ENTER(); + + if (!(custom_ie = kmalloc(sizeof(mlan_ds_misc_custom_ie), GFP_KERNEL))) { + ret = -ENOMEM; + goto done; + } + + memset(custom_ie, 0x00, sizeof(mlan_ds_misc_custom_ie)); + 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)); + + if (MLAN_STATUS_SUCCESS != + woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT)) { + 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 (ioctl_req) + kfree(ioctl_req); + if (custom_ie) + kfree(custom_ie); + LEAVE(); + return ret; +} + +/** + * @brief This function returns priv + * based on mgmt ie index + * + * @param handle A pointer to moal_handle + * @param index mgmt ie index + * + * @return Pointer to moal_private + */ +static moal_private * +woal_get_priv_by_mgmt_index(moal_handle * handle, t_u16 index) +{ + int i; + + for (i = 0; i < MIN(handle->priv_num, MLAN_MAX_BSS_NUM); i++) { + if (handle->priv[i]) { + if (handle->priv[i]->probereq_index == index) + return (handle->priv[i]); + } + } + return NULL; +} + +/** + * @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 + * + * @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) +{ + 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 */ + static t_u16 beacon_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + static t_u16 proberesp_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + static t_u16 assocresp_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + static t_u16 probereq_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + moal_private *pmpriv = NULL; + static t_u16 rsn_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + const t_u8 *rsn_ie; + + ENTER(); + + if (mask & MGMT_MASK_BEACON) { + if (!(beacon_ies_data = kmalloc(sizeof(custom_ie), GFP_KERNEL))) { + ret = -ENOMEM; + goto done; + } + if (beacon_ies && beacon_ies_len) { + rsn_ie = + woal_parse_ie_tlv((t_u8 *) beacon_ies, (int) beacon_ies_len, + RSN_IE); + if (rsn_ie) { + beacon_ies_data->ie_index = rsn_index; + beacon_ies_data->mgmt_subtype_mask = + MGMT_MASK_BEACON | MGMT_MASK_PROBE_RESP | + MGMT_MASK_ASSOC_RESP; + beacon_ies_data->ie_length = rsn_ie[1] + 2; + memcpy(beacon_ies_data->ie_buffer, rsn_ie, rsn_ie[1] + 2); + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_custom_ie(priv, beacon_ies_data, &rsn_index, + NULL, &proberesp_index, NULL, + &assocresp_index, NULL, + &probereq_index)) { + ret = -EFAULT; + goto done; + } + } + } else { + /* clear rsn_ie */ + if (rsn_index <= MAX_MGMT_IE_INDEX) { + beacon_ies_data->ie_index = rsn_index; + beacon_ies_data->mgmt_subtype_mask = MLAN_CUSTOM_IE_DELETE_MASK; + beacon_ies_data->ie_length = 0; + rsn_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_custom_ie(priv, beacon_ies_data, &rsn_index, + NULL, &proberesp_index, NULL, + &assocresp_index, NULL, + &probereq_index)) { + ret = -EFAULT; + goto done; + } + } + } + } + + if (mask & MGMT_MASK_PROBE_RESP) { + if (!(proberesp_ies_data = kmalloc(sizeof(custom_ie), GFP_KERNEL))) { + ret = -ENOMEM; + goto done; + } + } + + if (mask & MGMT_MASK_ASSOC_RESP) { + if (!(assocresp_ies_data = kmalloc(sizeof(custom_ie), GFP_KERNEL))) { + ret = -ENOMEM; + goto done; + } + } + if (mask & MGMT_MASK_PROBE_REQ) { + if (!(probereq_ies_data = kmalloc(sizeof(custom_ie), GFP_KERNEL))) { + ret = -ENOMEM; + goto done; + } + } + + if (beacon_ies_data) { + memset(beacon_ies_data, 0x00, sizeof(custom_ie)); + if (beacon_ies && beacon_ies_len) { + /* set the beacon ies */ + beacon_ies_data->ie_index = beacon_index; + beacon_ies_data->mgmt_subtype_mask = MGMT_MASK_BEACON; + beacon_ies_data->mgmt_subtype_mask |= MGMT_MASK_ASSOC_RESP; + beacon_ies_data->ie_length = woal_filter_beacon_ies(beacon_ies, + beacon_ies_len, + beacon_ies_data-> + ie_buffer); + } else { + /* clear the beacon ies */ + if (beacon_index > MAX_MGMT_IE_INDEX) { + PRINTM(MERROR, "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) { + memset(proberesp_ies_data, 0x00, sizeof(custom_ie)); + if (proberesp_ies && proberesp_ies_len) { + /* set the probe response ies */ + // proberesp_ies_data->ie_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + proberesp_ies_data->ie_index = proberesp_index; + proberesp_ies_data->mgmt_subtype_mask = MGMT_MASK_PROBE_RESP; + proberesp_ies_data->ie_length = proberesp_ies_len; + pos = proberesp_ies_data->ie_buffer; + memcpy(pos, proberesp_ies, proberesp_ies_len); + } 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) { + memset(assocresp_ies_data, 0x00, sizeof(custom_ie)); + 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; + assocresp_ies_data->ie_length = assocresp_ies_len; + pos = assocresp_ies_data->ie_buffer; + memcpy(pos, assocresp_ies, assocresp_ies_len); + } 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) { + memset(probereq_ies_data, 0x00, sizeof(custom_ie)); + if ((probereq_index != MLAN_CUSTOM_IE_AUTO_IDX_MASK) && + (priv->probereq_index != probereq_index)) { + pmpriv = woal_get_priv_by_mgmt_index(priv->phandle, probereq_index); + if (pmpriv) { + 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; + pmpriv->probereq_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_custom_ie(pmpriv, NULL, &beacon_index, + NULL, &proberesp_index, + NULL, &assocresp_index, + probereq_ies_data, + &probereq_index)) { + ret = -EFAULT; + goto done; + } + memset(probereq_ies_data, 0x00, sizeof(custom_ie)); + } + } + 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; + probereq_ies_data->ie_length = probereq_ies_len; + pos = probereq_ies_data->ie_buffer; + memcpy(pos, probereq_ies, probereq_ies_len); + } else { + /* clear the probe req ies */ + if (probereq_index > MAX_MGMT_IE_INDEX) { + PRINTM(MERROR, "Invalid probe resp 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)) { + ret = -EFAULT; + goto done; + } + if (probereq_ies_data) + priv->probereq_index = probereq_index; + + done: + if (beacon_ies_data) + kfree(beacon_ies_data); + if (proberesp_ies_data) + kfree(proberesp_ies_data); + if (assocresp_ies_data) + kfree(assocresp_ies_data); + + LEAVE(); + + return ret; +} + +/** + * @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) +{ + int ret = 0; + mlan_uap_bss_param sys_config; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,2,0) || defined(COMPAT_WIRELESS) + int i = 0; +#else + const t_u8 *ssid_ie = NULL; + struct ieee80211_mgmt *head = NULL; + t_u16 capab_info = 0; +#endif + + ENTER(); + + if (params == NULL) { + ret = -EFAULT; + goto done; + } + + if (priv->bss_type != MLAN_BSS_TYPE_UAP +#ifdef WIFI_DIRECT_SUPPORT + && priv->bss_type != MLAN_BSS_TYPE_WIFIDIRECT +#endif + ) { + 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; + + if (priv->bss_type == MLAN_BSS_TYPE_UAP) { + if (params->interval) + sys_config.beacon_period = params->interval; + if (params->dtim_period) + sys_config.dtim_period = params->dtim_period; + } + if (priv->channel) + sys_config.channel = priv->channel; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,2,0) || defined(COMPAT_WIRELESS) + 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); + if (params->hidden_ssid) + sys_config.bcast_ssid_ctl = 0; + else + sys_config.bcast_ssid_ctl = 1; + if (params->auth_type == NL80211_AUTHTYPE_SHARED_KEY) + sys_config.auth_mode = MLAN_AUTH_MODE_SHARED; + else + sys_config.auth_mode = MLAN_AUTH_MODE_OPEN; + + 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; + } + } + 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; + sys_config.wep_cfg.key0.key_index = priv->key_index; + sys_config.wep_cfg.key0.is_default = 1; + sys_config.wep_cfg.key0.length = priv->key_len; + memcpy(sys_config.wep_cfg.key0.key, priv->key_material, + priv->key_len); + } + 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; + } +#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; + sys_config.wep_cfg.key0.key_index = priv->key_index; + sys_config.wep_cfg.key0.is_default = 1; + sys_config.wep_cfg.key0.length = priv->key_len; + memcpy(sys_config.wep_cfg.key0.key, priv->key_material, priv->key_len); + } else { + /** Get cipher and key_mgmt from RSN/WPA IE */ + if (capab_info & WLAN_CAPABILITY_PRIVACY) { + if (MFALSE == + woal_find_wpa_ies(params->tail, params->tail_len, + &sys_config)) { + /* 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 /* COMPAT_WIRELESS */ + /* 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)) + woal_uap_set_11n_status(&sys_config, MLAN_ACT_DISABLE); + else + woal_uap_set_11n_status(&sys_config, MLAN_ACT_ENABLE); + if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv, + MLAN_ACT_SET, + MOAL_IOCTL_WAIT, + &sys_config)) { + ret = -EFAULT; + goto done; + } + done: + LEAVE(); + return ret; +} + +static int +woal_mon_open(struct net_device *ndev) +{ + ENTER(); + LEAVE(); + return 0; +} + +static int +woal_mon_close(struct net_device *ndev) +{ + ENTER(); + LEAVE(); + return 0; +} + +static int +woal_mon_set_mac_address(struct net_device *ndev, void *addr) +{ + ENTER(); + LEAVE(); + return 0; +} + +static void +woal_mon_set_multicast_list(struct net_device *ndev) +{ + ENTER(); + LEAVE(); +} + +static 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); + + if (mon_if == NULL || mon_if->base_ndev == NULL) { + goto fail; + } + + ENTER(); + /* 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); + return NETDEV_TX_OK; +} + +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_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 +}; + +static void +woal_mon_if_setup(struct net_device *dev) +{ + ENTER(); + ether_setup(dev); + dev->netdev_ops = &woal_cfg80211_mon_if_ops; + dev->destructor = free_netdev; + LEAVE(); +} + +/** + * @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 A pointer to net_device -- success, otherwise null + */ +static int +woal_cfg80211_add_mon_if(struct wiphy *wiphy, char *name, + u32 * flags, struct vif_params *params, + struct net_device **new_dev) +{ + int ret; + struct net_device *ndev; + monitor_iface *mon_if; + moal_private *priv; + + ENTER(); + + ASSERT_RTNL(); + + priv = (moal_private *) woal_get_wiphy_priv(wiphy); + + ndev = alloc_netdev_mq(sizeof(*mon_if), name, woal_mon_if_setup, 1); + if (!ndev) { + PRINTM(MFATAL, "Init virtual ethernet device failed\n"); + ret = -EFAULT; + goto fail; + } + + dev_net_set(ndev, wiphy_net(wiphy)); + + ret = dev_alloc_name(ndev, ndev->name); + if (ret < 0) { + PRINTM(MFATAL, "Net device alloc name fail.\n"); + goto fail; + } + + 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)); + + mon_if = netdev_priv(ndev); + ndev->ieee80211_ptr = &mon_if->wdev; + mon_if->wdev.iftype = NL80211_IFTYPE_MONITOR; + mon_if->wdev.wiphy = wiphy; + 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->radiotap_enabled = 1; + mon_if->flag = 1; + + ret = register_netdevice(ndev); + if (ret) { + PRINTM(MFATAL, "register net_device failed, ret=%d\n", ret); + goto fail; + } + + if (new_dev) + *new_dev = ndev; + + fail: + if (ret && ndev) + free_netdev(ndev); + LEAVE(); + return ret; +} + +/** + * @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 + */ +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,37) || defined(COMPAT_WIRELESS) +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 +{ + struct net_device *ndev = NULL; + int ret = 0; + + ENTER(); + PRINTM(MIOCTL, "add virtual intf: %d\n", type); + switch (type) { + case NL80211_IFTYPE_MONITOR: + ret = woal_cfg80211_add_mon_if(wiphy, name, flags, params, &ndev); + break; + default: + PRINTM(MWARN, "Not supported if type: %d\n", type); + ret = -EFAULT; + break; + } + LEAVE(); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,37) || defined(COMPAT_WIRELESS) + if (ret) + return NULL; + else + return ndev; +#else + return ret; +#endif +} + +/** + * @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) +{ + ENTER(); + + PRINTM(MIOCTL, "del virtual intf\n"); + ASSERT_RTNL(); + unregister_netdevice(dev); + + LEAVE(); + return 0; +} + +static int +woal_uap_cfg80211_scan(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_scan_request *request) +{ + ENTER(); + + cfg80211_scan_done(request, MTRUE); + + LEAVE(); + return 0; +} + +static int +woal_uap_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_connect_params *sme) +{ + ENTER(); + LEAVE(); + return 0; +} + +static int +woal_uap_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev, + t_u16 reason_code) +{ + ENTER(); + LEAVE(); + return 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 beacon_parameters structure + * @return 0 -- success, otherwise fail + */ +int +woal_cfg80211_add_beacon(struct wiphy *wiphy, + struct net_device *dev, + struct beacon_parameters *params) +{ + moal_private *priv = (moal_private *) woal_get_wiphy_priv(wiphy); + int ret = 0; + + ENTER(); + + PRINTM(MIOCTL, "add beacon\n"); + if (params != NULL) { + /* bss config */ + if (MLAN_STATUS_SUCCESS != woal_cfg80211_beacon_config(priv, params)) { + ret = -EFAULT; + goto done; + } + + /* set mgmt frame ies */ + if (MLAN_STATUS_SUCCESS != woal_cfg80211_mgmt_frame_ie(priv, +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,2,0) && !defined(COMPAT_WIRELESS) + params->tail, + params->tail_len, + NULL, 0, NULL, 0, + NULL, 0, + MGMT_MASK_BEACON +#else + params->tail, + params->tail_len, + params-> + proberesp_ies, + params-> + proberesp_ies_len, + params-> + assocresp_ies, + params-> + assocresp_ies_len, + NULL, 0, + MGMT_MASK_BEACON + | + MGMT_MASK_PROBE_RESP + | + MGMT_MASK_ASSOC_RESP +#endif + )) { + ret = -EFAULT; + goto done; + } + } + + /* if the bss is stopped, then start it */ + if (priv->bss_started == MFALSE) { + if (MLAN_STATUS_SUCCESS != + woal_uap_bss_ctrl(priv, MOAL_IOCTL_WAIT, UAP_BSS_START)) { + ret = -EFAULT; + goto done; + } + } + + done: + LEAVE(); + return ret; +} + +/** + * @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) +{ + moal_private *priv = (moal_private *) woal_get_wiphy_priv(wiphy); + int ret = 0; + + ENTER(); + + PRINTM(MIOCTL, "set beacon\n"); + if (params != NULL) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,2,0) && !defined(COMPAT_WIRELESS) + if (params->tail && params->tail_len) { + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_mgmt_frame_ie(priv, + params->tail, params->tail_len, + NULL, 0, NULL, 0, NULL, 0, + MGMT_MASK_BEACON)) { + ret = -EFAULT; + goto done; + } + } +#else + if (params->beacon_ies && params->beacon_ies_len) { + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_mgmt_frame_ie(priv, params->tail, + params->tail_len, NULL, 0, NULL, 0, + NULL, 0, MGMT_MASK_BEACON)) { + ret = -EFAULT; + goto done; + } + } + + if (params->proberesp_ies && params->proberesp_ies_len) { + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_mgmt_frame_ie(priv, NULL, 0, + params->proberesp_ies, + params->proberesp_ies_len, NULL, 0, + NULL, 0, MGMT_MASK_PROBE_RESP)) { + ret = -EFAULT; + goto done; + } + } + + if (params->assocresp_ies && params->assocresp_ies_len) { + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_mgmt_frame_ie(priv, NULL, 0, NULL, 0, + params->assocresp_ies, + params->assocresp_ies_len, NULL, 0, + MGMT_MASK_ASSOC_RESP)) { + ret = -EFAULT; + 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_wiphy_priv(wiphy); + int ret = 0; + + ENTER(); + + PRINTM(MIOCTL, "del beacon\n"); + /* 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; + } + } + + /* clear mgmt frame ies */ + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_mgmt_frame_ie(priv, NULL, 0, NULL, 0, NULL, 0, NULL, 0, + MGMT_MASK_BEACON | MGMT_MASK_PROBE_RESP | + MGMT_MASK_ASSOC_RESP)) { + ret = -EFAULT; + goto done; + } + + priv->cipher = 0; + priv->key_len = 0; + done: + LEAVE(); + return ret; +} + +/** + * @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, + u8 * mac, struct station_info *stainfo) +{ + moal_private *priv = (moal_private *) woal_get_wiphy_priv(wiphy); + int ret = -EFAULT; + int i = 0; + mlan_ds_get_info *info = NULL; + mlan_ioctl_req *ioctl_req = NULL; + + 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; + + if (MLAN_STATUS_SUCCESS != + woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT)) { + 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: %02x:%02x:%02x:%02x:%02x:%02x RSSI=%d\n", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], + (int) info->param.sta_list.info[i].rssi); + stainfo->filled = STATION_INFO_INACTIVE_TIME | STATION_INFO_SIGNAL; + stainfo->inactive_time = 0; + stainfo->signal = info->param.sta_list.info[i].rssi; + ret = 0; + break; + } + } + done: + if (ioctl_req) + kfree(ioctl_req); + 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 ap_cfg A pointer to mlan_uap_bss_param + * + * @return N/A + */ +static void +woal_cfg80211_setup_uap_ht_cap(struct ieee80211_sta_ht_cap *ht_info, + mlan_uap_bss_param * ap_cfg) +{ + ENTER(); + + ht_info->ht_supported = true; + ht_info->ampdu_factor = ap_cfg->ampdu_param & (MBIT(1) | MBIT(0)); + ht_info->ampdu_density = + ap_cfg->ampdu_param & (MBIT(4) | MBIT(3) | MBIT(2)); + + memset(&ht_info->mcs, 0, sizeof(ht_info->mcs)); + memcpy(ht_info->mcs.rx_mask, ap_cfg->supported_mcs_set, + sizeof(ht_info->mcs.rx_mask)); + + ht_info->cap = 0; + if (ap_cfg->ht_cap_info & MBIT(1)) /* 20/40 Mhz enable */ + ht_info->cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; + if (ap_cfg->ht_cap_info & MBIT(4)) /* Green field supported */ + ht_info->cap |= IEEE80211_HT_CAP_GRN_FLD; + if (ap_cfg->ht_cap_info & MBIT(5)) /* Short GI @ 20Mhz supported */ + ht_info->cap |= IEEE80211_HT_CAP_SGI_20; + if (ap_cfg->ht_cap_info & MBIT(6)) /* Short GI @ 40Mhz supported */ + ht_info->cap |= IEEE80211_HT_CAP_SGI_40; + if (ap_cfg->ht_cap_info & MBIT(12)) /* DSS/CCK mode in 40MHz enable */ + ht_info->cap |= IEEE80211_HT_CAP_DSSSCCK40; + ht_info->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; + + LEAVE(); +} + +/** + * @brief Initialize the uAP 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_uap_init_wiphy(moal_private * priv, t_u8 wait_option) +{ + struct wiphy *wiphy = priv->wdev->wiphy; + mlan_uap_bss_param ap_cfg; + + ENTER(); + + if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv, + MLAN_ACT_GET, + wait_option, &ap_cfg)) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + /* Initialize parameters for 2GHz and 5GHz bands */ + woal_cfg80211_setup_uap_ht_cap(&wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap, + &ap_cfg); + if (wiphy->bands[IEEE80211_BAND_5GHZ]) + woal_cfg80211_setup_uap_ht_cap(&wiphy->bands[IEEE80211_BAND_5GHZ]-> + ht_cap, &ap_cfg); + + /* Set retry limit count to wiphy */ + wiphy->retry_long = (t_u8) ap_cfg.retry_limit; + wiphy->retry_short = (t_u8) ap_cfg.retry_limit; + wiphy->max_scan_ie_len = MAX_IE_SIZE; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37) || defined(COMPAT_WIRELESS) + wiphy->mgmt_stypes = ieee80211_uap_mgmt_stypes; +#endif + /* Set RTS threshold to wiphy */ + wiphy->rts_threshold = (t_u32) ap_cfg.rts_threshold; + + /* Set fragment threshold to wiphy */ + wiphy->frag_threshold = (t_u32) ap_cfg.frag_threshold; + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @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); + void *wdev_priv = NULL; + struct wireless_dev *wdev = NULL; + mlan_fw_info fw_info; + + ENTER(); + + /* Allocate wireless device */ + wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL); + if (!wdev) { + PRINTM(MERROR, "Could not allocate wireless device\n"); + ret = MLAN_STATUS_FAILURE; + goto err_wdev; + } + + /* Allocate wiphy */ + wdev->wiphy = wiphy_new(&woal_cfg80211_uap_ops, sizeof(moal_private *)); + if (!wdev->wiphy) { + PRINTM(MERROR, "Could not allocate wiphy device\n"); + ret = MLAN_STATUS_FAILURE; + goto err_wdev; + } + if (bss_type == MLAN_BSS_TYPE_UAP) { + dev_set_name(&wdev->wiphy->dev, dev->name); + wdev->iftype = NL80211_IFTYPE_AP; + wdev->wiphy->interface_modes = + MBIT(NL80211_IFTYPE_AP) | MBIT(NL80211_IFTYPE_STATION) | + MBIT(NL80211_IFTYPE_MONITOR); + wdev->wiphy->max_scan_ssids = 10; + } + + /* Make this wiphy known to this driver only */ + wdev->wiphy->privid = mrvl_wiphy_privid; + + /* Supported bands */ + wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = &cfg80211_band_2ghz; + if (MLAN_STATUS_SUCCESS == + woal_request_get_fw_info(priv, MOAL_CMD_WAIT, &fw_info)) { + if (fw_info.fw_bands & BAND_A) + wdev->wiphy->bands[IEEE80211_BAND_5GHZ] = &cfg80211_band_5ghz; + } + + /* Initialize cipher suits */ + wdev->wiphy->cipher_suites = cfg80211_cipher_suites; + wdev->wiphy->n_cipher_suites = ARRAY_SIZE(cfg80211_cipher_suites); + + wdev->wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; + + /* We are using custom domains */ + wdev->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY; + + wdev->wiphy->reg_notifier = NULL; // TODO: woal_cfg80211_reg_notifier; + + /* Set moal_private pointer in wiphy_priv */ + wdev_priv = wiphy_priv(wdev->wiphy); + + *(unsigned long *) wdev_priv = (unsigned long) priv; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39) || defined(COMPAT_WIRELESS) + set_wiphy_dev(wdev->wiphy, (struct device *) priv->phandle->hotplug_device); +#endif + + if (wiphy_register(wdev->wiphy) < 0) { + PRINTM(MERROR, "Wiphy device registration failed!\n"); + ret = MLAN_STATUS_FAILURE; + goto err_wdev; + } + + dev_net_set(dev, wiphy_net(wdev->wiphy)); + dev->ieee80211_ptr = wdev; + SET_NETDEV_DEV(dev, wiphy_dev(wdev->wiphy)); + priv->wdev = wdev; + + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "Wiphy device registration failed!\n"); + } else { + PRINTM(MINFO, "Successfully registered wiphy device\n"); + LEAVE(); + return ret; + } + + wiphy_unregister(wdev->wiphy); + err_wdev: + dev->ieee80211_ptr = NULL; + if (wdev && wdev->wiphy) + wiphy_free(wdev->wiphy); + kfree(wdev); + LEAVE(); + return ret; +} diff --git a/drivers/net/wireless/sd8797/mlinux/moal_uap_cfg80211.h b/drivers/net/wireless/sd8797/mlinux/moal_uap_cfg80211.h new file mode 100644 index 000000000000..74c6f1a4e59b --- /dev/null +++ b/drivers/net/wireless/sd8797/mlinux/moal_uap_cfg80211.h @@ -0,0 +1,30 @@ +/** @file moal_uap_cfg80211.h + * + * @brief This file contains the uAP CFG80211 specific defines. + * + * Copyright (C) 2011, 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); +mlan_status woal_cfg80211_uap_init_wiphy(moal_private * priv, t_u8 wait_option); + +#endif /* _MOAL_UAP_CFG80211_H_ */ diff --git a/drivers/net/wireless/sd8797/mlinux/moal_uap_priv.c b/drivers/net/wireless/sd8797/mlinux/moal_uap_priv.c new file mode 100644 index 000000000000..dd06e03ecb1e --- /dev/null +++ b/drivers/net/wireless/sd8797/mlinux/moal_uap_priv.c @@ -0,0 +1,175 @@ +/** @file moal_uap_priv.c + * + * @brief This file contains standard ioctl functions + * + * Copyright (C) 2010-2011, 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/sd8797/mlinux/moal_uap_priv.h b/drivers/net/wireless/sd8797/mlinux/moal_uap_priv.h new file mode 100644 index 000000000000..fcd811efb168 --- /dev/null +++ b/drivers/net/wireless/sd8797/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-2011, 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 +************************************************************************/ + +#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/sd8797/mlinux/moal_uap_wext.c b/drivers/net/wireless/sd8797/mlinux/moal_uap_wext.c new file mode 100644 index 000000000000..0426a28339cb --- /dev/null +++ b/drivers/net/wireless/sd8797/mlinux/moal_uap_wext.c @@ -0,0 +1,1766 @@ +/** @file moal_uap_wext.c + * + * @brief This file contains wireless extension standard ioctl functions + * + * Copyright (C) 2010-2011, 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}, + {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 < sizeof(chan_to_freq) / sizeof(chan_to_freq_t); 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 : %02x:%02x:%02x:%02x:%02x:%02x\n", + (t_u8) awrq->sa_data[0], (t_u8) awrq->sa_data[1], + (t_u8) awrq->sa_data[2], (t_u8) awrq->sa_data[3], + (t_u8) awrq->sa_data[4], (t_u8) awrq->sa_data[5]); + + /* + * 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: + if (sys_cfg) + kfree(sys_cfg); + if (ap_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; + t_u8 band = 0; + int ret = 0; + + ENTER(); + + 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"); + LEAVE(); + return -EFAULT; + } + band = ap_cfg.band_cfg & BAND_CONFIG_5GHZ; + fwrq->i = (long) ap_cfg.channel; + fwrq->m = (long) (channel_to_frequency(ap_cfg.channel, band)) * 100000; + fwrq->e = 1; + + 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; + } + + sys_cfg->protocol = PROTOCOL_STATIC_WEP; + 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: + if (sys_cfg) + kfree(sys_cfg); + if (ap_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; + int ret = 0; + + 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_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: + 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; + IEEEtypes_Header_t *tlv = NULL; + int tlv_hdr_len = sizeof(IEEEtypes_Header_t), tlv_buf_left = 0; + int ret = 0; + + ENTER(); + + /* 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: + 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; + wep_key *pwep_key = NULL; + int ret = 0; + + ENTER(); + + key_index = (dwrq->flags & IW_ENCODE_INDEX) - 1; + if (key_index < 0 || key_index > 3) { + ret = -EINVAL; + goto done; + } + if (ext->key_len > (dwrq->length - sizeof(struct iw_encode_ext))) { + ret = -EINVAL; + 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; + } + 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 %02x:%02x:%02x:%02x:%02x:%02x\n", + key_index, ext->key_len, sec->param.encrypt_key.key_flags, + sec->param.encrypt_key.mac_addr[0], + sec->param.encrypt_key.mac_addr[1], + sec->param.encrypt_key.mac_addr[2], + sec->param.encrypt_key.mac_addr[3], + sec->param.encrypt_key.mac_addr[4], + sec->param.encrypt_key.mac_addr[5]); + DBG_HEXDUMP(MCMD_D, "uap wpa key", pkey_material, ext->key_len); + + if (MLAN_STATUS_SUCCESS != + woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + 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: + if (req) + 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; + + 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: %02x:%02x:%02x:%02x:%02x:%02x, " + "reason=%d\n", sta_addr[0], sta_addr[1], sta_addr[2], + sta_addr[3], sta_addr[4], sta_addr[5], 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; + if (MLAN_STATUS_SUCCESS != + woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + sta_list = + (mlan_ds_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)); + if (req) + kfree(req); + } + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + LEAVE(); + return -ENOMEM; + } + 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; + + if (MLAN_STATUS_SUCCESS != + woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + 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; + + if (MLAN_STATUS_SUCCESS != + woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + } + } + + done: + if (req) + kfree(req); + if (sta_list) + 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; + + ENTER(); + + /* 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); + else if (vwrq->value & IW_AUTH_CIPHER_WEP40); + else if (vwrq->value & IW_AUTH_CIPHER_WEP104); + 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); + else if (vwrq->value & IW_AUTH_CIPHER_WEP40); + else if (vwrq->value & IW_AUTH_CIPHER_WEP104); + 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: + 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: + 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; + + ENTER(); + + 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"); + 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: + LEAVE(); + return -EOPNOTSUPP; + } + + 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; + struct iw_range *range = (struct iw_range *) extra; + t_u8 band = 0; + int i; + + ENTER(); + + 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"); + 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].band_config_type & BAND_CONFIG_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 + + 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; + int ret = 0; + + ENTER(); + + /* Check the size of the string */ + if (dwrq->length > IW_ESSID_MAX_SIZE + 1) { + ret = -E2BIG; + 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: + 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; + + ENTER(); + + 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"); + 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; + + 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 + iw_handler_set_spy, /* SIOCSIWSPY */ + iw_handler_get_spy, /* SIOCGIWSPY */ + iw_handler_set_thrspy, /* SIOCSIWTHRSPY */ + iw_handler_get_thrspy, /* SIOCGIWTHRSPY */ +#else /* WIRELESS_EXT > 15 */ +#ifdef WIRELESS_SPY + (iw_handler) NULL, /* SIOCSIWSPY */ + (iw_handler) NULL, /* SIOCGIWSPY */ +#else /* WIRELESS_SPY */ + (iw_handler) NULL, /* SIOCSIWSPY */ + (iw_handler) NULL, /* SIOCGIWSPY */ +#endif /* WIRELESS_SPY */ + (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:sizeof(woal_handler) / sizeof(iw_handler), + num_private:sizeof(woal_private_handler) / sizeof(iw_handler), + num_private_args:sizeof(woal_uap_priv_args) / + sizeof(struct iw_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_NO_WAIT; + + ENTER(); + + /* + * 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 (write_can_lock(&dev_base_lock) + && (!in_atomic() || current->exit_state)) + wait_option = MOAL_WSTATS_WAIT; + + 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/sd8797/mlinux/moal_wext.c b/drivers/net/wireless/sd8797/mlinux/moal_wext.c new file mode 100644 index 000000000000..9b5ce1333ee8 --- /dev/null +++ b/drivers/net/wireless/sd8797/mlinux/moal_wext.c @@ -0,0 +1,3048 @@ +/** @file moal_wext.c + * + * @brief This file contains wireless extension standard ioctl functions + * + * Copyright (C) 2008-2011, 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 +********************************************************/ + +/******************************************************** + Local Functions +********************************************************/ +/** + * @brief This function validates a SSID as being able to be printed + * + * @param pssid SSID structure to validate + * + * @return MTRUE or MFALSE + */ +static 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; +} + +/** + * @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; + + 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; + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + if (MLAN_STATUS_SUCCESS != + woal_change_adhoc_chan(priv, bss->param.bss_chan.channel)) + ret = -EFAULT; + + done: + if (req) + 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; + + 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; + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + fwrq->m = (long) bss->param.bss_chan.freq * 100000; + fwrq->i = (long) bss->param.bss_chan.channel; + fwrq->e = 1; + fwrq->flags = IW_FREQ_FIXED; + done: + if (req) + 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; + + 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; + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + done: + if (req) + 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 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; + 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: %02x:%02x:%02x:%02x:%02x:%02x\n", + (t_u8) awrq->sa_data[0], (t_u8) awrq->sa_data[1], + (t_u8) awrq->sa_data[2], (t_u8) awrq->sa_data[3], + (t_u8) awrq->sa_data[4], (t_u8) awrq->sa_data[5]); + + 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); + 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; + /* disconnect before try to assoicate to the new AP */ + woal_disconnect(priv, MOAL_IOCTL_WAIT, NULL); + } + 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(); + + disabled = vwrq->disabled; + + if (MLAN_STATUS_SUCCESS != woal_set_get_power_mgmt(priv, + MLAN_ACT_SET, &disabled, + vwrq->flags)) { + 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)) { + 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; + + 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; + } + } + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + 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 (req) + 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); + + 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; + + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + 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 (req) + 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)) { + 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; + + 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; + + if (MLAN_STATUS_SUCCESS != + woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + } + + if (MLAN_STATUS_SUCCESS != + woal_set_get_gen_ie(priv, MLAN_ACT_SET, (t_u8 *) extra, &ie_len)) { + ret = -EFAULT; + goto done; + } + + done: + if (req) + 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; + + ENTER(); + key_index = (dwrq->flags & IW_ENCODE_INDEX) - 1; + if (key_index < 0 || key_index > 3) { + ret = -EINVAL; + goto done; + } + if (ext->key_len > (dwrq->length - sizeof(struct iw_encode_ext))) { + 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 %02x:%02x:%02x:%02x:%02x:%02x\n", + key_index, dwrq->flags, sec->param.encrypt_key.mac_addr[0], + sec->param.encrypt_key.mac_addr[1], + sec->param.encrypt_key.mac_addr[2], + sec->param.encrypt_key.mac_addr[3], + sec->param.encrypt_key.mac_addr[4], + sec->param.encrypt_key.mac_addr[5]); + } 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 %02x:%02x:%02x:%02x:%02x:%02x\n", + key_index, ext->key_len, sec->param.encrypt_key.key_flags, + sec->param.encrypt_key.mac_addr[0], + sec->param.encrypt_key.mac_addr[1], + sec->param.encrypt_key.mac_addr[2], + sec->param.encrypt_key.mac_addr[3], + sec->param.encrypt_key.mac_addr[4], + sec->param.encrypt_key.mac_addr[5]); + DBG_HEXDUMP(MCMD_D, "wpa key", pkey_material, ext->key_len); +#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); + } + } + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) + ret = -EFAULT; + done: + if (req) + 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)) + 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: + 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; + + ENTER(); + + if (!(pchan_list = kmalloc(sizeof(mlan_chan_list), GFP_KERNEL))) { + 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; + + memset(pchan_list, 0, sizeof(mlan_chan_list)); + + 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; + + 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; + + 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); + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + + done: + if (req) + 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; + t_u8 country_code[COUNTRY_CODE_LEN]; + int len = 0; + ENTER(); + if (!(buf = kmalloc(dwrq->length + 1, GFP_KERNEL))) { + ret = -ENOMEM; + goto done; + } + memset(buf, 0, dwrq->length + 1); + if (copy_from_user(buf, dwrq->pointer, dwrq->length)) { + ret = -EFAULT; + goto done; + } + PRINTM(MIOCTL, "SIOCSIWPRIV requst = %s\n", buf); + 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)) { + 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, "%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) { + pdata = buf + strlen("POWERMODE") + 1; + if (MLAN_STATUS_SUCCESS != woal_set_powermode(priv, pdata)) { + ret = -EFAULT; + goto done; + } + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "COUNTRY", strlen("COUNTRY")) == 0) { + memset(country_code, 0, sizeof(country_code)); + memcpy(country_code, buf + strlen("COUNTRY") + 1, + strlen(buf) - strlen("COUNTRY") - 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, WEXT_CSCAN_HEADER_SIZE) == 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, "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 (MLAN_STATUS_SUCCESS != woal_stop_bg_scan(priv)) { + 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 + if ((ret = woal_set_rxfilter(priv, MTRUE))) { + goto done; + } +#endif + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "RXFILTER-STOP", strlen("RXFILTER-STOP")) == 0) { +#ifdef MEF_CFG_RX_FILTER + if ((ret = woal_set_rxfilter(priv, MFALSE))) { + goto done; + } +#endif + len = sprintf(buf, "OK\n") + 1; + } 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; + if (MLAN_STATUS_SUCCESS != woal_set_qos_cfg(priv, pdata)) { + ret = -EFAULT; + goto done; + } + 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 { + 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: + if (buf) + 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) { + + 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; + } + + /* 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 (dwrq->flags != 0xFFFF) { + if (MLAN_STATUS_SUCCESS != woal_find_essid(priv, &ssid_bssid)) { + /* Do specific SSID scanning */ + if (MLAN_STATUS_SUCCESS != + woal_request_scan(priv, MOAL_IOCTL_WAIT, &req_ssid)) { + ret = -EFAULT; + goto setessid_ret; + } + } + } + + } + + /* disconnect before try to associate */ + woal_disconnect(priv, MOAL_IOCTL_WAIT, NULL); + mode = woal_get_mode(priv, MOAL_IOCTL_WAIT); + + if (mode != IW_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); + + /* 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; + + ENTER(); + + if (priv->phandle->scan_pending_on_block == MTRUE) { + LEAVE(); + return -EAGAIN; + } + + if (!(buf = kmalloc((buf_size), GFP_KERNEL))) { + 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 < scan_resp.num_in_scan_table; 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(MERROR, "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, + (t_s8 *) 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 * 100000; + iwe.u.freq.e = 1; + 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)); + memset(buf, 0, buf_size); + 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: + if (buf) + 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 + iw_handler_set_spy, /* SIOCSIWSPY */ + iw_handler_get_spy, /* SIOCGIWSPY */ + iw_handler_set_thrspy, /* SIOCSIWTHRSPY */ + iw_handler_get_thrspy, /* SIOCGIWTHRSPY */ +#else /* WIRELESS_EXT > 15 */ +#ifdef WIRELESS_SPY + (iw_handler) NULL, /* SIOCSIWSPY */ + (iw_handler) NULL, /* SIOCGIWSPY */ +#else /* WIRELESS_SPY */ + (iw_handler) NULL, /* SIOCSIWSPY */ + (iw_handler) NULL, /* SIOCGIWSPY */ +#endif /* WIRELESS_SPY */ + (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, t_s8 * 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 +/** + * @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; + 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; + if (MLAN_STATUS_SUCCESS != woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + done: + if (req) + kfree(req); + LEAVE(); + return ret; +} + +/** wlan_handler_def */ +struct iw_handler_def woal_handler_def = { + num_standard:sizeof(woal_handler) / sizeof(iw_handler), + num_private:sizeof(woal_private_handler) / sizeof(iw_handler), + num_private_args:sizeof(woal_private_args) / sizeof(struct iw_priv_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_NO_WAIT; + + ENTER(); + + /* + * 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 (write_can_lock(&dev_base_lock) + && (!in_atomic() || current->exit_state)) + wait_option = MOAL_WSTATS_WAIT; + + 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) { + 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/sd8797/mlinux/moal_wext.h b/drivers/net/wireless/sd8797/mlinux/moal_wext.h new file mode 100644 index 000000000000..91f918e1b130 --- /dev/null +++ b/drivers/net/wireless/sd8797/mlinux/moal_wext.h @@ -0,0 +1,116 @@ +/** @file moal_wext.h + * + * @brief This file contains definition for wireless extension IOCTL call. + * + * Copyright (C) 2008-2011, 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_ + +/** 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" +#define CUS_EVT_BG_SCAN "EVENT=BG_SCAN_REPORT" +/** 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" + +/** 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" + +/** 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 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 indiciation message sent to the application layer for WMM changes */ +#define WMM_CONFIG_CHANGE_INDICATION "WMM_CONFIG_CHANGE.indication" + +#ifdef UAP_SUPPORT +#ifdef UAP_WEXT +/** 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 +#endif +/** NF value for default scan */ +#define MRVDRV_NF_DEFAULT_SCAN_VALUE (-96) + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) +/** 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_ */ -- cgit v1.2.3